Sync CAM with FreeBSD using lockmgr locks instead of mutexes.
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 18 May 2008 20:30:23 +0000 (20:30 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 18 May 2008 20:30:23 +0000 (20:30 +0000)
Note:  This is mostly a code sync with FreeBSD which improves stability
in addition to the items listed below.  This provides a framework for
releasing the mplock, but for now it's still there.

Add an xpt_print function to reduce most of the xpt_print_path/printf
pairs. Convert the core code to use it.

Initial cut at Basic Domain Validation.

Make cam_xpt's pronouncements match camcontrol (Tagged -> Command) Queueing.

Pay attention to return value from xpt_bus_register in xpt_init.

Add an xpt_rescan function and a thread that will field rescan requests.
The purpose of this is to allow a SIM (or other entities) to request a bus
rescan and have it then fielded in a different (process) context from the
caller.

Check the return value from cam_periph_acquire.

Drop the periph/sim lock when calling disk_destroy().

Drop the topology lock before calling the periph oninvalidate and dtor
vectors.

For the XPT_SASYNC_CB operation, only decouple the broadcast to the bus
and device lists instead of decoupling the whole operation.  This avoids
problems with SIMs going away.

Split the camisr into per-SIM done queues.  This optimizes the locking a
little bit and allows for direct dispatch of the doneq from certain
contexts that would otherwise face recursive locking problems.

Zero the CCBs when mallocing them.

Only schedule the xpt_finishconfig_task once.

Eliminate the use of M_TEMP.

Add a helper function for registering async callbacks.

Release the bus reference that is acquired when doing a CAMIOCOMMAND ioctl.

Zero scsi_readcapacity allocations so we can really tell if there has
been data returned.

Remove duplicate includes and fix typos.

Add a bunch of definitions and structures to support newer drivers.

When probing a newly found device, don't automatically assume that the
device supports retrieving a serial number.  Instead, first query the
list of VPD pages it does support, and only query the serial number if
it's supported, else silently move on.  This eliminates a lot of noise
during verbose booting, and will likely eliminate the need for most
NOSERIAL quirks.

Reduce diffs from FreeBSD.

Obtained-from: FreeBSD

53 files changed:
sys/bus/cam/cam_periph.c
sys/bus/cam/cam_periph.h
sys/bus/cam/cam_queue.c
sys/bus/cam/cam_sim.c
sys/bus/cam/cam_sim.h
sys/bus/cam/cam_xpt.c
sys/bus/cam/cam_xpt.h
sys/bus/cam/scsi/scsi_all.c
sys/bus/cam/scsi/scsi_all.h
sys/bus/cam/scsi/scsi_cd.c
sys/bus/cam/scsi/scsi_ch.c
sys/bus/cam/scsi/scsi_da.c
sys/bus/cam/scsi/scsi_low.c
sys/bus/cam/scsi/scsi_pass.c
sys/bus/cam/scsi/scsi_pt.c
sys/bus/cam/scsi/scsi_sa.c
sys/bus/cam/scsi/scsi_ses.c
sys/bus/cam/scsi/scsi_targ_bh.c
sys/bus/cam/scsi/scsi_target.c
sys/dev/disk/advansys/advansys.c
sys/dev/disk/advansys/adwcam.c
sys/dev/disk/aha/aha.c
sys/dev/disk/ahb/ahb.c
sys/dev/disk/aic/aic.c
sys/dev/disk/aic7xxx/ahc_eisa.c
sys/dev/disk/aic7xxx/ahc_isa.c
sys/dev/disk/aic7xxx/ahc_pci.c
sys/dev/disk/aic7xxx/ahd_pci.c
sys/dev/disk/aic7xxx/aic79xx.c
sys/dev/disk/aic7xxx/aic79xx_osm.c
sys/dev/disk/aic7xxx/aic7xxx.c
sys/dev/disk/aic7xxx/aic7xxx_osm.c
sys/dev/disk/aic7xxx/aic_osm_lib.h
sys/dev/disk/amd/amd.c
sys/dev/disk/ata/atapi-cam.c
sys/dev/disk/buslogic/bt.c
sys/dev/disk/isp/isp_freebsd.c
sys/dev/disk/mpt/mpt_freebsd.c
sys/dev/disk/nata/atapi-cam.c
sys/dev/disk/ncr/ncr.c
sys/dev/disk/sbp/sbp.c
sys/dev/disk/sym/sym_hipd.c
sys/dev/disk/trm/trm.c
sys/dev/disk/vpo/vpo.c
sys/dev/raid/aac/aac_cam.c
sys/dev/raid/amr/amr_cam.c
sys/dev/raid/asr/asr.c
sys/dev/raid/ciss/ciss.c
sys/dev/raid/dpt/dpt_scsi.c
sys/dev/raid/iir/iir.c
sys/dev/raid/mly/mly.c
sys/dev/raid/twa/twa_cam.c
sys/dev/usbmisc/umass/umass.c

index 21143d9..57804fd 100644 (file)
@@ -26,8 +26,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/cam/cam_periph.c,v 1.24.2.3 2003/01/25 19:04:40 dillon Exp $
- * $DragonFly: src/sys/bus/cam/cam_periph.c,v 1.39 2007/12/02 04:54:40 pavalos Exp $
+ * $FreeBSD: src/sys/cam/cam_periph.c,v 1.70 2008/02/12 11:07:33 raj Exp $
+ * $DragonFly: src/sys/bus/cam/cam_periph.c,v 1.40 2008/05/18 20:30:19 pavalos Exp $
  */
 
 #include <sys/param.h>
@@ -35,6 +35,7 @@
 #include <sys/types.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
 #include <sys/buf.h>
 #include <sys/proc.h>
 #include <sys/devicestat.h>
@@ -49,6 +50,7 @@
 #include "cam_xpt_periph.h"
 #include "cam_periph.h"
 #include "cam_debug.h"
+#include "cam_sim.h"
 
 #include <bus/cam/scsi/scsi_all.h>
 #include <bus/cam/scsi/scsi_message.h>
@@ -99,7 +101,8 @@ periphdriver_register(void *data)
        int ndrivers;
 
        ndrivers = nperiph_drivers + 2;
-       newdrivers = kmalloc(sizeof(*newdrivers) * ndrivers, M_TEMP, M_WAITOK);
+       newdrivers = kmalloc(sizeof(*newdrivers) * ndrivers, M_CAMPERIPH,
+                            M_WAITOK);
        if (periph_drivers)
                bcopy(periph_drivers, newdrivers,
                      sizeof(*newdrivers) * nperiph_drivers);
@@ -108,7 +111,7 @@ periphdriver_register(void *data)
        old = periph_drivers;
        periph_drivers = newdrivers;
        if (old)
-               kfree(old, M_TEMP);
+               kfree(old, M_CAMPERIPH);
        nperiph_drivers++;
 }
 
@@ -120,6 +123,7 @@ cam_periph_alloc(periph_ctor_t *periph_ctor,
                 ac_callback_t *ac_callback, ac_code code, void *arg)
 {
        struct          periph_driver **p_drv;
+       struct          cam_sim *sim;
        struct          cam_periph *periph;
        struct          cam_periph *cur_periph;
        path_id_t       path_id;
@@ -156,11 +160,14 @@ cam_periph_alloc(periph_ctor_t *periph_ctor,
        
        init_level++;
 
+       xpt_lock_buses();
        for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) {
                if (strcmp((*p_drv)->driver_name, name) == 0)
                        break;
        }
-       
+       xpt_unlock_buses();
+
+       sim = xpt_path_sim(path);
        path_id = xpt_path_path_id(path);
        target_id = xpt_path_target_id(path);
        lun_id = xpt_path_lun_id(path);
@@ -173,6 +180,7 @@ cam_periph_alloc(periph_ctor_t *periph_ctor,
        periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id);
        periph->immediate_priority = CAM_PRIORITY_NONE;
        periph->refcount = 0;
+       periph->sim = sim;
        SLIST_INIT(&periph->ccb_list);
        status = xpt_create_path(&path, periph, path_id, target_id, lun_id);
        if (status != CAM_REQ_CMP)
@@ -186,7 +194,6 @@ cam_periph_alloc(periph_ctor_t *periph_ctor,
        if (status != CAM_REQ_CMP)
                goto failure;
 
-       crit_enter();
        cur_periph = TAILQ_FIRST(&(*p_drv)->units);
        while (cur_periph != NULL
            && cur_periph->unit_number < periph->unit_number)
@@ -199,8 +206,6 @@ cam_periph_alloc(periph_ctor_t *periph_ctor,
                (*p_drv)->generation++;
        }
 
-       crit_exit();
-
        init_level++;
 
        status = periph_ctor(periph, arg);
@@ -214,14 +219,15 @@ failure:
                /* Initialized successfully */
                break;
        case 3:
-               crit_enter();
                TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links);
-               crit_exit();
                xpt_remove_periph(periph);
+               /* FALLTHROUGH */
        case 2:
                xpt_free_path(periph->path);
+               /* FALLTHROUGH */
        case 1:
                kfree(periph, M_CAMPERIPH);
+               /* FALLTHROUGH */
        case 0:
                /* No cleanup to perform. */
                break;
@@ -242,21 +248,23 @@ cam_periph_find(struct cam_path *path, char *name)
        struct periph_driver **p_drv;
        struct cam_periph *periph;
 
+       xpt_lock_buses();
        for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) {
                if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0))
                        continue;
 
-               crit_enter();
                TAILQ_FOREACH(periph, &(*p_drv)->units, unit_links) {
                        if (xpt_path_comp(periph->path, path) == 0) {
-                               crit_exit();
+                               xpt_unlock_buses();
                                return(periph);
                        }
                }
-               crit_exit();
-               if (name != NULL)
+               if (name != NULL) {
+                       xpt_unlock_buses();
                        return(NULL);
+               }
        }
+       xpt_unlock_buses();
        return(NULL);
 }
 
@@ -266,9 +274,9 @@ cam_periph_acquire(struct cam_periph *periph)
        if (periph == NULL)
                return(CAM_REQ_CMP_ERR);
 
-       crit_enter();
+       xpt_lock_buses();
        periph->refcount++;
-       crit_exit();
+       xpt_unlock_buses();
 
        return(CAM_REQ_CMP);
 }
@@ -276,15 +284,62 @@ cam_periph_acquire(struct cam_periph *periph)
 void
 cam_periph_release(struct cam_periph *periph)
 {
+
        if (periph == NULL)
                return;
 
-       crit_enter();
+       xpt_lock_buses();
        if ((--periph->refcount == 0)
         && (periph->flags & CAM_PERIPH_INVALID)) {
                camperiphfree(periph);
        }
-       crit_exit();
+       xpt_unlock_buses();
+
+}
+
+int
+cam_periph_hold(struct cam_periph *periph, int flags)
+{
+       int error;
+
+       sim_lock_assert_owned(periph->sim->lock);
+
+       /*
+        * Increment the reference count on the peripheral
+        * while we wait for our lock attempt to succeed
+        * to ensure the peripheral doesn't disappear out
+        * from user us while we sleep.
+        */
+
+       if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+               return (ENXIO);
+
+       while ((periph->flags & CAM_PERIPH_LOCKED) != 0) {
+               periph->flags |= CAM_PERIPH_LOCK_WANTED;
+               if ((error = sim_lock_sleep(periph, flags, "caplck", 0,
+                                           periph->sim->lock)) != 0) {
+                       cam_periph_release(periph);
+                       return (error);
+               }
+       }
+
+       periph->flags |= CAM_PERIPH_LOCKED;
+       return (0);
+}
+
+void
+cam_periph_unhold(struct cam_periph *periph)
+{
+
+       sim_lock_assert_owned(periph->sim->lock);
+
+       periph->flags &= ~CAM_PERIPH_LOCKED;
+       if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) {
+               periph->flags &= ~CAM_PERIPH_LOCK_WANTED;
+               wakeup(periph);
+       }
+
+       cam_periph_release(periph);
 }
 
 /*
@@ -304,7 +359,6 @@ camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired,
        int     i, val, dunit;
        const char *dname;
 
-       crit_enter();
        periph_name = p_drv->driver_name;
        for (;;newunit++) {
 
@@ -315,12 +369,12 @@ camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired,
 
                if (periph != NULL && periph->unit_number == newunit) {
                        if (wired != 0) {
-                               xpt_print_path(periph->path);
-                               kprintf("Duplicate Wired Device entry!\n");
-                               xpt_print_path(periph->path);
-                               kprintf("Second device (%s device at scbus%d "
-                                      "target %d lun %d) will not be wired\n",
-                                      periph_name, pathid, target, lun);
+                               xpt_print(periph->path, "Duplicate Wired "
+                                   "Device entry!\n");
+                               xpt_print(periph->path, "Second device (%s "
+                                   "device at scbus%d target %d lun %d) will "
+                                   "not be wired\n", periph_name, pathid,
+                                   target, lun);
                                wired = 0;
                        }
                        continue;
@@ -348,7 +402,6 @@ camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired,
                if (i == -1)
                        break;
        }
-       crit_exit();
        return (newunit);
 }
 
@@ -407,10 +460,8 @@ cam_periph_invalidate(struct cam_periph *periph)
 {
        /*
         * We only call this routine the first time a peripheral is
-        * invalidated.  The oninvalidate() routine is always called in
-        * a critical section.
+        * invalidated.
         */
-       crit_enter();
        if (((periph->flags & CAM_PERIPH_INVALID) == 0)
         && (periph->periph_oninval != NULL))
                periph->periph_oninval(periph);
@@ -418,11 +469,12 @@ cam_periph_invalidate(struct cam_periph *periph)
        periph->flags |= CAM_PERIPH_INVALID;
        periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND;
 
+       xpt_lock_buses();
        if (periph->refcount == 0)
                camperiphfree(periph);
        else if (periph->refcount < 0)
                kprintf("cam_invalidate_periph: refcount < 0!!\n");
-       crit_exit();
+       xpt_unlock_buses();
 }
 
 static void
@@ -436,19 +488,16 @@ camperiphfree(struct cam_periph *periph)
        }
 
        if (*p_drv == NULL) {
-               kprintf("camperiphfree: attempt to free "
-                       "non-existent periph: %s\n", periph->periph_name);
+               kprintf("camperiphfree: attempt to free non-existent periph\n");
                return;
        }
-       
-       if (periph->periph_dtor != NULL)
-               periph->periph_dtor(periph);
-       
-       crit_enter();
+
        TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links);
        (*p_drv)->generation++;
-       crit_exit();
+       xpt_unlock_buses();
 
+       if (periph->periph_dtor != NULL)
+               periph->periph_dtor(periph);
        xpt_remove_periph(periph);
 
        if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) {
@@ -477,50 +526,7 @@ camperiphfree(struct cam_periph *periph)
        }
        xpt_free_path(periph->path);
        kfree(periph, M_CAMPERIPH);
-}
-
-/*
- * Wait interruptibly for an exclusive lock.
- */
-int
-cam_periph_lock(struct cam_periph *periph, int flags)
-{
-       int error;
-
-       /*
-        * Increment the reference count on the peripheral
-        * while we wait for our lock attempt to succeed
-        * to ensure the peripheral doesn't disappear out
-        * from under us while we sleep.
-        */
-       if (cam_periph_acquire(periph) != CAM_REQ_CMP)
-               return(ENXIO);
-
-       while ((periph->flags & CAM_PERIPH_LOCKED) != 0) {
-               periph->flags |= CAM_PERIPH_LOCK_WANTED;
-               if ((error = tsleep(periph, flags, "caplck", 0)) != 0) {
-                       cam_periph_release(periph);
-                       return error;
-               }
-       }
-
-       periph->flags |= CAM_PERIPH_LOCKED;
-       return 0;
-}
-
-/*
- * Unlock and wake up any waiters.
- */
-void
-cam_periph_unlock(struct cam_periph *periph)
-{
-       periph->flags &= ~CAM_PERIPH_LOCKED;
-       if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) {
-               periph->flags &= ~CAM_PERIPH_LOCK_WANTED;
-               wakeup(periph);
-       }
-
-       cam_periph_release(periph);
+       xpt_lock_buses();
 }
 
 /*
@@ -727,10 +733,9 @@ cam_periph_getccb(struct cam_periph *periph, u_int32_t priority)
 {
        struct ccb_hdr *ccb_h;
 
+       sim_lock_assert_owned(periph->sim->lock);
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n"));
 
-       crit_enter();
-       
        while (SLIST_FIRST(&periph->ccb_list) == NULL) {
                if (periph->immediate_priority > priority)
                        periph->immediate_priority = priority;
@@ -738,23 +743,24 @@ cam_periph_getccb(struct cam_periph *periph, u_int32_t priority)
                if ((SLIST_FIRST(&periph->ccb_list) != NULL)
                 && (SLIST_FIRST(&periph->ccb_list)->pinfo.priority == priority))
                        break;
-               tsleep(&periph->ccb_list, 0, "cgticb", 0);
+               sim_lock_sleep(&periph->ccb_list, 0, "cgticb", 0,
+                              periph->sim->lock);
        }
 
        ccb_h = SLIST_FIRST(&periph->ccb_list);
        SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle);
-       crit_exit();
        return ((union ccb *)ccb_h);
 }
 
 void
 cam_periph_ccbwait(union ccb *ccb)
 {
-       crit_enter();
+       struct cam_sim *sim;
+
+       sim = xpt_path_sim(ccb->ccb_h.path);
        if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX)
         || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG))
-               tsleep(&ccb->ccb_h.cbfcnp, 0, "cbwait", 0);
-       crit_exit();
+               sim_lock_sleep(&ccb->ccb_h.cbfcnp, 0, "cbwait", 0, sim->lock);
 }
 
 int
@@ -827,10 +833,13 @@ cam_periph_runccb(union ccb *ccb,
                  cam_flags camflags, u_int32_t sense_flags,
                  struct devstat *ds)
 {
+       struct cam_sim *sim;
        int error;
  
        error = 0;
-        
+       sim = xpt_path_sim(ccb->ccb_h.path);
+       sim_lock_assert_owned(sim->lock);
+
        /*
         * If the user has supplied a stats structure, and if we understand
         * this particular type of ccb, record the transaction start.
@@ -965,8 +974,8 @@ camperiphdone(struct cam_periph *periph, union ccb *done_ccb)
                                        saved_ccb->ccb_h.status |=
                                            CAM_AUTOSNS_VALID;
 #if 0
-                                       xpt_print_path(saved_ccb->ccb_h.path);
-                                       kprintf("Recovered Sense\n");
+                                       xpt_print(saved_ccb->ccb_h.path,
+                                           "Recovered Sense\n");
                                        scsi_sense_print(&saved_ccb->csio);
                                        cam_error_print(saved_ccb, CAM_ESF_ALL,
                                                        CAM_EPF_ALL);
@@ -1254,8 +1263,7 @@ camperiphscsistatuserror(union ccb *ccb, cam_flags camflags,
                        *timeout = 0;
                        error = ERESTART;
                        if (bootverbose) {
-                               xpt_print_path(ccb->ccb_h.path);
-                               kprintf("Queue Full\n");
+                               xpt_print(ccb->ccb_h.path, "Queue Full\n");
                        }
                        break;
                }
@@ -1267,8 +1275,7 @@ camperiphscsistatuserror(union ccb *ccb, cam_flags camflags,
                 * command completes or a 1 second timeout.
                 */
                if (bootverbose) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Device Busy\n");
+                       xpt_print(ccb->ccb_h.path, "Device Busy\n");
                }
                if (ccb->ccb_h.retry_count > 0) {
                        ccb->ccb_h.retry_count--;
@@ -1281,13 +1288,12 @@ camperiphscsistatuserror(union ccb *ccb, cam_flags camflags,
                }
                break;
        case SCSI_STATUS_RESERV_CONFLICT:
-               xpt_print_path(ccb->ccb_h.path);
-               kprintf("Reservation Conflict\n");
+               xpt_print(ccb->ccb_h.path, "Reservation Conflict\n");
                error = EIO;
                break;
        default:
-               xpt_print_path(ccb->ccb_h.path);
-               kprintf("SCSI Status 0x%x\n", ccb->csio.scsi_status);
+               xpt_print(ccb->ccb_h.path, "SCSI Status 0x%x\n",
+                   ccb->csio.scsi_status);
                error = EIO;
                break;
        }
@@ -1541,38 +1547,38 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
                                                 &timeout);
                break;
        case CAM_AUTOSENSE_FAIL:
-               xpt_print_path(ccb->ccb_h.path);
-               kprintf("AutoSense Failed\n");
+               xpt_print(ccb->ccb_h.path, "AutoSense Failed\n");
                error = EIO;    /* we have to kill the command */
                break;
        case CAM_REQ_CMP_ERR:
                if (bootverbose && printed == 0) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Request completed with CAM_REQ_CMP_ERR\n");
+                       xpt_print(ccb->ccb_h.path,
+                           "Request completed with CAM_REQ_CMP_ERR\n");
                        printed++;
                }
+               /* FALLTHROUGH */
        case CAM_CMD_TIMEOUT:
                if (bootverbose && printed == 0) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Command timed out\n");
+                       xpt_print(ccb->ccb_h.path, "Command timed out\n");
                        printed++;
                }
+               /* FALLTHROUGH */
        case CAM_UNEXP_BUSFREE:
                if (bootverbose && printed == 0) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Unexpected Bus Free\n");
+                       xpt_print(ccb->ccb_h.path, "Unexpected Bus Free\n");
                        printed++;
                }
+               /* FALLTHROUGH */
        case CAM_UNCOR_PARITY:
                if (bootverbose && printed == 0) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Uncorrected Parity Error\n");
+                       xpt_print(ccb->ccb_h.path,
+                           "Uncorrected Parity Error\n");
                        printed++;
                }
+               /* FALLTHROUGH */
        case CAM_DATA_RUN_ERR:
                if (bootverbose && printed == 0) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Data Overrun\n");
+                       xpt_print(ccb->ccb_h.path, "Data Overrun\n");
                        printed++;
                }
                error = EIO;    /* we have to kill the command */
@@ -1581,7 +1587,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
                        ccb->ccb_h.retry_count--;
                        error = ERESTART;
                } else {
-                       action_string = "Retries Exausted";
+                       action_string = "Retries Exhausted";
                        error = EIO;
                }
                break;
@@ -1601,8 +1607,8 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
                                ccb->ccb_h.retry_count--;
                                error = ERESTART;
                                if (bootverbose && printed == 0) {
-                                       xpt_print_path(ccb->ccb_h.path);
-                                       kprintf("Selection Timeout\n");
+                                       xpt_print(ccb->ccb_h.path,
+                                           "Selection Timeout\n");
                                        printed++;
                                }
 
@@ -1664,8 +1670,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
                /* Unconditional requeue */
                error = ERESTART;
                if (bootverbose && printed == 0) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("Request Requeued\n");
+                       xpt_print(ccb->ccb_h.path, "Request Requeued\n");
                        printed++;
                }
                break;
@@ -1686,8 +1691,8 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
                        ccb->ccb_h.retry_count--;
                        error = ERESTART;
                        if (bootverbose && printed == 0) {
-                               xpt_print_path(ccb->ccb_h.path);
-                               kprintf("CAM Status 0x%x\n", status);
+                               xpt_print(ccb->ccb_h.path, "CAM Status 0x%x\n",
+                                   status);
                                printed++;
                        }
                } else {
@@ -1726,11 +1731,9 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
                if (action_string == NULL)
                        action_string = "Unretryable Error";
                if (error != ERESTART) {
-                       xpt_print_path(ccb->ccb_h.path);
-                       kprintf("error %d\n", error);
+                       xpt_print(ccb->ccb_h.path, "error %d\n", error);
                }
-               xpt_print_path(ccb->ccb_h.path);
-               kprintf("%s\n", action_string);
+               xpt_print(ccb->ccb_h.path, "%s\n", action_string);
        }
 
        return (error);
index f67a134..d9c27a8 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*-
  * Data structures and definitions for CAM peripheral ("type") drivers.
  *
  * Copyright (c) 1997, 1998 Justin T. Gibbs.
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/cam/cam_periph.h,v 1.6.2.1 2000/05/07 18:16:49 n_hibma Exp $
- * $DragonFly: src/sys/bus/cam/cam_periph.h,v 1.10 2008/05/18 03:02:53 pavalos Exp $
+ * $FreeBSD: src/sys/cam/cam_periph.h,v 1.18 2007/04/19 22:46:26 scottl Exp $
+ * $DragonFly: src/sys/bus/cam/cam_periph.h,v 1.11 2008/05/18 20:30:19 pavalos Exp $
  */
 
 #ifndef _CAM_CAM_PERIPH_H
 #define _CAM_CAM_PERIPH_H 1
 
 #include <sys/queue.h>
+#include "cam_sim.h"
 
 #ifdef _KERNEL
 
