Date: Wed, 6 Dec 2017 23:05:07 +0000 (UTC) From: Warner Losh <imp@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r326643 - head/sys/cam Message-ID: <201712062305.vB6N57XP065402@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: imp Date: Wed Dec 6 23:05:07 2017 New Revision: 326643 URL: https://svnweb.freebsd.org/changeset/base/326643 Log: Make cam_periph_runccb be safe to call when we can only do polling. Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D13388 Modified: head/sys/cam/cam_periph.c head/sys/cam/cam_xpt.c head/sys/cam/cam_xpt.h Modified: head/sys/cam/cam_periph.c ============================================================================== --- head/sys/cam/cam_periph.c Wed Dec 6 23:03:34 2017 (r326642) +++ head/sys/cam/cam_periph.c Wed Dec 6 23:05:07 2017 (r326643) @@ -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); - } while (error == ERESTART); - + /* + * 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); + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { cam_release_devq(ccb->ccb_h.path, /* relsim_flags */0, Modified: head/sys/cam/cam_xpt.c ============================================================================== --- head/sys/cam/cam_xpt.c Wed Dec 6 23:03:34 2017 (r326642) +++ head/sys/cam/cam_xpt.c Wed Dec 6 23:05:07 2017 (r326643) @@ -3204,8 +3204,8 @@ call_sim: 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; } @@ -3305,6 +3331,7 @@ xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo, u_int32_t old_priority; CAM_DEBUG_PRINT(CAM_DEBUG_XPT, ("xpt_schedule_dev\n")); + old_priority = pinfo->priority; Modified: head/sys/cam/cam_xpt.h ============================================================================== --- head/sys/cam/cam_xpt.h Wed Dec 6 23:03:34 2017 (r326642) +++ head/sys/cam/cam_xpt.h Wed Dec 6 23:05:07 2017 (r326643) @@ -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 */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201712062305.vB6N57XP065402>