svn commit: r249152 - in stable/9/sys: cam cam/ata cam/scsi geom geom/part

Alexander Motin mav at FreeBSD.org
Fri Apr 5 11:41:59 UTC 2013


Author: mav
Date: Fri Apr  5 11:41:56 2013
New Revision: 249152
URL: http://svnweb.freebsd.org/changeset/base/249152

Log:
  MFC r238886, r238892:
  Implement media change notification for DA and CD removable media devices.
  It includes three parts:
   1) Modifications to CAM to detect media media changes and report them to
  disk(9) layer. For modern SATA (and potentially UAS) devices it utilizes
  Asynchronous Notification mechanism to receive events from hardware.
  Active polling with TEST UNIT READY commands with 3 seconds period is used
  for incapable hardware. After that both CD and DA drivers work the same way,
  detecting two conditions: "NOT READY: Medium not present" after medium was
  detected previously, and "UNIT ATTENTION: Not ready to ready change, medium
  may have changed". First one reported to disk(9) as media removal, second
  as media insert/change. To reliably receive second event new
  AC_UNIT_ATTENTION async added to make UAs broadcasted to all periphs by
  generic error handling code in cam_periph_error().
   2) Modifications to GEOM core to handle media remove and change events.
  Media removal handled by spoiling all consumers attached to the provider.
  Media change event also schedules provider retaste after spoiling to probe
  new media. New flag G_CF_ORPHAN was added to consumers to reflect that
  consumer is in process of destruction. It allows retaste to create new
  geom instance of the same class, while previous one is still dying.
   3) Modifications to some GEOM classes: DEV -- to report media change
  events to devd; PART class already handles spoiling alike to orphan.

Modified:
  stable/9/sys/cam/ata/ata_all.h
  stable/9/sys/cam/ata/ata_xpt.c
  stable/9/sys/cam/cam_ccb.h
  stable/9/sys/cam/cam_periph.c
  stable/9/sys/cam/cam_xpt.c
  stable/9/sys/cam/scsi/scsi_cd.c
  stable/9/sys/cam/scsi/scsi_da.c
  stable/9/sys/geom/geom.h
  stable/9/sys/geom/geom_dev.c
  stable/9/sys/geom/geom_disk.c
  stable/9/sys/geom/geom_disk.h
  stable/9/sys/geom/geom_event.c
  stable/9/sys/geom/geom_io.c
  stable/9/sys/geom/geom_slice.c
  stable/9/sys/geom/geom_subr.c
  stable/9/sys/geom/part/g_part.c
Directory Properties:
  stable/9/sys/   (props changed)

Modified: stable/9/sys/cam/ata/ata_all.h
==============================================================================
--- stable/9/sys/cam/ata/ata_all.h	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/ata/ata_all.h	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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 {

Modified: stable/9/sys/cam/ata/ata_xpt.c
==============================================================================
--- stable/9/sys/cam/ata/ata_xpt.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/ata/ata_xpt.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -463,6 +463,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,
@@ -1157,6 +1163,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);

Modified: stable/9/sys/cam/cam_ccb.h
==============================================================================
--- stable/9/sys/cam/cam_ccb.h	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/cam_ccb.h	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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 */