@@ -103,6 +104,7 @@ struct cam_periph {
        char                    *periph_name;
        struct cam_path         *path;  /* Compiled path to device */
        void                    *softc;
+       struct cam_sim          *sim;
        u_int32_t                unit_number;
        cam_periph_type          type;
        u_int32_t                flags;
@@ -112,6 +114,7 @@ struct cam_periph {
 #define CAM_PERIPH_INVALID             0x08
 #define CAM_PERIPH_NEW_DEV_FOUND       0x10
 #define CAM_PERIPH_RECOVERY_INPROG     0x20
+#define CAM_PERIPH_POLLED              0x40
        u_int32_t                immediate_priority;
        u_int32_t                refcount;
        SLIST_HEAD(, ccb_hdr)    ccb_list;      /* For "immediate" requests */
@@ -138,10 +141,10 @@ cam_status cam_periph_alloc(periph_ctor_t *periph_ctor,
                            char *name, cam_periph_type type, struct cam_path *,
                            ac_callback_t *, ac_code, void *arg);
 struct cam_periph *cam_periph_find(struct cam_path *path, char *name);
-int            cam_periph_lock(struct cam_periph *periph, int flags);
-void           cam_periph_unlock(struct cam_periph *periph);
 cam_status     cam_periph_acquire(struct cam_periph *periph);
 void           cam_periph_release(struct cam_periph *periph);
+int            cam_periph_hold(struct cam_periph *periph, int flags);
+void           cam_periph_unhold(struct cam_periph *periph);
 void           cam_periph_invalidate(struct cam_periph *periph);
 int            cam_periph_mapmem(union ccb *ccb,
                                  struct cam_periph_map_info *mapinfo);
@@ -175,5 +178,17 @@ void               cam_periph_freeze_after_event(struct cam_periph *periph,
 int            cam_periph_error(union ccb *ccb, cam_flags camflags,
                                 u_int32_t sense_flags, union ccb *save_ccb);
 
+static __inline void
+cam_periph_lock(struct cam_periph *periph)
+{
+       CAM_SIM_LOCK(periph->sim);
+}
+
+static __inline void
+cam_periph_unlock(struct cam_periph *periph)
+{
+       CAM_SIM_UNLOCK(periph->sim);
+}
+
 #endif /* _KERNEL */
 #endif /* _CAM_CAM_PERIPH_H */
index 03e57ae..c298175 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*-
  * CAM request queue management functions.
  *
  * Copyright (c) 1997 Justin T. Gibbs.
@@ -25,8 +25,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/cam/cam_queue.c,v 1.5 1999/08/28 00:40:41 peter Exp $
- * $DragonFly: src/sys/bus/cam/cam_queue.c,v 1.11 2007/12/01 22:21:17 pavalos Exp $
+ * $FreeBSD: src/sys/cam/cam_queue.c,v 1.9 2005/07/01 15:21:29 avatar Exp $
+ * $DragonFly: src/sys/bus/cam/cam_queue.c,v 1.12 2008/05/18 20:30:19 pavalos Exp $
  */
 #include <sys/param.h>
 #include <sys/systm.h>
index febc7f4..42288e2 100644 (file)
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam_sim.c,v 1.3 1999/08/28 00:40:42 peter Exp $
- * $DragonFly: src/sys/bus/cam/cam_sim.c,v 1.10 2007/12/01 22:21:17 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/cam_sim.c,v 1.11 2008/05/18 20:30:19 pavalos Exp $
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/thread2.h>
 
 #include "cam.h"
 #include "cam_ccb.h"
 
 MALLOC_DEFINE(M_CAMSIM, "CAM SIM", "CAM SIM buffers");
 
+/* Drivers will use sim_mplock if they need the BGL */
+sim_lock       sim_mplock;
+
+void
+cam_sim_lock(sim_lock *lock)
+{
+       if (lock == &sim_mplock)
+               get_mplock();
+       else
+               lockmgr(lock, LK_EXCLUSIVE);
+}
+
+void
+cam_sim_unlock(sim_lock *lock)
+{
+       if (lock == &sim_mplock)
+               rel_mplock();
+       else
+               lockmgr(lock, LK_RELEASE);
+}
+
+void
+sim_lock_assert_owned(sim_lock *lock)
+{
+       if (lock == &sim_mplock)
+               ASSERT_MP_LOCK_HELD(curthread);
+       else
+               KKASSERT(lockstatus(lock, curthread) != 0);
+}
+
+int
+sim_lock_sleep(void *ident, int flags, const char *wmesg, int timo,
+              sim_lock *lock)
+{
+       int retval;
+
+       if (lock != &sim_mplock) {
+               /* lock should be held already */
+               KKASSERT(lockstatus(lock, curthread) != 0);
+               crit_enter();
+               tsleep_interlock(ident);
+               lockmgr(lock, LK_RELEASE);
+       }
+
+       retval = tsleep(ident, flags, wmesg, timo);
+
+       if (lock != &sim_mplock) {
+               lockmgr(lock, LK_EXCLUSIVE);
+               crit_exit();
+       }
+
+       return (retval);
+}
+
 struct cam_devq *
 cam_simq_alloc(u_int32_t max_sim_transactions)
 {
@@ -64,7 +120,7 @@ cam_simq_release(struct cam_devq *devq)
 struct cam_sim *
 cam_sim_alloc(sim_action_func sim_action, sim_poll_func sim_poll,
              const char *sim_name, void *softc, u_int32_t unit,
-             int max_dev_transactions,
+             sim_lock *lock, int max_dev_transactions,
              int max_tagged_dev_transactions, struct cam_devq *queue)
 {
        struct cam_sim *sim;
@@ -87,6 +143,9 @@ cam_sim_alloc(sim_action_func sim_action, sim_poll_func sim_poll,
        else
                cam_devq_reference(queue);
 
+       if (lock == NULL)
+               return (NULL);
+
        sim = kmalloc(sizeof(struct cam_sim), M_CAMSIM, M_INTWAIT | M_ZERO);
        sim->sim_action = sim_action;
        sim->sim_poll = sim_poll;
@@ -99,8 +158,18 @@ cam_sim_alloc(sim_action_func sim_action, sim_poll_func sim_poll,
        sim->max_dev_openings = max_dev_transactions;
        sim->flags = 0;
        sim->refcount = 1;
-       callout_init(&sim->c_handle);
        sim->devq = queue;
+       sim->lock = lock;
+       if (lock == &sim_mplock) {
+               sim->flags |= 0;
+               callout_init(&sim->callout);
+       } else {
+               sim->flags |= CAM_SIM_MPSAFE;
+               callout_init_mp(&sim->callout);
+       }
+
+       SLIST_INIT(&sim->ccb_freeq);
+       TAILQ_INIT(&sim->sim_doneq);
 
        return (sim);
 }
index 63dbd8c..79a634b 100644 (file)
@@ -26,7 +26,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam_sim.h,v 1.4 1999/12/29 04:54:27 peter Exp $
- * $DragonFly: src/sys/bus/cam/cam_sim.h,v 1.6 2007/06/14 01:09:30 dillon Exp $
+ * $DragonFly: src/sys/bus/cam/cam_sim.h,v 1.7 2008/05/18 20:30:19 pavalos Exp $
  */
 
 #ifndef _CAM_CAM_SIM_H
@@ -49,6 +49,14 @@ struct cam_devq;
 typedef void (*sim_action_func)(struct cam_sim *sim, union ccb *ccb);
 typedef void (*sim_poll_func)(struct cam_sim *sim);
 
+typedef struct lock    sim_lock;
+extern sim_lock        sim_mplock;
+
+void             cam_sim_lock(sim_lock *lock);
+void             cam_sim_unlock(sim_lock *lock);
+void             sim_lock_assert_owned(sim_lock *lock);
+int              sim_lock_sleep(void *ident, int flags, const char *wmesg,
+                                int timo, sim_lock *lock);
 struct cam_devq * cam_simq_alloc(u_int32_t max_sim_transactions);
 void             cam_simq_release(struct cam_devq *devq);
 
@@ -57,6 +65,7 @@ struct cam_sim *  cam_sim_alloc(sim_action_func sim_action,
                                const char *sim_name,
                                void *softc,
                                u_int32_t unit,
+                               sim_lock *lock,
                                int max_dev_transactions,
                                int max_tagged_dev_transactions,
                                struct cam_devq *queue);
@@ -94,18 +103,37 @@ struct cam_sim {
        sim_poll_func           sim_poll;
        const char              *sim_name;
        void                    *softc;         /* might be NULL */
+       sim_lock                *lock;
+       TAILQ_HEAD(, ccb_hdr)   sim_doneq;
+       TAILQ_ENTRY(cam_sim)    links;
        u_int32_t               path_id;        /* bootdev may set this to 0? */
        u_int32_t               unit_number;
        u_int32_t               bus_id;
        int                     max_tagged_dev_openings;
        int                     max_dev_openings;
        u_int32_t               flags;
-#define                CAM_SIM_REL_TIMEOUT_PENDING     0x01
-       struct callout          c_handle;
+#define        CAM_SIM_REL_TIMEOUT_PENDING     0x01
+#define        CAM_SIM_MPSAFE                  0x02
+#define CAM_SIM_ON_DONEQ               0x04
+       struct callout          callout;
        struct cam_devq         *devq;  /* Device Queue to use for this SIM */
        int                     refcount;       /* References to the sim */
+
+       /* "Pool" of inactive ccbs managed by xpt_alloc_ccb and xpt_free_ccb */
+       SLIST_HEAD(,ccb_hdr)    ccb_freeq;
+       /*
+        * Maximum size of ccb pool.  Modified as devices are added/removed
+        * or have their * opening counts changed.
+        */
+       u_int                   max_ccbs;
+       /* Current count of allocated ccbs */
+       u_int                   ccb_count;
+
 };
 
+#define CAM_SIM_LOCK(sim)      cam_sim_lock((sim)->lock);
+#define CAM_SIM_UNLOCK(sim)    cam_sim_unlock((sim)->lock);
+
 static __inline u_int32_t
 cam_sim_path(struct cam_sim *sim)
 {
index 8a8e1b2..b5d5c73 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam_xpt.c,v 1.80.2.18 2002/12/09 17:31:55 gibbs Exp $
- * $DragonFly: src/sys/bus/cam/cam_xpt.c,v 1.64 2008/02/10 00:01:01 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/cam_xpt.c,v 1.65 2008/05/18 20:30:19 pavalos Exp $
  */
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/devicestat.h>
 #include <sys/interrupt.h>
 #include <sys/sbuf.h>
+#include <sys/taskqueue.h>
 #include <sys/bus.h>
 #include <sys/thread.h>
 #include <sys/thread2.h>
+#include <sys/lock.h>
 
 #include <machine/clock.h>
+#include <machine/stdarg.h>
 
 #include "cam.h"
 #include "cam_ccb.h"
 #include "scsi/scsi_all.h"
 #include "scsi/scsi_message.h"
 #include "scsi/scsi_pass.h"
+#include <sys/kthread.h>
 #include "opt_cam.h"
 
 /* Datastructures internal to the xpt layer */
 MALLOC_DEFINE(M_CAMXPT, "CAM XPT", "CAM XPT buffers");
 
+/* Object for defering XPT actions to a taskqueue */
+struct xpt_task {
+       struct task     task;
+       void            *data1;
+       uintptr_t       data2;
+};
+
 /*
  * Definition of an async handler callback block.  These are used to add
  * SIMs and peripherals to the async callback lists.
@@ -79,7 +90,6 @@ struct async_node {
 
 SLIST_HEAD(async_list, async_node);
 SLIST_HEAD(periph_list, cam_periph);
-static STAILQ_HEAD(highpowerlist, ccb_hdr) highpowerq;
 
 /*
  * This is the maximum number of high powered commands (e.g. start unit)
@@ -89,9 +99,6 @@ static STAILQ_HEAD(highpowerlist, ccb_hdr) highpowerq;
 #define CAM_MAX_HIGHPOWER  4
 #endif
 
-/* number of high powered commands that can go through right now */
-static int num_highpower = CAM_MAX_HIGHPOWER;
-
 /*
  * Structure for queueing a device in a run queue.
  * There is one run queue for allocating new ccbs,
@@ -112,6 +119,7 @@ struct cam_ed {
        struct  cam_ed_qinfo alloc_ccb_entry;
        struct  cam_ed_qinfo send_ccb_entry;
        struct  cam_et   *target;
+       struct  cam_sim  *sim;
        lun_id_t         lun_id;
        struct  camq drvq;              /*
                                         * Queue of type drivers wanting to do
@@ -147,11 +155,13 @@ struct cam_ed {
 #define CAM_DEV_RESIZE_QUEUE_NEEDED    0x10
 #define CAM_DEV_TAG_AFTER_COUNT                0x20
 #define CAM_DEV_INQUIRY_DATA_VALID     0x40
+#define        CAM_DEV_IN_DV                   0x80
+#define        CAM_DEV_DV_HIT_BOTTOM           0x100
        u_int32_t        tag_delay_count;
 #define        CAM_TAG_DELAY_COUNT             5
        u_int32_t        tag_saved_openings;
        u_int32_t        refcount;
-       struct           callout c_handle;
+       struct callout   callout;
 };
 
 /*
@@ -160,12 +170,12 @@ struct cam_ed {
  * identify, and removed when a device fails to respond after a number
  * of retries, or a bus rescan finds the device missing.
  */
-struct cam_et { 
+struct cam_et {
        TAILQ_HEAD(, cam_ed) ed_entries;
        TAILQ_ENTRY(cam_et) links;
-       struct  cam_eb  *bus;   
+       struct  cam_eb  *bus;
        target_id_t     target_id;
-       u_int32_t       refcount;       
+       u_int32_t       refcount;
        u_int           generation;
        struct          timeval last_reset;     /* uptime of last reset */
 };
@@ -175,7 +185,7 @@ struct cam_et {
  * are created by calls to xpt_bus_register and deleted by calls to
  * xpt_bus_deregister.
  */
-struct cam_eb { 
+struct cam_eb {
        TAILQ_HEAD(, cam_et) et_entries;
        TAILQ_ENTRY(cam_eb)  links;
        path_id_t            path_id;
@@ -235,8 +245,24 @@ typedef enum {
 } xpt_flags;
 
 struct xpt_softc {
-       xpt_flags       flags;
-       u_int32_t       generation;
+       xpt_flags               flags;
+       u_int32_t               xpt_generation;
+
+       /* number of high powered commands that can go through right now */
+       STAILQ_HEAD(highpowerlist, ccb_hdr)     highpowerq;
+       int                     num_highpower;
+
+       /* queue for handling async rescan requests. */
+       TAILQ_HEAD(, ccb_hdr) ccb_scanq;
+
+       /* Registered busses */
+       TAILQ_HEAD(,cam_eb)     xpt_busses;
+       u_int                   bus_generation;
+
+       struct intr_config_hook *xpt_config_hook;
+
+       struct lock             xpt_topo_lock;
+       struct lock             xpt_lock;
 };
 
 static const char quantum[] = "QUANTUM";
@@ -246,7 +272,7 @@ static const char samsung[] = "SAMSUNG";
 static const char seagate[] = "SEAGATE";
 static const char microp[] = "MICROP";
 
-static struct xpt_quirk_entry xpt_quirk_table[] = 
+static struct xpt_quirk_entry xpt_quirk_table[] =
 {
        {
                /* Reports QUEUE FULL for temporary resource shortages */
@@ -286,12 +312,12 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                 *
                 * For future reference, the drive with the problem was:
                 * QUANTUM QM39100TD-SW N1B0
-                * 
+                *
                 * It's possible that Quantum will fix the problem in later
                 * firmware revisions.  If that happens, the quirk entry
                 * will need to be made specific to the firmware revisions
                 * with the problem.
-                * 
+                *
                 */
                /* Reports QUEUE FULL for temporary resource shortages */
                { T_DIRECT, SIP_MEDIA_FIXED, quantum, "QM39100*", "*" },
@@ -322,7 +348,7 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                /*
                 * The Seagate Medalist Pro drives have very poor write
                 * performance with anything more than 2 tags.
-                * 
+                *
                 * Reported by:  Paul van der Zwan <paulz@trantor.xs4all.nl>
                 * Drive:  <SEAGATE ST36530N 1444>
                 *
@@ -377,7 +403,7 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                /*quirks*/0, /*mintags*/0, /*maxtags*/0
        },
        {
-               /* Broken tagged queuing drive */ 
+               /* Broken tagged queuing drive */
                { T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CFP2107*", "*" },
                /*quirks*/0, /*mintags*/0, /*maxtags*/0
        },
@@ -435,7 +461,7 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
        {
                /* I can't believe we need a quirk for DPT volumes. */
                { T_ANY, SIP_MEDIA_FIXED|SIP_MEDIA_REMOVABLE, "DPT", "*", "*" },
-               CAM_QUIRK_NOSERIAL|CAM_QUIRK_NOLUNS,
+               CAM_QUIRK_NOLUNS,
                /*mintags*/0, /*maxtags*/255
        },
        {
@@ -466,7 +492,7 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                        T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE",
                        "EXB-8200*", "*"
                },
-               CAM_QUIRK_NOSERIAL|CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0
+               CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0
        },
        {
                /*
@@ -477,7 +503,7 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                        T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "EXABYTE",
                        "IPL-6860*", "*"
                },
-               CAM_QUIRK_NOSERIAL|CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0
+               CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0
        },
        {
                /*
@@ -520,17 +546,6 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                },
                CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
        },
-       {
-               /*
-                * Maxtor Personal Storage 3000XT (Firewire)
-                * hangs upon serial number probing.
-                */
-               {
-                       T_DIRECT, SIP_MEDIA_FIXED, "Maxtor",
-                       "1394 storage", "*"
-               },
-               CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
-       },
        {
                /*
                 * Would repond to all LUNs if asked for.
@@ -590,18 +605,6 @@ static struct xpt_quirk_entry xpt_quirk_table[] =
                { T_ENCLOSURE, SIP_MEDIA_FIXED, "DP", "BACKPLANE", "*" },
                CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0
        },
-       {
-               /*
-                * Western Digital My Book 250GB (USB)
-                * hangs upon serial number probing.
-                * PR: 107495
-                */
-               {
-                       T_DIRECT, SIP_MEDIA_FIXED, "WD",
-                       "2500JB External", "*"
-               },
-               CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
-       },
        {
                /* Default tagged queuing parameters for all devices */
                {
@@ -649,16 +652,9 @@ static struct xpt_softc xsoftc;
 
 /* Queues for our software interrupt handler */
 typedef TAILQ_HEAD(cam_isrq, ccb_hdr) cam_isrq_t;
-static cam_isrq_t cam_bioq;
-
-/* "Pool" of inactive ccbs managed by xpt_alloc_ccb and xpt_free_ccb */
-static SLIST_HEAD(,ccb_hdr) ccb_freeq;
-static u_int xpt_max_ccbs;     /*
-                                * Maximum size of ccb pool.  Modified as
-                                * devices are added/removed or have their
-                                * opening counts changed.
-                                */
-static u_int xpt_ccb_count;    /* Current count of allocated ccbs */
+typedef TAILQ_HEAD(cam_simq, cam_sim) cam_simq_t;
+static cam_simq_t cam_simq;
+static struct lock cam_simq_lock;
 
 struct cam_periph *xpt_periph;
 
@@ -694,8 +690,6 @@ static struct dev_ops xpt_ops = {
        .d_ioctl = xptioctl
 };
 
-static struct intr_config_hook *xpt_config_hook;
-
 static void dead_sim_action(struct cam_sim *sim, union ccb *ccb);
 static void dead_sim_poll(struct cam_sim *sim);
 
@@ -708,10 +702,6 @@ static struct cam_sim cam_dead_sim = {
 
 #define SIM_DEAD(sim)  ((sim) == &cam_dead_sim)
 
-/* Registered busses */
-static TAILQ_HEAD(,cam_eb) xpt_busses;
-static u_int bus_generation;
-
 /* Storage for debugging datastructures */
 #ifdef CAMDEBUG
 struct cam_path *cam_dpath;
@@ -750,7 +740,7 @@ static moduledata_t cam_moduledata = {
        NULL
 };
 
-static void    xpt_init(void *);
+static int     xpt_init(void *);
 
 DECLARE_MODULE(cam, cam_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND);
 MODULE_VERSION(cam, 1);
@@ -812,12 +802,8 @@ static void         xpt_finishconfig(struct cam_periph *periph, union ccb *ccb);
 static void     xptaction(struct cam_sim *sim, union ccb *work_ccb);
 static void     xptpoll(struct cam_sim *sim);
 static inthand2_t swi_cambio;
-static void     camisr(cam_isrq_t *queue);
-#if 0
-static void     xptstart(struct cam_periph *periph, union ccb *work_ccb);
-static void     xptasync(struct cam_periph *periph,
-                         u_int32_t code, cam_path *path);
-#endif
+static void     camisr(void *);
+static void     camisr_runqueue(void *);
 static dev_match_ret   xptbusmatch(struct dev_match_pattern *patterns,
                                    u_int num_patterns, struct cam_eb *bus);
 static dev_match_ret   xptdevicematch(struct dev_match_pattern *patterns,
@@ -856,16 +842,8 @@ static xpt_targetfunc_t    xptdeftargetfunc;
 static xpt_devicefunc_t        xptdefdevicefunc;
 static xpt_periphfunc_t        xptdefperiphfunc;
 static int             xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg);
-#ifdef notusedyet
-static int             xpt_for_all_targets(xpt_targetfunc_t *tr_func,
-                                           void *arg);
-#endif
 static int             xpt_for_all_devices(xpt_devicefunc_t *tr_func,
                                            void *arg);
-#ifdef notusedyet
-static int             xpt_for_all_periphs(xpt_periphfunc_t *tr_func,
-                                           void *arg);
-#endif
 static xpt_devicefunc_t        xptsetasyncfunc;
 static xpt_busfunc_t   xptsetasyncbusfunc;
 static cam_status      xptregister(struct cam_periph *periph,
@@ -875,6 +853,8 @@ static cam_status   proberegister(struct cam_periph *periph,
 static void     probeschedule(struct cam_periph *probe_periph);
 static void     probestart(struct cam_periph *periph, union ccb *start_ccb);
 static void     proberequestdefaultnegotiation(struct cam_periph *periph);
+static int       proberequestbackoff(struct cam_periph *periph,
+                                    struct cam_ed *device);
 static void     probedone(struct cam_periph *periph, union ccb *done_ccb);
 static void     probecleanup(struct cam_periph *periph);
 static void     xpt_find_quirk(struct cam_ed *device);
@@ -912,7 +892,7 @@ xpt_schedule_dev_allocq(struct cam_eb *bus, struct cam_ed *dev)
                 */
                retval = xpt_schedule_dev(&bus->sim->devq->alloc_queue,
                                          &dev->alloc_ccb_entry.pinfo,
-                                         CAMQ_GET_HEAD(&dev->drvq)->priority); 
+                                         CAMQ_GET_HEAD(&dev->drvq)->priority);
        } else {
                retval = 0;
        }
@@ -996,9 +976,6 @@ static int
 xptopen(struct dev_open_args *ap)
 {
        cdev_t dev = ap->a_head.a_dev;
-       int unit;
-
-       unit = minor(dev) & 0xff;
 
        /*
         * Only allow read-write access.
@@ -1010,68 +987,42 @@ xptopen(struct dev_open_args *ap)
         * We don't allow nonblocking access.
         */
        if ((ap->a_oflags & O_NONBLOCK) != 0) {
-               kprintf("xpt%d: can't do nonblocking access\n", unit);
+               kprintf("%s: can't do nonblocking access\n", devtoname(dev));
                return(ENODEV);
        }
 
-       /*
-        * We only have one transport layer right now.  If someone accesses
-        * us via something other than minor number 1, point out their
-        * mistake.
-        */
-       if (unit != 0) {
-               kprintf("xptopen: got invalid xpt unit %d\n", unit);
-               return(ENXIO);
-       }
-
        /* Mark ourselves open */
+       lockmgr(&xsoftc.xpt_lock, LK_EXCLUSIVE);
        xsoftc.flags |= XPT_FLAG_OPEN;
-       
+       lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
+
        return(0);
 }
 
 static int
 xptclose(struct dev_close_args *ap)
 {
-       cdev_t dev = ap->a_head.a_dev;
-       int unit;
-
-       unit = minor(dev) & 0xff;
-
-       /*
-        * We only have one transport layer right now.  If someone accesses
-        * us via something other than minor number 1, point out their
-        * mistake.
-        */
-       if (unit != 0) {
-               kprintf("xptclose: got invalid xpt unit %d\n", unit);
-               return(ENXIO);
-       }
 
        /* Mark ourselves closed */
+       lockmgr(&xsoftc.xpt_lock, LK_EXCLUSIVE);
        xsoftc.flags &= ~XPT_FLAG_OPEN;
+       lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
 
        return(0);
 }
 
+/*
+ * Don't automatically grab the xpt softc lock here even though this is going
+ * through the xpt device.  The xpt device is really just a back door for
+ * accessing other devices and SIMs, so the right thing to do is to grab
+ * the appropriate SIM lock once the bus/SIM is located.
+ */
 static int
 xptioctl(struct dev_ioctl_args *ap)
 {
-       cdev_t dev = ap->a_head.a_dev;
-       int unit, error;
+       int error;
 
        error = 0;
-       unit = minor(dev) & 0xff;
-
-       /*
-        * We only have one transport layer right now.  If someone accesses
-        * us via something other than minor number 1, point out their
-        * mistake.
-        */
-       if (unit != 0) {
-               kprintf("xptioctl: got invalid xpt unit %d\n", unit);
-               return(ENXIO);
-       }
 
        switch(ap->a_cmd) {
        /*
@@ -1082,9 +1033,16 @@ xptioctl(struct dev_ioctl_args *ap)
        case CAMIOCOMMAND: {
                union ccb *ccb;
                union ccb *inccb;
+               struct cam_eb *bus;
 
                inccb = (union ccb *)ap->a_data;
 
+               bus = xpt_find_bus(inccb->ccb_h.path_id);
+               if (bus == NULL) {
+                       error = EINVAL;
+                       break;
+               }
+
                switch(inccb->ccb_h.func_code) {
                case XPT_SCAN_BUS:
                case XPT_RESET_BUS:
@@ -1100,6 +1058,8 @@ xptioctl(struct dev_ioctl_args *ap)
 
                        ccb = xpt_alloc_ccb();
 
+                       CAM_SIM_LOCK(bus->sim);
+
                        /*
                         * Create a path using the bus, target, and lun the
                         * user passed in.
@@ -1110,6 +1070,7 @@ xptioctl(struct dev_ioctl_args *ap)
                                            inccb->ccb_h.target_lun) !=
                                            CAM_REQ_CMP){
                                error = EINVAL;
+                               CAM_SIM_UNLOCK(bus->sim);
                                xpt_free_ccb(ccb);
                                break;
                        }
@@ -1122,6 +1083,7 @@ xptioctl(struct dev_ioctl_args *ap)
                        bcopy(ccb, inccb, sizeof(union ccb));
                        xpt_free_path(ccb->ccb_h.path);
                        xpt_free_ccb(ccb);
+                       CAM_SIM_UNLOCK(bus->sim);
                        break;
 
                case XPT_DEBUG: {
@@ -1132,6 +1094,8 @@ xptioctl(struct dev_ioctl_args *ap)
                         * allocate it on the stack.
                         */
 
+                       CAM_SIM_LOCK(bus->sim);
+
                        /*
                         * Create a path using the bus, target, and lun the
                         * user passed in.
@@ -1142,6 +1106,7 @@ xptioctl(struct dev_ioctl_args *ap)
                                            inccb->ccb_h.target_lun) !=
                                            CAM_REQ_CMP){
                                error = EINVAL;
+                               CAM_SIM_UNLOCK(bus->sim);
                                break;
                        }
                        /* Ensure all of our fields are correct */
@@ -1150,6 +1115,7 @@ xptioctl(struct dev_ioctl_args *ap)
                        xpt_merge_ccb(&ccb, inccb);
                        ccb.ccb_h.cbfcnp = xptdone;
                        xpt_action(&ccb);
+                       CAM_SIM_UNLOCK(bus->sim);
                        bcopy(&ccb, inccb, sizeof(union ccb));
                        xpt_free_path(ccb.ccb_h.path);
                        break;
@@ -1214,6 +1180,7 @@ xptioctl(struct dev_ioctl_args *ap)
                        error = ENOTSUP;
                        break;
                }
+               xpt_release_bus(bus);
                break;
        }
        /*
@@ -1227,8 +1194,8 @@ xptioctl(struct dev_ioctl_args *ap)
         * we never return a status of CAM_GDEVLIST_LIST_CHANGED.  It is
         * (or rather should be) impossible for the device peripheral driver
         * list to change since we look at the whole thing in one pass, and
-        * we do it within a critical section.
-        * 
+        * we do it with lock protection.
+        *
         */
        case CAMGETPASSTHRU: {
                union ccb *ccb;
@@ -1244,9 +1211,8 @@ xptioctl(struct dev_ioctl_args *ap)
                unit = ccb->cgdl.unit_number;
                name = ccb->cgdl.periph_name;
                /*
-                * Every 100 devices, we want to call splz() to check for
-                * and allow the software interrupt handler a chance to run.
-                *
+                * Every 100 devices, we want to drop our lock protection to
+                * give the software interrupt handler a chance to run.
                 * Most systems won't run into this check, but this should
                 * avoid starvation in the software interrupt handler in
                 * large systems.
@@ -1267,9 +1233,9 @@ xptioctl(struct dev_ioctl_args *ap)
                }
 
                /* Keep the list from changing while we traverse it */
-               crit_enter();
+               lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
 ptstartover:
-               cur_generation = xsoftc.generation;
+               cur_generation = xsoftc.xpt_generation;
 
                /* first find our driver in the list of drivers */
                for (p_drv = periph_drivers; *p_drv != NULL; p_drv++) {
@@ -1278,14 +1244,14 @@ ptstartover:
                }
 
                if (*p_drv == NULL) {
-                       crit_exit();
+                       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
                        ccb->ccb_h.status = CAM_REQ_CMP_ERR;
                        ccb->cgdl.status = CAM_GDEVLIST_ERROR;
                        *ccb->cgdl.periph_name = '\0';
                        ccb->cgdl.unit_number = 0;
                        error = ENOENT;
                        break;
-               }       
+               }
 
                /*
                 * Run through every peripheral instance of this driver
@@ -1299,9 +1265,10 @@ ptstartover:
                        if (periph->unit_number == unit) {
                                break;
                        } else if (--splbreaknum == 0) {
-                               splz();
+                               lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
+                               lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
                                splbreaknum = 100;
-                               if (cur_generation != xsoftc.generation)
+                               if (cur_generation != xsoftc.xpt_generation)
                                       goto ptstartover;
                        }
                }
@@ -1321,7 +1288,7 @@ ptstartover:
                             periph = SLIST_NEXT(periph, periph_links), i++) {
                                /*
                                 * Check to see whether we have a
-                                * passthrough device or not. 
+                                * passthrough device or not.
                                 */
                                if (strcmp(periph->periph_name, "pass") == 0) {
                                        /*
@@ -1384,11 +1351,11 @@ ptstartover:
                        if (base_periph_found == 1) {
                                kprintf("xptioctl: pass driver is not in the "
                                       "kernel\n");
-                               kprintf("xptioctl: put \"device pass0\" in "
+                               kprintf("xptioctl: put \"device pass\" in "
                                       "your kernel config file\n");
                        }
                }
-               crit_exit();
+               lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
                break;
                }
        default:
@@ -1402,19 +1369,90 @@ ptstartover:
 static int
 cam_module_event_handler(module_t mod, int what, void *arg)
 {
-       if (what == MOD_LOAD) {
-               xpt_init(NULL);
-       } else if (what == MOD_UNLOAD) {
+       int error;
+
+       switch (what) {
+       case MOD_LOAD:
+               if ((error = xpt_init(NULL)) != 0)
+                       return (error);
+               break;
+       case MOD_UNLOAD:
                return EBUSY;
-       } else {
+       default:
                return EOPNOTSUPP;
        }
 
        return 0;
 }
 
-/* Functions accessed by the peripheral drivers */
+/* thread to handle bus rescans */
 static void
+xpt_scanner_thread(void *dummy)
+{
+       cam_isrq_t      queue;
+       union ccb       *ccb;
+       struct cam_sim  *sim;
+
+       for (;;) {
+               /*
+                * Wait for a rescan request to come in.  When it does, splice
+                * it onto a queue from local storage so that the xpt lock
+                * doesn't need to be held while the requests are being
+                * processed.
+                */
+               crit_enter();
+               tsleep_interlock(&xsoftc.ccb_scanq);
+               xpt_unlock_buses();
+               tsleep(&xsoftc.ccb_scanq, 0, "ccb_scanq", 0);
+               xpt_lock_buses();
+               crit_exit();
+               TAILQ_INIT(&queue);
+               TAILQ_CONCAT(&queue, &xsoftc.ccb_scanq, sim_links.tqe);
+               xpt_unlock_buses();
+
+               while ((ccb = (union ccb *)TAILQ_FIRST(&queue)) != NULL) {
+                       TAILQ_REMOVE(&queue, &ccb->ccb_h, sim_links.tqe);
+
+                       sim = ccb->ccb_h.path->bus->sim;
+                       CAM_SIM_LOCK(sim);
+
+                       ccb->ccb_h.func_code = XPT_SCAN_BUS;
+                       ccb->ccb_h.cbfcnp = xptdone;
+                       xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, 5);
+                       cam_periph_runccb(ccb, NULL, 0, 0, NULL);
+                       xpt_free_path(ccb->ccb_h.path);
+                       xpt_free_ccb(ccb);
+                       CAM_SIM_UNLOCK(sim);
+               }
+       }
+}
+
+void
+xpt_rescan(union ccb *ccb)
+{
+       struct ccb_hdr *hdr;
+
+       /*
+        * Don't make duplicate entries for the same paths.
+        */
+       xpt_lock_buses();
+       TAILQ_FOREACH(hdr, &xsoftc.ccb_scanq, sim_links.tqe) {
+               if (xpt_path_comp(hdr->path, ccb->ccb_h.path) == 0) {
+                       xpt_unlock_buses();
+                       xpt_print(ccb->ccb_h.path, "rescan already queued\n");
+                       xpt_free_path(ccb->ccb_h.path);
+                       xpt_free_ccb(ccb);
+                       return;
+               }
+       }
+       TAILQ_INSERT_TAIL(&xsoftc.ccb_scanq, &ccb->ccb_h, sim_links.tqe);
+       wakeup(&xsoftc.ccb_scanq);
+       xpt_unlock_buses();
+}
+
+
+/* Functions accessed by the peripheral drivers */
+static int
 xpt_init(void *dummy)
 {
        struct cam_sim *xpt_sim;
@@ -1422,10 +1460,15 @@ xpt_init(void *dummy)
        struct cam_devq *devq;
        cam_status status;
 
-       TAILQ_INIT(&xpt_busses);
-       TAILQ_INIT(&cam_bioq);
-       SLIST_INIT(&ccb_freeq);
-       STAILQ_INIT(&highpowerq);
+       TAILQ_INIT(&xsoftc.xpt_busses);
+       TAILQ_INIT(&cam_simq);
+       TAILQ_INIT(&xsoftc.ccb_scanq);
+       STAILQ_INIT(&xsoftc.highpowerq);
+       xsoftc.num_highpower = CAM_MAX_HIGHPOWER;
+
+       lockinit(&cam_simq_lock, "CAM SIMQ lock", 0, LK_CANRECURSE);
+       lockinit(&xsoftc.xpt_lock, "XPT lock", 0, LK_CANRECURSE);
+       lockinit(&xsoftc.xpt_topo_lock, "XPT topology lock", 0, LK_CANRECURSE);
 
        /*
         * The xpt layer is, itself, the equivelent of a SIM.
@@ -1439,13 +1482,22 @@ xpt_init(void *dummy)
                                "xpt",
                                /*softc*/NULL,
                                /*unit*/0,
+                               /*lock*/&xsoftc.xpt_lock,
                                /*max_dev_transactions*/0,
                                /*max_tagged_dev_transactions*/0,
                                devq);
        cam_simq_release(devq);
-       xpt_max_ccbs = 16;
-                               
-       xpt_bus_register(xpt_sim, /*bus #*/0);
+       if (xpt_sim == NULL)
+               return (ENOMEM);
+
+       xpt_sim->max_ccbs = 16;
+
+       lockmgr(&xsoftc.xpt_lock, LK_EXCLUSIVE);
+       if ((status = xpt_bus_register(xpt_sim, /*bus #*/0)) != CAM_SUCCESS) {
+               kprintf("xpt_init: xpt_bus_register failed with status %#x,"
+                      " failing attach\n", status);
+               return (EINVAL);
+       }
 
        /*
         * Looking at the XPT from the SIM layer, the XPT is
@@ -1457,44 +1509,53 @@ xpt_init(void *dummy)
                                      CAM_LUN_WILDCARD)) != CAM_REQ_CMP) {
                kprintf("xpt_init: xpt_create_path failed with status %#x,"
                       " failing attach\n", status);
-               return;
+               return (EINVAL);
        }
 
        cam_periph_alloc(xptregister, NULL, NULL, NULL, "xpt", CAM_PERIPH_BIO,
-                        path, NULL, 0, NULL);
+                        path, NULL, 0, xpt_sim);
        xpt_free_path(path);
 
-       xpt_sim->softc = xpt_periph;
+       lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
 
        /*
         * Register a callback for when interrupts are enabled.
         */
-       xpt_config_hook = kmalloc(sizeof(struct intr_config_hook),
-                                 M_TEMP, M_INTWAIT | M_ZERO);
-       xpt_config_hook->ich_func = xpt_config;
-       xpt_config_hook->ich_desc = "xpt";
-       xpt_config_hook->ich_order = 1000;
-       if (config_intrhook_establish(xpt_config_hook) != 0) {
-               kfree (xpt_config_hook, M_TEMP);
+       xsoftc.xpt_config_hook = kmalloc(sizeof(struct intr_config_hook),
+                                 M_CAMXPT, M_INTWAIT | M_ZERO);
+       xsoftc.xpt_config_hook->ich_func = xpt_config;
+       xsoftc.xpt_config_hook->ich_desc = "xpt";
+       xsoftc.xpt_config_hook->ich_order = 1000;
+       if (config_intrhook_establish(xsoftc.xpt_config_hook) != 0) {
+               kfree (xsoftc.xpt_config_hook, M_CAMXPT);
                kprintf("xpt_init: config_intrhook_establish failed "
                       "- failing attach\n");
        }
 
+       /* fire up rescan thread */
+       if (kthread_create(xpt_scanner_thread, NULL, NULL, "xpt_thrd")) {
+               kprintf("xpt_init: failed to create rescan thread\n");
+       }
        /* Install our software interrupt handlers */
        register_swi(SWI_CAMBIO, swi_cambio, NULL, "swi_cambio", NULL);
+
+       return (0);
 }
 
 static cam_status
 xptregister(struct cam_periph *periph, void *arg)
 {
+       struct cam_sim *xpt_sim;
+
        if (periph == NULL) {
                kprintf("xptregister: periph was NULL!!\n");
                return(CAM_REQ_CMP_ERR);
        }
 
-       periph->softc = NULL;
-
+       xpt_sim = (struct cam_sim *)arg;
+       xpt_sim->softc = periph;
        xpt_periph = periph;
+       periph->softc = NULL;
 
        return(CAM_REQ_CMP);
 }
@@ -1506,6 +1567,8 @@ xpt_add_periph(struct cam_periph *periph)
        int32_t  status;
        struct periph_list *periph_head;
 
+       sim_lock_assert_owned(periph->sim->lock);
+
        device = periph->path->device;
 
        periph_head = &device->periphs;
@@ -1518,17 +1581,17 @@ xpt_add_periph(struct cam_periph *periph)
                 * so it will fit in the queue
                 * when it's scheduled to run
                 */
-               crit_enter();
                status = camq_resize(&device->drvq,
                                     device->drvq.array_size + 1);
 
                device->generation++;
 
                SLIST_INSERT_HEAD(periph_head, periph, periph_links);
-               crit_exit();
        }
 
-       xsoftc.generation++;
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+       xsoftc.xpt_generation++;
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
 
        return (status);
 }
@@ -1538,25 +1601,26 @@ xpt_remove_periph(struct cam_periph *periph)
 {
        struct cam_ed *device;
 
+       sim_lock_assert_owned(periph->sim->lock);
+
        device = periph->path->device;
 
        if (device != NULL) {
                struct periph_list *periph_head;
 
                periph_head = &device->periphs;
-               
+
                /* Release the slot for this peripheral */
-               crit_enter();
                camq_resize(&device->drvq, device->drvq.array_size - 1);
 
                device->generation++;
 
                SLIST_REMOVE(periph_head, periph, cam_periph, periph_links);
-               crit_exit();
        }
 
-       xsoftc.generation++;
-
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+       xsoftc.xpt_generation++;
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
 }
 
 void
@@ -1569,12 +1633,13 @@ xpt_announce_periph(struct cam_periph *periph, char *announce_string)
        u_int   freq;
        u_int   mb;
 
+       sim_lock_assert_owned(periph->sim->lock);
+
        path = periph->path;
        /*
         * To ensure that this is printed in one piece,
         * mask out CAM interrupts.
         */
-       crit_enter();
        kprintf("%s%d at %s%d bus %d target %d lun %d\n",
               periph->periph_name, periph->unit_number,
               path->bus->sim->sim_name,
@@ -1677,7 +1742,7 @@ xpt_announce_periph(struct cam_periph *periph, char *announce_string)
 
        if (path->device->inq_flags & SID_CmdQue
         || path->device->flags & CAM_DEV_TAG_AFTER_COUNT) {
-               kprintf("\n%s%d: Tagged Queueing Enabled",
+               kprintf("\n%s%d: Command Queueing Enabled",
                       periph->periph_name, periph->unit_number);
        }
        kprintf("\n");
@@ -1689,7 +1754,6 @@ xpt_announce_periph(struct cam_periph *periph, char *announce_string)
        if (announce_string != NULL)
                kprintf("%s%d: %s\n", periph->periph_name,
                       periph->unit_number, announce_string);
-       crit_exit();
 }
 
 static dev_match_ret
@@ -1772,7 +1836,7 @@ xptbusmatch(struct dev_match_pattern *patterns, u_int num_patterns,
                        continue;
 
                /*
-                * If we get to this point, the user definitely wants 
+                * If we get to this point, the user definitely wants
                 * information on this bus.  So tell the caller to copy the
                 * data out.
                 */
@@ -1850,7 +1914,7 @@ xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns,
                        /* set the copy flag */
                        retval |= DM_RET_COPY;
 
-                       
+
                        /*
                         * If we've already decided on an action, go ahead
                         * and return.
@@ -1885,7 +1949,7 @@ xptdevicematch(struct dev_match_pattern *patterns, u_int num_patterns,
                        continue;
 
                /*
-                * If we get to this point, the user definitely wants 
+                * If we get to this point, the user definitely wants
                 * information on this device.  So tell the caller to copy
                 * the data out.
                 */
@@ -2008,7 +2072,7 @@ xptperiphmatch(struct dev_match_pattern *patterns, u_int num_patterns,
                        continue;
 
                /*
-                * If we get to this point, the user definitely wants 
+                * If we get to this point, the user definitely wants
                 * information on this peripheral.  So tell the caller to
                 * copy the data out.
                 */
@@ -2072,12 +2136,12 @@ xptedtbusfunc(struct cam_eb *bus, void *arg)
                 */
                if (spaceleft < sizeof(struct dev_match_result)) {
                        bzero(&cdm->pos, sizeof(cdm->pos));
-                       cdm->pos.position_type = 
+                       cdm->pos.position_type =
                                CAM_DEV_POS_EDT | CAM_DEV_POS_BUS;
 
                        cdm->pos.cookie.bus = bus;
                        cdm->pos.generations[CAM_BUS_GENERATION]=
-                               bus_generation;
+                               xsoftc.bus_generation;
                        cdm->status = CAM_DEV_MATCH_MORE;
                        return(0);
                }
@@ -2203,18 +2267,18 @@ xptedtdevicefunc(struct cam_ed *device, void *arg)
                 */
                if (spaceleft < sizeof(struct dev_match_result)) {
                        bzero(&cdm->pos, sizeof(cdm->pos));
-                       cdm->pos.position_type = 
+                       cdm->pos.position_type =
                                CAM_DEV_POS_EDT | CAM_DEV_POS_BUS |
                                CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE;
 
                        cdm->pos.cookie.bus = device->target->bus;
                        cdm->pos.generations[CAM_BUS_GENERATION]=
-                               bus_generation;
+                               xsoftc.bus_generation;
                        cdm->pos.cookie.target = device->target;
                        cdm->pos.generations[CAM_TARGET_GENERATION] =
                                device->target->bus->generation;
                        cdm->pos.cookie.device = device;
-                       cdm->pos.generations[CAM_DEV_GENERATION] = 
+                       cdm->pos.generations[CAM_DEV_GENERATION] =
                                device->target->generation;
                        cdm->status = CAM_DEV_MATCH_MORE;
                        return(0);
@@ -2312,19 +2376,19 @@ xptedtperiphfunc(struct cam_periph *periph, void *arg)
                 */
                if (spaceleft < sizeof(struct dev_match_result)) {
                        bzero(&cdm->pos, sizeof(cdm->pos));
-                       cdm->pos.position_type = 
+                       cdm->pos.position_type =
                                CAM_DEV_POS_EDT | CAM_DEV_POS_BUS |
                                CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE |
                                CAM_DEV_POS_PERIPH;
 
                        cdm->pos.cookie.bus = periph->path->bus;
                        cdm->pos.generations[CAM_BUS_GENERATION]=
-                               bus_generation;
+                               xsoftc.bus_generation;
                        cdm->pos.cookie.target = periph->path->target;
                        cdm->pos.generations[CAM_TARGET_GENERATION] =
                                periph->path->bus->generation;
                        cdm->pos.cookie.device = periph->path->device;
-                       cdm->pos.generations[CAM_DEV_GENERATION] = 
+                       cdm->pos.generations[CAM_DEV_GENERATION] =
                                periph->path->target->generation;
                        cdm->pos.cookie.periph = periph;
                        cdm->pos.generations[CAM_PERIPH_GENERATION] =
@@ -2364,7 +2428,7 @@ xptedtmatch(struct ccb_dev_match *cdm)
         */
        if ((cdm->pos.position_type & CAM_DEV_POS_BUS)
         && (cdm->pos.generations[CAM_BUS_GENERATION] != 0)
-        && (cdm->pos.generations[CAM_BUS_GENERATION] != bus_generation)) {
+        && (cdm->pos.generations[CAM_BUS_GENERATION] != xsoftc.bus_generation)) {
                cdm->status = CAM_DEV_MATCH_LIST_CHANGED;
                return(0);
        }
@@ -2450,7 +2514,7 @@ xptplistperiphfunc(struct cam_periph *periph, void *arg)
 
                        pdrv = NULL;
                        bzero(&cdm->pos, sizeof(cdm->pos));
-                       cdm->pos.position_type = 
+                       cdm->pos.position_type =
                                CAM_DEV_POS_PDRV | CAM_DEV_POS_PDPTR |
                                CAM_DEV_POS_PERIPH;
 
@@ -2563,15 +2627,21 @@ xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg)
 
        retval = 1;
 
-       for (bus = (start_bus ? start_bus : TAILQ_FIRST(&xpt_busses));
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+       for (bus = (start_bus ? start_bus : TAILQ_FIRST(&xsoftc.xpt_busses));
             bus != NULL;
             bus = next_bus) {
                next_bus = TAILQ_NEXT(bus, links);
 
+               lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
+               CAM_SIM_LOCK(bus->sim);
                retval = tr_func(bus, arg);
+               CAM_SIM_UNLOCK(bus->sim);
                if (retval == 0)
                        return(retval);
+               lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
        }
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
 
        return(retval);
 }
@@ -2782,23 +2852,6 @@ xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg)
        return(xptbustraverse(NULL, xptdefbusfunc, &tr_config));
 }
 
-#ifdef notusedyet
-/*
- * Execute the given function for every target in the EDT.
- */
-static int
-xpt_for_all_targets(xpt_targetfunc_t *tr_func, void *arg)
-{
-       struct xpt_traverse_config tr_config;
-
-       tr_config.depth = XPT_DEPTH_TARGET;
-       tr_config.tr_func = tr_func;
-       tr_config.tr_arg = arg;
-
-       return(xptbustraverse(NULL, xptdefbusfunc, &tr_config));
-}
-#endif /* notusedyet */
-
 /*
  * Execute the given function for every device in the EDT.
  */
@@ -2814,23 +2867,6 @@ xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg)
        return(xptbustraverse(NULL, xptdefbusfunc, &tr_config));
 }
 
-#ifdef notusedyet
-/*
- * Execute the given function for every peripheral in the EDT.
- */
-static int
-xpt_for_all_periphs(xpt_periphfunc_t *tr_func, void *arg)
-{
-       struct xpt_traverse_config tr_config;
-
-       tr_config.depth = XPT_DEPTH_PERIPH;
-       tr_config.tr_func = tr_func;
-       tr_config.tr_arg = arg;
-
-       return(xptbustraverse(NULL, xptdefbusfunc, &tr_config));
-}
-#endif /* notusedyet */
-
 static int
 xptsetasyncfunc(struct cam_ed *device, void *arg)
 {
@@ -2889,6 +2925,35 @@ xptsetasyncbusfunc(struct cam_eb *bus, void *arg)
        return(1);
 }
 
+static void
+xpt_action_sasync_cb(void *context, int pending)
+{
+       struct async_node *cur_entry;
+       struct xpt_task *task;
+       uint32_t added;
+
+       task = (struct xpt_task *)context;
+       cur_entry = (struct async_node *)task->data1;
+       added = task->data2;
+
+       if ((added & AC_FOUND_DEVICE) != 0) {
+               /*
+                * Get this peripheral up to date with all
+                * the currently existing devices.
+                */
+               xpt_for_all_devices(xptsetasyncfunc, cur_entry);
+       }
+       if ((added & AC_PATH_REGISTERED) != 0) {
+               /*
+                * Get this peripheral up to date with all
+                * the currently existing busses.
+                */
+               xpt_for_all_busses(xptsetasyncbusfunc, cur_entry);
+               }
+
+       kfree(task, M_CAMXPT);
+}
+
 void
 xpt_action(union ccb *start_ccb)
 {
@@ -2896,8 +2961,6 @@ xpt_action(union ccb *start_ccb)
 
        start_ccb->ccb_h.status = CAM_REQ_INPROG;
 
-       crit_enter();
-
        switch (start_ccb->ccb_h.func_code) {
        case XPT_SCSI_IO:
        {
@@ -3029,7 +3092,7 @@ xpt_action(union ccb *start_ccb)
                                start_ccb->ccb_h.status = CAM_REQ_CMP;
                                break;
                        }
-               } 
+               }
                if (XPT_FC_IS_QUEUED(abort_ccb)
                 && (abort_ccb->ccb_h.pinfo.index == CAM_DONEQ_INDEX)) {
                        /*
@@ -3094,7 +3157,7 @@ xpt_action(union ccb *start_ccb)
                                bcopy(dev->serial_num, cgd->serial_num,
                                      dev->serial_num_len);
                }
-               break; 
+               break;
        }
        case XPT_GDEV_STATS:
        {
@@ -3147,17 +3210,17 @@ xpt_action(union ccb *start_ccb)
                /*
                 * Check and see if the list has changed since the user
                 * last requested a list member.  If so, tell them that the
-                * list has changed, and therefore they need to start over 
+                * list has changed, and therefore they need to start over
                 * from the beginning.
                 */
-               if ((cgdl->index != 0) && 
+               if ((cgdl->index != 0) &&
                    (cgdl->generation != device->generation)) {
                        cgdl->status = CAM_GDEVLIST_LIST_CHANGED;
                        break;
                }
 
                /*
-                * Traverse the list of peripherals and attempt to find 
+                * Traverse the list of peripherals and attempt to find
                 * the requested peripheral.
                 */
                for (nperiph = SLIST_FIRST(periph_head), i = 0;
@@ -3195,9 +3258,6 @@ xpt_action(union ccb *start_ccb)
 
                cdm = &start_ccb->cdm;
 
-               /*
-                * Prevent EDT changes while we traverse it.
-                */
                /*
                 * There are two ways of getting at information in the EDT.
                 * The first way is via the primary EDT tree.  It starts
@@ -3290,8 +3350,8 @@ xpt_action(union ccb *start_ccb)
                                cur_entry->event_enable = csa->event_enable;
                        }
                } else {
-                       cur_entry = kmalloc(sizeof(*cur_entry), 
-                                           M_CAMXPT, M_INTWAIT);
+                       cur_entry = kmalloc(sizeof(*cur_entry), M_CAMXPT,
+                                          M_INTWAIT);
                        cur_entry->event_enable = csa->event_enable;
                        cur_entry->callback_arg = csa->callback_arg;
                        cur_entry->callback = csa->callback;
@@ -3299,20 +3359,23 @@ xpt_action(union ccb *start_ccb)
                        csa->ccb_h.path->device->refcount++;
                }
 
-               if ((added & AC_FOUND_DEVICE) != 0) {
-                       /*
-                        * Get this peripheral up to date with all
-                        * the currently existing devices.
-                        */
-                       xpt_for_all_devices(xptsetasyncfunc, cur_entry);
-               }
-               if ((added & AC_PATH_REGISTERED) != 0) {
-                       /*
-                        * Get this peripheral up to date with all
-                        * the currently existing busses.
-                        */
-                       xpt_for_all_busses(xptsetasyncbusfunc, cur_entry);
+               /*
+                * Need to decouple this operation via a taskqueue so that
+                * the locking doesn't become a mess.
+                */
+               if ((added & (AC_FOUND_DEVICE | AC_PATH_REGISTERED)) != 0) {
+                       struct xpt_task *task;
+
+                       task = kmalloc(sizeof(struct xpt_task), M_CAMXPT,
+                                     M_INTWAIT);
+
+                       TASK_INIT(&task->task, 0, xpt_action_sasync_cb, task);
+                       task->data1 = cur_entry;
+                       task->data2 = added;
+                       taskqueue_enqueue(taskqueue_thread[mycpuid],
+                                         &task->task);
                }
+
                start_ccb->ccb_h.status = CAM_REQ_CMP;
                break;
        }
@@ -3338,10 +3401,9 @@ xpt_action(union ccb *start_ccb)
                                                            crs->openings);
 
                                        if (bootverbose) {
-                                               xpt_print_path(crs->ccb_h.path);
-                                               kprintf("tagged openings "
-                                                      "now %d\n",
-                                                      crs->openings);
+                                               xpt_print(crs->ccb_h.path,
+                                                   "tagged openings now %d\n",
+                                                   crs->openings);
                                        }
                                }
                        }
@@ -3357,13 +3419,13 @@ xpt_action(union ccb *start_ccb)
                                 * is sufficient for releasing the queue.
                                 */
                                start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE;
-                               callout_stop(&dev->c_handle);
+                               callout_stop(&dev->callout);
                        } else {
 
                                start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
                        }
 
-                       callout_reset(&dev->c_handle,
+                       callout_reset(&dev->callout,
                                      (crs->release_timeout * hz) / 1000, 
                                      xpt_release_devq_timeout, dev);
 
@@ -3381,7 +3443,7 @@ xpt_action(union ccb *start_ccb)
                                 */
                                start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE;
                        } else {
-                               
+
                                dev->flags |= CAM_DEV_REL_ON_COMPLETE;
                                start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
                        }
@@ -3394,7 +3456,7 @@ xpt_action(union ccb *start_ccb)
 
                                start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE;
                        } else {
-                               
+
                                dev->flags |= CAM_DEV_REL_ON_QUEUE_EMPTY;
                                start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE;
                        }
@@ -3438,8 +3500,8 @@ xpt_action(union ccb *start_ccb)
                                cam_dflags = CAM_DEBUG_NONE;
                        } else {
                                start_ccb->ccb_h.status = CAM_REQ_CMP;
-                               xpt_print_path(cam_dpath);
-                               kprintf("debugging flags now %x\n", cam_dflags);
+                               xpt_print(cam_dpath, "debugging flags now %x\n",
+                                   cam_dflags);
                        }
                } else {
                        cam_dpath = NULL;
@@ -3463,14 +3525,13 @@ xpt_action(union ccb *start_ccb)
                start_ccb->ccb_h.status = CAM_PROVIDE_FAIL;
                break;
        }
-       crit_exit();
 }
 
 void
 xpt_polled_action(union ccb *start_ccb)
 {
        u_int32_t timeout;
-       struct    cam_sim *sim; 
+       struct    cam_sim *sim;
        struct    cam_devq *devq;
        struct    cam_ed *dev;
 
@@ -3479,30 +3540,30 @@ xpt_polled_action(union ccb *start_ccb)
        devq = sim->devq;
        dev = start_ccb->ccb_h.path->device;
 
-       crit_enter();
+       sim_lock_assert_owned(sim->lock);
 
        /*
         * Steal an opening so that no other queued requests
         * can get it before us while we simulate interrupts.
         */
        dev->ccbq.devq_openings--;
-       dev->ccbq.dev_openings--;       
-       
+       dev->ccbq.dev_openings--;
+
        while(((devq && devq->send_openings <= 0) || dev->ccbq.dev_openings < 0)
           && (--timeout > 0)) {
                DELAY(1000);
                (*(sim->sim_poll))(sim);
-               swi_cambio(NULL, NULL);         
+               camisr_runqueue(&sim->sim_doneq);
        }
-       
+
        dev->ccbq.devq_openings++;
        dev->ccbq.dev_openings++;
-       
+
        if (timeout != 0) {
                xpt_action(start_ccb);
                while(--timeout > 0) {
                        (*(sim->sim_poll))(sim);
-                       swi_cambio(NULL, NULL);
+                       camisr_runqueue(&sim->sim_doneq);
                        if ((start_ccb->ccb_h.status  & CAM_STATUS_MASK)
                            != CAM_REQ_INPROG)
                                break;
@@ -3520,9 +3581,8 @@ xpt_polled_action(union ccb *start_ccb)
        } else {
                start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
        }
-       crit_exit();
 }
-       
+
 /*
  * Schedule a peripheral driver to receive a ccb when it's
  * target device has space for more transactions.
@@ -3534,9 +3594,10 @@ xpt_schedule(struct cam_periph *perph, u_int32_t new_priority)
        union ccb *work_ccb;
        int runq;
 
+       sim_lock_assert_owned(perph->sim->lock);
+
        CAM_DEBUG(perph->path, CAM_DEBUG_TRACE, ("xpt_schedule\n"));
        device = perph->path->device;
-       crit_enter();
        if (periph_is_queued(perph)) {
                /* Simply reorder based on new priority */
                CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE,
@@ -3550,7 +3611,6 @@ xpt_schedule(struct cam_periph *perph, u_int32_t new_priority)
        } else if (SIM_DEAD(perph->path->bus->sim)) {
                /* The SIM is gone so just call periph_start directly. */
                work_ccb = xpt_get_ccb(perph->path->device);
-               crit_exit();
                if (work_ccb == NULL)
                        return; /* XXX */
                xpt_setup_ccb(&work_ccb->ccb_h, perph->path, new_priority);
@@ -3566,7 +3626,6 @@ xpt_schedule(struct cam_periph *perph, u_int32_t new_priority)
                camq_insert(&device->drvq, &perph->pinfo);
                runq = xpt_schedule_dev_allocq(perph->path->bus, device);
        }
-       crit_exit();
        if (runq != 0) {
                CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE,
                          ("   calling xpt_run_devq\n"));
@@ -3581,7 +3640,7 @@ xpt_schedule(struct cam_periph *perph, u_int32_t new_priority)
  * return 1 meaning the device queue should be run. If we
  * were already queued, implying someone else has already
  * started the queue, return 0 so the caller doesn't attempt
- * to run the queue.  Must be run in a critical section.
+ * to run the queue.
  */
 static int
 xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo,
@@ -3640,7 +3699,6 @@ xpt_run_dev_allocq(struct cam_eb *bus)
                         devq->alloc_openings,
                         devq->alloc_active));
 
-       crit_enter();
        devq->alloc_queue.qfrozen_cnt++;
        while ((devq->alloc_queue.entries > 0)
            && (devq->alloc_openings > 0)
@@ -3650,7 +3708,7 @@ xpt_run_dev_allocq(struct cam_eb *bus)
                union   ccb *work_ccb;
                struct  cam_periph *drv;
                struct  camq *drvq;
-               
+
                qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->alloc_queue,
                                                           CAMQ_HEAD);
                device = qinfo->device;
@@ -3670,7 +3728,6 @@ xpt_run_dev_allocq(struct cam_eb *bus)
                        devq->alloc_openings--;
                        devq->alloc_active++;
                        drv = (struct cam_periph*)camq_remove(drvq, CAMQ_HEAD);
-                       crit_exit();
                        xpt_setup_ccb(&work_ccb->ccb_h, drv->path,
                                      drv->pinfo.priority);
                        CAM_DEBUG_PRINT(CAM_DEBUG_XPT,
@@ -3688,9 +3745,6 @@ xpt_run_dev_allocq(struct cam_eb *bus)
                         */
                        break;
                }
-       
-               /* Raise IPL for possible insertion and test at top of loop */
-               crit_enter();
 
                if (drvq->entries > 0) {
                        /* We have more work.  Attempt to reschedule */
@@ -3698,7 +3752,6 @@ xpt_run_dev_allocq(struct cam_eb *bus)
                }
        }
        devq->alloc_queue.qfrozen_cnt--;
-       crit_exit();
 }
 
 static void
@@ -3712,7 +3765,6 @@ xpt_run_dev_sendq(struct cam_eb *bus)
        }
        CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_run_dev_sendq\n"));
 
-       crit_enter();
        devq->send_queue.qfrozen_cnt++;
        while ((devq->send_queue.entries > 0)
            && (devq->send_openings > 0)) {
@@ -3748,7 +3800,8 @@ xpt_run_dev_sendq(struct cam_eb *bus)
 
                if ((work_ccb->ccb_h.flags & CAM_HIGH_POWER) != 0) {
 
-                       if (num_highpower <= 0) {
+                       lockmgr(&xsoftc.xpt_lock, LK_EXCLUSIVE);
+                       if (xsoftc.num_highpower <= 0) {
                                /*
                                 * We got a high power command, but we
                                 * don't have any available slots.  Freeze
@@ -3756,18 +3809,20 @@ xpt_run_dev_sendq(struct cam_eb *bus)
                                 * available.
                                 */
                                device->qfrozen_cnt++;
-                               STAILQ_INSERT_TAIL(&highpowerq, 
-                                                  &work_ccb->ccb_h, 
+                               STAILQ_INSERT_TAIL(&xsoftc.highpowerq,
+                                                  &work_ccb->ccb_h,
                                                   xpt_links.stqe);
 
+                               lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
                                continue;
                        } else {
                                /*
                                 * Consume a high power slot while
                                 * this ccb runs.
                                 */
-                               num_highpower--;
+                               xsoftc.num_highpower--;
                        }
+                       lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
                }
                devq->active_dev = device;
                cam_ccbq_remove_ccb(&device->ccbq, work_ccb);
@@ -3775,8 +3830,8 @@ xpt_run_dev_sendq(struct cam_eb *bus)
                cam_ccbq_send_ccb(&device->ccbq, work_ccb);
 
                devq->send_openings--;
-               devq->send_active++;            
-               
+               devq->send_active++;
+
                if (device->ccbq.queue.entries > 0)
                        xpt_schedule_dev_sendq(bus, device);
 
@@ -3811,10 +3866,8 @@ xpt_run_dev_sendq(struct cam_eb *bus)
                (*(sim->sim_action))(sim, work_ccb);
 
                devq->active_dev = NULL;
-               /* Raise IPL for possible insertion and test at top of loop */
        }
        devq->send_queue.qfrozen_cnt--;
-       crit_exit();
 }
 
 /*
@@ -3876,6 +3929,36 @@ xpt_create_path(struct cam_path **new_path_ptr, struct cam_periph *perph,
        return (status);
 }
 
+cam_status
+xpt_create_path_unlocked(struct cam_path **new_path_ptr,
+                        struct cam_periph *periph, path_id_t path_id,
+                        target_id_t target_id, lun_id_t lun_id)
+{
+       struct     cam_path *path;
+       struct     cam_eb *bus = NULL;
+       cam_status status;
+       int        need_unlock = 0;
+
+       path = (struct cam_path *)kmalloc(sizeof(*path), M_CAMXPT, M_WAITOK);
+
+       if (path_id != CAM_BUS_WILDCARD) {
+               bus = xpt_find_bus(path_id);
+               if (bus != NULL) {
+                       need_unlock = 1;
+                       CAM_SIM_LOCK(bus->sim);
+               }
+       }
+       status = xpt_compile_path(path, periph, path_id, target_id, lun_id);
+       if (need_unlock)
+               CAM_SIM_UNLOCK(bus->sim);
+       if (status != CAM_REQ_CMP) {
+               kfree(path, M_CAMXPT);
+               path = NULL;
+       }
+       *new_path_ptr = path;
+       return (status);
+}
+
 static cam_status
 xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph,
                 path_id_t path_id, target_id_t target_id, lun_id_t lun_id)
@@ -3893,7 +3976,6 @@ xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph,
         * We will potentially modify the EDT, so block interrupts
         * that may attempt to create cam paths.
         */
-       crit_enter();
        bus = xpt_find_bus(path_id);
        if (bus == NULL) {
                status = CAM_PATH_INVALID;
@@ -3927,7 +4009,6 @@ xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph,
                        }
                }
        }
-       crit_exit();
 
        /*
         * Only touch the user's data if we are successful.
@@ -4017,6 +4098,7 @@ xpt_path_comp(struct cam_path *path1, struct cam_path *path2)
 void
 xpt_print_path(struct cam_path *path)
 {
+
        if (path == NULL)
                kprintf("(nopath): ");
        else {
@@ -4045,11 +4127,23 @@ xpt_print_path(struct cam_path *path)
        }
 }
 
+void
+xpt_print(struct cam_path *path, const char *fmt, ...)
+{
+       __va_list ap;
+       xpt_print_path(path);
+       __va_start(ap, fmt);
+       kvprintf(fmt, ap);
+       __va_end(ap);
+}
+
 int
 xpt_path_string(struct cam_path *path, char *str, size_t str_len)
 {
        struct sbuf sb;
 
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        sbuf_new(&sb, str, str_len, 0);
 
        if (path == NULL)
@@ -4086,12 +4180,16 @@ xpt_path_string(struct cam_path *path, char *str, size_t str_len)
 path_id_t
 xpt_path_path_id(struct cam_path *path)
 {
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        return(path->bus->path_id);
 }
 
 target_id_t
 xpt_path_target_id(struct cam_path *path)
 {
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        if (path->target != NULL)
                return (path->target->target_id);
        else
@@ -4101,6 +4199,8 @@ xpt_path_target_id(struct cam_path *path)
 lun_id_t
 xpt_path_lun_id(struct cam_path *path)
 {
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        if (path->device != NULL)
                return (path->device->lun_id);
        else
@@ -4116,6 +4216,8 @@ xpt_path_sim(struct cam_path *path)
 struct cam_periph*
 xpt_path_periph(struct cam_path *path)
 {
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        return (path->periph);
 }
 
@@ -4131,32 +4233,35 @@ xpt_release_ccb(union ccb *free_ccb)
        struct   cam_path *path;
        struct   cam_ed *device;
        struct   cam_eb *bus;
+       struct   cam_sim *sim;
 
        CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_release_ccb\n"));
        path = free_ccb->ccb_h.path;
        device = path->device;
        bus = path->bus;
-       crit_enter();
+       sim = bus->sim;
+
+       sim_lock_assert_owned(sim->lock);
+
        cam_ccbq_release_opening(&device->ccbq);
-       if (xpt_ccb_count > xpt_max_ccbs) {
+       if (sim->ccb_count > sim->max_ccbs) {
                xpt_free_ccb(free_ccb);
-               xpt_ccb_count--;
+               sim->ccb_count--;
        } else {
-               SLIST_INSERT_HEAD(&ccb_freeq, &free_ccb->ccb_h, xpt_links.sle);
+               SLIST_INSERT_HEAD(&sim->ccb_freeq, &free_ccb->ccb_h,
+                   xpt_links.sle);
        }
-       if (bus->sim->devq == NULL) {
-               crit_exit();
+       if (sim->devq == NULL) {
                return;
        }
-       bus->sim->devq->alloc_openings++;
-       bus->sim->devq->alloc_active--;
+       sim->devq->alloc_openings++;
+       sim->devq->alloc_active--;
        /* XXX Turn this into an inline function - xpt_run_device?? */
        if ((device_is_alloc_queued(device) == 0)
         && (device->drvq.entries > 0)) {
                xpt_schedule_dev_allocq(bus, device);
        }
-       crit_exit();
-       if (bus->sim->devq && dev_allocq_is_runnable(bus->sim->devq))
+       if (dev_allocq_is_runnable(sim->devq))
                xpt_run_dev_allocq(bus);
 }
 
