diff options
Diffstat (limited to 'sys/cam/scsi/scsi_da.c')
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 133 |
1 files changed, 127 insertions, 6 deletions
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; |