diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c index d70813655800..51ffb62563a3 100644 --- a/sys/cam/cam_periph.c +++ b/sys/cam/cam_periph.c @@ -1160,7 +1160,11 @@ cam_periph_runccb(union ccb *ccb, struct bintime *starttime; struct bintime ltime; int error; - + bool sched_stopped; + struct mtx *periph_mtx; + struct cam_periph *periph; + uint32_t timeout = 1; + starttime = NULL; xpt_path_assert(ccb->ccb_h.path, MA_OWNED); KASSERT((ccb->ccb_h.flags & CAM_UNLOCKED) == 0, @@ -1180,21 +1184,47 @@ cam_periph_runccb(union ccb *ccb, devstat_start_transaction(ds, starttime); } + sched_stopped = SCHEDULER_STOPPED(); ccb->ccb_h.cbfcnp = cam_periph_done; - xpt_action(ccb); - - do { - cam_periph_ccbwait(ccb); - if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) - error = 0; - else if (error_routine != NULL) { - ccb->ccb_h.cbfcnp = cam_periph_done; - error = (*error_routine)(ccb, camflags, sense_flags); - } else - error = 0; + periph = xpt_path_periph(ccb->ccb_h.path); + periph_mtx = cam_periph_mtx(periph); + + /* + * If we're polling, then we need to ensure that we have ample resources + * in the periph. We also need to drop the periph lock while we're polling. + * cam_periph_error can reschedule the ccb by calling xpt_action and returning + * ERESTART, so we have to effect the polling in the do loop below. + */ + if (sched_stopped) { + mtx_unlock(periph_mtx); + timeout = xpt_poll_setup(ccb); + } + + if (timeout == 0) { + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + error = EBUSY; + } else { + xpt_action(ccb); + do { + if (!sched_stopped) + cam_periph_ccbwait(ccb); + else { + xpt_pollwait(ccb, timeout); + timeout = ccb->ccb_h.timeout * 10; + } + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + error = 0; + else if (error_routine != NULL) { + ccb->ccb_h.cbfcnp = cam_periph_done; + error = (*error_routine)(ccb, camflags, sense_flags); + } else + error = 0; + } while (error == ERESTART); + } + + if (sched_stopped) + mtx_lock(periph_mtx); - } while (error == ERESTART); - if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { cam_release_devq(ccb->ccb_h.path, /* relsim_flags */0, diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c index a7e5bf8060fe..71752fc153f0 100644 --- a/sys/cam/cam_xpt.c +++ b/sys/cam/cam_xpt.c @@ -3204,8 +3204,8 @@ xpt_action_default(union ccb *start_ccb) start_ccb->ccb_h.status)); } -void -xpt_polled_action(union ccb *start_ccb) +uint32_t +xpt_poll_setup(union ccb *start_ccb) { u_int32_t timeout; struct cam_sim *sim; @@ -3219,8 +3219,6 @@ xpt_polled_action(union ccb *start_ccb) mtx = sim->mtx; dev = start_ccb->ccb_h.path->device; - mtx_unlock(&dev->device_mtx); - /* * Steal an opening so that no other queued requests * can get it before us while we simulate interrupts. @@ -3242,29 +3240,57 @@ xpt_polled_action(union ccb *start_ccb) dev->ccbq.dev_openings++; mtx_unlock(&devq->send_mtx); - if (timeout != 0) { + return (timeout); +} + +void +xpt_pollwait(union ccb *start_ccb, uint32_t timeout) +{ + struct cam_sim *sim; + struct mtx *mtx; + + sim = start_ccb->ccb_h.path->bus->sim; + mtx = sim->mtx; + + while (--timeout > 0) { + if (mtx) + mtx_lock(mtx); + (*(sim->sim_poll))(sim); + if (mtx) + mtx_unlock(mtx); + camisr_runqueue(); + if ((start_ccb->ccb_h.status & CAM_STATUS_MASK) + != CAM_REQ_INPROG) + break; + DELAY(100); + } + + if (timeout == 0) { + /* + * XXX Is it worth adding a sim_timeout entry + * point so we can attempt recovery? If + * this is only used for dumps, I don't think + * it is. + */ + start_ccb->ccb_h.status = CAM_CMD_TIMEOUT; + } +} + +void +xpt_polled_action(union ccb *start_ccb) +{ + uint32_t timeout; + struct cam_ed *dev; + + timeout = start_ccb->ccb_h.timeout * 10; + dev = start_ccb->ccb_h.path->device; + + mtx_unlock(&dev->device_mtx); + + timeout = xpt_poll_setup(start_ccb); + if (timeout > 0) { xpt_action(start_ccb); - while(--timeout > 0) { - if (mtx) - mtx_lock(mtx); - (*(sim->sim_poll))(sim); - if (mtx) - mtx_unlock(mtx); - camisr_runqueue(); - if ((start_ccb->ccb_h.status & CAM_STATUS_MASK) - != CAM_REQ_INPROG) - break; - DELAY(100); - } - if (timeout == 0) { - /* - * XXX Is it worth adding a sim_timeout entry - * point so we can attempt recovery? If - * this is only used for dumps, I don't think - * it is. - */ - start_ccb->ccb_h.status = CAM_CMD_TIMEOUT; - } + xpt_pollwait(start_ccb, timeout); } else { start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; } @@ -3306,6 +3332,7 @@ xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo, CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_schedule_dev\n")); + old_priority = pinfo->priority; /* diff --git a/sys/cam/cam_xpt.h b/sys/cam/cam_xpt.h index 8baec2947e8e..4a160423bcda 100644 --- a/sys/cam/cam_xpt.h +++ b/sys/cam/cam_xpt.h @@ -144,6 +144,8 @@ void xpt_copy_path(struct cam_path *new_path, void xpt_release_path(struct cam_path *path); const char * xpt_action_name(uint32_t action); +void xpt_pollwait(union ccb *start_ccb, uint32_t timeout); +uint32_t xpt_poll_setup(union ccb *start_ccb); #endif /* _KERNEL */