@@ -4178,6 +4283,8 @@ xpt_bus_register(struct cam_sim *sim, u_int32_t bus)
        struct cam_eb *old_bus;
        struct ccb_pathinq cpi;
 
+       sim_lock_assert_owned(sim->lock);
+
        sim->bus_id = bus;
        new_bus = kmalloc(sizeof(*new_bus), M_CAMXPT, M_INTWAIT);
 
@@ -4194,17 +4301,17 @@ xpt_bus_register(struct cam_sim *sim, u_int32_t bus)
        new_bus->flags = 0;
        new_bus->refcount = 1;  /* Held until a bus_deregister event */
        new_bus->generation = 0;
-       crit_enter();
-       old_bus = TAILQ_FIRST(&xpt_busses);
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+       old_bus = TAILQ_FIRST(&xsoftc.xpt_busses);
        while (old_bus != NULL
            && old_bus->path_id < new_bus->path_id)
                old_bus = TAILQ_NEXT(old_bus, links);
        if (old_bus != NULL)
                TAILQ_INSERT_BEFORE(old_bus, new_bus, links);
        else
-               TAILQ_INSERT_TAIL(&xpt_busses, new_bus, links);
-       bus_generation++;
-       crit_exit();
+               TAILQ_INSERT_TAIL(&xsoftc.xpt_busses, new_bus, links);
+       xsoftc.bus_generation++;
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
 
        /* Notify interested parties */
        if (sim->path_id != CAM_XPT_PATH_ID) {
@@ -4255,6 +4362,7 @@ xpt_bus_deregister(path_id_t pathid)
 
        /* The SIM may be gone, so use a dummy SIM for any stray operations. */
        devq = bus_path.bus->sim->devq;
+       ccbsim = bus_path.bus->sim;
        bus_path.bus->sim = &cam_dead_sim;
 
        /* Execute any pending operations now. */
@@ -4269,7 +4377,6 @@ xpt_bus_deregister(path_id_t pathid)
                                devq->active_dev = device;
                                cam_ccbq_remove_ccb(&device->ccbq, work_ccb);
                                cam_ccbq_send_ccb(&device->ccbq, work_ccb);
-                               ccbsim = work_ccb->ccb_h.path->bus->sim;
                                (*(ccbsim->sim_action))(ccbsim, work_ccb);
                        }
 
