Make cam_periph_runccb be safe to call when we can only do polling.
Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D13388
This commit is contained in:
parent
9c54c9c64c
commit
8c7073931a
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user