diff options
Diffstat (limited to 'sys/cam/scsi')
-rw-r--r-- | sys/cam/scsi/scsi_cd.c | 130 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 133 |
2 files changed, 251 insertions, 12 deletions
diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c index 7b24ddb..c102c60 100644 --- a/sys/cam/scsi/scsi_cd.c +++ b/sys/cam/scsi/scsi_cd.c @@ -97,6 +97,7 @@ typedef enum { CD_FLAG_NEW_DISC = 0x0002, CD_FLAG_DISC_LOCKED = 0x0004, CD_FLAG_DISC_REMOVABLE = 0x0008, + CD_FLAG_SAW_MEDIA = 0x0010, CD_FLAG_CHANGER = 0x0040, CD_FLAG_ACTIVE = 0x0080, CD_FLAG_SCHED_ON_COMP = 0x0100, @@ -110,6 +111,7 @@ typedef enum { CD_CCB_PROBE = 0x01, CD_CCB_BUFFER_IO = 0x02, CD_CCB_WAITING = 0x03, + CD_CCB_TUR = 0x04, CD_CCB_TYPE_MASK = 0x0F, CD_CCB_RETRY_UA = 0x10 } cd_ccb_state; @@ -154,12 +156,14 @@ struct cd_softc { struct cam_periph *periph; int minimum_command_size; int outstanding_cmds; + int tur; struct task sysctl_task; struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; STAILQ_HEAD(, cd_mode_params) mode_queue; struct cd_tocdata toc; struct disk *disk; + struct callout mediapoll_c; }; struct cd_page_sizes { @@ -281,6 +285,7 @@ static int cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo); static int cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct); +static timeout_t cdmediapoll; static struct periph_driver cddriver = { @@ -290,6 +295,9 @@ static struct periph_driver cddriver = PERIPHDRIVER_DECLARE(cd, cddriver); +#ifndef CD_DEFAULT_POLL_PERIOD +#define CD_DEFAULT_POLL_PERIOD 3 +#endif #ifndef CD_DEFAULT_RETRY #define CD_DEFAULT_RETRY 4 #endif @@ -303,6 +311,7 @@ PERIPHDRIVER_DECLARE(cd, cddriver); #define CHANGER_MAX_BUSY_SECONDS 15 #endif +static int cd_poll_period = CD_DEFAULT_POLL_PERIOD; static int cd_retry_count = CD_DEFAULT_RETRY; static int cd_timeout = CD_DEFAULT_TIMEOUT; static int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS; @@ -311,6 +320,9 @@ static int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS; static SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver"); static SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer"); +SYSCTL_INT(_kern_cam_cd, OID_AUTO, poll_period, CTLFLAG_RW, + &cd_poll_period, 0, "Media polling period in seconds"); +TUNABLE_INT("kern.cam.cd.poll_period", &cd_poll_period); SYSCTL_INT(_kern_cam_cd, OID_AUTO, retry_count, CTLFLAG_RW, &cd_retry_count, 0, "Normal I/O retry count"); TUNABLE_INT("kern.cam.cd.retry_count", &cd_retry_count); @@ -494,6 +506,7 @@ cdcleanup(struct cam_periph *periph) xpt_print(periph->path, "can't remove sysctl context\n"); } + callout_drain(&softc->mediapoll_c); disk_destroy(softc->disk); free(softc, M_DEVBUF); cam_periph_lock(periph); @@ -504,6 +517,7 @@ cdasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; + struct cd_softc *softc; periph = (struct cam_periph *)callback_arg; switch (code) { @@ -541,10 +555,39 @@ cdasync(void *callback_arg, u_int32_t code, break; } + case AC_UNIT_ATTENTION: + { + union ccb *ccb; + int error_code, sense_key, asc, ascq; + + softc = (struct cd_softc *)periph->softc; + ccb = (union ccb *)arg; + + /* + * Handle all media change UNIT ATTENTIONs except + * our own, as they will be handled by cderror(). + */ + if (xpt_path_periph(ccb->ccb_h.path) != periph && + scsi_extract_sense_ccb(ccb, + &error_code, &sense_key, &asc, &ascq)) { + if (asc == 0x28 && ascq == 0x00) + disk_media_changed(softc->disk, M_NOWAIT); + } + cam_periph_async(periph, code, path, arg); + break; + } + case AC_SCSI_AEN: + softc = (struct cd_softc *)periph->softc; + if (softc->state == CD_STATE_NORMAL && !softc->tur) { + if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + softc->tur = 1; + xpt_schedule(periph, CAM_PRIORITY_DEV); + } + } + /* FALLTHROUGH */ case AC_SENT_BDR: case AC_BUS_RESET: { - struct cd_softc *softc; struct ccb_hdr *ccbh; softc = (struct cd_softc *)periph->softc; @@ -788,8 +831,8 @@ cdregister(struct cam_periph *periph, void *arg) * Add an async callback so that we get * notified if this device goes away. */ - xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE, - cdasync, periph, periph->path); + xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | + AC_SCSI_AEN | AC_UNIT_ATTENTION, cdasync, periph, periph->path); /* * If the target lun is greater than 0, we most likely have a CD @@ -1005,6 +1048,17 @@ cdregister(struct cam_periph *periph, void *arg) } } + /* + * Schedule a periodic media polling events. + */ + callout_init_mtx(&softc->mediapoll_c, periph->sim->mtx, 0); + if ((softc->flags & CD_FLAG_DISC_REMOVABLE) && + (softc->flags & CD_FLAG_CHANGER) == 0 && + (cgd->inq_flags & SID_AEN) == 0 && + cd_poll_period != 0) + callout_reset(&softc->mediapoll_c, cd_poll_period * hz, + cdmediapoll, periph); + cdregisterexit: if ((softc->flags & CD_FLAG_CHANGER) == 0) @@ -1500,8 +1554,25 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb) periph->immediate_priority = CAM_PRIORITY_NONE; wakeup(&periph->ccb_list); } else if (bp == NULL) { - xpt_release_ccb(start_ccb); + if (softc->tur) { + softc->tur = 0; + csio = &start_ccb->csio; + scsi_test_unit_ready(csio, + /*retries*/ cd_retry_count, + cddone, + MSG_SIMPLE_Q_TAG, + SSD_FULL_SIZE, + cd_timeout); + start_ccb->ccb_h.ccb_bp = NULL; + start_ccb->ccb_h.ccb_state = CD_CCB_TUR; + xpt_action(start_ccb); + } else + xpt_release_ccb(start_ccb); } else { + if (softc->tur) { + softc->tur = 0; + cam_periph_release_locked(periph); + } bioq_remove(&softc->bio_queue, bp); scsi_read_write(&start_ccb->csio, @@ -1545,7 +1616,7 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb) xpt_action(start_ccb); } - if (bp != NULL) { + if (bp != NULL || softc->tur) { /* Have more work to do, so ensure we stay scheduled */ xpt_schedule(periph, CAM_PRIORITY_NORMAL); } @@ -1840,6 +1911,25 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) wakeup(&done_ccb->ccb_h.cbfcnp); return; } + case CD_CCB_TUR: + { + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + + if (cderror(done_ccb, CAM_RETRY_SELTO, + SF_RETRY_UA | SF_NO_RECOVERY | SF_NO_PRINT) == + ERESTART) + return; + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(done_ccb->ccb_h.path, + /*relsim_flags*/0, + /*reduction*/0, + /*timeout*/0, + /*getcount_only*/0); + } + xpt_release_ccb(done_ccb); + cam_periph_release_locked(periph); + return; + } default: break; } @@ -2830,7 +2920,7 @@ cdcheckmedia(struct cam_periph *periph) cdprevent(periph, PR_ALLOW); return (error); } else { - softc->flags |= CD_FLAG_VALID_MEDIA; + softc->flags |= CD_FLAG_SAW_MEDIA | CD_FLAG_VALID_MEDIA; softc->disk->d_sectorsize = softc->params.blksize; softc->disk->d_mediasize = (off_t)softc->params.blksize * softc->params.disksize; @@ -3175,6 +3265,14 @@ cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) &error_code, &sense_key, &asc, &ascq)) { if (sense_key == SSD_KEY_ILLEGAL_REQUEST) error = cd6byteworkaround(ccb); + else if (sense_key == SSD_KEY_UNIT_ATTENTION && + asc == 0x28 && ascq == 0x00) + disk_media_changed(softc->disk, M_NOWAIT); + else if (sense_key == SSD_KEY_NOT_READY && + asc == 0x3a && (softc->flags & CD_FLAG_SAW_MEDIA)) { + softc->flags &= ~CD_FLAG_SAW_MEDIA; + disk_media_gone(softc->disk, M_NOWAIT); + } } if (error == ERESTART) @@ -3190,6 +3288,26 @@ cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) &softc->saved_ccb)); } +static void +cdmediapoll(void *arg) +{ + struct cam_periph *periph = arg; + struct cd_softc *softc = periph->softc; + + if (softc->flags & CD_FLAG_CHANGER) + return; + + if (softc->state == CD_STATE_NORMAL && !softc->tur) { + if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + softc->tur = 1; + xpt_schedule(periph, CAM_PRIORITY_DEV); + } + } + /* Queue us up again */ + if (cd_poll_period != 0) + callout_schedule(&softc->mediapoll_c, cd_poll_period * hz); +} + /* * Read table of contents */ diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index 5c82f32..3253e02 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -77,6 +77,7 @@ typedef enum { DA_FLAG_NEW_PACK = 0x002, DA_FLAG_PACK_LOCKED = 0x004, DA_FLAG_PACK_REMOVABLE = 0x008, + DA_FLAG_SAW_MEDIA = 0x010, DA_FLAG_NEED_OTAG = 0x020, DA_FLAG_WENT_IDLE = 0x040, DA_FLAG_RETRY_UA = 0x080, @@ -101,6 +102,7 @@ typedef enum { DA_CCB_WAITING = 0x04, DA_CCB_DUMP = 0x05, DA_CCB_DELETE = 0x06, + DA_CCB_TUR = 0x07, DA_CCB_TYPE_MASK = 0x0F, DA_CCB_RETRY_UA = 0x10 } da_ccb_state; @@ -150,6 +152,7 @@ struct da_softc { int unmap_max_ranges; int unmap_max_lba; int delete_running; + int tur; da_delete_methods delete_method; struct disk_params params; struct disk *disk; @@ -161,6 +164,7 @@ struct da_softc { uint64_t wwpn; uint8_t unmap_buf[UNMAP_MAX_RANGES * 16 + 8]; struct scsi_read_capacity_data_long rcaplong; + struct callout mediapoll_c; }; struct da_quirk_entry { @@ -857,6 +861,11 @@ static void dasetgeom(struct cam_periph *periph, uint32_t block_len, size_t rcap_size); static timeout_t dasendorderedtag; static void dashutdown(void *arg, int howto); +static timeout_t damediapoll; + +#ifndef DA_DEFAULT_POLL_PERIOD +#define DA_DEFAULT_POLL_PERIOD 3 +#endif #ifndef DA_DEFAULT_TIMEOUT #define DA_DEFAULT_TIMEOUT 60 /* Timeout in seconds */ @@ -871,12 +880,16 @@ static void dashutdown(void *arg, int howto); #endif +static int da_poll_period = DA_DEFAULT_POLL_PERIOD; static int da_retry_count = DA_DEFAULT_RETRY; static int da_default_timeout = DA_DEFAULT_TIMEOUT; static int da_send_ordered = DA_DEFAULT_SEND_ORDERED; static SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0, "CAM Direct Access Disk driver"); +SYSCTL_INT(_kern_cam_da, OID_AUTO, poll_period, CTLFLAG_RW, + &da_poll_period, 0, "Media polling period in seconds"); +TUNABLE_INT("kern.cam.da.poll_period", &da_poll_period); SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RW, &da_retry_count, 0, "Normal I/O retry count"); TUNABLE_INT("kern.cam.da.retry_count", &da_retry_count); @@ -966,6 +979,9 @@ daopen(struct disk *dp) (softc->quirks & DA_Q_NO_PREVENT) == 0) daprevent(periph, PR_PREVENT); + if (error == 0) + softc->flags |= DA_FLAG_SAW_MEDIA; + cam_periph_unhold(periph); cam_periph_unlock(periph); @@ -1050,7 +1066,8 @@ daschedule(struct cam_periph *periph) /* Check if we have more work to do. */ if (bioq_first(&softc->bio_queue) || - (!softc->delete_running && bioq_first(&softc->delete_queue))) { + (!softc->delete_running && bioq_first(&softc->delete_queue)) || + softc->tur) { prio = CAM_PRIORITY_NORMAL; } @@ -1297,6 +1314,7 @@ dacleanup(struct cam_periph *periph) xpt_print(periph->path, "can't remove sysctl context\n"); } + callout_drain(&softc->mediapoll_c); disk_destroy(softc->disk); callout_drain(&softc->sendordered_c); free(softc, M_DEVBUF); @@ -1308,6 +1326,7 @@ daasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_periph *periph; + struct da_softc *softc; periph = (struct cam_periph *)callback_arg; switch (code) { @@ -1359,10 +1378,43 @@ daasync(void *callback_arg, u_int32_t code, } break; } + case AC_UNIT_ATTENTION: + { + union ccb *ccb; + int error_code, sense_key, asc, ascq; + + softc = (struct da_softc *)periph->softc; + ccb = (union ccb *)arg; + + /* + * Handle all UNIT ATTENTIONs except our own, + * as they will be handled by daerror(). + */ + if (xpt_path_periph(ccb->ccb_h.path) != periph && + scsi_extract_sense_ccb(ccb, + &error_code, &sense_key, &asc, &ascq)) { + if (asc == 0x2A && ascq == 0x09) { + xpt_print(ccb->ccb_h.path, + "capacity data has changed\n"); + dareprobe(periph); + } else if (asc == 0x28 && ascq == 0x00) + disk_media_changed(softc->disk, M_NOWAIT); + } + cam_periph_async(periph, code, path, arg); + break; + } + case AC_SCSI_AEN: + softc = (struct da_softc *)periph->softc; + if (softc->state == DA_STATE_NORMAL && !softc->tur) { + if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + softc->tur = 1; + xpt_schedule(periph, CAM_PRIORITY_DEV); + } + } + /* FALLTHROUGH */ case AC_SENT_BDR: case AC_BUS_RESET: { - struct da_softc *softc; struct ccb_hdr *ccbh; softc = (struct da_softc *)periph->softc; @@ -1698,9 +1750,9 @@ daregister(struct cam_periph *periph, void *arg) * fine without them and the only alternative * would be to not attach the device on failure. */ - xpt_register_async(AC_SENT_BDR | AC_BUS_RESET - | AC_LOST_DEVICE | AC_ADVINFO_CHANGED, - daasync, periph, periph->path); + xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE | + AC_ADVINFO_CHANGED | AC_SCSI_AEN | AC_UNIT_ATTENTION, + daasync, periph, periph->path); /* * Emit an attribute changed notification just in case @@ -1710,6 +1762,16 @@ daregister(struct cam_periph *periph, void *arg) */ disk_attr_changed(softc->disk, "GEOM::physpath", M_NOWAIT); + /* + * Schedule a periodic media polling events. + */ + callout_init_mtx(&softc->mediapoll_c, periph->sim->mtx, 0); + if ((softc->flags & DA_FLAG_PACK_REMOVABLE) && + (cgd->inq_flags & SID_AEN) == 0 && + da_poll_period != 0) + callout_reset(&softc->mediapoll_c, da_poll_period * hz, + damediapoll, periph); + xpt_schedule(periph, CAM_PRIORITY_DEV); return(CAM_REQ_CMP); @@ -1847,9 +1909,25 @@ dastart(struct cam_periph *periph, union ccb *start_ccb) /* Run regular command. */ bp = bioq_takefirst(&softc->bio_queue); if (bp == NULL) { - xpt_release_ccb(start_ccb); + if (softc->tur) { + softc->tur = 0; + scsi_test_unit_ready(&start_ccb->csio, + /*retries*/ da_retry_count, + dadone, + MSG_SIMPLE_Q_TAG, + SSD_FULL_SIZE, + da_default_timeout * 1000); + start_ccb->ccb_h.ccb_bp = NULL; + start_ccb->ccb_h.ccb_state = DA_CCB_TUR; + xpt_action(start_ccb); + } else + xpt_release_ccb(start_ccb); break; } + if (softc->tur) { + softc->tur = 0; + cam_periph_release_locked(periph); + } if ((bp->bio_flags & BIO_ORDERED) != 0 || (softc->flags & DA_FLAG_NEED_OTAG) != 0) { @@ -2417,6 +2495,25 @@ dadone(struct cam_periph *periph, union ccb *done_ccb) case DA_CCB_DUMP: /* No-op. We're polling */ return; + case DA_CCB_TUR: + { + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + + if (daerror(done_ccb, CAM_RETRY_SELTO, + SF_RETRY_UA | SF_NO_RECOVERY | SF_NO_PRINT) == + ERESTART) + return; + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(done_ccb->ccb_h.path, + /*relsim_flags*/0, + /*reduction*/0, + /*timeout*/0, + /*getcount_only*/0); + } + xpt_release_ccb(done_ccb); + cam_periph_release_locked(periph); + return; + } default: break; } @@ -2477,6 +2574,13 @@ daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) xpt_print(periph->path, "capacity data has changed\n"); dareprobe(periph); sense_flags |= SF_NO_PRINT; + } else if (sense_key == SSD_KEY_UNIT_ATTENTION && + asc == 0x28 && ascq == 0x00) + disk_media_changed(softc->disk, M_NOWAIT); + else if (sense_key == SSD_KEY_NOT_READY && + asc == 0x3a && (softc->flags & DA_FLAG_SAW_MEDIA)) { + softc->flags &= ~DA_FLAG_SAW_MEDIA; + disk_media_gone(softc->disk, M_NOWAIT); } } if (error == ERESTART) @@ -2493,6 +2597,23 @@ daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) } static void +damediapoll(void *arg) +{ + struct cam_periph *periph = arg; + struct da_softc *softc = periph->softc; + + if (softc->state == DA_STATE_NORMAL && !softc->tur) { + if (cam_periph_acquire(periph) == CAM_REQ_CMP) { + softc->tur = 1; + daschedule(periph); + } + } + /* Queue us up again */ + if (da_poll_period != 0) + callout_schedule(&softc->mediapoll_c, da_poll_period * hz); +} + +static void daprevent(struct cam_periph *periph, int action) { struct da_softc *softc; |