@@ -4281,8 +4388,8 @@ xpt_bus_deregister(path_id_t pathid)
        }
 
        /* Make sure all completed CCBs are processed. */
-       while (!TAILQ_EMPTY(&cam_bioq)) {
-               camisr(&cam_bioq);
+       while (!TAILQ_EMPTY(&ccbsim->sim_doneq)) {
+               camisr_runqueue(&ccbsim->sim_doneq);
 
                /* Repeat the async's for the benefit of any new devices. */
                xpt_async(AC_LOST_DEVICE, &bus_path, NULL);
@@ -4293,10 +4400,6 @@ xpt_bus_deregister(path_id_t pathid)
        xpt_release_bus(bus_path.bus);
        xpt_release_path(&bus_path);
 
-       /* Recheck for more completed CCBs. */
-       while (!TAILQ_EMPTY(&cam_bioq))
-               camisr(&cam_bioq);
-
        return (CAM_REQ_CMP);
 }
 
@@ -4308,15 +4411,16 @@ xptnextfreepathid(void)
        char *strval;
 
        pathid = 0;
-       bus = TAILQ_FIRST(&xpt_busses);
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+       bus = TAILQ_FIRST(&xsoftc.xpt_busses);
 retry:
        /* Find an unoccupied pathid */
-       while (bus != NULL
-           && bus->path_id <= pathid) {
+       while (bus != NULL && bus->path_id <= pathid) {
                if (bus->path_id == pathid)
                        pathid++;
                bus = TAILQ_NEXT(bus, links);
        }
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
 
        /*
         * Ensure that this pathid is not reserved for
@@ -4325,6 +4429,7 @@ retry:
        if (resource_string_value("scbus", pathid, "at", &strval) == 0) {
                ++pathid;
                /* Start the search over */
+               lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
                goto retry;
        }
        return (pathid);
@@ -4380,20 +4485,20 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
        struct cam_et *target, *next_target;
        struct cam_ed *device, *next_device;
 
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_async\n"));
 
        /*
         * Most async events come from a CAM interrupt context.  In
         * a few cases, the error recovery code at the peripheral layer,
         * which may run from our SWI or a process context, may signal
-        * deferred events with a call to xpt_async. Ensure async
-        * notifications are serialized by blocking cam interrupts.
+        * deferred events with a call to xpt_async.
         */
-       crit_enter();
 
        bus = path->bus;
 
-       if (async_code == AC_BUS_RESET) { 
+       if (async_code == AC_BUS_RESET) {
                /* Update our notion of when the last reset occurred */
                microuptime(&bus->last_reset);
        }
@@ -4420,7 +4525,7 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
 
                        next_device = TAILQ_NEXT(device, links);
 
-                       if (path->device != device 
+                       if (path->device != device
                         && path->device->lun_id != CAM_LUN_WILDCARD
                         && device->lun_id != CAM_LUN_WILDCARD)
                                continue;
@@ -4432,7 +4537,7 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
                                        path, async_arg);
                }
        }
-       
+
        /*
         * If this wasn't a fully wildcarded async, tell all
         * clients that want all async events.
@@ -4440,7 +4545,6 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg)
        if (bus != xpt_periph->path->bus)
                xpt_async_bcast(&xpt_periph->path->device->asyncs, async_code,
                                path, async_arg);
-       crit_exit();
 }
 
 static void
@@ -4546,7 +4650,8 @@ xpt_freeze_devq(struct cam_path *path, u_int count)
 {
        struct ccb_hdr *ccbh;
 
-       crit_enter();
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        path->device->qfrozen_cnt += count;
 
        /*
@@ -4558,26 +4663,27 @@ xpt_freeze_devq(struct cam_path *path, u_int count)
         * freezes the queue.  To completly close the
         * hole, controller drives must check to see
         * if a ccb's status is still CAM_REQ_INPROG
-        * under critical section protection just before they queue
+        * just before they queue
         * the CCB.  See ahc_action/ahc_freeze_devq for
         * an example.
         */
        ccbh = TAILQ_LAST(&path->device->ccbq.active_ccbs, ccb_hdr_tailq);
        if (ccbh && ccbh->status == CAM_REQ_INPROG)
                ccbh->status = CAM_REQUEUE_REQ;
-       crit_exit();
        return (path->device->qfrozen_cnt);
 }
 
 u_int32_t
 xpt_freeze_simq(struct cam_sim *sim, u_int count)
 {
+       sim_lock_assert_owned(sim->lock);
+
        if (sim->devq == NULL)
                return(count);
        sim->devq->send_queue.qfrozen_cnt += count;
        if (sim->devq->active_dev != NULL) {
                struct ccb_hdr *ccbh;
-               
+
                ccbh = TAILQ_LAST(&sim->devq->active_dev->ccbq.active_ccbs,
                                  ccb_hdr_tailq);
                if (ccbh && ccbh->status == CAM_REQ_INPROG)
@@ -4608,6 +4714,8 @@ xpt_release_devq_timeout(void *arg)
 void
 xpt_release_devq(struct cam_path *path, u_int count, int run_queue)
 {
+       sim_lock_assert_owned(path->bus->sim->lock);
+
        xpt_release_devq_device(path->device, count, run_queue);
 }
 
@@ -4617,7 +4725,6 @@ xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue)
        int     rundevq;
 
        rundevq = 0;
-       crit_enter();
 
        if (dev->qfrozen_cnt > 0) {
 
@@ -4636,7 +4743,7 @@ xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue)
                         * to release this queue.
                         */
                        if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) {
-                               callout_stop(&dev->c_handle);
+                               callout_stop(&dev->callout);
                                dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING;
                        }
 
@@ -4654,7 +4761,6 @@ xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue)
        }
        if (rundevq != 0)
                xpt_run_dev_sendq(dev->target->bus);
-       crit_exit();
 }
 
 void
