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
17eea3202a
commit
f93a843cd2
@ -1160,7 +1160,11 @@ cam_periph_runccb(union ccb *ccb,
|
|||||||
struct bintime *starttime;
|
struct bintime *starttime;
|
||||||
struct bintime ltime;
|
struct bintime ltime;
|
||||||
int error;
|
int error;
|
||||||
|
bool sched_stopped;
|
||||||
|
struct mtx *periph_mtx;
|
||||||
|
struct cam_periph *periph;
|
||||||
|
uint32_t timeout = 1;
|
||||||
|
|
||||||
starttime = NULL;
|
starttime = NULL;
|
||||||
xpt_path_assert(ccb->ccb_h.path, MA_OWNED);
|
xpt_path_assert(ccb->ccb_h.path, MA_OWNED);
|
||||||
KASSERT((ccb->ccb_h.flags & CAM_UNLOCKED) == 0,
|
KASSERT((ccb->ccb_h.flags & CAM_UNLOCKED) == 0,
|
||||||
@ -1180,21 +1184,47 @@ cam_periph_runccb(union ccb *ccb,
|
|||||||
devstat_start_transaction(ds, starttime);
|
devstat_start_transaction(ds, starttime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sched_stopped = SCHEDULER_STOPPED();
|
||||||
ccb->ccb_h.cbfcnp = cam_periph_done;
|
ccb->ccb_h.cbfcnp = cam_periph_done;
|
||||||
xpt_action(ccb);
|
periph = xpt_path_periph(ccb->ccb_h.path);
|
||||||
|
periph_mtx = cam_periph_mtx(periph);
|
||||||
do {
|
|
||||||
cam_periph_ccbwait(ccb);
|
/*
|
||||||
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
|
* If we're polling, then we need to ensure that we have ample resources
|
||||||
error = 0;
|
* in the periph. We also need to drop the periph lock while we're polling.
|
||||||
else if (error_routine != NULL) {
|
* cam_periph_error can reschedule the ccb by calling xpt_action and returning
|
||||||
ccb->ccb_h.cbfcnp = cam_periph_done;
|
* ERESTART, so we have to effect the polling in the do loop below.
|
||||||
error = (*error_routine)(ccb, camflags, sense_flags);
|
*/
|
||||||
} else
|
if (sched_stopped) {
|
||||||
error = 0;
|
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) {
|
if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
|
||||||
cam_release_devq(ccb->ccb_h.path,
|
cam_release_devq(ccb->ccb_h.path,
|
||||||
/* relsim_flags */0,
|
/* relsim_flags */0,
|
||||||
|
@ -3204,8 +3204,8 @@ call_sim:
|
|||||||
start_ccb->ccb_h.status));
|
start_ccb->ccb_h.status));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
uint32_t
|
||||||
xpt_polled_action(union ccb *start_ccb)
|
xpt_poll_setup(union ccb *start_ccb)
|
||||||
{
|
{
|
||||||
u_int32_t timeout;
|
u_int32_t timeout;
|
||||||
struct cam_sim *sim;
|
struct cam_sim *sim;
|
||||||
@ -3219,8 +3219,6 @@ xpt_polled_action(union ccb *start_ccb)
|
|||||||
mtx = sim->mtx;
|
mtx = sim->mtx;
|
||||||
dev = start_ccb->ccb_h.path->device;
|
dev = start_ccb->ccb_h.path->device;
|
||||||
|
|
||||||
mtx_unlock(&dev->device_mtx);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Steal an opening so that no other queued requests
|
* Steal an opening so that no other queued requests
|
||||||
* can get it before us while we simulate interrupts.
|
* can get it before us while we simulate interrupts.
|
||||||
@ -3242,29 +3240,57 @@ xpt_polled_action(union ccb *start_ccb)
|
|||||||
dev->ccbq.dev_openings++;
|
dev->ccbq.dev_openings++;
|
||||||
mtx_unlock(&devq->send_mtx);
|
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);
|
xpt_action(start_ccb);
|
||||||
while(--timeout > 0) {
|
xpt_pollwait(start_ccb, timeout);
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
|
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"));
|
CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_schedule_dev\n"));
|
||||||
|
|
||||||
|
|
||||||
old_priority = pinfo->priority;
|
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);
|
void xpt_release_path(struct cam_path *path);
|
||||||
|
|
||||||
const char * xpt_action_name(uint32_t action);
|
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 */
|
#endif /* _KERNEL */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user