summaryrefslogtreecommitdiffstats
path: root/sys/cam/scsi/scsi_da.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cam/scsi/scsi_da.c')
-rw-r--r--sys/cam/scsi/scsi_da.c133
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;
OpenPOWER on IntegriCloud