@@ -4662,12 +4768,12 @@ xpt_release_simq(struct cam_sim *sim, int run_queue)
 {
        struct  camq *sendq;
 
+       sim_lock_assert_owned(sim->lock);
+
        if (sim->devq == NULL)
                return;
 
        sendq = &(sim->devq->send_queue);
-       crit_enter();
-
        if (sendq->qfrozen_cnt > 0) {
                sendq->qfrozen_cnt--;
                if (sendq->qfrozen_cnt == 0) {
@@ -4679,11 +4785,10 @@ xpt_release_simq(struct cam_sim *sim, int run_queue)
                         * already at 0.
                         */
                        if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) != 0){
-                               callout_stop(&sim->c_handle);
+                               callout_stop(&sim->callout);
                                sim->flags &= ~CAM_SIM_REL_TIMEOUT_PENDING;
                        }
                        bus = xpt_find_bus(sim->path_id);
-                       crit_exit();
 
                        if (run_queue) {
                                /*
@@ -4692,18 +4797,14 @@ xpt_release_simq(struct cam_sim *sim, int run_queue)
                                xpt_run_dev_sendq(bus);
                        }
                        xpt_release_bus(bus);
-               } else {
-                       crit_exit();
                }
-       } else {
-               crit_exit();
        }
 }
 
 void
 xpt_done(union ccb *done_ccb)
 {
-       crit_enter();
+       struct cam_sim *sim;
 
        CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_done\n"));
        if ((done_ccb->ccb_h.func_code & XPT_FC_QUEUED) != 0) {
@@ -4711,19 +4812,28 @@ xpt_done(union ccb *done_ccb)
                 * Queue up the request for handling by our SWI handler
                 * any of the "non-immediate" type of ccbs.
                 */
+               sim = done_ccb->ccb_h.path->bus->sim;
                switch (done_ccb->ccb_h.path->periph->type) {
                case CAM_PERIPH_BIO:
-                       TAILQ_INSERT_TAIL(&cam_bioq, &done_ccb->ccb_h,
+                       TAILQ_INSERT_TAIL(&sim->sim_doneq, &done_ccb->ccb_h,
                                          sim_links.tqe);
                        done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX;
-                       setsoftcambio();
+                       if ((sim->flags & CAM_SIM_ON_DONEQ) == 0) {
+                               lockmgr(&cam_simq_lock, LK_EXCLUSIVE);
+                               TAILQ_INSERT_TAIL(&cam_simq, sim,
+                                                 links);
+                               sim->flags |= CAM_SIM_ON_DONEQ;
+                               lockmgr(&cam_simq_lock, LK_RELEASE);
+                       }
+                       if ((done_ccb->ccb_h.path->periph->flags &
+                           CAM_PERIPH_POLLED) == 0)
+                               setsoftcambio();
                        break;
                default:
                        panic("unknown periph type %d",
                                done_ccb->ccb_h.path->periph->type);
                }
        }
-       crit_exit();
 }
 
 union ccb *
@@ -4731,7 +4841,7 @@ xpt_alloc_ccb(void)
 {
        union ccb *new_ccb;
 
-       new_ccb = kmalloc(sizeof(*new_ccb), M_CAMXPT, M_INTWAIT);
+       new_ccb = kmalloc(sizeof(*new_ccb), M_CAMXPT, M_INTWAIT | M_ZERO);
        return (new_ccb);
 }
 
@@ -4756,17 +4866,19 @@ static union ccb *
 xpt_get_ccb(struct cam_ed *device)
 {
        union ccb *new_ccb;
+       struct cam_sim *sim;
 
-       crit_enter();
-       if ((new_ccb = (union ccb *)SLIST_FIRST(&ccb_freeq)) == NULL) {
-               new_ccb = kmalloc(sizeof(*new_ccb), M_CAMXPT, M_INTWAIT);
-               SLIST_INSERT_HEAD(&ccb_freeq, &new_ccb->ccb_h,
+       sim = device->sim;
+       if ((new_ccb = (union ccb *)SLIST_FIRST(&sim->ccb_freeq)) == NULL) {
+               new_ccb = xpt_alloc_ccb();
+               if ((sim->flags & CAM_SIM_MPSAFE) == 0)
+                       callout_init(&new_ccb->ccb_h.timeout_ch);
+               SLIST_INSERT_HEAD(&sim->ccb_freeq, &new_ccb->ccb_h,
                                  xpt_links.sle);
-               xpt_ccb_count++;
+               sim->ccb_count++;
        }
        cam_ccbq_take_opening(&device->ccbq);
-       SLIST_REMOVE_HEAD(&ccb_freeq, xpt_links.sle);
-       crit_exit();
+       SLIST_REMOVE_HEAD(&sim->ccb_freeq, xpt_links.sle);
        return (new_ccb);
 }
 
@@ -4774,21 +4886,14 @@ static void
 xpt_release_bus(struct cam_eb *bus)
 {
 
-       crit_enter();
-       if (bus->refcount == 1) {
-               KKASSERT(TAILQ_FIRST(&bus->et_entries) == NULL);
-               TAILQ_REMOVE(&xpt_busses, bus, links);
-               if (bus->sim) {
-                       cam_sim_release(bus->sim, 0);
-                       bus->sim = NULL;
-               }
-               bus_generation++;
-               KKASSERT(bus->refcount == 1);
+       if ((--bus->refcount == 0)
+        && (TAILQ_FIRST(&bus->et_entries) == NULL)) {
+               lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+               TAILQ_REMOVE(&xsoftc.xpt_busses, bus, links);
+               xsoftc.bus_generation++;
+               lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
                kfree(bus, M_CAMXPT);
-       } else {
-               --bus->refcount;
        }
-       crit_exit();
 }
 
 static struct cam_et *
@@ -4828,7 +4933,6 @@ xpt_alloc_target(struct cam_eb *bus, target_id_t target_id)
 static void
 xpt_release_target(struct cam_eb *bus, struct cam_et *target)
 {
-       crit_enter();
        if (target->refcount == 1) {
                KKASSERT(TAILQ_FIRST(&target->ed_entries) == NULL);
                TAILQ_REMOVE(&bus->et_entries, target, links);
@@ -4839,7 +4943,6 @@ xpt_release_target(struct cam_eb *bus, struct cam_et *target)
        } else {
                --target->refcount;
        }
-       crit_exit();
 }
 
 static struct cam_ed *
@@ -4874,6 +4977,7 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
                device->send_ccb_entry.device = device;
                device->target = target;
                device->lun_id = lun_id;
+               device->sim = bus->sim;
                /* Initialize our queues */
                if (camq_init(&device->drvq, 0) != 0) {
                        kfree(device, M_CAMXPT);
@@ -4904,7 +5008,7 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
                device->tag_delay_count = 0;
                device->tag_saved_openings = 0;
                device->refcount = 1;
-               callout_init(&device->c_handle);
+               callout_init(&device->callout);
 
                /*
                 * Hold a reference to our parent target so it
@@ -4916,7 +5020,7 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id)
                 * XXX should be limited by number of CCBs this bus can
                 * do.
                 */
-               xpt_max_ccbs += device->ccbq.devq_openings;
+               bus->sim->max_ccbs += device->ccbq.devq_openings;
                /* Insertion sort into our target's device list */
                cur_device = TAILQ_FIRST(&target->ed_entries);
                while (cur_device != NULL && cur_device->lun_id < lun_id)
@@ -4952,7 +5056,6 @@ xpt_release_device(struct cam_eb *bus, struct cam_et *target,
 {
        struct cam_devq *devq;
 
-       crit_enter();
        if (device->refcount == 1) {
                KKASSERT(device->flags & CAM_DEV_UNCONFIGURED);
 
@@ -4962,12 +5065,12 @@ xpt_release_device(struct cam_eb *bus, struct cam_et *target,
 
                if ((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) {
                        device->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING;
-                       callout_stop(&device->c_handle);
+                       callout_stop(&device->callout);
                }
 
                TAILQ_REMOVE(&target->ed_entries, device,links);
                target->generation++;
-               xpt_max_ccbs -= device->ccbq.devq_openings;
+               bus->sim->max_ccbs -= device->ccbq.devq_openings;
                if (!SIM_DEAD(bus->sim)) {
                        /* Release our slot in the devq */
                        devq = bus->sim->devq;
@@ -4981,7 +5084,6 @@ xpt_release_device(struct cam_eb *bus, struct cam_et *target,
        } else {
                --device->refcount;
        }
-       crit_exit();
 }
 
 static u_int32_t
@@ -4993,8 +5095,6 @@ xpt_dev_ccbq_resize(struct cam_path *path, int newopenings)
 
        dev = path->device;
 
-       crit_enter();
-
        diff = newopenings - (dev->ccbq.dev_active + dev->ccbq.dev_openings);
        result = cam_ccbq_resize(&dev->ccbq, newopenings);
        if (result == CAM_REQ_CMP && (diff < 0)) {
@@ -5004,8 +5104,7 @@ xpt_dev_ccbq_resize(struct cam_path *path, int newopenings)
         || (dev->inq_flags & SID_CmdQue) != 0)
                dev->tag_saved_openings = newopenings;
        /* Adjust the global limit */
-       xpt_max_ccbs += diff;
-       crit_exit();
+       dev->sim->max_ccbs += diff;
        return (result);
 }
 
@@ -5014,12 +5113,14 @@ xpt_find_bus(path_id_t path_id)
 {
        struct cam_eb *bus;
 
-       TAILQ_FOREACH(bus, &xpt_busses, links) {
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+       TAILQ_FOREACH(bus, &xsoftc.xpt_busses, links) {
                if (bus->path_id == path_id) {
                        bus->refcount++;
                        break;
                }
        }
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
        return (bus);
 }
 
@@ -5103,7 +5204,7 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
 
                /* Save some state for use while we probe for devices */
                scan_info = (xpt_scan_bus_info *)
-                   kmalloc(sizeof(xpt_scan_bus_info), M_TEMP, M_INTWAIT);
+                   kmalloc(sizeof(xpt_scan_bus_info), M_CAMXPT, M_INTWAIT);
                scan_info->request_ccb = request_ccb;
                scan_info->cpi = &work_ccb->cpi;
 
@@ -5124,7 +5225,7 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
                                scan_info->counter--;
                        }
                }
-               
+
                for (i = 0; i <= max_target; i++) {
                        cam_status status;
                        if (i == initiator_id)
@@ -5137,7 +5238,7 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
                                kprintf("xpt_scan_bus: xpt_create_path failed"
                                       " with status %#x, bus scan halted\n",
                                       status);
-                               kfree(scan_info, M_TEMP);
+                               kfree(scan_info, M_CAMXPT);
                                request_ccb->ccb_h.status = status;
                                xpt_free_ccb(work_ccb);
                                xpt_done(request_ccb);
@@ -5194,21 +5295,19 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
                         * path in the request ccb.
                         */
                        phl = 0;
-                       crit_enter();
                        device = TAILQ_FIRST(&target->ed_entries);
                        if (device != NULL) {
                                phl = CAN_SRCH_HI_SPARSE(device);
                                if (device->lun_id == 0)
                                        device = TAILQ_NEXT(device, links);
                        }
-                       crit_exit();
                        if ((lun_id != 0) || (device != NULL)) {
                                if (lun_id < (CAM_SCSI2_MAXLUN-1) || phl)
                                        lun_id++;
                        }
                } else {
                        struct cam_ed *device;
-                       
+
                        device = request_ccb->ccb_h.path->device;
 
                        if ((device->quirk->quirks & CAM_QUIRK_NOLUNS) == 0) {
@@ -5235,7 +5334,7 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
                        done = 0;
                        if (scan_info->cpi->hba_misc & PIM_SEQSCAN) {
                                scan_info->counter++;
-                               if (scan_info->counter == 
+                               if (scan_info->counter ==
                                    scan_info->cpi->initiator_id) {
                                        scan_info->counter++;
                                }
@@ -5253,7 +5352,7 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
                                xpt_free_ccb(request_ccb);
                                xpt_free_ccb((union ccb *)scan_info->cpi);
                                request_ccb = scan_info->request_ccb;
-                               kfree(scan_info, M_TEMP);
+                               kfree(scan_info, M_CAMXPT);
                                request_ccb->ccb_h.status = CAM_REQ_CMP;
                                xpt_done(request_ccb);
                                break;
@@ -5272,7 +5371,7 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
                                xpt_free_ccb(request_ccb);
                                xpt_free_ccb((union ccb *)scan_info->cpi);
                                request_ccb = scan_info->request_ccb;
-                               kfree(scan_info, M_TEMP);
+                               kfree(scan_info, M_CAMXPT);
                                request_ccb->ccb_h.status = status;
                                xpt_done(request_ccb);
                                break;
@@ -5311,11 +5410,15 @@ xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb)
 
 typedef enum {
        PROBE_TUR,
-       PROBE_INQUIRY,
+       PROBE_INQUIRY,  /* this counts as DV0 for Basic Domain Validation */
        PROBE_FULL_INQUIRY,
        PROBE_MODE_SENSE,
-       PROBE_SERIAL_NUM,
-       PROBE_TUR_FOR_NEGOTIATION
+       PROBE_SERIAL_NUM_0,
+       PROBE_SERIAL_NUM_1,
+       PROBE_TUR_FOR_NEGOTIATION,
+       PROBE_INQUIRY_BASIC_DV1,
+       PROBE_INQUIRY_BASIC_DV2,
+       PROBE_DV_EXIT
 } probe_action;
 
 typedef enum {
@@ -5344,7 +5447,7 @@ xpt_scan_lun(struct cam_periph *periph, struct cam_path *path,
        
        CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE,
                  ("xpt_scan_lun\n"));
-       
+
        xpt_setup_ccb(&cpi.ccb_h, path, /*priority*/1);
        cpi.ccb_h.func_code = XPT_PATH_INQ;
        xpt_action((union ccb *)&cpi);
@@ -5370,19 +5473,18 @@ xpt_scan_lun(struct cam_periph *periph, struct cam_path *path,
        }
 
        if (request_ccb == NULL) {
-               request_ccb = kmalloc(sizeof(union ccb), M_TEMP, M_INTWAIT);
-               new_path = kmalloc(sizeof(*new_path), M_TEMP, M_INTWAIT);
+               request_ccb = kmalloc(sizeof(union ccb), M_CAMXPT, M_INTWAIT);
+               new_path = kmalloc(sizeof(*new_path), M_CAMXPT, M_INTWAIT);
                status = xpt_compile_path(new_path, xpt_periph,
                                          path->bus->path_id,
                                          path->target->target_id,
                                          path->device->lun_id);
 
                if (status != CAM_REQ_CMP) {
-                       xpt_print_path(path);
-                       kprintf("xpt_scan_lun: can't compile path, can't "
-                              "continue\n");
-                       kfree(request_ccb, M_TEMP);
-                       kfree(new_path, M_TEMP);
+                       xpt_print(path, "xpt_scan_lun: can't compile path, "
+                           "can't continue\n");
+                       kfree(request_ccb, M_CAMXPT);
+                       kfree(new_path, M_CAMXPT);
                        return;
                }
                xpt_setup_ccb(&request_ccb->ccb_h, new_path, /*priority*/ 1);
@@ -5391,7 +5493,6 @@ xpt_scan_lun(struct cam_periph *periph, struct cam_path *path,
                request_ccb->crcn.flags = flags;
        }
 
-       crit_enter();
        if ((old_periph = cam_periph_find(path, "probe")) != NULL) {
                probe_softc *softc;
 
@@ -5406,28 +5507,27 @@ xpt_scan_lun(struct cam_periph *periph, struct cam_path *path,
                                          request_ccb);
 
                if (status != CAM_REQ_CMP) {
-                       xpt_print_path(path);
-                       kprintf("xpt_scan_lun: cam_alloc_periph returned an "
-                              "error, can't continue probe\n");
+                       xpt_print(path, "xpt_scan_lun: cam_alloc_periph "
+                           "returned an error, can't continue probe\n");
                        request_ccb->ccb_h.status = status;
                        xpt_done(request_ccb);
                }
        }
-       crit_exit();
 }
 
 static void
 xptscandone(struct cam_periph *periph, union ccb *done_ccb)
 {
        xpt_release_path(done_ccb->ccb_h.path);
-       kfree(done_ccb->ccb_h.path, M_TEMP);
-       kfree(done_ccb, M_TEMP);
+       kfree(done_ccb->ccb_h.path, M_CAMXPT);
+       kfree(done_ccb, M_CAMXPT);
 }
 
 static cam_status
 proberegister(struct cam_periph *periph, void *arg)
 {
        union ccb *request_ccb; /* CCB representing the probe request */
+       cam_status status;
        probe_softc *softc;
 
        request_ccb = (union ccb *)arg;
@@ -5442,13 +5542,18 @@ proberegister(struct cam_periph *periph, void *arg)
                return(CAM_REQ_CMP_ERR);
        }
 
-       softc = kmalloc(sizeof(*softc), M_TEMP, M_INTWAIT | M_ZERO);
+       softc = kmalloc(sizeof(*softc), M_CAMXPT, M_INTWAIT | M_ZERO);
        TAILQ_INIT(&softc->request_ccbs);
        TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h,
                          periph_links.tqe);
        softc->flags = 0;
        periph->softc = softc;
-       cam_periph_acquire(periph);
+       status = cam_periph_acquire(periph);
+       if (status != CAM_REQ_CMP) {
+               return (status);
+       }
+
+
        /*
         * Ensure we've waited at least a bus settle
         * delay before attempting to probe the device.
@@ -5480,7 +5585,7 @@ probeschedule(struct cam_periph *periph)
         * condition pending.  It will not report the unit attention in
         * response to an inquiry, which may leave invalid transfer
         * negotiations in effect.  The TUR will reveal the unit attention
-        * condition.  Only send the TUR for lun 0, since some devices 
+        * condition.  Only send the TUR for lun 0, since some devices
         * will get confused by commands other than inquiry to non-existent
         * luns.  If you think a device has gone away start your scan from
         * lun 0.  This will insure that any bogus transfer settings are
@@ -5526,6 +5631,7 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
        switch (softc->action) {
        case PROBE_TUR:
        case PROBE_TUR_FOR_NEGOTIATION:
+       case PROBE_DV_EXIT:
        {
                scsi_test_unit_ready(csio,
                                     /*retries*/4,
@@ -5537,11 +5643,14 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
        }
        case PROBE_INQUIRY:
        case PROBE_FULL_INQUIRY:
+       case PROBE_INQUIRY_BASIC_DV1:
+       case PROBE_INQUIRY_BASIC_DV2:
        {
                u_int inquiry_len;
                struct scsi_inquiry_data *inq_buf;
 
                inq_buf = &periph->path->device->inq_data;
+
                /*
                 * If the device is currently configured, we calculate an
                 * MD5 checksum of the inquiry data, and if the serial number
@@ -5551,7 +5660,7 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
                 * whether we still have the same device.
                 */
                if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
-                       
+
                        MD5Init(&softc->context);
                        MD5Update(&softc->context, (unsigned char *)inq_buf,
                                  sizeof(struct scsi_inquiry_data));
@@ -5563,14 +5672,12 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
                                softc->flags |= PROBE_SERIAL_CKSUM;
                        }
                        MD5Final(softc->digest, &softc->context);
-               } 
+               }
 
                if (softc->action == PROBE_INQUIRY)
                        inquiry_len = SHORT_INQUIRY_LENGTH;
                else
-                       inquiry_len = inq_buf->additional_length
-                                   + offsetof(struct scsi_inquiry_data,
-                                               additional_length) + 1;
+                       inquiry_len = SID_ADDITIONAL_LENGTH(inq_buf);
 
                /*
                 * Some parallel SCSI devices fail to send an
@@ -5579,7 +5686,11 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
                 * safe.
                 */
                inquiry_len = roundup2(inquiry_len, 2);
-       
+
+               if (softc->action == PROBE_INQUIRY_BASIC_DV1
+                || softc->action == PROBE_INQUIRY_BASIC_DV2) {
+                       inq_buf = kmalloc(inquiry_len, M_CAMXPT, M_INTWAIT);
+               }
                scsi_inquiry(csio,
                             /*retries*/4,
                             probedone,
@@ -5600,7 +5711,7 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
                mode_buf_len = sizeof(struct scsi_mode_header_6)
                             + sizeof(struct scsi_mode_blk_desc)
                             + sizeof(struct scsi_control_page);
-               mode_buf = kmalloc(mode_buf_len, M_TEMP, M_INTWAIT);
+               mode_buf = kmalloc(mode_buf_len, M_CAMXPT, M_INTWAIT);
                scsi_mode_sense(csio,
                                /*retries*/4,
                                probedone,
@@ -5614,27 +5725,26 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
                                /*timeout*/60000);
                break;
        }
-       case PROBE_SERIAL_NUM:
+       case PROBE_SERIAL_NUM_0:
        {
-               struct scsi_vpd_unit_serial_number *serial_buf;
-               struct cam_eddevice;
+               struct scsi_vpd_supported_page_list *vpd_list = NULL;
+               struct cam_ed *device;
 
-               serial_buf = NULL;
                device = periph->path->device;
-               device->serial_num = NULL;
-               device->serial_num_len = 0;
-
                if ((device->quirk->quirks & CAM_QUIRK_NOSERIAL) == 0) {
-                       serial_buf = kmalloc(sizeof(*serial_buf), M_TEMP,
-                                           M_INTWAIT | M_ZERO);
+                       vpd_list = kmalloc(sizeof(*vpd_list), M_CAMXPT,
+                           M_INTWAIT | M_ZERO);
+               }
+
+               if (vpd_list != NULL) {
                        scsi_inquiry(csio,
                                     /*retries*/4,
                                     probedone,
                                     MSG_SIMPLE_Q_TAG,
-                                    (u_int8_t *)serial_buf,
-                                    sizeof(*serial_buf),
+                                    (u_int8_t *)vpd_list,
+                                    sizeof(*vpd_list),
                                     /*evpd*/TRUE,
-                                    SVPD_UNIT_SERIAL_NUMBER,
+                                    SVPD_SUPPORTED_PAGE_LIST,
                                     SSD_MIN_SIZE,
                                     /*timeout*/60 * 1000);
                        break;
@@ -5647,6 +5757,31 @@ probestart(struct cam_periph *periph, union ccb *start_ccb)
                probedone(periph, start_ccb);
                return;
        }
+       case PROBE_SERIAL_NUM_1:
+       {
+               struct scsi_vpd_unit_serial_number *serial_buf;
+               struct cam_ed* device;
+
+               serial_buf = NULL;
+               device = periph->path->device;
+               device->serial_num = NULL;
+               device->serial_num_len = 0;
+
+               serial_buf = (struct scsi_vpd_unit_serial_number *)
+                       kmalloc(sizeof(*serial_buf), M_CAMXPT,
+                               M_INTWAIT | M_ZERO);
+               scsi_inquiry(csio,
+                            /*retries*/4,
+                            probedone,
+                            MSG_SIMPLE_Q_TAG,
+                            (u_int8_t *)serial_buf,
+                            sizeof(*serial_buf),
+                            /*evpd*/TRUE,
+                            SVPD_UNIT_SERIAL_NUMBER,
+                            SSD_MIN_SIZE,
+                            /*timeout*/60 * 1000);
+               break;
+       }
        }
        xpt_action(start_ccb);
 }
@@ -5668,6 +5803,111 @@ proberequestdefaultnegotiation(struct cam_periph *periph)
        xpt_action((union ccb *)&cts);
 }
 
+/*
+ * Backoff Negotiation Code- only pertinent for SPI devices.
+ */
+static int
+proberequestbackoff(struct cam_periph *periph, struct cam_ed *device)
+{
+       struct ccb_trans_settings cts;
+       struct ccb_trans_settings_spi *spi;
+
+       memset(&cts, 0, sizeof (cts));
+       xpt_setup_ccb(&cts.ccb_h, periph->path, /*priority*/1);
+       cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+       cts.type = CTS_TYPE_CURRENT_SETTINGS;
+       xpt_action((union ccb *)&cts);
+       if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+               if (bootverbose) {
+                       xpt_print(periph->path,
+                           "failed to get current device settings\n");
+               }
+               return (0);
+       }
+       if (cts.transport != XPORT_SPI) {
+               if (bootverbose) {
+                       xpt_print(periph->path, "not SPI transport\n");
+               }
+               return (0);
+       }
+       spi = &cts.xport_specific.spi;
+
+       /*
+        * We cannot renegotiate sync rate if we don't have one.
+        */
+       if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) {
+               if (bootverbose) {
+                       xpt_print(periph->path, "no sync rate known\n");
+               }
+               return (0);
+       }
+
+       /*
+        * We'll assert that we don't have to touch PPR options- the
+        * SIM will see what we do with period and offset and adjust
+        * the PPR options as appropriate.
+        */
+
+       /*
+        * A sync rate with unknown or zero offset is nonsensical.
+        * A sync period of zero means Async.
+        */
+       if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0
+        || spi->sync_offset == 0 || spi->sync_period == 0) {
+               if (bootverbose) {
+                       xpt_print(periph->path, "no sync rate available\n");
+               }
+               return (0);
+       }
+
+       if (device->flags & CAM_DEV_DV_HIT_BOTTOM) {
+               CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                   ("hit async: giving up on DV\n"));
+               return (0);
+       }
+
+
+       /*
+        * Jump sync_period up by one, but stop at 5MHz and fall back to Async.
+        * We don't try to remember 'last' settings to see if the SIM actually
+        * gets into the speed we want to set. We check on the SIM telling
+        * us that a requested speed is bad, but otherwise don't try and
+        * check the speed due to the asynchronous and handshake nature
+        * of speed setting.
+        */
+       spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET;
+       for (;;) {
+               spi->sync_period++;
+               if (spi->sync_period >= 0xf) {
+                       spi->sync_period = 0;
+                       spi->sync_offset = 0;
+                       CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                           ("setting to async for DV\n"));
+                       /*
+                        * Once we hit async, we don't want to try
+                        * any more settings.
+                        */
+                       device->flags |= CAM_DEV_DV_HIT_BOTTOM;
+               } else if (bootverbose) {
+                       CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                           ("DV: period 0x%x\n", spi->sync_period));
+                       kprintf("setting period to 0x%x\n", spi->sync_period);
+               }
+               cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+               cts.type = CTS_TYPE_CURRENT_SETTINGS;
+               xpt_action((union ccb *)&cts);
+               if ((cts.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+                       break;
+               }
+               CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                   ("DV: failed to set period 0x%x\n", spi->sync_period));
+               if (spi->sync_period == 0) {
+                       return (0);
+               }
+       }
+       return (1);
+}
+
 static void
 probedone(struct cam_periph *periph, union ccb *done_ccb)
 {
@@ -5711,7 +5951,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                        inq_buf = &path->device->inq_data;
 
                        periph_qual = SID_QUAL(inq_buf);
-                       
+
                        switch(periph_qual) {
                        case SID_QUAL_LU_CONNECTED:
                        {
@@ -5731,7 +5971,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                                    + offsetof(struct scsi_inquiry_data,
                                                additional_length) + 1;
                                if (softc->action == PROBE_INQUIRY
-                                && len > SHORT_INQUIRY_LENGTH) {
+                                   && len > SHORT_INQUIRY_LENGTH) {
                                        softc->action = PROBE_FULL_INQUIRY;
                                        xpt_release_ccb(done_ccb);
                                        xpt_schedule(periph, priority);
@@ -5744,7 +5984,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                                if (INQ_DATA_TQ_ENABLED(inq_buf))
                                        softc->action = PROBE_MODE_SENSE;
                                else
-                                       softc->action = PROBE_SERIAL_NUM;
+                                       softc->action = PROBE_SERIAL_NUM_0;
 
                                path->device->flags &= ~CAM_DEV_UNCONFIGURED;
                                xpt_reference_device(path->device);
@@ -5810,12 +6050,63 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                                         /*count*/1, /*run_queue*/TRUE);
                }
                xpt_release_ccb(done_ccb);
-               kfree(mode_hdr, M_TEMP);
-               softc->action = PROBE_SERIAL_NUM;
+               kfree(mode_hdr, M_CAMXPT);
+               softc->action = PROBE_SERIAL_NUM_0;
                xpt_schedule(periph, priority);
                return;
        }
-       case PROBE_SERIAL_NUM:
+       case PROBE_SERIAL_NUM_0:
+       {
+               struct ccb_scsiio *csio;
+               struct scsi_vpd_supported_page_list *page_list;
+               int length, serialnum_supported, i;
+
+               serialnum_supported = 0;
+               csio = &done_ccb->csio;
+               page_list =
+                   (struct scsi_vpd_supported_page_list *)csio->data_ptr;
+
+               if (page_list == NULL) {
+                       /*
+                        * Don't process the command as it was never sent
+                        */
+               } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP
+                   && (page_list->length > 0)) {
+                       length = min(page_list->length,
+                           SVPD_SUPPORTED_PAGES_SIZE);
+                       for (i = 0; i < length; i++) {
+                               if (page_list->list[i] ==
+                                   SVPD_UNIT_SERIAL_NUMBER) {
+                                       serialnum_supported = 1;
+                                       break;
+                               }
+                       }
+               } else if (cam_periph_error(done_ccb, 0,
+                                           SF_RETRY_UA|SF_NO_PRINT,
+                                           &softc->saved_ccb) == ERESTART) {
+                       return;
+               } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+                       /* Don't wedge the queue */
+                       xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+                                        /*run_queue*/TRUE);
+               }
+
+               if (page_list != NULL)
+                       kfree(page_list, M_DEVBUF);
+
+               if (serialnum_supported) {
+                       xpt_release_ccb(done_ccb);
+                       softc->action = PROBE_SERIAL_NUM_1;
+                       xpt_schedule(periph, priority);
+                       return;
+               }
+               xpt_release_ccb(done_ccb);
+               softc->action = PROBE_TUR_FOR_NEGOTIATION;
+               xpt_schedule(periph, done_ccb->ccb_h.pinfo.priority);
+               return;
+       }
+
+       case PROBE_SERIAL_NUM_1:
        {
                struct ccb_scsiio *csio;
                struct scsi_vpd_unit_serial_number *serial_buf;
@@ -5862,7 +6153,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                        xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
                                         /*run_queue*/TRUE);
                }
-               
+
                /*
                 * Let's see if we have seen this device before.
                 */
@@ -5871,7 +6162,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                        u_int8_t digest[16];
 
                        MD5Init(&context);
-                       
+
                        MD5Update(&context,
                                  (unsigned char *)&path->device->inq_data,
                                  sizeof(struct scsi_inquiry_data));
@@ -5893,7 +6184,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                                xpt_async(AC_LOST_DEVICE, path, NULL);
                }
                if (serial_buf != NULL)
-                       kfree(serial_buf, M_TEMP);
+                       kfree(serial_buf, M_CAMXPT);
 
                if (changed != 0) {
                        /*
@@ -5902,7 +6193,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                         * negotiations... Controllers don't perform
                         * any negotiation or tagged queuing until
                         * after the first XPT_SET_TRAN_SETTINGS ccb is
-                        * received.  So, on a new device, just retreive
+                        * received.  So, on a new device, just retrieve
                         * the user settings, and set them as the current
                         * settings to set the device up.
                         */
@@ -5921,26 +6212,98 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
                break;
        }
        case PROBE_TUR_FOR_NEGOTIATION:
+       case PROBE_DV_EXIT:
                if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
                        /* Don't wedge the queue */
                        xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
                                         /*run_queue*/TRUE);
                }
 
-               path->device->flags &= ~CAM_DEV_UNCONFIGURED;
                xpt_reference_device(path->device);
