summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/cam/ata/ata_all.h1
-rw-r--r--sys/cam/ata/ata_xpt.c12
-rw-r--r--sys/cam/cam_ccb.h1
-rw-r--r--sys/cam/cam_periph.c7
-rw-r--r--sys/cam/cam_xpt.c1
-rw-r--r--sys/cam/scsi/scsi_cd.c130
-rw-r--r--sys/cam/scsi/scsi_da.c133
-rw-r--r--sys/geom/geom.h6
-rw-r--r--sys/geom/geom_dev.c20
-rw-r--r--sys/geom/geom_disk.c26
-rw-r--r--sys/geom/geom_disk.h2
-rw-r--r--sys/geom/geom_event.c6
-rw-r--r--sys/geom/geom_io.c2
-rw-r--r--sys/geom/geom_slice.c1
-rw-r--r--sys/geom/geom_subr.c95
-rw-r--r--sys/geom/geom_vfs.c31
-rw-r--r--sys/geom/part/g_part.c1
17 files changed, 434 insertions, 41 deletions
diff --git a/sys/cam/ata/ata_all.h b/sys/cam/ata/ata_all.h
index 924fdfe..848a8fc 100644
--- a/sys/cam/ata/ata_all.h
+++ b/sys/cam/ata/ata_all.h
@@ -35,6 +35,7 @@ struct ccb_ataio;
struct cam_periph;
union ccb;
+#define SID_AEN 0x04 /* Abuse inq_flags bit to track enabled AEN. */
#define SID_DMA 0x10 /* Abuse inq_flags bit to track enabled DMA. */
struct ata_cmd {
diff --git a/sys/cam/ata/ata_xpt.c b/sys/cam/ata/ata_xpt.c
index 20f22eb..91f985d 100644
--- a/sys/cam/ata/ata_xpt.c
+++ b/sys/cam/ata/ata_xpt.c
@@ -468,6 +468,12 @@ negotiate:
0, 0x02);
break;
case PROBE_SETAN:
+ /* Remember what transport thinks about AEN. */
+ if (softc->caps & CTS_SATA_CAPS_H_AN)
+ path->device->inq_flags |= SID_AEN;
+ else
+ path->device->inq_flags &= ~SID_AEN;
+ xpt_async(AC_GETDEV_CHANGED, path, NULL);
cam_fill_ataio(ataio,
1,
probedone,
@@ -1154,6 +1160,12 @@ notsata:
cts.xport_specific.sata.valid = CTS_SATA_VALID_CAPS;
xpt_action((union ccb *)&cts);
softc->caps = caps;
+ /* Remember what transport thinks about AEN. */
+ if (softc->caps & CTS_SATA_CAPS_H_AN)
+ path->device->inq_flags |= SID_AEN;
+ else
+ path->device->inq_flags &= ~SID_AEN;
+ xpt_async(AC_GETDEV_CHANGED, path, NULL);
if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) {
path->device->flags &= ~CAM_DEV_UNCONFIGURED;
xpt_acquire_device(path->device);
diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h
index 52d7496..1f12d91 100644
--- a/sys/cam/cam_ccb.h
+++ b/sys/cam/cam_ccb.h
@@ -755,6 +755,7 @@ struct ccb_relsim {
* Definitions for the asynchronous callback CCB fields.
*/
typedef enum {
+ AC_UNIT_ATTENTION = 0x4000,/* Device reported UNIT ATTENTION */
AC_ADVINFO_CHANGED = 0x2000,/* Advance info might have changes */
AC_CONTRACT = 0x1000,/* A contractual callback */
AC_GETDEV_CHANGED = 0x800,/* Getdev info might have changed */
diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c
index d947732..4ebf84c 100644
--- a/sys/cam/cam_periph.c
+++ b/sys/cam/cam_periph.c
@@ -1593,6 +1593,7 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
const char *action_string;
cam_status status;
int frozen, error, openings, print, lost_device;
+ int error_code, sense_key, asc, ascq;
u_int32_t relsim_flags, timeout;
print = 1;
@@ -1759,6 +1760,12 @@ cam_periph_error(union ccb *ccb, cam_flags camflags,
xpt_async(AC_LOST_DEVICE, newpath, NULL);
xpt_free_path(newpath);
}
+
+ /* Broadcast UNIT ATTENTIONs to all periphs. */
+ } else if (scsi_extract_sense_ccb(ccb,
+ &error_code, &sense_key, &asc, &ascq) &&
+ sense_key == SSD_KEY_UNIT_ATTENTION) {
+ xpt_async(AC_UNIT_ATTENTION, orig_ccb->ccb_h.path, orig_ccb);
}
/* Attempt a retry */
diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c
index 3065c12..4a174d7 100644
--- a/sys/cam/cam_xpt.c
+++ b/sys/cam/cam_xpt.c
@@ -4055,6 +4055,7 @@ xpt_async_string(u_int32_t async_code)
case AC_GETDEV_CHANGED: return ("AC_GETDEV_CHANGED");
case AC_CONTRACT: return ("AC_CONTRACT");
case AC_ADVINFO_CHANGED: return ("AC_ADVINFO_CHANGED");
+ case AC_UNIT_ATTENTION: return ("AC_UNIT_ATTENTION");
}
return ("AC_UNKNOWN");
}
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;
diff --git a/sys/geom/geom.h b/sys/geom/geom.h
index 526fbdb..6a447ae 100644
--- a/sys/geom/geom.h
+++ b/sys/geom/geom.h
@@ -174,7 +174,9 @@ struct g_consumer {
struct g_provider *provider;
LIST_ENTRY(g_consumer) consumers; /* XXX: better name */
int acr, acw, ace;
- int spoiled;
+ int flags;
+#define G_CF_SPOILED 0x1
+#define G_CF_ORPHAN 0x4
struct devstat *stat;
u_int nstart, nend;
@@ -247,6 +249,8 @@ int g_post_event(g_event_t *func, void *arg, int flag, ...);
int g_waitfor_event(g_event_t *func, void *arg, int flag, ...);
void g_cancel_event(void *ref);
int g_attr_changed(struct g_provider *pp, const char *attr, int flag);
+int g_media_changed(struct g_provider *pp, int flag);
+int g_media_gone(struct g_provider *pp, int flag);
void g_orphan_provider(struct g_provider *pp, int error);
void g_waitidlelock(void);
diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c
index f3165a0..7936740 100644
--- a/sys/geom/geom_dev.c
+++ b/sys/geom/geom_dev.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/ctype.h>
#include <sys/bio.h>
+#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
@@ -105,6 +106,21 @@ g_dev_print(void)
static void
g_dev_attrchanged(struct g_consumer *cp, const char *attr)
{
+ struct cdev *dev;
+ char buf[SPECNAMELEN + 6];
+
+ if (strcmp(attr, "GEOM::media") == 0) {
+ dev = cp->geom->softc;
+ snprintf(buf, sizeof(buf), "cdev=%s", dev->si_name);
+ devctl_notify_f("DEVFS", "CDEV", "MEDIACHANGE", buf, M_WAITOK);
+ dev = cp->cp_alias_dev;
+ if (dev != NULL) {
+ snprintf(buf, sizeof(buf), "cdev=%s", dev->si_name);
+ devctl_notify_f("DEVFS", "CDEV", "MEDIACHANGE", buf,
+ M_WAITOK);
+ }
+ return;
+ }
if (strcmp(attr, "GEOM::physpath") != 0)
return;
@@ -119,7 +135,6 @@ g_dev_attrchanged(struct g_consumer *cp, const char *attr)
g_io_getattr("GEOM::physpath", cp, &physpath_len, physpath);
g_access(cp, -1, 0, 0);
if (error == 0 && strlen(physpath) != 0) {
- struct cdev *dev;
struct cdev *old_alias_dev;
struct cdev **alias_devp;
@@ -161,9 +176,6 @@ g_dev_taste(struct g_class *mp, struct g_provider *pp, int insist __unused)
g_trace(G_T_TOPOLOGY, "dev_taste(%s,%s)", mp->name, pp->name);
g_topology_assert();
- LIST_FOREACH(cp, &pp->consumers, consumers)
- if (cp->geom->class == mp)
- return (NULL);
gp = g_new_geomf(mp, pp->name);
cp = g_new_consumer(gp);
error = g_attach(cp, pp);
diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c
index 1d6f867..f863b2c 100644
--- a/sys/geom/geom_disk.c
+++ b/sys/geom/geom_disk.c
@@ -660,6 +660,32 @@ disk_attr_changed(struct disk *dp, const char *attr, int flag)
}
void
+disk_media_changed(struct disk *dp, int flag)
+{
+ struct g_geom *gp;
+ struct g_provider *pp;
+
+ gp = dp->d_geom;
+ if (gp != NULL) {
+ LIST_FOREACH(pp, &gp->provider, provider)
+ g_media_changed(pp, flag);
+ }
+}
+
+void
+disk_media_gone(struct disk *dp, int flag)
+{
+ struct g_geom *gp;
+ struct g_provider *pp;
+
+ gp = dp->d_geom;
+ if (gp != NULL) {
+ LIST_FOREACH(pp, &gp->provider, provider)
+ g_media_gone(pp, flag);
+ }
+}
+
+void
disk_resize(struct disk *dp)
{
struct g_geom *gp;
diff --git a/sys/geom/geom_disk.h b/sys/geom/geom_disk.h
index 4862fc5..cf53839 100644
--- a/sys/geom/geom_disk.h
+++ b/sys/geom/geom_disk.h
@@ -109,6 +109,8 @@ void disk_create(struct disk *disk, int version);
void disk_destroy(struct disk *disk);
void disk_gone(struct disk *disk);
void disk_attr_changed(struct disk *dp, const char *attr, int flag);
+void disk_media_changed(struct disk *dp, int flag);
+void disk_media_gone(struct disk *dp, int flag);
void disk_resize(struct disk *dp);
#define DISK_VERSION_00 0x58561059
diff --git a/sys/geom/geom_event.c b/sys/geom/geom_event.c
index 1c92ccb..b02d088 100644
--- a/sys/geom/geom_event.c
+++ b/sys/geom/geom_event.c
@@ -202,14 +202,12 @@ g_orphan_register(struct g_provider *pp)
* Tell all consumers the bad news.
* Don't be surprised if they self-destruct.
*/
- cp = LIST_FIRST(&pp->consumers);
- while (cp != NULL) {
- cp2 = LIST_NEXT(cp, consumers);
+ LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, cp2) {
KASSERT(cp->geom->orphan != NULL,
("geom %s has no orphan, class %s",
cp->geom->name, cp->geom->class->name));
+ cp->flags |= G_CF_ORPHAN;
cp->geom->orphan(cp);
- cp = cp2;
}
if (LIST_EMPTY(&pp->consumers) && wf)
g_destroy_provider(pp);
diff --git a/sys/geom/geom_io.c b/sys/geom/geom_io.c
index b4044a7..1e0f28e 100644
--- a/sys/geom/geom_io.c
+++ b/sys/geom/geom_io.c
@@ -305,6 +305,8 @@ g_io_check(struct bio *bp)
/* if provider is marked for error, don't disturb. */
if (pp->error)
return (pp->error);
+ if (cp->flags & G_CF_ORPHAN)
+ return (ENXIO);
switch(bp->bio_cmd) {
case BIO_READ:
diff --git a/sys/geom/geom_slice.c b/sys/geom/geom_slice.c
index 407afdc..1100854 100644
--- a/sys/geom/geom_slice.c
+++ b/sys/geom/geom_slice.c
@@ -465,6 +465,7 @@ g_slice_spoiled(struct g_consumer *cp)
g_topology_assert();
gp = cp->geom;
g_trace(G_T_TOPOLOGY, "g_slice_spoiled(%p/%s)", cp, gp->name);
+ cp->flags |= G_CF_ORPHAN;
gsp = gp->softc;
gp->softc = NULL;
g_slice_free(gsp);
diff --git a/sys/geom/geom_subr.c b/sys/geom/geom_subr.c
index 6f009f0..4d8623f 100644
--- a/sys/geom/geom_subr.c
+++ b/sys/geom/geom_subr.c
@@ -262,10 +262,11 @@ g_modevent(module_t mod, int type, void *data)
static void
g_retaste_event(void *arg, int flag)
{
- struct g_class *cp, *mp;
- struct g_geom *gp, *gp2;
+ struct g_class *mp, *mp2;
+ struct g_geom *gp;
struct g_hh00 *hh;
struct g_provider *pp;
+ struct g_consumer *cp;
g_topology_assert();
if (flag == EV_CANCEL) /* XXX: can't happen ? */
@@ -282,17 +283,20 @@ g_retaste_event(void *arg, int flag)
}
g_trace(G_T_TOPOLOGY, "g_retaste(%s)", mp->name);
- LIST_FOREACH(cp, &g_classes, class) {
- LIST_FOREACH(gp, &cp->geom, geom) {
+ LIST_FOREACH(mp2, &g_classes, class) {
+ LIST_FOREACH(gp, &mp2->geom, geom) {
LIST_FOREACH(pp, &gp->provider, provider) {
if (pp->acr || pp->acw || pp->ace)
continue;
- LIST_FOREACH(gp2, &mp->geom, geom) {
- if (!strcmp(pp->name, gp2->name))
+ LIST_FOREACH(cp, &pp->consumers, consumers) {
+ if (cp->geom->class == mp &&
+ (cp->flags & G_CF_ORPHAN) == 0)
break;
}
- if (gp2 != NULL)
- g_wither_geom(gp2, ENXIO);
+ if (cp != NULL) {
+ cp->flags |= G_CF_ORPHAN;
+ g_wither_geom(cp->geom, ENXIO);
+ }
mp->taste(mp, pp, 0);
g_topology_assert();
}
@@ -534,7 +538,7 @@ g_new_provider_event(void *arg, int flag)
{
struct g_class *mp;
struct g_provider *pp;
- struct g_consumer *cp;
+ struct g_consumer *cp, *next_cp;
g_topology_assert();
if (flag == EV_CANCEL)
@@ -545,11 +549,17 @@ g_new_provider_event(void *arg, int flag)
G_VALID_PROVIDER(pp);
KASSERT(!(pp->flags & G_PF_WITHER),
("g_new_provider_event but withered"));
+ LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, next_cp) {
+ if ((cp->flags & G_CF_ORPHAN) == 0 &&
+ cp->geom->attrchanged != NULL)
+ cp->geom->attrchanged(cp, "GEOM::media");
+ }
LIST_FOREACH(mp, &g_classes, class) {
if (mp->taste == NULL)
continue;
LIST_FOREACH(cp, &pp->consumers, consumers)
- if (cp->geom->class == mp)
+ if (cp->geom->class == mp &&
+ (cp->flags & G_CF_ORPHAN) == 0)
break;
if (cp != NULL)
continue;
@@ -628,8 +638,10 @@ g_resize_provider_event(void *arg, int flag)
LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, cp2) {
gp = cp->geom;
- if (gp->resize == NULL && size < pp->mediasize)
+ if (gp->resize == NULL && size < pp->mediasize) {
+ cp->flags |= G_CF_ORPHAN;
cp->geom->orphan(cp);
+ }
}
pp->mediasize = size;
@@ -648,7 +660,8 @@ g_resize_provider_event(void *arg, int flag)
if (mp->taste == NULL)
continue;
LIST_FOREACH(cp, &pp->consumers, consumers)
- if (cp->geom->class == mp)
+ if (cp->geom->class == mp &&
+ (cp->flags & G_CF_ORPHAN) == 0)
break;
if (cp != NULL)
continue;
@@ -867,7 +880,7 @@ g_access(struct g_consumer *cp, int dcr, int dcw, int dce)
* are probably just ahead of the event telling us that. Fail
* now rather than having to unravel this later.
*/
- if (cp->geom->spoiled != NULL && cp->spoiled &&
+ if (cp->geom->spoiled != NULL && (cp->flags & G_CF_SPOILED) &&
(dcr > 0 || dcw > 0 || dce > 0))
return (ENXIO);
@@ -1017,6 +1030,7 @@ g_std_spoiled(struct g_consumer *cp)
g_topology_assert();
G_VALID_CONSUMER(cp);
g_trace(G_T_TOPOLOGY, "g_std_spoiled(%p)", cp);
+ cp->flags |= G_CF_ORPHAN;
g_detach(cp);
gp = cp->geom;
LIST_FOREACH(pp, &gp->provider, provider)
@@ -1052,9 +1066,9 @@ g_spoil_event(void *arg, int flag)
G_VALID_PROVIDER(pp);
for (cp = LIST_FIRST(&pp->consumers); cp != NULL; cp = cp2) {
cp2 = LIST_NEXT(cp, consumers);
- if (!cp->spoiled)
+ if ((cp->flags & G_CF_SPOILED) == 0)
continue;
- cp->spoiled = 0;
+ cp->flags &= ~G_CF_SPOILED;
if (cp->geom->spoiled == NULL)
continue;
cp->geom->spoiled(cp);
@@ -1079,11 +1093,54 @@ g_spoil(struct g_provider *pp, struct g_consumer *cp)
KASSERT(cp2->acw == 0, ("spoiling cp->acw = %d", cp2->acw));
*/
KASSERT(cp2->ace == 0, ("spoiling cp->ace = %d", cp2->ace));
- cp2->spoiled++;
+ cp2->flags |= G_CF_SPOILED;
}
g_post_event(g_spoil_event, pp, M_WAITOK, pp, NULL);
}
+static void
+g_media_changed_event(void *arg, int flag)
+{
+ struct g_provider *pp;
+ int retaste;
+
+ g_topology_assert();
+ if (flag == EV_CANCEL)
+ return;
+ pp = arg;
+ G_VALID_PROVIDER(pp);
+
+ /*
+ * If provider was not open for writing, queue retaste after spoiling.
+ * If it was, retaste will happen automatically on close.
+ */
+ retaste = (pp->acw == 0 && pp->error == 0 &&
+ !(pp->geom->flags & G_GEOM_WITHER));
+ g_spoil_event(arg, flag);
+ if (retaste)
+ g_post_event(g_new_provider_event, pp, M_WAITOK, pp, NULL);
+}
+
+int
+g_media_changed(struct g_provider *pp, int flag)
+{
+ struct g_consumer *cp;
+
+ LIST_FOREACH(cp, &pp->consumers, consumers)
+ cp->flags |= G_CF_SPOILED;
+ return (g_post_event(g_media_changed_event, pp, flag, pp, NULL));
+}
+
+int
+g_media_gone(struct g_provider *pp, int flag)
+{
+ struct g_consumer *cp;
+
+ LIST_FOREACH(cp, &pp->consumers, consumers)
+ cp->flags |= G_CF_SPOILED;
+ return (g_post_event(g_spoil_event, pp, flag, pp, NULL));
+}
+
int
g_getattr__(const char *attr, struct g_consumer *cp, void *var, int len)
{
@@ -1239,15 +1296,15 @@ db_show_geom_consumer(int indent, struct g_consumer *cp)
cp->provider);
}
gprintln(" access: r%dw%de%d", cp->acr, cp->acw, cp->ace);
- gprintln(" spoiled: %d", cp->spoiled);
+ gprintln(" flags: 0x%04x", cp->flags);
gprintln(" nstart: %u", cp->nstart);
gprintln(" nend: %u", cp->nend);
} else {
gprintf("consumer: %p (%s), access=r%dw%de%d", cp,
cp->provider != NULL ? cp->provider->name : "none",
cp->acr, cp->acw, cp->ace);
- if (cp->spoiled)
- db_printf(", spoiled=%d", cp->spoiled);
+ if (cp->flags)
+ db_printf(", flags=0x%04x", cp->flags);
db_printf("\n");
}
}
diff --git a/sys/geom/geom_vfs.c b/sys/geom/geom_vfs.c
index ade1790..86d0b55 100644
--- a/sys/geom/geom_vfs.c
+++ b/sys/geom/geom_vfs.c
@@ -65,11 +65,13 @@ static struct buf_ops __g_vfs_bufops = {
struct buf_ops *g_vfs_bufops = &__g_vfs_bufops;
static g_orphan_t g_vfs_orphan;
+static g_spoiled_t g_vfs_spoiled;
static struct g_class g_vfs_class = {
.name = "VFS",
.version = G_VERSION,
.orphan = g_vfs_orphan,
+ .spoiled = g_vfs_spoiled,
};
DECLARE_GEOM_CLASS(g_vfs_class, g_vfs);
@@ -215,8 +217,35 @@ g_vfs_orphan(struct g_consumer *cp)
if (sc == NULL)
return;
mtx_lock(&sc->sc_mtx);
+ destroy = (sc->sc_active == 0 && sc->sc_orphaned == 0);
+ sc->sc_orphaned = 1;
+ mtx_unlock(&sc->sc_mtx);
+ if (destroy)
+ g_vfs_destroy(cp, 0);
+
+ /*
+ * Do not destroy the geom. Filesystem will do that during unmount.
+ */
+}
+
+static void
+g_vfs_spoiled(struct g_consumer *cp)
+{
+ struct g_geom *gp;
+ struct g_vfs_softc *sc;
+ int destroy;
+
+ g_topology_assert();
+
+ gp = cp->geom;
+ g_trace(G_T_TOPOLOGY, "g_vfs_spoiled(%p(%s))", cp, gp->name);
+ cp->flags |= G_CF_ORPHAN;
+ sc = gp->softc;
+ if (sc == NULL)
+ return;
+ mtx_lock(&sc->sc_mtx);
+ destroy = (sc->sc_active == 0 && sc->sc_orphaned == 0);
sc->sc_orphaned = 1;
- destroy = (sc->sc_active == 0);
mtx_unlock(&sc->sc_mtx);
if (destroy)
g_vfs_destroy(cp, 0);
diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c
index 45ab5ce..48ae931 100644
--- a/sys/geom/part/g_part.c
+++ b/sys/geom/part/g_part.c
@@ -2055,6 +2055,7 @@ g_part_spoiled(struct g_consumer *cp)
G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
g_topology_assert();
+ cp->flags |= G_CF_ORPHAN;
g_part_wither(cp->geom, ENXIO);
}
OpenPOWER on IntegriCloud