[Midnightbsd-cvs] src [9046] trunk/sys: implement media change notifications for DA and CD.

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Sat Oct 1 05:59:20 EDT 2016


Revision: 9046
          http://svnweb.midnightbsd.org/src/?rev=9046
Author:   laffer1
Date:     2016-10-01 05:59:20 -0400 (Sat, 01 Oct 2016)
Log Message:
-----------
implement media change notifications for DA and CD.

Modified Paths:
--------------
    trunk/sys/cam/ata/ata_all.h
    trunk/sys/cam/ata/ata_xpt.c
    trunk/sys/cam/cam_ccb.h
    trunk/sys/cam/cam_periph.c
    trunk/sys/cam/cam_xpt.c
    trunk/sys/cam/scsi/scsi_cd.c
    trunk/sys/cam/scsi/scsi_da.c
    trunk/sys/geom/geom.h
    trunk/sys/geom/geom_dev.c
    trunk/sys/geom/geom_disk.c
    trunk/sys/geom/geom_disk.h
    trunk/sys/geom/geom_event.c
    trunk/sys/geom/geom_io.c
    trunk/sys/geom/geom_slice.c
    trunk/sys/geom/geom_subr.c
    trunk/sys/geom/part/g_part.c

Modified: trunk/sys/cam/ata/ata_all.h
===================================================================
--- trunk/sys/cam/ata/ata_all.h	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/ata/ata_all.h	2016-10-01 09:59:20 UTC (rev 9046)
@@ -35,6 +35,7 @@
 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 {

Modified: trunk/sys/cam/ata/ata_xpt.c
===================================================================
--- trunk/sys/cam/ata/ata_xpt.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/ata/ata_xpt.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -463,6 +463,12 @@
 		    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,
@@ -1157,6 +1163,12 @@
 		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);

Modified: trunk/sys/cam/cam_ccb.h
===================================================================
--- trunk/sys/cam/cam_ccb.h	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/cam_ccb.h	2016-10-01 09:59:20 UTC (rev 9046)
@@ -755,6 +755,7 @@
  * 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 */

Modified: trunk/sys/cam/cam_periph.c
===================================================================
--- trunk/sys/cam/cam_periph.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/cam_periph.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -1604,6 +1604,7 @@
 	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;
@@ -1770,6 +1771,12 @@
 			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 */

Modified: trunk/sys/cam/cam_xpt.c
===================================================================
--- trunk/sys/cam/cam_xpt.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/cam_xpt.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -4053,6 +4053,7 @@
 	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");
 }

Modified: trunk/sys/cam/scsi/scsi_cd.c
===================================================================
--- trunk/sys/cam/scsi/scsi_cd.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/scsi/scsi_cd.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -97,6 +97,7 @@
 	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 @@
 	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,6 +156,7 @@
 	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;
@@ -160,6 +163,7 @@
 	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 @@
 				  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 @@
 
 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 @@
 #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 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 @@
 		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 @@
 	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 @@
 
 		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;
@@ -784,8 +827,8 @@
 	 * 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
@@ -1001,6 +1044,17 @@
 		}
 	}
 
+	/*
+	 * 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)
@@ -1496,8 +1550,25 @@
 			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,
@@ -1541,7 +1612,7 @@
 
 			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);
 		}
@@ -1835,6 +1906,25 @@
 		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;
 	}
@@ -2826,7 +2916,7 @@
 		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;
@@ -3171,6 +3261,14 @@
 	    &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)
@@ -3186,6 +3284,26 @@
 				 &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
  */

Modified: trunk/sys/cam/scsi/scsi_da.c
===================================================================
--- trunk/sys/cam/scsi/scsi_da.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/cam/scsi/scsi_da.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -77,6 +77,7 @@
 	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 @@
 	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 @@
 	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 @@
 	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 {
@@ -875,7 +879,12 @@
 				  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 */
 #endif
@@ -889,6 +898,7 @@
 #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;
@@ -895,6 +905,9 @@
 
 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);
@@ -984,6 +997,9 @@
 	    (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);
 
