diff options
Diffstat (limited to 'sys/cam/scsi/scsi_cd.c')
-rw-r--r-- | sys/cam/scsi/scsi_cd.c | 438 |
1 files changed, 244 insertions, 194 deletions
diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c index 07782ba..fa85ada 100644 --- a/sys/cam/scsi/scsi_cd.c +++ b/sys/cam/scsi/scsi_cd.c @@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$"); #include <cam/cam_periph.h> #include <cam/cam_xpt_periph.h> #include <cam/cam_queue.h> +#include <cam/cam_sim.h> #include <cam/scsi/scsi_message.h> #include <cam/scsi/scsi_da.h> @@ -103,7 +104,8 @@ typedef enum { CD_FLAG_RETRY_UA = 0x0200, CD_FLAG_VALID_MEDIA = 0x0400, CD_FLAG_VALID_TOC = 0x0800, - CD_FLAG_SCTX_INIT = 0x1000 + CD_FLAG_SCTX_INIT = 0x1000, + CD_FLAG_OPEN = 0x2000 } cd_flags; typedef enum { @@ -290,9 +292,6 @@ static struct periph_driver cddriver = PERIPHDRIVER_DECLARE(cd, cddriver); - -static int num_changers; - #ifndef CHANGER_MIN_BUSY_SECONDS #define CHANGER_MIN_BUSY_SECONDS 5 #endif @@ -319,15 +318,16 @@ struct cdchanger { struct camq devq; struct timeval start_time; struct cd_softc *cur_device; - struct callout_handle short_handle; - struct callout_handle long_handle; + struct callout short_handle; + struct callout long_handle; volatile cd_changer_flags flags; STAILQ_ENTRY(cdchanger) changer_links; STAILQ_HEAD(chdevlist, cd_softc) chluns; }; +static struct mtx changerq_mtx; static STAILQ_HEAD(changerlist, cdchanger) changerq; - +static int num_changers; static void cdinit(void) @@ -335,6 +335,9 @@ cdinit(void) cam_status status; struct cam_path *path; + mtx_init(&changerq_mtx, "cdchangerq", "SCSI CD Changer List", MTX_DEF); + STAILQ_INIT(&changerq); + /* * Install a global async callback. This callback will * receive async callbacks like "new device found". @@ -364,7 +367,6 @@ cdinit(void) static void cdoninvalidate(struct cam_periph *periph) { - int s; struct cd_softc *softc; struct ccb_setasync csa; @@ -384,19 +386,11 @@ cdoninvalidate(struct cam_periph *periph) softc->flags |= CD_FLAG_INVALID; /* - * Although the oninvalidate() routines are always called at - * splsoftcam, we need to be at splbio() here to keep the buffer - * queue from being modified while we traverse it. - */ - s = splbio(); - - /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ bioq_flush(&softc->bio_queue, NULL, ENXIO); - splx(s); /* * If this device is part of a changer, and it was scheduled @@ -415,7 +409,6 @@ static void cdcleanup(struct cam_periph *periph) { struct cd_softc *softc; - int s; softc = (struct cd_softc *)periph->softc; @@ -426,7 +419,6 @@ cdcleanup(struct cam_periph *periph) xpt_print(periph->path, "can't remove sysctl context\n"); } - s = splsoftcam(); /* * In the queued, non-active case, the device in question * has already been removed from the changer run queue. Since this @@ -456,8 +448,7 @@ cdcleanup(struct cam_periph *periph) * be any bogus pointer references there. */ if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { - untimeout(cdshorttimeout, softc->changer, - softc->changer->short_handle); + callout_stop(&softc->changer->short_handle); softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } softc->changer->devq.qfrozen_cnt--; @@ -478,26 +469,25 @@ cdcleanup(struct cam_periph *periph) * it won't hurt to check and see if there are any left. */ if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) { - untimeout(cdrunchangerqueue, softc->changer, - softc->changer->long_handle); + callout_stop(&softc->changer->long_handle); softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED; } if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { - untimeout(cdshorttimeout, softc->changer, - softc->changer->short_handle); + callout_stop(&softc->changer->short_handle); softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } + mtx_lock(&changerq_mtx); STAILQ_REMOVE(&changerq, softc->changer, cdchanger, changer_links); + num_changers--; + mtx_unlock(&changerq_mtx); xpt_print(periph->path, "removing changer entry\n"); free(softc->changer, M_DEVBUF); - num_changers--; } disk_destroy(softc->disk); free(softc, M_DEVBUF); - splx(s); } static void @@ -544,10 +534,8 @@ cdasync(void *callback_arg, u_int32_t code, { struct cd_softc *softc; struct ccb_hdr *ccbh; - int s; softc = (struct cd_softc *)periph->softc; - s = splsoftcam(); /* * Don't fail on the expected unit attention * that will occur. @@ -555,7 +543,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; - splx(s); /* FALLTHROUGH */ } default: @@ -572,8 +559,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; snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); @@ -588,6 +577,7 @@ cdsysctlinit(void *context, int pending) if (softc->sysctl_tree == NULL) { printf("cdsysctlinit: unable to allocate sysctl tree\n"); mtx_unlock(&Giant); + cam_periph_release(periph); return; } @@ -601,6 +591,7 @@ cdsysctlinit(void *context, int pending) "Minimum CDB size"); mtx_unlock(&Giant); + cam_periph_release(periph); } /* @@ -734,6 +725,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); softc->disk = disk_alloc(); softc->disk->d_devstat = devstat_new_entry("cd", periph->unit_number, 0, @@ -747,8 +739,9 @@ cdregister(struct cam_periph *periph, void *arg) softc->disk->d_name = "cd"; softc->disk->d_unit = periph->unit_number; softc->disk->d_drv1 = periph; - softc->disk->d_flags = DISKFLAG_NEEDSGIANT; + softc->disk->d_flags = 0; disk_create(softc->disk, DISK_VERSION); + cam_periph_lock(periph); /* * Add an async callback so that we get @@ -783,13 +776,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. */ + mtx_lock(&changerq_mtx); for (found = 0, nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq); nchanger != NULL; @@ -800,6 +791,7 @@ cdregister(struct cam_periph *periph, void *arg) break; } } + mtx_unlock(&changerq_mtx); /* * If we found a matching entry, just add this device to @@ -905,8 +897,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; @@ -915,8 +905,16 @@ cdregister(struct cam_periph *periph, void *arg) STAILQ_INIT(&nchanger->chluns); + callout_init_mtx(&nchanger->long_handle, + periph->sim->mtx, 0); + callout_init_mtx(&nchanger->short_handle, + periph->sim->mtx, 0); + + mtx_lock(&changerq_mtx); + num_changers++; STAILQ_INSERT_TAIL(&changerq, nchanger, changer_links); + mtx_unlock(&changerq_mtx); /* * Create a path with lun id 0, and see if we can @@ -978,9 +976,11 @@ cdregister(struct cam_periph *periph, void *arg) cdregisterexit: - /* Lock this peripheral until we are setup */ - /* Can't block */ - cam_periph_lock(periph, PRIBIO); + /* + * Refcount and block open attempts until we are setup + * Can't block + */ + (void)cam_periph_hold(periph, PRIBIO); if ((softc->flags & CD_FLAG_CHANGER) == 0) xpt_schedule(periph, /*priority*/5); @@ -996,7 +996,6 @@ cdopen(struct disk *dp) struct cam_periph *periph; struct cd_softc *softc; int error; - int s; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) @@ -1004,24 +1003,28 @@ cdopen(struct disk *dp) softc = (struct cd_softc *)periph->softc; - /* - * Grab splsoftcam and hold it until we lock the peripheral. - */ - s = splsoftcam(); + if (cam_periph_acquire(periph) != CAM_REQ_CMP) + return(ENXIO); + + cam_periph_lock(periph); + if (softc->flags & CD_FLAG_INVALID) { - splx(s); + cam_periph_unlock(periph); + cam_periph_release(periph); return(ENXIO); } - if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) { - splx(s); + if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) { + cam_periph_unlock(periph); + cam_periph_release(periph); return (error); } - splx(s); - - 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 @@ -1030,11 +1033,11 @@ cdopen(struct disk *dp) */ cdcheckmedia(periph); - 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 @@ -1042,7 +1045,6 @@ cdclose(struct disk *dp) { struct cam_periph *periph; struct cd_softc *softc; - int error; periph = (struct cam_periph *)dp->d_drv1; if (periph == NULL) @@ -1050,8 +1052,8 @@ cdclose(struct disk *dp) softc = (struct cd_softc *)periph->softc; - if ((error = cam_periph_lock(periph, PRIBIO)) != 0) - return (error); + cam_periph_lock(periph); + cam_periph_hold(periph, PRIBIO); if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0) cdprevent(periph, PR_ALLOW); @@ -1065,8 +1067,9 @@ cdclose(struct disk *dp) /* * 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_VALID_MEDIA|CD_FLAG_VALID_TOC|CD_FLAG_OPEN); + cam_periph_unhold(periph); cam_periph_unlock(periph); cam_periph_release(periph); @@ -1077,9 +1080,6 @@ static void cdshorttimeout(void *arg) { struct cdchanger *changer; - int s; - - s = splsoftcam(); changer = (struct cdchanger *)arg; @@ -1095,8 +1095,6 @@ cdshorttimeout(void *arg) changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(changer); } - - splx(s); } /* @@ -1106,9 +1104,6 @@ static void cdschedule(struct cam_periph *periph, int priority) { struct cd_softc *softc; - int s; - - s = splsoftcam(); softc = (struct cd_softc *)periph->softc; @@ -1147,9 +1142,6 @@ cdschedule(struct cam_periph *periph, int priority) } else if ((softc->flags & CD_FLAG_ACTIVE) && ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0)) xpt_schedule(periph, priority); - - splx(s); - } static void @@ -1158,9 +1150,6 @@ cdrunchangerqueue(void *arg) struct cd_softc *softc; struct cdchanger *changer; int called_from_timeout; - int s; - - s = splsoftcam(); changer = (struct cdchanger *)arg; @@ -1180,7 +1169,6 @@ cdrunchangerqueue(void *arg) /* nothing to do if the queue is empty */ if (changer->devq.entries <= 0) { - splx(s); return; } @@ -1190,29 +1178,28 @@ 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 = changer->cur_device->outstanding_cmds; if (called_from_timeout) { - changer->long_handle = - timeout(cdrunchangerqueue, changer, - changer_max_busy_seconds * hz); + callout_reset(&changer->long_handle, + changer_max_busy_seconds * hz, + cdrunchangerqueue, changer); changer->flags |= CHANGER_TIMEOUT_SCHED; } - splx(s); 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 * not, there is no need to requeue it. @@ -1242,12 +1229,12 @@ cdrunchangerqueue(void *arg) * ones so this device gets its full time quantum. */ if (changer->flags & CHANGER_TIMEOUT_SCHED) { - untimeout(cdrunchangerqueue, changer, changer->long_handle); + callout_stop(&changer->long_handle); changer->flags &= ~CHANGER_TIMEOUT_SCHED; } if (changer->flags & CHANGER_SHORT_TMOUT_SCHED) { - untimeout(cdshorttimeout, changer, changer->short_handle); + callout_stop(&changer->short_handle); changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } @@ -1257,17 +1244,12 @@ cdrunchangerqueue(void *arg) * switch time. */ changer->flags |= CHANGER_NEED_TIMEOUT; - - splx(s); } static void cdchangerschedule(struct cd_softc *softc) { struct cdchanger *changer; - int s; - - s = splsoftcam(); changer = softc->changer; @@ -1313,18 +1295,18 @@ cdchangerschedule(struct cd_softc *softc) * and schedule our timeouts. */ if ((changer->flags & CHANGER_TIMEOUT_SCHED) == 0) { - changer->long_handle = - timeout(cdrunchangerqueue, changer, - changer_max_busy_seconds * hz); + callout_reset(&changer->long_handle, + changer_max_busy_seconds * hz, + cdrunchangerqueue, changer); changer->flags |= CHANGER_TIMEOUT_SCHED; } else printf("cdchangerschedule: already have a long" " timeout!\n"); if ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) { - changer->short_handle = - timeout(cdshorttimeout, changer, - changer_min_busy_seconds * hz); + callout_reset(&changer->short_handle, + changer_min_busy_seconds * hz, + cdshorttimeout, changer); changer->flags |= CHANGER_SHORT_TMOUT_SCHED; } else printf("cdchangerschedule: already have a short " @@ -1337,7 +1319,6 @@ cdchangerschedule(struct cd_softc *softc) changer->flags &= ~CHANGER_NEED_TIMEOUT; } - splx(s); } static int @@ -1366,14 +1347,10 @@ static union ccb * cdgetccb(struct cam_periph *periph, u_int32_t priority) { struct cd_softc *softc; - int s; softc = (struct cd_softc *)periph->softc; if (softc->flags & CD_FLAG_CHANGER) { - - s = splsoftcam(); - /* * This should work the first time this device is woken up, * but just in case it doesn't, we use a while loop. @@ -1396,9 +1373,9 @@ cdgetccb(struct cam_periph *periph, u_int32_t priority) softc->changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(softc->changer); } else - tsleep(&softc->changer, PRIBIO, "cgticb", 0); + msleep(&softc->changer, periph->sim->mtx, + PRIBIO, "cgticb", 0); } - splx(s); } return(cam_periph_getccb(periph, priority)); } @@ -1414,7 +1391,6 @@ cdstrategy(struct bio *bp) { struct cam_periph *periph; struct cd_softc *softc; - int s; periph = (struct cam_periph *)bp->bio_disk->d_drv1; if (periph == NULL) { @@ -1422,22 +1398,16 @@ cdstrategy(struct bio *bp) return; } + 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. - */ - s = splbio(); - - /* * If the device has been made invalid, error out */ if ((softc->flags & CD_FLAG_INVALID)) { - splx(s); + cam_periph_unlock(periph); biofinish(bp, NULL, ENXIO); return; } @@ -1451,7 +1421,7 @@ cdstrategy(struct bio *bp) error = cdcheckmedia(periph); if (error != 0) { - splx(s); + cam_periph_unlock(periph); biofinish(bp, NULL, error); return; } @@ -1462,8 +1432,6 @@ cdstrategy(struct bio *bp) */ bioq_disksort(&softc->bio_queue, bp); - splx(s); - /* * Schedule ourselves for performing the work. We do things * differently for changers. @@ -1473,6 +1441,7 @@ cdstrategy(struct bio *bp) else cdschedule(periph, /* priority */ 1); + cam_periph_unlock(periph); return; } @@ -1483,7 +1452,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb) struct bio *bp; struct ccb_scsiio *csio; struct scsi_read_capacity_data *rcap; - int s; softc = (struct cd_softc *)periph->softc; @@ -1492,9 +1460,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb) switch (softc->state) { case CD_STATE_NORMAL: { - int oldspl; - - s = splbio(); bp = bioq_first(&softc->bio_queue); if (periph->immediate_priority <= periph->pinfo.priority) { start_ccb->ccb_h.ccb_state = CD_CCB_WAITING; @@ -1502,10 +1467,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; - splx(s); wakeup(&periph->ccb_list); } else if (bp == NULL) { - splx(s); xpt_release_ccb(start_ccb); } else { bioq_remove(&softc->bio_queue, bp); @@ -1529,15 +1492,9 @@ 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. - */ - oldspl = splcam(); LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h, periph_links.le); softc->outstanding_cmds++; - splx(oldspl); /* We expect a unit attention from this device */ if ((softc->flags & CD_FLAG_RETRY_UA) != 0) { @@ -1547,7 +1504,6 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb) start_ccb->ccb_h.ccb_bp = bp; bp = bioq_first(&softc->bio_queue); - splx(s); xpt_action(start_ccb); } @@ -1601,7 +1557,6 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) { struct bio *bp; int error; - int oldspl; bp = (struct bio *)done_ccb->ccb_h.ccb_bp; error = 0; @@ -1625,13 +1580,9 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) } if (error != 0) { - int s; - xpt_print(periph->path, "cddone: got error %#x back\n", error); - s = splbio(); bioq_flush(&softc->bio_queue, NULL, EIO); - splx(s); bp->bio_resid = bp->bio_bcount; bp->bio_error = error; bp->bio_flags |= BIO_ERROR; @@ -1654,14 +1605,8 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) } } - /* - * Block out any asyncronous callbacks - * while we touch the pending ccb list. - */ - oldspl = splcam(); LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); softc->outstanding_cmds--; - splx(oldspl); if (softc->flags & CD_FLAG_CHANGER) cdchangerschedule(softc); @@ -1852,7 +1797,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: @@ -1905,12 +1850,13 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cam_periph *periph; struct cd_softc *softc; - int error, nocopyout; + int nocopyout, error = 0; periph = (struct cam_periph *)dp->d_drv1; 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; @@ -1918,10 +1864,12 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("trying to do ioctl %#lx\n", cmd)); - error = cam_periph_lock(periph, PRIBIO | PCATCH); + if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) { + cam_periph_unlock(periph); + cam_periph_release(periph); + return (error); + } - if (error != 0) - return(error); /* * If we don't have media loaded, check for it. If still don't * have media loaded, we can only do a load or eject. @@ -1936,11 +1884,14 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) && (cmd != CDIOCEJECT)) && (IOCGROUP(cmd) == 'c')) { error = cdcheckmedia(periph); - if (error != 0) { - 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); + if (error != 0) + return (error); nocopyout = 0; switch (cmd) { @@ -1956,12 +1907,14 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) params.mode_buf = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYTRACKS\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -1970,8 +1923,10 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); - if (error) + if (error) { + cam_periph_unlock(periph); break; + } /* * This was originally implemented with the PLAY @@ -2033,6 +1988,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) args->end_track, args->end_index); } + cam_periph_unlock(periph); } break; case CDIOCPLAYMSF: @@ -2046,12 +2002,14 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) params.mode_buf = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOCPLAYMSF\n")); error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2060,8 +2018,10 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); - if (error) + if (error) { + cam_periph_unlock(periph); break; + } error = cdplaymsf(periph, args->start_m, args->start_s, @@ -2069,6 +2029,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) args->end_m, args->end_s, args->end_f); + cam_periph_unlock(periph); } break; case CDIOCPLAYBLOCKS: @@ -2078,16 +2039,19 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCPLAYBLOCKS\n")); + + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2096,9 +2060,12 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.flags |= CD_PA_IMMED; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); - if (error) + if (error) { + cam_periph_unlock(periph); break; + } error = cdplay(periph, args->blk, args->len); + cam_periph_unlock(periph); } break; case CDIOCREADSUBCHANNEL_SYSSPACE: @@ -2111,12 +2078,13 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_sub_channel_info *data; u_int32_t len = args->data_len; - CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, - ("trying to do CDIOCREADSUBCHANNEL\n")); - data = malloc(sizeof(struct cd_sub_channel_info), M_TEMP, M_WAITOK); + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCREADSUBCHANNEL\n")); + if ((len > sizeof(struct cd_sub_channel_info)) || (len < sizeof(struct cd_sub_channel_header))) { printf( @@ -2125,6 +2093,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) len); error = EINVAL; free(data, M_TEMP); + cam_periph_unlock(periph); break; } @@ -2136,6 +2105,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) if (error) { free(data, M_TEMP); + cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) @@ -2144,6 +2114,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) 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 (nocopyout == 0) { if (copyout(data, args->data, len) != 0) { error = EFAULT; @@ -2159,15 +2130,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) { struct ioc_toc_header *th; + th = malloc(sizeof(struct ioc_toc_header), M_TEMP, + M_WAITOK); + + cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCHEADER\n")); - th = malloc(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) { free(th, M_TEMP); + cam_periph_unlock(periph); break; } if (softc->quirks & CD_Q_BCD_TRACKS) { @@ -2181,6 +2155,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) th->len = ntohs(th->len); bcopy(th, addr, sizeof(*th)); free(th, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOREADTOCENTRYS: @@ -2193,12 +2168,13 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) u_int32_t len, readlen, idx, num; u_int32_t starting_track = te->starting_track; - CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, - ("trying to do CDIOREADTOCENTRYS\n")); - data = malloc(sizeof(*data), M_TEMP, M_WAITOK); lead = malloc(sizeof(*lead), M_TEMP, M_WAITOK); + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOREADTOCENTRYS\n")); + if (te->data_len < sizeof(struct cd_toc_entry) || (te->data_len % sizeof(struct cd_toc_entry)) != 0 || (te->address_format != CD_MSF_FORMAT @@ -2208,6 +2184,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) "returning EINVAL\n"); free(data, M_TEMP); free(lead, M_TEMP); + cam_periph_unlock(periph); break; } @@ -2217,6 +2194,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) if (error) { free(data, M_TEMP); free(lead, M_TEMP); + cam_periph_unlock(periph); break; } @@ -2239,6 +2217,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) "returning EINVAL\n"); free(data, M_TEMP); free(lead, M_TEMP); + cam_periph_unlock(periph); error = EINVAL; break; } @@ -2260,6 +2239,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) error = EINVAL; free(data, M_TEMP); free(lead, M_TEMP); + cam_periph_unlock(periph); break; } num = len / sizeof(struct cd_toc_entry); @@ -2273,6 +2253,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) if (error) { free(data, M_TEMP); free(lead, M_TEMP); + cam_periph_unlock(periph); break; } } @@ -2289,6 +2270,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) if (error) { free(data, M_TEMP); free(lead, M_TEMP); + cam_periph_unlock(periph); break; } data->entries[idx - starting_track] = @@ -2301,6 +2283,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) } } + cam_periph_unlock(periph); error = copyout(data->entries, te->data, len); free(data, M_TEMP); free(lead, M_TEMP); @@ -2314,17 +2297,19 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct ioc_toc_header *th; u_int32_t track; + data = malloc(sizeof(*data), M_TEMP, M_WAITOK); + + cam_periph_lock(periph); CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, ("trying to do CDIOREADTOCENTRY\n")); - data = malloc(sizeof(*data), M_TEMP, M_WAITOK); - if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) { printf("error in readtocentry, " " returning EINVAL\n"); free(data, M_TEMP); error = EINVAL; + cam_periph_unlock(periph); break; } @@ -2333,6 +2318,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) sizeof (*th), /*sense_flags*/0); if (error) { free(data, M_TEMP); + cam_periph_unlock(periph); break; } @@ -2355,6 +2341,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) " returning EINVAL\n"); free(data, M_TEMP); error = EINVAL; + cam_periph_unlock(periph); break; } @@ -2363,6 +2350,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) /*sense_flags*/0); if (error) { free(data, M_TEMP); + cam_periph_unlock(periph); break; } @@ -2371,6 +2359,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) bcopy(&data->entry, &te->entry, sizeof(struct cd_toc_entry)); free(data, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOCSETPATCH: @@ -2379,15 +2368,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETPATCH\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2400,6 +2392,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[3].channels = arg->patch[3]; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOCGETVOL: @@ -2408,15 +2401,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCGETVOL\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2428,6 +2424,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) arg->vol[2] = page->audio.port[2].volume; arg->vol[3] = page->audio.port[3].volume; free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOCSETVOL: @@ -2436,15 +2433,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETVOL\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2458,6 +2458,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[2].volume = arg->vol[2]; page->audio.port[3].volume = arg->vol[3]; error = cdsetmode(periph, ¶ms); + cam_periph_unlock(periph); free(params.mode_buf, M_TEMP); } break; @@ -2466,15 +2467,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETMONO\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2486,6 +2490,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[2].channels = 0; page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); + cam_periph_unlock(periph); free(params.mode_buf, M_TEMP); } break; @@ -2494,15 +2499,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETSTEREO\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2515,6 +2523,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOCSETMUTE: @@ -2522,15 +2531,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETMUTE\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(¶ms, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2541,6 +2553,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOCSETLEFT: @@ -2548,16 +2561,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) struct cd_mode_params params; union cd_pages *page; - 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); - + + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETLEFT\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2568,6 +2583,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); } break; case CDIOCSETRIGHT: @@ -2575,16 +2591,18 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) 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 = malloc(params.alloc_len, M_TEMP, M_WAITOK | M_ZERO); + cam_periph_lock(periph); + CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, + ("trying to do CDIOCSETRIGHT\n")); + error = cdgetmode(periph, ¶ms, AUDIO_PAGE); if (error) { free(params.mode_buf, M_TEMP); + cam_periph_unlock(periph); break; } page = cdgetpage(¶ms); @@ -2595,31 +2613,48 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) page->audio.port[3].channels = 0; error = cdsetmode(periph, ¶ms); free(params.mode_buf, M_TEMP); + 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); + 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); + 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); */ @@ -2634,10 +2669,14 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) 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: { @@ -2645,10 +2684,12 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) authinfo = (struct dvd_authinfo *)addr; + cam_periph_lock(periph); if (cmd == DVDIOCREPORTKEY) error = cdreportkey(periph, authinfo); else error = cdsendkey(periph, authinfo); + cam_periph_unlock(periph); break; } case DVDIOCREADSTRUCTURE: { @@ -2656,21 +2697,27 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) 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, 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")); if (error && bootverbose) { printf("scsi_cd.c::ioctl cmd=%08lx error=%d\n", cmd, error); } + cam_periph_unlock(periph); return (error); } @@ -2869,8 +2916,11 @@ cdsize(struct cam_periph *periph, u_int32_t *size) ccb = cdgetccb(periph, /* priority */ 1); + /* XXX Should be M_WAITOK */ rcap_buf = malloc(sizeof(struct scsi_read_capacity_data), - M_TEMP, M_WAITOK); + M_TEMP, M_NOWAIT); + if (rcap_buf == NULL) + return (ENOMEM); scsi_read_capacity(&ccb->csio, /*retries*/ 1, |