-
+               /*
+                * Do Domain Validation for lun 0 on devices that claim
+                * to support Synchronous Transfer modes.
+                */
+               if (softc->action == PROBE_TUR_FOR_NEGOTIATION
+                && done_ccb->ccb_h.target_lun == 0
+                && (path->device->inq_data.flags & SID_Sync) != 0
+                 && (path->device->flags & CAM_DEV_IN_DV) == 0) {
+                       CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                           ("Begin Domain Validation\n"));
+                       path->device->flags |= CAM_DEV_IN_DV;
+                       xpt_release_ccb(done_ccb);
+                       softc->action = PROBE_INQUIRY_BASIC_DV1;
+                       xpt_schedule(periph, priority);
+                       return;
+               }
+               if (softc->action == PROBE_DV_EXIT) {
+                       CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                           ("Leave Domain Validation\n"));
+               }
+               path->device->flags &=
+                   ~(CAM_DEV_UNCONFIGURED|CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM);
                if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) {
                        /* Inform the XPT that a new device has been found */
                        done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
                        xpt_action(done_ccb);
+                       xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path,
+                                 done_ccb);
+               }
+               xpt_release_ccb(done_ccb);
+               break;
+       case PROBE_INQUIRY_BASIC_DV1:
+       case PROBE_INQUIRY_BASIC_DV2:
+       {
+               struct scsi_inquiry_data *nbuf;
+               struct ccb_scsiio *csio;
 
+               if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+                       /* Don't wedge the queue */
+                       xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+                                        /*run_queue*/TRUE);
+               }
+               csio = &done_ccb->csio;
+               nbuf = (struct scsi_inquiry_data *)csio->data_ptr;
+               if (bcmp(nbuf, &path->device->inq_data, SHORT_INQUIRY_LENGTH)) {
+                       xpt_print(path,
+                           "inquiry data fails comparison at DV%d step\n",
+                           softc->action == PROBE_INQUIRY_BASIC_DV1 ? 1 : 2);
+                       if (proberequestbackoff(periph, path->device)) {
+                               path->device->flags &= ~CAM_DEV_IN_DV;
+                               softc->action = PROBE_TUR_FOR_NEGOTIATION;
+                       } else {
+                               /* give up */
+                               softc->action = PROBE_DV_EXIT;
+                       }
+                       kfree(nbuf, M_CAMXPT);
+                       xpt_release_ccb(done_ccb);
+                       xpt_schedule(periph, priority);
+                       return;
+               }
+               kfree(nbuf, M_CAMXPT);
+               if (softc->action == PROBE_INQUIRY_BASIC_DV1) {
+                       softc->action = PROBE_INQUIRY_BASIC_DV2;
+                       xpt_release_ccb(done_ccb);
+                       xpt_schedule(periph, priority);
+                       return;
+               }
+               if (softc->action == PROBE_DV_EXIT) {
+                       CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+                           ("Leave Domain Validation Successfully\n"));
+               }
+               path->device->flags &=
+                   ~(CAM_DEV_UNCONFIGURED|CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM);
+               if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) {
+                       /* Inform the XPT that a new device has been found */
+                       done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
+                       xpt_action(done_ccb);
                        xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path,
                                  done_ccb);
                }
                xpt_release_ccb(done_ccb);
                break;
        }
+       }
        done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs);
        TAILQ_REMOVE(&softc->request_ccbs, &done_ccb->ccb_h, periph_links.tqe);
        done_ccb->ccb_h.status = CAM_REQ_CMP;
@@ -5956,7 +6319,7 @@ probedone(struct cam_periph *periph, union ccb *done_ccb)
 static void
 probecleanup(struct cam_periph *periph)
 {
-       kfree(periph->softc, M_TEMP);
+       kfree(periph->softc, M_CAMXPT);
 }
 
 static void
@@ -5981,7 +6344,7 @@ sysctl_cam_search_luns(SYSCTL_HANDLER_ARGS)
        int error, bool;
 
        bool = cam_srch_hi;
-       error = sysctl_handle_int(oidp, &bool, sizeof(bool), req);
+       error = sysctl_handle_int(oidp, &bool, 0, req);
        if (error != 0 || req->newptr == NULL)
                return (error);
        if (bool == 0 || bool == 1) {
@@ -6100,17 +6463,16 @@ xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
                cts->protocol_version = device->protocol_version;
 
        if (cts->protocol != device->protocol) {
-               xpt_print_path(cts->ccb_h.path);
-               kprintf("Uninitialized Protocol %x:%x?\n",
+               xpt_print(cts->ccb_h.path, "Uninitialized Protocol %x:%x?\n",
                       cts->protocol, device->protocol);
                cts->protocol = device->protocol;
        }
 
        if (cts->protocol_version > device->protocol_version) {
                if (bootverbose) {
-                       xpt_print_path(cts->ccb_h.path);
-                       kprintf("Down reving Protocol Version from %d to %d?\n",
-                              cts->protocol_version, device->protocol_version);
+                       xpt_print(cts->ccb_h.path, "Down reving Protocol "
+                           "Version from %d to %d?\n", cts->protocol_version,
+                           device->protocol_version);
                }
                cts->protocol_version = device->protocol_version;
        }
@@ -6126,18 +6488,16 @@ xpt_set_transfer_settings(struct ccb_trans_settings *cts, struct cam_ed *device,
                cts->transport_version = device->transport_version;
 
        if (cts->transport != device->transport) {
-               xpt_print_path(cts->ccb_h.path);
-               kprintf("Uninitialized Transport %x:%x?\n",
-                      cts->transport, device->transport);
+               xpt_print(cts->ccb_h.path, "Uninitialized Transport %x:%x?\n",
+                   cts->transport, device->transport);
                cts->transport = device->transport;
        }
 
        if (cts->transport_version > device->transport_version) {
                if (bootverbose) {
-                       xpt_print_path(cts->ccb_h.path);
-                       kprintf("Down reving Transport Version from %d to %d?\n",
-                              cts->transport_version,
-                              device->transport_version);
+                       xpt_print(cts->ccb_h.path, "Down reving Transport "
+                           "Version from %d to %d?\n", cts->transport_version,
+                           device->transport_version);
                }
                cts->transport_version = device->transport_version;
        }
@@ -6418,7 +6778,7 @@ xpt_start_tags(struct cam_path *path)
        crs.ccb_h.func_code = XPT_REL_SIMQ;
        crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY;
        crs.openings
-           = crs.release_timeout 
+           = crs.release_timeout
            = crs.qfrozen_cnt
            = 0;
        xpt_action((union ccb *)&crs);
@@ -6430,6 +6790,9 @@ static int busses_to_reset;
 static int
 xptconfigbuscountfunc(struct cam_eb *bus, void *arg)
 {
+
+       sim_lock_assert_owned(bus->sim->lock);
+
        if (bus->path_id != CAM_XPT_PATH_ID) {
                struct cam_path path;
                struct ccb_pathinq cpi;
@@ -6458,6 +6821,8 @@ xptconfigfunc(struct cam_eb *bus, void *arg)
        struct  cam_path *path;
        union   ccb *work_ccb;
 
+       sim_lock_assert_owned(bus->sim->lock);
+
        if (bus->path_id != CAM_XPT_PATH_ID) {
                cam_status status;
                int can_negotiate;
@@ -6522,6 +6887,11 @@ xpt_config(void *arg)
 #endif /* CAM_DEBUG_FLAGS */
 #ifdef CAM_DEBUG_BUS
        if (cam_dflags != CAM_DEBUG_NONE) {
+               /*
+                * Locking is specifically omitted here.  No SIMs have
+                * registered yet, so xpt_create_path will only be searching
+                * empty lists of targets and devices.
+                */
                if (xpt_create_path(&cam_dpath, xpt_periph,
                                    CAM_DEBUG_BUS, CAM_DEBUG_TARGET,
                                    CAM_DEBUG_LUN) != CAM_REQ_CMP) {
@@ -6577,11 +6947,40 @@ xptpassannouncefunc(struct cam_ed *device, void *arg)
 }
 
 static void
-xpt_finishconfig(struct cam_periph *periph, union ccb *done_ccb)
+xpt_finishconfig_task(void *context, int pending)
 {
        struct  periph_driver **p_drv;
        int     i;
 
+       if (busses_to_config == 0) {
+               /* Register all the peripheral drivers */
+               /* XXX This will have to change when we have loadable modules */
+               p_drv = periph_drivers;
+               for (i = 0; p_drv[i] != NULL; i++) {
+                       (*p_drv[i]->init)();
+               }
+
+               /*
+                * Check for devices with no "standard" peripheral driver
+                * attached.  For any devices like that, announce the
+                * passthrough driver so the user will see something.
+                */
+               xpt_for_all_devices(xptpassannouncefunc, NULL);
+
+               /* Release our hook so that the boot can continue. */
+               config_intrhook_disestablish(xsoftc.xpt_config_hook);
+               kfree(xsoftc.xpt_config_hook, M_CAMXPT);
+               xsoftc.xpt_config_hook = NULL;
+       }
+
+       kfree(context, M_CAMXPT);
+}
+
+static void
+xpt_finishconfig(struct cam_periph *periph, union ccb *done_ccb)
+{
+       struct  xpt_task *task;
+
        if (done_ccb != NULL) {
                CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE,
                          ("xpt_finishconfig\n"));
@@ -6604,29 +7003,48 @@ xpt_finishconfig(struct cam_periph *periph, union ccb *done_ccb)
        }
 
        if (busses_to_config == 0) {
-               /* Register all the peripheral drivers */
-               /* XXX This will have to change when we have loadable modules */
-               p_drv = periph_drivers;
-               for (i = 0; p_drv[i] != NULL; i++) {
-                       (*p_drv[i]->init)();
-               }
-
-               /*
-                * Check for devices with no "standard" peripheral driver
-                * attached.  For any devices like that, announce the
-                * passthrough driver so the user will see something.
-                */
-               xpt_for_all_devices(xptpassannouncefunc, NULL);
-
-               /* Release our hook so that the boot can continue. */
-               config_intrhook_disestablish(xpt_config_hook);
-               kfree(xpt_config_hook, M_TEMP);
-               xpt_config_hook = NULL;
+               task = kmalloc(sizeof(struct xpt_task), M_CAMXPT, M_INTWAIT);
+               TASK_INIT(&task->task, 0, xpt_finishconfig_task, task);
+               taskqueue_enqueue(taskqueue_thread[mycpuid], &task->task);
        }
+
        if (done_ccb != NULL)
                xpt_free_ccb(done_ccb);
 }
 
+cam_status
+xpt_register_async(int event, ac_callback_t *cbfunc, void *cbarg,
+                  struct cam_path *path)
+{
+       struct ccb_setasync csa;
+       cam_status status;
+       int xptpath = 0;
+
+       if (path == NULL) {
+               lockmgr(&xsoftc.xpt_lock, LK_EXCLUSIVE);
+               status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
+                                        CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
+               if (status != CAM_REQ_CMP) {
+                       lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
+                       return (status);
+               }
+               xptpath = 1;
+       }
+
+       xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
+       csa.ccb_h.func_code = XPT_SASYNC_CB;
+       csa.event_enable = event;
+       csa.callback = cbfunc;
+       csa.callback_arg = cbarg;
+       xpt_action((union ccb *)&csa);
+       status = csa.ccb_h.status;
+       if (xptpath) {
+               xpt_free_path(path);
+               lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
+       }
+       return (status);
+}
+
 static void
 xptaction(struct cam_sim *sim, union ccb *work_ccb)
 {
@@ -6677,6 +7095,19 @@ xptpoll(struct cam_sim *sim)
 {
 }
 
+void
+xpt_lock_buses(void)
+{
+       lockmgr(&xsoftc.xpt_topo_lock, LK_EXCLUSIVE);
+}
+
+void
+xpt_unlock_buses(void)
+{
+       lockmgr(&xsoftc.xpt_topo_lock, LK_RELEASE);
+}
+
+
 /*
  * Should only be called by the machine interrupt dispatch routines,
  * so put these prototypes here instead of in the header.
@@ -6685,21 +7116,40 @@ xptpoll(struct cam_sim *sim)
 static void
 swi_cambio(void *arg, void *frame)
 {
-       camisr(&cam_bioq);
+       camisr(NULL);
 }
 
 static void
-camisr(cam_isrq_t *queue)
+camisr(void *dummy)
 {
+       cam_simq_t queue;
+       struct cam_sim *sim;
+
+       lockmgr(&cam_simq_lock, LK_EXCLUSIVE);
+       TAILQ_INIT(&queue);
+       TAILQ_CONCAT(&queue, &cam_simq, links);
+       lockmgr(&cam_simq_lock, LK_RELEASE);
+
+       while ((sim = TAILQ_FIRST(&queue)) != NULL) {
+               TAILQ_REMOVE(&queue, sim, links);
+               CAM_SIM_LOCK(sim);
+               sim->flags &= ~CAM_SIM_ON_DONEQ;
+               camisr_runqueue(&sim->sim_doneq);
+               CAM_SIM_UNLOCK(sim);
+       }
+}
+
+static void
+camisr_runqueue(void *V_queue)
+{
+       cam_isrq_t *queue = V_queue;
        struct  ccb_hdr *ccb_h;
 
-       crit_enter();
        while ((ccb_h = TAILQ_FIRST(queue)) != NULL) {
                int     runq;
 
                TAILQ_REMOVE(queue, ccb_h, sim_links.tqe);
                ccb_h->pinfo.index = CAM_UNQUEUED_INDEX;
-               splz();
 
                CAM_DEBUG(ccb_h->path, CAM_DEBUG_TRACE,
                          ("camisr\n"));
@@ -6711,27 +7161,31 @@ camisr(cam_isrq_t *queue)
                        struct cam_ed           *device;
                        union ccb               *send_ccb;
 
-                       hphead = &highpowerq;
+                       lockmgr(&xsoftc.xpt_lock, LK_EXCLUSIVE);
+                       hphead = &xsoftc.highpowerq;
 
                        send_ccb = (union ccb *)STAILQ_FIRST(hphead);
 
                        /*
                         * Increment the count since this command is done.
                         */
-                       num_highpower++;
+                       xsoftc.num_highpower++;
 
-                       /* 
+                       /*
                         * Any high powered commands queued up?
                         */
                        if (send_ccb != NULL) {
                                device = send_ccb->ccb_h.path->device;
 
                                STAILQ_REMOVE_HEAD(hphead, xpt_links.stqe);
+                               lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
 
                                xpt_release_devq(send_ccb->ccb_h.path,
                                                 /*count*/1, /*runqueue*/TRUE);
-                       }
+                       } else
+                               lockmgr(&xsoftc.xpt_lock, LK_RELEASE);
                }
+
                if ((ccb_h->func_code & XPT_FC_USER_CCB) == 0) {
                        struct cam_ed *dev;
 
@@ -6743,12 +7197,12 @@ camisr(cam_isrq_t *queue)
                                ccb_h->path->bus->sim->devq->send_active--;
                                ccb_h->path->bus->sim->devq->send_openings++;
                        }
-                       
+
                        if (((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0
                          && (ccb_h->status&CAM_STATUS_MASK) != CAM_REQUEUE_REQ)
                         || ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0
                          && (dev->ccbq.dev_active == 0))) {
-                               
+
                                xpt_release_devq(ccb_h->path, /*count*/1,
                                                 /*run_queue*/TRUE);
                        }
@@ -6770,7 +7224,7 @@ camisr(cam_isrq_t *queue)
                                         /*run_queue*/TRUE);
                        ccb_h->status &= ~CAM_RELEASE_SIMQ;
                        runq = FALSE;
-               } 
+               }
 
                if ((ccb_h->flags & CAM_DEV_QFRZDIS)
                 && (ccb_h->status & CAM_DEV_QFRZN)) {
@@ -6784,7 +7238,6 @@ camisr(cam_isrq_t *queue)
                /* Call the peripheral driver's callback */
                (*ccb_h->cbfcnp)(ccb_h->path->periph, (union ccb *)ccb_h);
        }
-       crit_exit();
 }
 
 static void
index 7b132b7..a9b5492 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/cam_xpt.h,v 1.3 1999/12/29 04:54:27 peter Exp $
- * $DragonFly: src/sys/bus/cam/cam_xpt.h,v 1.3 2007/11/18 17:53:01 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/cam_xpt.h,v 1.4 2008/05/18 20:30:19 pavalos Exp $
  */
 
 #ifndef _CAM_CAM_XPT_H
@@ -59,10 +59,15 @@ cam_status          xpt_create_path(struct cam_path **new_path_ptr,
                                        struct cam_periph *perph,
                                        path_id_t path_id,
                                        target_id_t target_id, lun_id_t lun_id);
+cam_status             xpt_create_path_unlocked(struct cam_path **new_path_ptr,
+                                       struct cam_periph *perph,
+                                       path_id_t path_id,
+                                       target_id_t target_id, lun_id_t lun_id);
 void                   xpt_free_path(struct cam_path *path);
 int                    xpt_path_comp(struct cam_path *path1,
                                      struct cam_path *path2);
 void                   xpt_print_path(struct cam_path *path);
+void                   xpt_print(struct cam_path *path, const char *fmt, ...);
 int                    xpt_path_string(struct cam_path *path, char *str,
                                        size_t str_len);
 path_id_t              xpt_path_path_id(struct cam_path *path);
@@ -72,6 +77,11 @@ struct cam_sim               *xpt_path_sim(struct cam_path *path);
 struct cam_periph      *xpt_path_periph(struct cam_path *path);
 void                   xpt_async(u_int32_t async_code, struct cam_path *path,
                                  void *async_arg);
+void                   xpt_rescan(union ccb *ccb);
+void                   xpt_lock_buses(void);
+void                   xpt_unlock_buses(void);
+cam_status             xpt_register_async(int event, ac_callback_t *cbfunc,
+                                          void *cbarg, struct cam_path *path);
 #endif /* _KERNEL */
 
 #endif /* _CAM_CAM_XPT_H */
index 3f209d5..8cf3226 100644 (file)
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.14.2.11 2003/10/30 15:06:35 thomas Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_all.c,v 1.26 2007/12/02 04:44:03 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/scsi/scsi_all.c,v 1.27 2008/05/18 20:30:19 pavalos Exp $
  */
 
 #include <sys/param.h>
@@ -732,8 +732,6 @@ scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data)
 #endif
 
 
-#include <sys/param.h>
-
 #if !defined(SCSI_NO_SENSE_STRINGS)
 #define SST(asc, ascq, action, desc) \
        asc, ascq, action, desc
@@ -2997,7 +2995,7 @@ sysctl_scsi_delay(SYSCTL_HANDLER_ARGS)
        int error, delay;
 
        delay = scsi_delay;
-       error = sysctl_handle_int(oidp, &delay, sizeof(delay), req);
+       error = sysctl_handle_int(oidp, &delay, 0, req);
        if (error != 0 || req->newptr == NULL)
                return (error);
        return (set_scsi_delay(delay));
index 8a4852c..d885519 100644 (file)
@@ -15,7 +15,7 @@
  * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_all.h,v 1.14.2.5 2003/08/24 03:26:37 ken Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_all.h,v 1.13 2007/12/02 04:44:03 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/scsi/scsi_all.h,v 1.14 2008/05/18 20:30:19 pavalos Exp $
  */
 
 /*
@@ -176,8 +176,11 @@ struct scsi_mode_sense_6
 #define        SMS_PAGE_CODE                   0x3F
 #define SMS_VENDOR_SPECIFIC_PAGE       0x00
 #define SMS_DISCONNECT_RECONNECT_PAGE  0x02
+#define SMS_CACHE_PAGE                 0x08
 #define SMS_PERIPHERAL_DEVICE_PAGE     0x09
 #define SMS_CONTROL_MODE_PAGE          0x0A
+#define SMS_PROTO_SPECIFIC_PAGE                0x19
+#define SMS_INFO_EXCEPTIONS_PAGE       0x1C
 #define SMS_ALL_PAGES_PAGE             0x3F
 #define        SMS_PAGE_CTRL_MASK              0xC0
 #define        SMS_PAGE_CTRL_CURRENT           0x00
@@ -263,6 +266,8 @@ struct scsi_log_sense
 #define        SLS_ERROR_VERIFY_PAGE           0x05
 #define        SLS_ERROR_NONMEDIUM_PAGE        0x06
 #define        SLS_ERROR_LASTN_PAGE            0x07
+#define SLS_SELF_TEST_PAGE             0x10
+#define SLS_IE_PAGE                    0x2f
 #define        SLS_PAGE_CTRL_MASK              0xC0
 #define        SLS_PAGE_CTRL_THRESHOLD         0x00
 #define        SLS_PAGE_CTRL_CUMULATIVE        0x40
@@ -335,6 +340,55 @@ struct scsi_control_page {
        u_int8_t aen_holdoff_period[2];
 };
 
+struct scsi_cache_page {
+       u_int8_t page_code;
+#define SCHP_PAGE_SAVABLE              0x80    /* Page is savable */
+       u_int8_t page_length;
+       u_int8_t cache_flags;
+#define SCHP_FLAGS_WCE                 0x04    /* Write Cache Enable */
+#define SCHP_FLAGS_MF                  0x02    /* Multiplication factor */
+#define SCHP_FLAGS_RCD                 0x01    /* Read Cache Disable */
+       u_int8_t rw_cache_policy;
+       u_int8_t dis_prefetch[2];
+       u_int8_t min_prefetch[2];
+       u_int8_t max_prefetch[2];
+       u_int8_t max_prefetch_ceil[2];
+};
+
+struct scsi_info_exceptions_page {
+       u_int8_t page_code;
+#define SIEP_PAGE_SAVABLE              0x80    /* Page is savable */
+       u_int8_t page_length;
+       u_int8_t info_flags;
+#define SIEP_FLAGS_PERF                        0x80
+#define SIEP_FLAGS_EBF                 0x20
+#define SIEP_FLAGS_EWASC               0x10
+#define SIEP_FLAGS_DEXCPT              0x08
+#define SIEP_FLAGS_TEST                        0x04
+#define SIEP_FLAGS_EBACKERR            0x02
+#define SIEP_FLAGS_LOGERR              0x01
+       u_int8_t mrie;
+       u_int8_t interval_timer[4];
+       u_int8_t report_count[4];
+};
+
+struct scsi_proto_specific_page {
+       u_int8_t page_code;
+#define SPSP_PAGE_SAVABLE              0x80    /* Page is savable */
+       u_int8_t page_length;
+       u_int8_t protocol;
+#define SPSP_PROTO_FC                  0x00
+#define SPSP_PROTO_SPI                 0x01
+#define SPSP_PROTO_SSA                 0x02
+#define SPSP_PROTO_1394                        0x03
+#define SPSP_PROTO_RDMA                        0x04
+#define SPSP_PROTO_ISCSI               0x05
+#define SPSP_PROTO_SAS                 0x06
+#define SPSP_PROTO_ADT                 0x07
+#define SPSP_PROTO_ATA                 0x08
+#define SPSP_PROTO_NONE                        0x0f
+};
+
 struct scsi_reserve
 {
        u_int8_t opcode;
@@ -475,6 +529,47 @@ struct scsi_start_stop_unit
        u_int8_t control;
 };
 
+struct ata_pass_12 {
+       u_int8_t opcode;
+       u_int8_t protocol;
+#define AP_MULTI       0xe0
+       u_int8_t flags;
+#define AP_T_LEN       0x03
+#define AP_BB          0x04
+#define AP_T_DIR       0x08
+#define AP_CK_COND     0x20
+#define AP_OFFLINE     0x60
+       u_int8_t features;
+       u_int8_t sector_count;
+       u_int8_t lba_low;
+       u_int8_t lba_mid;
+       u_int8_t lba_high;
+       u_int8_t device;
+       u_int8_t command;
+       u_int8_t reserved;
+       u_int8_t control;
+};
+
+struct ata_pass_16 {
+       u_int8_t opcode;
+       u_int8_t protocol;
+#define AP_EXTEND      0x01
+       u_int8_t flags;
+       u_int8_t features_ext;
+       u_int8_t features;
+       u_int8_t sector_count_ext;
+       u_int8_t sector_count;
+       u_int8_t lba_low_ext;
+       u_int8_t lba_low;
+       u_int8_t lba_mid_ext;
+       u_int8_t lba_mid;
+       u_int8_t lba_high_ext;
+       u_int8_t lba_high;
+       u_int8_t device;
+       u_int8_t command;
+       u_int8_t control;
+};
+
 #define SC_SCSI_1 0x01
 #define SC_SCSI_2 0x03
 
@@ -501,17 +596,20 @@ struct scsi_start_stop_unit
 #define WRITE_10               0x2a
 #define POSITION_TO_ELEMENT    0x2b
 #define        SYNCHRONIZE_CACHE       0x35
+#define        READ_DEFECT_DATA_10     0x37
 #define        WRITE_BUFFER            0x3b
 #define        READ_BUFFER             0x3c
 #define        CHANGE_DEFINITION       0x40
 #define        LOG_SELECT              0x4c
 #define        LOG_SENSE               0x4d
 #define        MODE_SELECT_10          0x55
-#define        MODE_SENSE_10           0x5A
+#define        MODE_SENSE_10           0x5a
+#define        ATA_PASS_16             0x85
 #define READ_16                        0x88
 #define WRITE_16               0x8a
 #define SERVICE_ACTION_IN      0x9e
 #define REPORT_LUNS            0xa0
+#define        ATA_PASS_12             0xa1
 #define MOVE_MEDIUM            0xa5
 #define READ_12                        0xa8
 #define WRITE_12               0xaa
@@ -605,6 +703,9 @@ struct scsi_inquiry_data
 #define SID_AENC       0x80
 #define SID_TrmIOP     0x40
        u_int8_t additional_length;
+#define        SID_ADDITIONAL_LENGTH(iqd)                                      \
+       ((iqd)->additional_length +                                     \
+       offsetof(struct scsi_inquiry_data, additional_length) + 1)
        u_int8_t reserved;
        u_int8_t spc2_flags;
 #define SPC2_SID_MChngr        0x08
@@ -667,6 +768,17 @@ struct scsi_inquiry_data
        u_int8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
 };
 