@@ -1068,7 +1084,8 @@
 
 	/* 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;
 	}
 
@@ -1315,6 +1332,7 @@
 		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);
@@ -1326,6 +1344,7 @@
 	struct cam_path *path, void *arg)
 {
 	struct cam_periph *periph;
+	struct da_softc *softc;
 
 	periph = (struct cam_periph *)callback_arg;
 	switch (code) {
@@ -1377,10 +1396,43 @@
 		}
 		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;
@@ -1711,9 +1763,9 @@
 	 * 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 
@@ -1723,6 +1775,16 @@
 	 */
 	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);
@@ -1860,9 +1922,25 @@
 		/* 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) {
@@ -2429,6 +2507,25 @@
 	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;
 	}
@@ -2489,6 +2586,13 @@
 			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)
@@ -2505,6 +2609,23 @@
 }
 
 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;

Modified: trunk/sys/geom/geom.h
===================================================================
--- trunk/sys/geom/geom.h	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom.h	2016-10-01 09:59:20 UTC (rev 9046)
@@ -169,7 +169,9 @@
 	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;
 
@@ -242,6 +244,8 @@
 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);
 

Modified: trunk/sys/geom/geom_dev.c
===================================================================
--- trunk/sys/geom/geom_dev.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_dev.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -44,6 +44,7 @@
 #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>
@@ -106,7 +107,22 @@
 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;
 
@@ -120,7 +136,6 @@
 		    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;
 
@@ -162,9 +177,6 @@
 
 	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, "%s", pp->name);
 	cp = g_new_consumer(gp);
 	error = g_attach(cp, pp);

Modified: trunk/sys/geom/geom_disk.c
===================================================================
--- trunk/sys/geom/geom_disk.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_disk.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -650,6 +650,32 @@
 			(void)g_attr_changed(pp, attr, 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);
+	}
+}
+
 static void
 g_kern_disks(void *p, int flag __unused)
 {

Modified: trunk/sys/geom/geom_disk.h
===================================================================
--- trunk/sys/geom/geom_disk.h	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_disk.h	2016-10-01 09:59:20 UTC (rev 9046)
@@ -113,6 +113,8 @@
 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);
 
 #define DISK_VERSION_00		0x58561059
 #define DISK_VERSION_01		0x5856105a

Modified: trunk/sys/geom/geom_event.c
===================================================================
--- trunk/sys/geom/geom_event.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_event.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -1,4 +1,4 @@
-/* $MidnightBSD: src/sys/geom/geom_event.c,v 1.5 2011/12/10 22:55:34 laffer1 Exp $ */
+/* $MidnightBSD$ */
 /*-
  * Copyright (c) 2002 Poul-Henning Kamp
  * Copyright (c) 2002 Networks Associates Technology, Inc.
@@ -203,14 +203,12 @@
 	 * 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);

Modified: trunk/sys/geom/geom_io.c
===================================================================
--- trunk/sys/geom/geom_io.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_io.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -312,6 +312,8 @@
 	/* 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:

Modified: trunk/sys/geom/geom_slice.c
===================================================================
--- trunk/sys/geom/geom_slice.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_slice.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -466,6 +466,7 @@
 	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);

Modified: trunk/sys/geom/geom_subr.c
===================================================================
--- trunk/sys/geom/geom_subr.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/geom_subr.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -260,10 +260,11 @@
 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 ? */
@@ -280,17 +281,20 @@
 	}
 	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();
 			}
@@ -531,7 +535,7 @@
 {
 	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)
@@ -542,11 +546,17 @@
 	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;
@@ -803,7 +813,7 @@
 	 * 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);
 
@@ -953,6 +963,7 @@
 	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)
@@ -988,9 +999,9 @@
 	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);
@@ -1015,12 +1026,55 @@
 		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)
 {
 	int error, i;
@@ -1175,7 +1229,7 @@
 			    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 {
@@ -1182,8 +1236,8 @@
 		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");
 	}
 }

Modified: trunk/sys/geom/part/g_part.c
===================================================================
--- trunk/sys/geom/part/g_part.c	2016-10-01 09:58:43 UTC (rev 9045)
+++ trunk/sys/geom/part/g_part.c	2016-10-01 09:59:20 UTC (rev 9046)
@@ -2062,6 +2062,7 @@
 	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);
 }
 



More information about the Midnightbsd-cvs mailing list