Modified: stable/9/sys/cam/cam_periph.c
==============================================================================
--- stable/9/sys/cam/cam_periph.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/cam_periph.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -1604,6 +1604,7 @@ cam_periph_error(union ccb *ccb, cam_fla
 	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 @@ cam_periph_error(union ccb *ccb, cam_fla
 			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: stable/9/sys/cam/cam_xpt.c
==============================================================================
--- stable/9/sys/cam/cam_xpt.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/cam_xpt.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -4053,6 +4053,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");
 }

Modified: stable/9/sys/cam/scsi/scsi_cd.c
==============================================================================
--- stable/9/sys/cam/scsi/scsi_cd.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/scsi/scsi_cd.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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 
 				  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 = CH
 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 co
 	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 co
 
 		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 @@ cdregister(struct cam_periph *periph, vo
 	 * 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 @@ cdregister(struct cam_periph *periph, vo
 		}
 	}
 
+	/*
+	 * 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 @@ cdstart(struct cam_periph *periph, union
 			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 @@ cdstart(struct cam_periph *periph, union
 
 			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 @@ cddone(struct cam_periph *periph, union 
 		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 @@ 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;
@@ -3171,6 +3261,14 @@ cderror(union ccb *ccb, u_int32_t cam_fl
 	    &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 @@ cderror(union ccb *ccb, u_int32_t cam_fl
 				 &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: stable/9/sys/cam/scsi/scsi_da.c
==============================================================================
--- stable/9/sys/cam/scsi/scsi_da.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/cam/scsi/scsi_da.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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 {
@@ -875,6 +879,11 @@ static void		dasetgeom(struct cam_periph
 				  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 */
@@ -889,12 +898,16 @@ static void		dashutdown(void *arg, int h
 #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);
@@ -984,6 +997,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);
 
@@ -1068,7 +1084,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;
 	}
 
@@ -1315,6 +1332,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);
@@ -1326,6 +1344,7 @@ daasync(void *callback_arg, u_int32_t co
 	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 @@ daasync(void *callback_arg, u_int32_t co
 		}
 		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 @@ daregister(struct cam_periph *periph, vo
 	 * 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 @@ daregister(struct cam_periph *periph, vo
 	 */
 	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 @@ dastart(struct cam_periph *periph, union
 		/* 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 @@ dadone(struct cam_periph *periph, union 
 	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 @@ daerror(union ccb *ccb, u_int32_t cam_fl
 			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 @@ daerror(union ccb *ccb, u_int32_t cam_fl
 }
 
 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: stable/9/sys/geom/geom.h
==============================================================================
--- stable/9/sys/geom/geom.h	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom.h	Fri Apr  5 11:41:56 2013	(r249152)
@@ -169,7 +169,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;
 
@@ -242,6 +244,8 @@ int g_post_event(g_event_t *func, void *
 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: stable/9/sys/geom/geom_dev.c
==============================================================================
--- stable/9/sys/geom/geom_dev.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_dev.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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,
 		    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
 
 	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: stable/9/sys/geom/geom_disk.c
==============================================================================
--- stable/9/sys/geom/geom_disk.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_disk.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -649,6 +649,32 @@ disk_attr_changed(struct disk *dp, const
 			(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: stable/9/sys/geom/geom_disk.h
==============================================================================
--- stable/9/sys/geom/geom_disk.h	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_disk.h	Fri Apr  5 11:41:56 2013	(r249152)
@@ -112,6 +112,8 @@ void disk_create(struct disk *disk, int 
 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: stable/9/sys/geom/geom_event.c
==============================================================================
--- stable/9/sys/geom/geom_event.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_event.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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);

Modified: stable/9/sys/geom/geom_io.c
==============================================================================
--- stable/9/sys/geom/geom_io.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_io.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -311,6 +311,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:

Modified: stable/9/sys/geom/geom_slice.c
==============================================================================
--- stable/9/sys/geom/geom_slice.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_slice.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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);

Modified: stable/9/sys/geom/geom_subr.c
==============================================================================
--- stable/9/sys/geom/geom_subr.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/geom_subr.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -260,10 +260,11 @@ g_modevent(module_t mod, int type, void 
 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_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();
 			}
@@ -531,7 +535,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)
@@ -542,11 +546,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;
@@ -803,7 +813,7 @@ g_access(struct g_consumer *cp, int dcr,
 	 * 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_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)
@@ -988,9 +999,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);
@@ -1015,11 +1026,54 @@ g_spoil(struct g_provider *pp, struct g_
 		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)
 {
@@ -1175,15 +1229,15 @@ db_show_geom_consumer(int indent, struct
 			    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");
 	}
 }

Modified: stable/9/sys/geom/part/g_part.c
==============================================================================
--- stable/9/sys/geom/part/g_part.c	Fri Apr  5 11:30:31 2013	(r249151)
+++ stable/9/sys/geom/part/g_part.c	Fri Apr  5 11:41:56 2013	(r249152)
@@ -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);
 }
 


More information about the svn-src-stable-9 mailing list