+struct scsi_vpd_supported_page_list
+{
+       u_int8_t device;
+       u_int8_t page_code;
+#define SVPD_SUPPORTED_PAGE_LIST 0x00
+       u_int8_t reserved;
+       u_int8_t length;        /* number of VPD entries */
+#define SVPD_SUPPORTED_PAGES_SIZE      251
+       u_int8_t list[SVPD_SUPPORTED_PAGES_SIZE];
+};
+
 struct scsi_vpd_unit_serial_number
 {
        u_int8_t device;
index 7c8ae1f..5eac850 100644 (file)
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_cd.c,v 1.31.2.16 2003/10/21 22:26:11 thomas Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_cd.c,v 1.41 2007/11/29 02:32:43 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/scsi/scsi_cd.c,v 1.42 2008/05/18 20:30:19 pavalos Exp $
  */
 /*
  * Portions of this driver taken from the original FreeBSD cd driver.
@@ -74,6 +74,7 @@
 #include "../cam_periph.h"
 #include "../cam_xpt_periph.h"
 #include "../cam_queue.h"
+#include "../cam_sim.h"
 
 #include "scsi_message.h"
 #include "scsi_da.h"
@@ -107,8 +108,8 @@ typedef enum {
        CD_FLAG_RETRY_UA        = 0x0200,
        CD_FLAG_VALID_MEDIA     = 0x0400,
        CD_FLAG_VALID_TOC       = 0x0800,
-       CD_FLAG_OPEN            = 0x1000,
-       CD_FLAG_SCTX_INIT       = 0x2000
+       CD_FLAG_SCTX_INIT       = 0x1000,
+       CD_FLAG_OPEN            = 0x2000
 } cd_flags;
 
 typedef enum {
@@ -296,10 +297,6 @@ static struct periph_driver cddriver =
 
 PERIPHDRIVER_DECLARE(cd, cddriver);
 
-/* For 2.2-stable support */
-#ifndef D_DISK
-#define D_DISK 0
-#endif
 static struct dev_ops cd_ops = {
        { "cd", SCSICD_CDEV_MAJOR, D_DISK },
        .d_open = cdopen,
@@ -311,7 +308,6 @@ static struct dev_ops cd_ops = {
 };
 
 static struct extend_array *cdperiphs;
-static int num_changers;
 
 #ifndef CHANGER_MIN_BUSY_SECONDS
 #define CHANGER_MIN_BUSY_SECONDS       5
@@ -346,13 +342,19 @@ struct cdchanger {
        STAILQ_HEAD(chdevlist, cd_softc) chluns;
 };
 
+static struct lock changerq_lock;
 static STAILQ_HEAD(changerlist, cdchanger) changerq;
+static int num_changers;
+
+MALLOC_DEFINE(M_SCSICD, "scsi_cd", "scsi_cd buffers");
 
 static void
 cdinit(void)
 {
        cam_status status;
-       struct cam_path *path;
+
+       lockinit(&changerq_lock, "cdchangerq", 0, LK_CANRECURSE);
+       STAILQ_INIT(&changerq);
 
        /*
         * Create our extend array for storing the devices we attach to.
@@ -367,21 +369,7 @@ cdinit(void)
         * Install a global async callback.  This callback will
         * receive async callbacks like "new device found".
         */
-       status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
-                                CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
-
-       if (status == CAM_REQ_CMP) {
-               struct ccb_setasync csa;
-
-                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
-                csa.ccb_h.func_code = XPT_SASYNC_CB;
-                csa.event_enable = AC_FOUND_DEVICE;
-                csa.callback = cdasync;
-                csa.callback_arg = NULL;
-                xpt_action((union ccb *)&csa);
-               status = csa.ccb_h.status;
-                xpt_free_path(path);
-        }
+       status = xpt_register_async(AC_FOUND_DEVICE, cdasync, NULL, NULL);
 
        if (status != CAM_REQ_CMP) {
                kprintf("cd: Failed to attach master async callback "
@@ -395,30 +383,16 @@ cdoninvalidate(struct cam_periph *periph)
        struct cd_softc *softc;
        struct buf *q_bp;
        struct bio *q_bio;
-       struct ccb_setasync csa;
 
        softc = (struct cd_softc *)periph->softc;
 
        /*
         * De-register any async callbacks.
         */
-       xpt_setup_ccb(&csa.ccb_h, periph->path,
-                     /* priority */ 5);
-       csa.ccb_h.func_code = XPT_SASYNC_CB;
-       csa.event_enable = 0;
-       csa.callback = cdasync;
-       csa.callback_arg = periph;
-       xpt_action((union ccb *)&csa);
+       xpt_register_async(0, cdasync, periph, periph->path);
 
        softc->flags |= CD_FLAG_INVALID;
 
-       /*
-        * Although the oninvalidate() routines are always while in a
-        * critical section, we need to be in a critical section here to
-        * keep the buffer queue from being modified while we traverse it.
-        */
-       crit_enter();
-
        /*
         * Return all queued I/O with ENXIO.
         * XXX Handle any transactions queued to the card
@@ -432,7 +406,6 @@ cdoninvalidate(struct cam_periph *periph)
                q_bp->b_flags |= B_ERROR;
                biodone(q_bio);
        }
-       crit_exit();
 
        /*
         * If this device is part of a changer, and it was scheduled
@@ -443,8 +416,7 @@ cdoninvalidate(struct cam_periph *periph)
         && (softc->pinfo.index != CAM_UNQUEUED_INDEX))
                camq_remove(&softc->changer->devq, softc->pinfo.index);
 
-       xpt_print_path(periph->path);
-       kprintf("lost device\n");
+       xpt_print(periph->path, "lost device\n");
 }
 
 static void
@@ -454,16 +426,13 @@ cdcleanup(struct cam_periph *periph)
 
        softc = (struct cd_softc *)periph->softc;
 
-       xpt_print_path(periph->path);
-       kprintf("removing device entry\n");
+       xpt_print(periph->path, "removing device entry\n");
 
        if ((softc->flags & CD_FLAG_SCTX_INIT) != 0
            && sysctl_ctx_free(&softc->sysctl_ctx) != 0) {
-               xpt_print_path(periph->path);
-               kprintf("can't remove sysctl context\n");
+               xpt_print(periph->path, "can't remove sysctl context\n");
        }
 
-       crit_enter();
        /*
         * In the queued, non-active case, the device in question
         * has already been removed from the changer run queue.  Since this
@@ -523,20 +492,22 @@ cdcleanup(struct cam_periph *periph)
                        softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED;
                }
 
+               lockmgr(&changerq_lock, LK_EXCLUSIVE);
                STAILQ_REMOVE(&changerq, softc->changer, cdchanger,
                              changer_links);
-               xpt_print_path(periph->path);
-               kprintf("removing changer entry\n");
-               kfree(softc->changer, M_DEVBUF);
                num_changers--;
+               lockmgr(&changerq_lock, LK_RELEASE);
+               xpt_print(periph->path, "removing changer entry\n");
+               kfree(softc->changer, M_DEVBUF);
        }
        devstat_remove_entry(&softc->device_stats);
        cam_extend_release(cdperiphs, periph->unit_number);
        if (softc->disk.d_rawdev) {
+               cam_periph_unlock(periph);
                disk_destroy(&softc->disk);
+               cam_periph_lock(periph);
        }
        kfree(softc, M_DEVBUF);
-       crit_exit();
 }
 
 static void
@@ -585,7 +556,6 @@ cdasync(void *callback_arg, u_int32_t code,
                struct ccb_hdr *ccbh;
 
                softc = (struct cd_softc *)periph->softc;
-               crit_enter();
                /*
                 * Don't fail on the expected unit attention
                 * that will occur.
@@ -593,7 +563,6 @@ cdasync(void *callback_arg, u_int32_t code,
                softc->flags |= CD_FLAG_RETRY_UA;
                LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le)
                        ccbh->ccb_state |= CD_CCB_RETRY_UA;
-               crit_exit();
                /* FALLTHROUGH */
        }
        default:
@@ -610,8 +579,10 @@ cdsysctlinit(void *context, int pending)
        char tmpstr[80], tmpstr2[80];
 
        periph = (struct cam_periph *)context;
-       softc = (struct cd_softc *)periph->softc;
+       if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+               return;
 
+       softc = (struct cd_softc *)periph->softc;
        ksnprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number);
        ksnprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
 
@@ -623,6 +594,7 @@ cdsysctlinit(void *context, int pending)
 
        if (softc->sysctl_tree == NULL) {
                kprintf("cdsysctlinit: unable to allocate sysctl tree\n");
+               cam_periph_release(periph);
                return;
        }
 
@@ -634,6 +606,8 @@ cdsysctlinit(void *context, int pending)
                        OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW,
                        &softc->minimum_command_size, 0, cdcmdsizesysctl, "I",
                        "Minimum CDB size");
+
+       cam_periph_release(periph);
 }
 
 /*
@@ -677,7 +651,6 @@ static cam_status
 cdregister(struct cam_periph *periph, void *arg)
 {
        struct cd_softc *softc;
-       struct ccb_setasync csa;
        struct ccb_pathinq cpi;
        struct ccb_getdev *cgd;
        char tmpstr[80];
@@ -761,6 +734,7 @@ cdregister(struct cam_periph *periph, void *arg)
         * WORM peripheral driver.  WORM drives will also have the WORM
         * driver attached to them.
         */
+       cam_periph_unlock(periph);
        devstat_add_entry(&softc->device_stats, "cd", 
                          periph->unit_number, 0,
                          DEVSTAT_BS_UNAVAILABLE,
@@ -770,18 +744,14 @@ cdregister(struct cam_periph *periph, void *arg)
        softc->disk.d_rawdev->si_iosize_max = MAXPHYS;
        softc->disk.d_info.d_dsflags = DSO_ONESLICE | DSO_COMPATLABEL |
                                        DSO_COMPATPARTA;
+       cam_periph_lock(periph);
 
        /*
         * Add an async callback so that we get
         * notified if this device goes away.
         */
-       xpt_setup_ccb(&csa.ccb_h, periph->path,
-                     /* priority */ 5);
-       csa.ccb_h.func_code = XPT_SASYNC_CB;
-       csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE;
-       csa.callback = cdasync;
-       csa.callback_arg = periph;
-       xpt_action((union ccb *)&csa);
+       xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE,
+                          cdasync, periph, periph->path);
 
        /*
         * If the target lun is greater than 0, we most likely have a CD
@@ -804,13 +774,11 @@ cdregister(struct cam_periph *periph, void *arg)
                /* Set the changer flag in the current device's softc */
                softc->flags |= CD_FLAG_CHANGER;
 
-               if (num_changers == 0)
-                       STAILQ_INIT(&changerq);
-
                /*
                 * Now, look around for an existing changer device with the
                 * same path and target ID as the current device.
                 */
+               lockmgr(&changerq_lock, LK_EXCLUSIVE);
                for (found = 0,
                     nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq);
                     nchanger != NULL;
@@ -821,6 +789,7 @@ cdregister(struct cam_periph *periph, void *arg)
                                break;
                        }
                }
+               lockmgr(&changerq_lock, LK_RELEASE);
 
                /*
                 * If we found a matching entry, just add this device to
@@ -911,8 +880,6 @@ cdregister(struct cam_periph *periph, void *arg)
                                goto cdregisterexit;
                        }
 
-                       num_changers++;
-
                        nchanger->path_id = cgd->ccb_h.path_id;
                        nchanger->target_id = cgd->ccb_h.target_id;
 
@@ -921,8 +888,14 @@ cdregister(struct cam_periph *periph, void *arg)
 
                        STAILQ_INIT(&nchanger->chluns);
 
+                       callout_init(&nchanger->long_handle);
+                       callout_init(&nchanger->short_handle);
+
+                       lockmgr(&changerq_lock, LK_EXCLUSIVE);
+                       num_changers++;
                        STAILQ_INSERT_TAIL(&changerq, nchanger,
                                           changer_links);
+                       lockmgr(&changerq_lock, LK_RELEASE);
                        
                        /*
                         * Create a path with lun id 0, and see if we can
@@ -984,9 +957,11 @@ cdregister(struct cam_periph *periph, void *arg)
 
 cdregisterexit:
 
-       /* Lock this peripheral until we are setup */
-       /* Can't block */
-       cam_periph_lock(periph, 0); 
+       /*
+        * Refcount and block open attempts until we are setup
+        * Can't block
+        */
+       cam_periph_hold(periph, 0);
 
        if ((softc->flags & CD_FLAG_CHANGER) == 0)
                xpt_schedule(periph, /*priority*/5);
@@ -1012,24 +987,28 @@ cdopen(struct dev_open_args *ap)
 
        softc = (struct cd_softc *)periph->softc;
 
-       /*
-        * Grab a critical section and hold it until we lock the peripheral.
-        */
-       crit_enter();
+       if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+               return(ENXIO);
+
+       cam_periph_lock(periph);
+
        if (softc->flags & CD_FLAG_INVALID) {
-               crit_exit();
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
                return(ENXIO);
        }
 
-       if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
-               crit_exit();
+       if ((error = cam_periph_hold(periph, PCATCH)) != 0) {
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
                return (error);
        }
 
-       crit_exit();
-
-       if (cam_periph_acquire(periph) != CAM_REQ_CMP)
-               return(ENXIO);
+       /* Closes aren't symmetrical with opens, so fix up the refcounting. */
+       if (softc->flags & CD_FLAG_OPEN)
+               cam_periph_release(periph);
+       else
+               softc->flags |= CD_FLAG_OPEN;
 
        /*
         * Check for media, and set the appropriate flags.  We don't bail
@@ -1045,11 +1024,11 @@ cdopen(struct dev_open_args *ap)
        if (error == 0)
                softc->flags |= CD_FLAG_OPEN;
 
-       cam_periph_unlock(periph);
-
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
+       cam_periph_unhold(periph);
+       cam_periph_unlock(periph);
 
-       return (error);
+       return (0);
 }
 
 static int
@@ -1059,7 +1038,7 @@ cdclose(struct dev_close_args *ap)
        struct  cam_periph *periph;
        struct  cd_softc *softc;
        struct  disk_info *info;
-       int     unit, error;
+       int     unit;
 
        unit = dkunit(dev);
        periph = cam_extend_get(cdperiphs, unit);
@@ -1068,8 +1047,8 @@ cdclose(struct dev_close_args *ap)
 
        softc = (struct cd_softc *)periph->softc;
 
-       if ((error = cam_periph_lock(periph, 0)) != 0)
-               return (error);
+       cam_periph_lock(periph);
+       cam_periph_hold(periph, 0);
 
        if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0)
                cdprevent(periph, PR_ALLOW);
@@ -1091,10 +1070,9 @@ cdclose(struct dev_close_args *ap)
        /*
         * We'll check the media and toc again at the next open().
         */
-       softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC);
-       
-       softc->flags &= ~CD_FLAG_OPEN;
+       softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC|CD_FLAG_OPEN);
 
+       cam_periph_unhold(periph);
        cam_periph_unlock(periph);
        cam_periph_release(periph);
 
@@ -1106,8 +1084,6 @@ cdshorttimeout(void *arg)
 {
        struct cdchanger *changer;
 
-       crit_enter();
-
        changer = (struct cdchanger *)arg;
 
        /* Always clear the short timeout flag, since that's what we're in */
@@ -1122,7 +1098,6 @@ cdshorttimeout(void *arg)
                changer->flags |= CHANGER_MANUAL_CALL;
                cdrunchangerqueue(changer);
        }
-       crit_exit();
 }
 
 /*
@@ -1133,8 +1108,6 @@ cdschedule(struct cam_periph *periph, int priority)
 {
        struct cd_softc *softc;
 
-       crit_enter();
-
        softc = (struct cd_softc *)periph->softc;
 
        /*
@@ -1173,7 +1146,6 @@ cdschedule(struct cam_periph *periph, int priority)
                && ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0)) {
                xpt_schedule(periph, priority);
        }
-       crit_exit();
 }
 
 static void
@@ -1183,8 +1155,6 @@ cdrunchangerqueue(void *arg)
        struct cdchanger *changer;
        int called_from_timeout;
 
-       crit_enter();
-
        changer = (struct cdchanger *)arg;
 
        /*
@@ -1203,7 +1173,6 @@ cdrunchangerqueue(void *arg)
 
        /* nothing to do if the queue is empty */
        if (changer->devq.entries <= 0) {
-               crit_exit();
                return;
        }
 
@@ -1213,6 +1182,14 @@ cdrunchangerqueue(void *arg)
         */
        if (changer->devq.qfrozen_cnt > 0) {
 
+               /*
+                * We always need to reset the frozen count and clear the
+                * active flag.
+                */
+               changer->devq.qfrozen_cnt--;
+               changer->cur_device->flags &= ~CD_FLAG_ACTIVE;
+               changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP;
+
                if (changer->cur_device->outstanding_cmds > 0) {
                        changer->cur_device->flags |= CD_FLAG_SCHED_ON_COMP;
                        changer->cur_device->bufs_left = 
@@ -1223,18 +1200,9 @@ cdrunchangerqueue(void *arg)
                                        cdrunchangerqueue, changer);
                                changer->flags |= CHANGER_TIMEOUT_SCHED;
                        }
-                       crit_exit();
                        return;
                }
 
-               /*
-                * We always need to reset the frozen count and clear the
-                * active flag.
-                */
-               changer->devq.qfrozen_cnt--;
-               changer->cur_device->flags &= ~CD_FLAG_ACTIVE;
-               changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP;
-
                /*
                 * Check to see whether the current device has any I/O left
                 * to do.  If so, requeue it at the end of the queue.  If
@@ -1280,8 +1248,6 @@ cdrunchangerqueue(void *arg)
         * switch time.
         */
        changer->flags |= CHANGER_NEED_TIMEOUT;
-
-       crit_exit();
 }
 
 static void
@@ -1289,8 +1255,6 @@ cdchangerschedule(struct cd_softc *softc)
 {
        struct cdchanger *changer;
 
-       crit_enter();
-
        changer = softc->changer;
 
        /*
@@ -1359,7 +1323,6 @@ cdchangerschedule(struct cd_softc *softc)
                changer->flags &= ~CHANGER_NEED_TIMEOUT;
 
        }
-       crit_exit();
 }
 
 static int
@@ -1392,8 +1355,6 @@ cdgetccb(struct cam_periph *periph, u_int32_t priority)
        softc = (struct cd_softc *)periph->softc;
 
        if (softc->flags & CD_FLAG_CHANGER) {
-               crit_enter();
-
                /*
                 * This should work the first time this device is woken up,
                 * but just in case it doesn't, we use a while loop.
@@ -1416,9 +1377,9 @@ cdgetccb(struct cam_periph *periph, u_int32_t priority)
                                softc->changer->flags |= CHANGER_MANUAL_CALL;
                                cdrunchangerqueue(softc->changer);
                        } else
-                               tsleep(&softc->changer, 0, "cgticb", 0);
+                               sim_lock_sleep(&softc->changer, 0, "cgticb", 0,
+                                              periph->sim->lock);
                }
-               crit_exit();
        }
        return(cam_periph_getccb(periph, priority));
 }
@@ -1445,22 +1406,16 @@ cdstrategy(struct dev_strategy_args *ap)
                goto bad;
        }
 
+       cam_periph_lock(periph);
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstrategy\n"));
 
        softc = (struct cd_softc *)periph->softc;
 
-       /*
-        * Mask interrupts so that the pack cannot be invalidated until
-        * after we are in the queue.  Otherwise, we might not properly
-        * clean up one of the buffers.
-        */
-       crit_enter();
-       
        /*
         * If the device has been made invalid, error out
         */
        if ((softc->flags & CD_FLAG_INVALID)) {
-               crit_exit();
+               cam_periph_unlock(periph);
                bp->b_error = ENXIO;
                goto bad;
        }
@@ -1474,7 +1429,7 @@ cdstrategy(struct dev_strategy_args *ap)
 
                error = cdcheckmedia(periph);
                if (error != 0) {
-                       crit_exit();
+                       cam_periph_unlock(periph);
                        bp->b_error = error;
                        goto bad;
                }
@@ -1485,8 +1440,6 @@ cdstrategy(struct dev_strategy_args *ap)
         */
        bioqdisksort(&softc->bio_queue, bio);
 
-       crit_exit();
-       
        /*
         * Schedule ourselves for performing the work.  We do things
         * differently for changers.
@@ -1496,6 +1449,7 @@ cdstrategy(struct dev_strategy_args *ap)
        else
                cdschedule(periph, /* priority */ 1);
 
+       cam_periph_unlock(periph);
        return(0);
 bad:
        bp->b_flags |= B_ERROR;
@@ -1523,7 +1477,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
        switch (softc->state) {
        case CD_STATE_NORMAL:
        {
-               crit_enter();
                bio = bioq_first(&softc->bio_queue);
                if (periph->immediate_priority <= periph->pinfo.priority) {
                        start_ccb->ccb_h.ccb_state = CD_CCB_WAITING;
@@ -1531,10 +1484,8 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
                        SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
                                          periph_links.sle);
                        periph->immediate_priority = CAM_PRIORITY_NONE;
-                       crit_exit();
                        wakeup(&periph->ccb_list);
                } else if (bio == NULL) {
-                       crit_exit();
                        xpt_release_ccb(start_ccb);
                } else {
                        bp = bio->bio_buf;
@@ -1563,10 +1514,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
                        start_ccb->ccb_h.ccb_state = CD_CCB_BUFFER_IO;
 
                        
-                       /*
-                        * Block out any asyncronous callbacks
-                        * while we touch the pending ccb list.
-                        */
                        LIST_INSERT_HEAD(&softc->pending_ccbs,
                                         &start_ccb->ccb_h, periph_links.le);
 
@@ -1579,7 +1526,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
 
                        start_ccb->ccb_h.ccb_bio = bio;
                        bio = bioq_first(&softc->bio_queue);
-                       crit_exit();
 
                        xpt_action(start_ccb);
                }
@@ -1592,7 +1538,7 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb)
        case CD_STATE_PROBE:
        {
 
-               rcap = kmalloc(sizeof(*rcap), M_TEMP, M_INTWAIT);
+               rcap = kmalloc(sizeof(*rcap), M_SCSICD, M_INTWAIT);
                csio = &start_ccb->csio;
                scsi_read_capacity(csio,
                                   /*retries*/1,
@@ -1653,9 +1599,8 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                        struct bio *q_bio;
                        struct buf *q_bp;
 
-                       xpt_print_path(periph->path);
-                       kprintf("cddone: got error %#x back\n", error);
-                       crit_enter();
+                       xpt_print(periph->path,
+                                 "cddone: got error %#x back\n", error);
                        while ((q_bio = bioq_first(&softc->bio_queue)) != NULL) {
                                bioq_remove(&softc->bio_queue, q_bio);
                                q_bp = q_bio->bio_buf;
@@ -1664,7 +1609,6 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                                q_bp->b_flags |= B_ERROR;
                                biodone(q_bio);
                        }
-                       crit_exit();
                        bp->b_resid = bp->b_bcount;
                        bp->b_error = error;
                        bp->b_flags |= B_ERROR;
@@ -1683,14 +1627,8 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                        }
                }
 
-               /*
-                * Block out any asyncronous callbacks
-                * while we touch the pending ccb list.
-                */
-               crit_enter();
                LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
                softc->outstanding_cmds--;
-               crit_exit();
 
                if (softc->flags & CD_FLAG_CHANGER)
                        cdchangerschedule(softc);
@@ -1829,14 +1767,12 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                                                scsi_sense_print(
                                                        &done_ccb->csio);
                                        else {
-                                               xpt_print_path(periph->path);
-                                               kprintf("got CAM status %#x\n",
-                                                      done_ccb->ccb_h.status);
+                                               xpt_print(periph->path,
+                                                   "got CAM status %#x\n",
+                                                   done_ccb->ccb_h.status);
                                        }
-                                       xpt_print_path(periph->path);
-                                       kprintf("fatal error, failed" 
-                                              " to attach to device\n");
-
+                                       xpt_print(periph->path, "fatal error, "
+                                           "failed to attach to device\n");
                                        /*
                                         * Invalidate this peripheral.
                                         */
@@ -1853,7 +1789,7 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                                }
                        }
                }
-               kfree(rdcap, M_TEMP);
+               kfree(rdcap, M_SCSICD);
                if (announce_buf[0] != '\0') {
                        xpt_announce_periph(periph, announce_buf);
                        if (softc->flags & CD_FLAG_CHANGER)
@@ -1875,7 +1811,7 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
                 * operation.
                 */
                xpt_release_ccb(done_ccb);
-               cam_periph_unlock(periph);
+               cam_periph_unhold(periph);
                return;
        }
        case CD_CCB_WAITING:
@@ -1928,7 +1864,7 @@ cdioctl(struct dev_ioctl_args *ap)
        caddr_t addr = ap->a_data;
        struct  cam_periph *periph;
        struct  cd_softc *softc;
-       int     error, unit;
+       int     unit, error = 0;
 
        unit = dkunit(dev);
 
@@ -1936,6 +1872,7 @@ cdioctl(struct dev_ioctl_args *ap)
        if (periph == NULL)
                return(ENXIO);  
 
+       cam_periph_lock(periph);
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdioctl\n"));
 
        softc = (struct cd_softc *)periph->softc;
@@ -1943,10 +1880,11 @@ cdioctl(struct dev_ioctl_args *ap)
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 
                  ("trying to do ioctl %#lx\n", ap->a_cmd));
 
-       error = cam_periph_lock(periph, PCATCH);
-
-       if (error != 0)
-               return(error);
+       if ((error = cam_periph_hold(periph, PCATCH)) != 0) {
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
+               return (error);
+       }
 
        /*
         * If we don't have media loaded, check for it.  If still don't
@@ -1963,10 +1901,16 @@ cdioctl(struct dev_ioctl_args *ap)
            && (IOCGROUP(ap->a_cmd) == 'c')) {
                error = cdcheckmedia(periph);
                if (error != 0) {
+                       cam_periph_unhold(periph);
                        cam_periph_unlock(periph);
                        return (error);
                }
        }
+       /*
+        * Drop the lock here so later mallocs can use WAITOK.  The periph
+        * is essentially locked still with the cam_periph_hold call above.
+        */
+       cam_periph_unlock(periph);
 
        switch (ap->a_cmd) {
 
@@ -1978,15 +1922,17 @@ cdioctl(struct dev_ioctl_args *ap)
                        union cd_pages *page;
 
                        params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
                                                 M_WAITOK | M_ZERO);
 
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCPLAYTRACKS\n"));
 
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -1994,9 +1940,11 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.flags &= ~CD_PA_SOTC;
                        page->audio.flags |= CD_PA_IMMED;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
