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:
Warner Losh 2017-12-06 23:05:07 +00:00
parent 17eea3202a
commit f93a843cd2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=326643
3 changed files with 99 additions and 40 deletions

View File

@ -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,

View File

@ -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;
/*

View File

@ -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 */