-                       if (error)
+                       kfree(params.mode_buf, M_SCSICD);
+                       if (error) {
+                               cam_periph_unlock(periph);
                                break;
+                       }
 
                        /*
                         * This was originally implemented with the PLAY
@@ -2058,6 +2006,7 @@ cdioctl(struct dev_ioctl_args *ap)
                                                     args->end_track,
                                                     args->end_index);
                        }
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCPLAYMSF:
@@ -2068,15 +2017,17 @@ cdioctl(struct dev_ioctl_args *ap)
                        union cd_pages *page;
 
                        params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
                                                 M_WAITOK | M_ZERO);
 
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCPLAYMSF\n"));
 
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2084,9 +2035,11 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.flags &= ~CD_PA_SOTC;
                        page->audio.flags |= CD_PA_IMMED;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
-                       if (error)
+                       kfree(params.mode_buf, M_SCSICD);
+                       if (error) {
+                               cam_periph_unlock(periph);
                                break;
+                       }
                        error = cdplaymsf(periph,
                                          args->start_m,
                                          args->start_s,
@@ -2094,6 +2047,7 @@ cdioctl(struct dev_ioctl_args *ap)
                                          args->end_m,
                                          args->end_s,
                                          args->end_f);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCPLAYBLOCKS:
@@ -2103,16 +2057,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCPLAYBLOCKS\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
-                                                M_WAITOK | M_ZERO);
 
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2120,10 +2076,13 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.flags &= ~CD_PA_SOTC;
                        page->audio.flags |= CD_PA_IMMED;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
-                       if (error)
+                       kfree(params.mode_buf, M_SCSICD);
+                       if (error) {
+                               cam_periph_unlock(periph);
                                break;
+                       }
                        error = cdplay(periph, args->blk, args->len);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCREADSUBCHANNEL:
@@ -2133,12 +2092,13 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_sub_channel_info *data;
                        u_int32_t len = args->data_len;
 
+                       data = kmalloc(sizeof(struct cd_sub_channel_info),
+                                     M_SCSICD, M_WAITOK);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCREADSUBCHANNEL\n"));
 
-                       data = kmalloc(sizeof(struct cd_sub_channel_info), 
-                                     M_TEMP, M_WAITOK);
-
                        if ((len > sizeof(struct cd_sub_channel_info)) ||
                            (len < sizeof(struct cd_sub_channel_header))) {
                                kprintf(
@@ -2146,7 +2106,8 @@ cdioctl(struct dev_ioctl_args *ap)
                                        "cdioreadsubchannel: error, len=%d\n",
                                        len);
                                error = EINVAL;
-                               kfree(data, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2157,7 +2118,8 @@ cdioctl(struct dev_ioctl_args *ap)
                                args->data_format, args->track, data, len);
 
                        if (error) {
-                               kfree(data, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        if (softc->quirks & CD_Q_BCD_TRACKS)
@@ -2166,10 +2128,11 @@ cdioctl(struct dev_ioctl_args *ap)
                        len = min(len, ((data->header.data_len[0] << 8) +
                                data->header.data_len[1] +
                                sizeof(struct cd_sub_channel_header)));
+                       cam_periph_unlock(periph);
                        if (copyout(data, args->data, len) != 0) {
                                error = EFAULT;
                        }
-                       kfree(data, M_TEMP);
+                       kfree(data, M_SCSICD);
                }
                break;
 
@@ -2177,15 +2140,18 @@ cdioctl(struct dev_ioctl_args *ap)
                {
                        struct ioc_toc_header *th;
 
+                       th = kmalloc(sizeof(struct ioc_toc_header), M_SCSICD,
+                                   M_WAITOK);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOREADTOCHEADER\n"));
 
-                       th = kmalloc(sizeof(struct ioc_toc_header), M_TEMP,
-                                   M_WAITOK);
                        error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, 
                                          sizeof (*th), /*sense_flags*/0);
                        if (error) {
-                               kfree(th, M_TEMP);
+                               kfree(th, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        if (softc->quirks & CD_Q_BCD_TRACKS) {
@@ -2198,7 +2164,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        }
                        th->len = ntohs(th->len);
                        bcopy(th, addr, sizeof(*th));
-                       kfree(th, M_TEMP);
+                       kfree(th, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOREADTOCENTRYS:
@@ -2211,12 +2178,13 @@ cdioctl(struct dev_ioctl_args *ap)
                        u_int32_t len, readlen, idx, num;
                        u_int32_t starting_track = te->starting_track;
 
+                       data = kmalloc(sizeof(*data), M_SCSICD, M_WAITOK);
+                       lead = kmalloc(sizeof(*lead), M_SCSICD, M_WAITOK);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOREADTOCENTRYS\n"));
 
-                       data = kmalloc(sizeof(*data), M_TEMP, M_WAITOK);
-                       lead = kmalloc(sizeof(*lead), M_TEMP, M_WAITOK);
-
                        if (te->data_len < sizeof(struct cd_toc_entry)
                         || (te->data_len % sizeof(struct cd_toc_entry)) != 0
                         || (te->address_format != CD_MSF_FORMAT
@@ -2224,8 +2192,9 @@ cdioctl(struct dev_ioctl_args *ap)
                                error = EINVAL;
                                kprintf("scsi_cd: error in readtocentries, "
                                       "returning EINVAL\n");
-                               kfree(data, M_TEMP);
-                               kfree(lead, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               kfree(lead, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2233,8 +2202,9 @@ cdioctl(struct dev_ioctl_args *ap)
                        error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, 
                                          sizeof (*th), /*sense_flags*/0);
                        if (error) {
-                               kfree(data, M_TEMP);
-                               kfree(lead, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               kfree(lead, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2255,8 +2225,9 @@ cdioctl(struct dev_ioctl_args *ap)
                                 starting_track > th->ending_track + 1) {
                                kprintf("scsi_cd: error in readtocentries, "
                                       "returning EINVAL\n");
-                               kfree(data, M_TEMP);
-                               kfree(lead, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               kfree(lead, M_SCSICD);
+                               cam_periph_unlock(periph);
                                error = EINVAL;
                                break;
                        }
@@ -2276,8 +2247,9 @@ cdioctl(struct dev_ioctl_args *ap)
                                kprintf("scsi_cd: error in readtocentries, "
                                       "returning EINVAL\n");
                                error = EINVAL;
-                               kfree(data, M_TEMP);
-                               kfree(lead, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               kfree(lead, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        num = len / sizeof(struct cd_toc_entry);
@@ -2289,8 +2261,9 @@ cdioctl(struct dev_ioctl_args *ap)
                                                  readlen + sizeof (*th),
                                                  /*sense_flags*/0);
                                if (error) {
-                                       kfree(data, M_TEMP);
-                                       kfree(lead, M_TEMP);
+                                       kfree(data, M_SCSICD);
+                                       kfree(lead, M_SCSICD);
+                                       cam_periph_unlock(periph);
                                        break;
                                }
                        }
@@ -2305,8 +2278,9 @@ cdioctl(struct dev_ioctl_args *ap)
                                                  sizeof(*lead),
                                                  /*sense_flags*/0);
                                if (error) {
-                                       kfree(data, M_TEMP);
-                                       kfree(lead, M_TEMP);
+                                       kfree(data, M_SCSICD);
+                                       kfree(lead, M_SCSICD);
+                                       cam_periph_unlock(periph);
                                        break;
                                }
                                data->entries[idx - starting_track] = 
@@ -2319,9 +2293,10 @@ cdioctl(struct dev_ioctl_args *ap)
                                }
                        }
 
+                       cam_periph_unlock(periph);
                        error = copyout(data->entries, te->data, len);
-                       kfree(data, M_TEMP);
-                       kfree(lead, M_TEMP);
+                       kfree(data, M_SCSICD);
+                       kfree(lead, M_SCSICD);
                }
                break;
        case CDIOREADTOCENTRY:
@@ -2332,17 +2307,19 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct ioc_toc_header *th;
                        u_int32_t track;
 
-                       CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
-                                 ("trying to do CDIOREADTOCENTRY\n"));
+                       data = kmalloc(sizeof(*data), M_SCSICD, M_WAITOK);
 
-                       data = kmalloc(sizeof(*data), M_TEMP, M_WAITOK);
+                       cam_periph_lock(periph);
+                       CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
+                                 ("trying to do CDIOREADTOCENTRY\n"));
 
                        if (te->address_format != CD_MSF_FORMAT
                            && te->address_format != CD_LBA_FORMAT) {
                                kprintf("error in readtocentry, "
                                       " returning EINVAL\n");
-                               kfree(data, M_TEMP);
+                               kfree(data, M_SCSICD);
                                error = EINVAL;
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2350,7 +2327,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
                                          sizeof (*th), /*sense_flags*/0);
                        if (error) {
-                               kfree(data, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2371,8 +2349,9 @@ cdioctl(struct dev_ioctl_args *ap)
                                 track > th->ending_track + 1) {
                                kprintf("error in readtocentry, "
                                       " returning EINVAL\n");
-                               kfree(data, M_TEMP);
+                               kfree(data, M_SCSICD);
                                error = EINVAL;
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2380,7 +2359,8 @@ cdioctl(struct dev_ioctl_args *ap)
                                          (u_int8_t *)data, sizeof(*data),
                                          /*sense_flags*/0);
                        if (error) {
-                               kfree(data, M_TEMP);
+                               kfree(data, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
 
@@ -2388,7 +2368,8 @@ cdioctl(struct dev_ioctl_args *ap)
                                data->entry.track = bcd2bin(data->entry.track);
                        bcopy(&data->entry, &te->entry,
                              sizeof(struct cd_toc_entry));
-                       kfree(data, M_TEMP);
+                       kfree(data, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCSETPATCH:
@@ -2397,15 +2378,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCSETPATCH\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP, 
-                                                M_WAITOK | M_ZERO);
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2417,7 +2401,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].channels = arg->patch[2];
                        page->audio.port[3].channels = arg->patch[3];
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       kfree(params.mode_buf, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCGETVOL:
@@ -2426,15 +2411,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCGETVOL\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP, 
-                                                M_WAITOK | M_ZERO);
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2445,7 +2433,8 @@ cdioctl(struct dev_ioctl_args *ap)
                                page->audio.port[RIGHT_PORT].volume;
                        arg->vol[2] = page->audio.port[2].volume;
                        arg->vol[3] = page->audio.port[3].volume;
-                       kfree(params.mode_buf, M_TEMP);
+                       kfree(params.mode_buf, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCSETVOL:
@@ -2454,15 +2443,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCSETVOL\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP, 
-                                                M_WAITOK | M_ZERO);
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2476,7 +2468,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].volume = arg->vol[2];
                        page->audio.port[3].volume = arg->vol[3];
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       cam_periph_unlock(periph);
+                       kfree(params.mode_buf, M_SCSICD);
                }
                break;
        case CDIOCSETMONO:
@@ -2484,15 +2477,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCSETMONO\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
-                                                M_WAITOK | M_ZERO);
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2504,7 +2500,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].channels = 0;
                        page->audio.port[3].channels = 0;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       cam_periph_unlock(periph);
+                       kfree(params.mode_buf, M_SCSICD);
                }
                break;
        case CDIOCSETSTEREO:
@@ -2512,15 +2509,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCSETSTEREO\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
-                                                M_WAITOK | M_ZERO);
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2532,7 +2532,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].channels = 0;
                        page->audio.port[3].channels = 0;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       kfree(params.mode_buf, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCSETMUTE:
@@ -2540,15 +2541,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCSETMUTE\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
-                                                M_WAITOK | M_ZERO);
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(&params, M_TEMP);
+                               kfree(&params, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2558,7 +2562,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].channels = 0;
                        page->audio.port[3].channels = 0;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       kfree(params.mode_buf, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCSETLEFT:
@@ -2566,16 +2571,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
+                       params.alloc_len = sizeof(union cd_mode_data_6_10);
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
+                                                M_WAITOK | M_ZERO);
+
+                       cam_periph_lock(periph);
                        CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
                                  ("trying to do CDIOCSETLEFT\n"));
 
-                       params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
-                                                M_WAITOK | M_ZERO);
-                       
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2585,7 +2592,8 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].channels = 0;
                        page->audio.port[3].channels = 0;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       kfree(params.mode_buf, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCSETRIGHT:
@@ -2593,16 +2601,18 @@ cdioctl(struct dev_ioctl_args *ap)
                        struct cd_mode_params params;
                        union cd_pages *page;
 
-                       CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 
-                                 ("trying to do CDIOCSETRIGHT\n"));
-
                        params.alloc_len = sizeof(union cd_mode_data_6_10);
-                       params.mode_buf = kmalloc(params.alloc_len, M_TEMP,
+                       params.mode_buf = kmalloc(params.alloc_len, M_SCSICD,
                                                 M_WAITOK | M_ZERO);
 
+                       cam_periph_lock(periph);
+                       CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
+                                 ("trying to do CDIOCSETRIGHT\n"));
+
                        error = cdgetmode(periph, &params, AUDIO_PAGE);
                        if (error) {
-                               kfree(params.mode_buf, M_TEMP);
+                               kfree(params.mode_buf, M_SCSICD);
+                               cam_periph_unlock(periph);
                                break;
                        }
                        page = cdgetpage(&params);
@@ -2612,56 +2622,49 @@ cdioctl(struct dev_ioctl_args *ap)
                        page->audio.port[2].channels = 0;
                        page->audio.port[3].channels = 0;
                        error = cdsetmode(periph, &params);
-                       kfree(params.mode_buf, M_TEMP);
+                       kfree(params.mode_buf, M_SCSICD);
+                       cam_periph_unlock(periph);
                }
                break;
        case CDIOCRESUME:
+               cam_periph_lock(periph);
                error = cdpause(periph, 1);
+               cam_periph_unlock(periph);
                break;
        case CDIOCPAUSE:
+               cam_periph_lock(periph);
                error = cdpause(periph, 0);
+               cam_periph_unlock(periph);
                break;
        case CDIOCSTART:
+               cam_periph_lock(periph);
                error = cdstartunit(periph, 0);
+               cam_periph_unlock(periph);
                break;
        case CDIOCCLOSE:
+               cam_periph_lock(periph);
                error = cdstartunit(periph, 1);
-
-#ifdef notyet
-               if (error != 0)
-                       break;
-
-               /*
-                * The user successfully closed the tray, run
-                * cdcheckmedia() again so we can re-sync the disklabel
-                * information.
-                */
-               cdcheckmedia(periph);
-#endif /* notyet */
+               cam_periph_unlock(periph);
                break;
        case CDIOCSTOP:
+               cam_periph_lock(periph);
                error = cdstopunit(periph, 0);
+               cam_periph_unlock(periph);
                break;
        case CDIOCEJECT:
+               cam_periph_lock(periph);
                error = cdstopunit(periph, 1);
-
-#ifdef notyet
-               if (error != 0)
-                       break;
-
-               /*
-                * Since we've successfully ejected the media, run
-                * cdcheckmedia() again so we re-sync the disklabel
-                * information.
-                */
-               cdcheckmedia(periph);
-#endif /* notyet */
+               cam_periph_unlock(periph);
                break;
        case CDIOCALLOW:
+               cam_periph_lock(periph);
                cdprevent(periph, PR_ALLOW);
+               cam_periph_unlock(periph);
                break;
        case CDIOCPREVENT:
+               cam_periph_lock(periph);
                cdprevent(periph, PR_PREVENT);
+               cam_periph_unlock(periph);
                break;
        case CDIOCSETDEBUG:
                /* sc_link->flags |= (SDEV_DB1 | SDEV_DB2); */
@@ -2676,10 +2679,14 @@ cdioctl(struct dev_ioctl_args *ap)
                error = ENOTTY;
                break;
        case CDRIOCREADSPEED:
+               cam_periph_lock(periph);
                error = cdsetspeed(periph, *(u_int32_t *)addr, CDR_MAX_SPEED);
+               cam_periph_unlock(periph);
                break;
        case CDRIOCWRITESPEED:
+               cam_periph_lock(periph);
                error = cdsetspeed(periph, CDR_MAX_SPEED, *(u_int32_t *)addr);
+               cam_periph_unlock(periph);
                break;
        case DVDIOCSENDKEY:
        case DVDIOCREPORTKEY: {
@@ -2687,10 +2694,12 @@ cdioctl(struct dev_ioctl_args *ap)
 
                authinfo = (struct dvd_authinfo *)addr;
 
+               cam_periph_lock(periph);
                if (ap->a_cmd == DVDIOCREPORTKEY)
                        error = cdreportkey(periph, authinfo);
                else
                        error = cdsendkey(periph, authinfo);
+               cam_periph_unlock(periph);
                break;
        }
        case DVDIOCREADSTRUCTURE: {
@@ -2698,19 +2707,26 @@ cdioctl(struct dev_ioctl_args *ap)
 
                dvdstruct = (struct dvd_struct *)addr;
 
+               cam_periph_lock(periph);
                error = cdreaddvdstructure(periph, dvdstruct);
+               cam_periph_unlock(periph);
 
                break;
        }
        default:
+               cam_periph_lock(periph);
                error = cam_periph_ioctl(periph, ap->a_cmd, addr, cderror);
+               cam_periph_unlock(periph);
                break;
        }
 
-       cam_periph_unlock(periph);
+       cam_periph_lock(periph);
+       cam_periph_unhold(periph);
 
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdioctl\n"));
 
+       cam_periph_unlock(periph);
+
        return (error);
 }
 
@@ -3034,7 +3050,7 @@ cdsize(struct cam_periph *periph, u_int32_t *size)
        ccb = cdgetccb(periph, /* priority */ 1);
 
        rcap_buf = kmalloc(sizeof(struct scsi_read_capacity_data), 
-                         M_TEMP, M_INTWAIT | M_ZERO);
+                         M_SCSICD, M_INTWAIT | M_ZERO);
 
        scsi_read_capacity(&ccb->csio, 
                           /*retries*/ 1,
@@ -3062,7 +3078,7 @@ cdsize(struct cam_periph *periph, u_int32_t *size)
        if (softc->params.blksize > 2048 && softc->params.blksize <= 2352)
                softc->params.blksize = 2048;
 
-       kfree(rcap_buf, M_TEMP);
+       kfree(rcap_buf, M_SCSICD);
        *size = softc->params.disksize;
 
        return (error);
@@ -3113,16 +3129,16 @@ cd6byteworkaround(union ccb *ccb)
         * cdsetmode()!
         */
        if (found == 0) {
-               xpt_print_path(periph->path);
-               kprintf("mode buffer not found in mode queue!\n");
+               xpt_print(periph->path,
+                   "mode buffer not found in mode queue!\n");
                return (0);
        }
 
        params->cdb_size = 10;
        softc->minimum_command_size = 10;
-       xpt_print_path(ccb->ccb_h.path);
-       kprintf("%s(6) failed, increasing minimum CDB size to 10 bytes\n",
-              (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT");
+       xpt_print(ccb->ccb_h.path,
+           "%s(6) failed, increasing minimum CDB size to 10 bytes\n",
+           (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT");
 
        if (cdb[0] == MODE_SENSE_6) {
                struct scsi_mode_sense_10 ms10;
@@ -3453,10 +3469,9 @@ cdgetmode(struct cam_periph *periph, struct cd_mode_params *data,
                 * the data length incorrectly.
                 */
                if (data_len > data->alloc_len) {
-                       xpt_print_path(periph->path);
-                       kprintf("allocated modepage %d length %d < returned "
-                              "length %d\n", page, data->alloc_len, data_len);
-
+                       xpt_print(periph->path, "allocated modepage %d length "
+                           "%d < returned length %d\n", page, data->alloc_len,
+                           data_len);
                        error = ENOSPC;
                }
        }
@@ -3916,9 +3931,8 @@ cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo)
                goto bailout;
 
        if (ccb->csio.resid != 0) {
-               xpt_print_path(periph->path);
-               kprintf("warning, residual for report key command is %d\n",
-                      ccb->csio.resid);
+               xpt_print(periph->path, "warning, residual for report key "
+                   "command is %d\n", ccb->csio.resid);
        }
 
        switch(authinfo->format) {
index 7707918..dc5de89 100644 (file)
@@ -25,7 +25,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/cam/scsi/scsi_ch.c,v 1.20.2.2 2000/10/31 08:09:49 dwmalone Exp $
- * $DragonFly: src/sys/bus/cam/scsi/scsi_ch.c,v 1.26 2007/11/29 01:48:15 pavalos Exp $
+ * $DragonFly: src/sys/bus/cam/scsi/scsi_ch.c,v 1.27 2008/05/18 20:30:20 pavalos Exp $
  */
 /*
  * Derived from the NetBSD SCSI changer driver.
@@ -223,11 +223,12 @@ static struct dev_ops ch_ops = {
 
 static struct extend_array *chperiphs;
 
+MALLOC_DEFINE(M_SCSICH, "scsi_ch", "scsi_ch buffers");
+
 static void
 chinit(void)
 {
        cam_status status;
-       struct cam_path *path;
 
        /*
         * Create our extend array for storing the devices we attach to.
@@ -242,21 +243,7 @@ chinit(void)
         * Install a global async callback.  This callback will
         * receive async callbacks like "new device found".
         */
-       status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID,
-                                CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
-
-       if (status == CAM_REQ_CMP) {
-               struct ccb_setasync csa;
-
-                xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
-                csa.ccb_h.func_code = XPT_SASYNC_CB;
-                csa.event_enable = AC_FOUND_DEVICE;
-                csa.callback = chasync;
-                csa.callback_arg = NULL;
-                xpt_action((union ccb *)&csa);
-               status = csa.ccb_h.status;
-                xpt_free_path(path);
-        }
+       status = xpt_register_async(AC_FOUND_DEVICE, chasync, NULL, NULL);
 
        if (status != CAM_REQ_CMP) {
                kprintf("ch: Failed to attach master async callback "
@@ -268,25 +255,17 @@ static void
 choninvalidate(struct cam_periph *periph)
 {
        struct ch_softc *softc;
-       struct ccb_setasync csa;
 
        softc = (struct ch_softc *)periph->softc;
 
        /*
         * De-register any async callbacks.
         */
-       xpt_setup_ccb(&csa.ccb_h, periph->path,
-                     /* priority */ 5);
-       csa.ccb_h.func_code = XPT_SASYNC_CB;
-       csa.event_enable = 0;
-       csa.callback = chasync;
-       csa.callback_arg = periph;
-       xpt_action((union ccb *)&csa);
+       xpt_register_async(0, chasync, periph, periph->path);
 
        softc->flags |= CH_FLAG_INVALID;
 
-       xpt_print_path(periph->path);
-       kprintf("lost device\n");
+       xpt_print(periph->path, "lost device\n");
 
 }
 
@@ -299,8 +278,7 @@ chcleanup(struct cam_periph *periph)
 
        devstat_remove_entry(&softc->device_stats);
        cam_extend_release(chperiphs, periph->unit_number);
-       xpt_print_path(periph->path);
-       kprintf("removing device entry\n");
+       xpt_print(periph->path, "removing device entry\n");
        dev_ops_remove(&ch_ops, -1, periph->unit_number);
        kfree(softc, M_DEVBUF);
 }
@@ -353,7 +331,6 @@ static cam_status
 chregister(struct cam_periph *periph, void *arg)
 {
        struct ch_softc *softc;
-       struct ccb_setasync csa;
        struct ccb_getdev *cgd;
 
        cgd = (struct ccb_getdev *)arg;
@@ -377,6 +354,7 @@ chregister(struct cam_periph *periph, void *arg)
         * Changers don't have a blocksize, and obviously don't support
         * tagged queueing.
         */
+       cam_periph_unlock(periph);
        devstat_add_entry(&softc->device_stats, "ch",
                          periph->unit_number, 0,
                          DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS,
@@ -388,23 +366,19 @@ chregister(struct cam_periph *periph, void *arg)
        make_dev(&ch_ops, periph->unit_number, UID_ROOT,
                  GID_OPERATOR, 0600, "%s%d", periph->periph_name,
                  periph->unit_number);
+       cam_periph_lock(periph);
 
        /*
         * Add an async callback so that we get
         * notified if this device goes away.
         */
-       xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
-       csa.ccb_h.func_code = XPT_SASYNC_CB;
-       csa.event_enable = AC_LOST_DEVICE;
-       csa.callback = chasync;
-       csa.callback_arg = periph;
-       xpt_action((union ccb *)&csa);
+       xpt_register_async(AC_LOST_DEVICE, chasync, periph, periph->path);
 
        /*
-        * Lock this peripheral until we are setup.
+        * Lock this periph until we are setup.
         * This first call can't block
         */
-       cam_periph_lock(periph, 0);
+       cam_periph_hold(periph, 0);
        xpt_schedule(periph, /*priority*/5);
 
        return(CAM_REQ_CMP);
@@ -421,28 +395,28 @@ chopen(struct dev_open_args *ap)
        unit = CHUNIT(dev);
        periph = cam_extend_get(chperiphs, unit);
 
-       if (periph == NULL)
-               return(ENXIO);
+       if (cam_periph_acquire(periph) != CAM_REQ_CMP)
+               return (ENXIO);
 
        softc = (struct ch_softc *)periph->softc;
 
-       crit_enter();
+       cam_periph_lock(periph);
+
        if (softc->flags & CH_FLAG_INVALID) {
-               crit_exit();
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
                return(ENXIO);
        }
 
-       if ((error = cam_periph_lock(periph, PCATCH)) != 0) {
-               crit_exit();
-               return (error);
-       }
-       
-       crit_exit();
-
-       if ((softc->flags & CH_FLAG_OPEN) == 0) {
-               if (cam_periph_acquire(periph) != CAM_REQ_CMP)
-                       return(ENXIO);
+       if ((softc->flags & CH_FLAG_OPEN) == 0)
                softc->flags |= CH_FLAG_OPEN;
+       else
+               cam_periph_release(periph);
+
+       if ((error = cam_periph_hold(periph, PCATCH)) != 0) {
+               cam_periph_unlock(periph);
+               cam_periph_release(periph);
+               return (error);
        }
 
        /*
@@ -455,6 +429,7 @@ chopen(struct dev_open_args *ap)
                return(error);
        }
 
+       cam_periph_unhold(periph);
        cam_periph_unlock(periph);
 
        return(error);
@@ -477,8 +452,7 @@ chclose(struct dev_close_args *ap)
 
        softc = (struct ch_softc *)periph->softc;
 
-       if ((error = cam_periph_lock(periph, 0)) != 0)
-               return(error);
+       cam_periph_lock(periph);
 
        softc->flags &= ~CH_FLAG_OPEN;
 
@@ -498,7 +472,6 @@ chstart(struct cam_periph *periph, union ccb *start_ccb)
        switch (softc->state) {
        case CH_STATE_NORMAL:
        {
-               crit_enter();
                if (periph->immediate_priority <= periph->pinfo.priority){
                        start_ccb->ccb_h.ccb_state = CH_CCB_WAITING;
 
@@ -507,7 +480,6 @@ chstart(struct cam_periph *periph, union ccb *start_ccb)
                        periph->immediate_priority = CAM_PRIORITY_NONE;
                        wakeup(&periph->ccb_list);
                }
-               crit_exit();
                break;
        }
        case CH_STATE_PROBE:
@@ -523,7 +495,7 @@ chstart(struct cam_periph *periph, union ccb *start_ccb)
                                  sizeof(struct scsi_mode_blk_desc) +
                                 sizeof(struct page_element_address_assignment);
 
-               mode_buffer = kmalloc(mode_buffer_len, M_TEMP, M_INTWAIT | M_ZERO);
+               mode_buffer = kmalloc(mode_buffer_len, M_SCSICH, M_INTWAIT | M_ZERO);
                /*
                 * Get the element address assignment page.
                 */
@@ -648,13 +620,12 @@ chdone(struct cam_periph *periph, union ccb *done_ccb)
                                    == CAM_SCSI_STATUS_ERROR) 
                                        scsi_sense_print(&done_ccb->csio);
                                else {
-                                       xpt_print_path(periph->path);
-                                       kprintf("got CAM status %#x\n",
-                                              done_ccb->ccb_h.status);
+                                       xpt_print(periph->path,
+                                           "got CAM status %#x\n",
+                                           done_ccb->ccb_h.status);
                                }
-                               xpt_print_path(periph->path);
-                               kprintf("fatal error, failed to attach to"
-                                      " device\n");
+                               xpt_print(periph->path, "fatal error, failed "
+                                   "to attach to device\n");
 
                                cam_periph_invalidate(periph);
 
@@ -664,7 +635,7 @@ chdone(struct cam_periph *periph, union ccb *done_ccb)
                if (announce_buf[0] != '\0')
                        xpt_announce_periph(periph, announce_buf);
                softc->state = CH_STATE_NORMAL;
-               kfree(mode_header, M_TEMP);
+               kfree(mode_header, M_SCSICH);
                /*
                 * Since our peripheral may be invalidated by an error
                 * above or an external event, we must release our CCB
@@ -674,7 +645,7 @@ chdone(struct cam_periph *periph, union ccb *done_ccb)
                 * operation.
                 */
                xpt_release_ccb(done_ccb);
-               cam_periph_unlock(periph);
+               cam_periph_unhold(periph);
                return;
        }
        case CH_CCB_WAITING:
@@ -719,6 +690,7 @@ chioctl(struct dev_ioctl_args *ap)
        if (periph == NULL)
                return(ENXIO);
 
+       cam_periph_lock(periph);
        CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering chioctl\n"));
 
        softc = (struct ch_softc *)periph->softc;
@@ -739,8 +711,10 @@ chioctl(struct dev_ioctl_args *ap)
                break;
 
        default:
-               if ((flag & FWRITE) == 0)
+               if ((flag & FWRITE) == 0) {
+                       cam_periph_unlock(periph);
                        return