Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 29 Aug 2019 07:51:11 +0000 (UTC)
From:      Andriy Gapon <avg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r351599 - head/sys/cam/scsi
Message-ID:  <201908290751.x7T7pB2X077563@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avg
Date: Thu Aug 29 07:51:11 2019
New Revision: 351599
URL: https://svnweb.freebsd.org/changeset/base/351599

Log:
  scsi_cd: make the media check asynchronous
  
  This makes the media check process asynchronous, so we no longer block
  in cdstrategy() to check for media.
  
  PR:		219857
  Obtained from:	ken
  MFC after:	3 weeks

Modified:
  head/sys/cam/scsi/scsi_cd.c
  head/sys/cam/scsi/scsi_cd.h

Modified: head/sys/cam/scsi/scsi_cd.c
==============================================================================
--- head/sys/cam/scsi/scsi_cd.c	Thu Aug 29 07:50:25 2019	(r351598)
+++ head/sys/cam/scsi/scsi_cd.c	Thu Aug 29 07:51:11 2019	(r351599)
@@ -114,13 +114,21 @@ typedef enum {
 	CD_FLAG_RETRY_UA	= 0x0200,
 	CD_FLAG_VALID_MEDIA	= 0x0400,
 	CD_FLAG_VALID_TOC	= 0x0800,
-	CD_FLAG_SCTX_INIT	= 0x1000
+	CD_FLAG_SCTX_INIT	= 0x1000,
+	CD_FLAG_MEDIA_WAIT	= 0x2000,
+	CD_FLAG_MEDIA_SCAN_ACT	= 0x4000
 } cd_flags;
 
 typedef enum {
 	CD_CCB_PROBE		= 0x01,
 	CD_CCB_BUFFER_IO	= 0x02,
-	CD_CCB_TUR		= 0x04,
+	CD_CCB_TUR		= 0x03,
+	CD_CCB_MEDIA_PREVENT	= 0x04,
+	CD_CCB_MEDIA_ALLOW	= 0x05,
+	CD_CCB_MEDIA_SIZE	= 0x06,
+	CD_CCB_MEDIA_TOC_HDR	= 0x07,
+	CD_CCB_MEDIA_TOC_FULL	= 0x08,
+	CD_CCB_MEDIA_TOC_LEAD	= 0x09,
 	CD_CCB_TYPE_MASK	= 0x0F,
 	CD_CCB_RETRY_UA		= 0x10
 } cd_ccb_state;
@@ -140,7 +148,13 @@ struct cd_toc_single {
 
 typedef enum {
 	CD_STATE_PROBE,
-	CD_STATE_NORMAL
+	CD_STATE_NORMAL,
+	CD_STATE_MEDIA_PREVENT,
+	CD_STATE_MEDIA_ALLOW,
+	CD_STATE_MEDIA_SIZE,
+	CD_STATE_MEDIA_TOC_HDR,
+	CD_STATE_MEDIA_TOC_FULL,
+	CD_STATE_MEDIA_TOC_LEAD
 } cd_state;
 
 struct cd_softc {
@@ -161,6 +175,8 @@ struct cd_softc {
 	struct sysctl_oid	*sysctl_tree;
 	STAILQ_HEAD(, cd_mode_params)	mode_queue;
 	struct cd_tocdata	toc;
+	int			toc_read_len;
+	struct cd_toc_single	leadout;
 	struct disk		*disk;
 	struct callout		mediapoll_c;
 
@@ -246,7 +262,8 @@ static	void		cddone(struct cam_periph *periph,
 static	union cd_pages	*cdgetpage(struct cd_mode_params *mode_params);
 static	int		cdgetpagesize(int page_num);
 static	void		cdprevent(struct cam_periph *periph, int action);
-static	int		cdcheckmedia(struct cam_periph *periph);
+static	void		cdmediaprobedone(struct cam_periph *periph);
+static	int		cdcheckmedia(struct cam_periph *periph, int do_wait);
 static	int		cdsize(struct cam_periph *periph, u_int32_t *size);
 static	int		cd6byteworkaround(union ccb *ccb);
 static	int		cderror(union ccb *ccb, u_int32_t cam_flags,
@@ -755,7 +772,7 @@ cdopen(struct disk *dp)
 	 * if we don't have media, but then we don't allow anything but the
 	 * CDIOCEJECT/CDIOCCLOSE ioctls if there is no media.
 	 */
-	cdcheckmedia(periph);
+	cdcheckmedia(periph, /*do_wait*/ 1);
 
 	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
 	cam_periph_unhold(periph);
@@ -851,27 +868,19 @@ cdstrategy(struct bio *bp)
 		return;
 	}
 
-        /*
-	 * If we don't have valid media, look for it before trying to
-	 * schedule the I/O.
-	 */
-	if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) {
-		int error;
-
-		error = cdcheckmedia(periph);
-		if (error != 0) {
-			cam_periph_unlock(periph);
-			biofinish(bp, NULL, error);
-			return;
-		}
-	}
-
 	/*
 	 * Place it in the queue of disk activities for this disk
 	 */
 	bioq_disksort(&softc->bio_queue, bp);
 
-	xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+        /*
+	 * If we don't know that we have valid media, schedule the media 
+	 * check first.  The I/O will get executed after the media check.
+	 */
+	if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0)
+		cdcheckmedia(periph, /*do_wait*/ 0);
+	else
+		xpt_schedule(periph, CAM_PRIORITY_NORMAL);
 
 	cam_periph_unlock(periph);
 	return;
@@ -883,7 +892,6 @@ cdstart(struct cam_periph *periph, union ccb *start_cc
 	struct cd_softc *softc;
 	struct bio *bp;
 	struct ccb_scsiio *csio;
-	struct scsi_read_capacity_data *rcap;
 
 	softc = (struct cd_softc *)periph->softc;
 
@@ -964,16 +972,40 @@ cdstart(struct cam_periph *periph, union ccb *start_cc
 		break;
 	}
 	case CD_STATE_PROBE:
+	case CD_STATE_MEDIA_SIZE:
 	{
+		struct scsi_read_capacity_data *rcap;
 
 		rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
 		    M_SCSICD, M_NOWAIT | M_ZERO);
 		if (rcap == NULL) {
 			xpt_print(periph->path,
-			    "cdstart: Couldn't malloc read_capacity data\n");
-			/* cd_free_periph??? */
+			    "%s: Couldn't malloc read_capacity data\n",
+			    __func__);
+			xpt_release_ccb(start_ccb);
+			/*
+			 * We can't probe because we can't allocate memory,
+			 * so invalidate the peripheral.  The system probably
+			 * has larger problems at this stage.  If we've
+			 * already probed (and are re-probing capacity), we
+			 * don't need to invalidate.
+			 *
+			 * XXX KDM need to reset probe state and kick out
+			 * pending I/O.
+			 */
+			if (softc->state == CD_STATE_PROBE)
+				cam_periph_invalidate(periph);
 			break;
 		}
+
+		/*
+		 * Set the default capacity and sector size to something that
+		 * GEOM can handle.  This will get reset when a read capacity
+		 * completes successfully.
+		 */
+		softc->disk->d_sectorsize = 2048;
+		softc->disk->d_mediasize = 0;
+
 		csio = &start_ccb->csio;
 		scsi_read_capacity(csio,
 				   /*retries*/ cd_retry_count,
@@ -983,11 +1015,112 @@ cdstart(struct cam_periph *periph, union ccb *start_cc
 				   SSD_FULL_SIZE,
 				   /*timeout*/20000);
 		start_ccb->ccb_h.ccb_bp = NULL;
-		start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
+		if (softc->state == CD_STATE_PROBE)
+			start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
+		else
+			start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_SIZE;
 		xpt_action(start_ccb);
 		break;
 	}
+	case CD_STATE_MEDIA_ALLOW:
+	case CD_STATE_MEDIA_PREVENT:
+	{
+		/*
+		 * If the CD is already locked, we don't need to do this.
+		 * Move on to the capacity check.
+		 */
+		if ((softc->flags & CD_FLAG_DISC_LOCKED) != 0) {
+			softc->state = CD_STATE_MEDIA_SIZE;
+			xpt_release_ccb(start_ccb);
+			xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+			break;
+		}
+
+		scsi_prevent(&start_ccb->csio, 
+			     /*retries*/ cd_retry_count,
+			     /*cbfcnp*/ cddone,
+			     /*tag_action*/ MSG_SIMPLE_Q_TAG,
+			     /*action*/ (softc->state == CD_STATE_MEDIA_ALLOW) ?
+					PR_ALLOW : PR_PREVENT,
+			     /*sense_len*/ SSD_FULL_SIZE,
+			     /*timeout*/ 60000);
+
+		start_ccb->ccb_h.ccb_bp = NULL;
+		if (softc->state == CD_STATE_MEDIA_ALLOW)
+			start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_ALLOW;
+		else
+			start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_PREVENT;
+		xpt_action(start_ccb);
+		break;
 	}
+	case CD_STATE_MEDIA_TOC_HDR: {
+		struct ioc_toc_header *toch;
+		
+		bzero(&softc->toc, sizeof(softc->toc));
+
+		toch = &softc->toc.header;
+
+		scsi_read_toc(&start_ccb->csio,
+			      /*retries*/ cd_retry_count,
+			      /*cbfcnp*/ cddone,
+			      /*tag_action*/ MSG_SIMPLE_Q_TAG,
+			      /*byte1_flags*/ 0,
+			      /*format*/ SRTOC_FORMAT_TOC,
+			      /*track*/ 0,
+			      /*data_ptr*/ (uint8_t *)toch, 
+			      /*dxfer_len*/ sizeof(*toch),
+			      /*sense_len*/ SSD_FULL_SIZE,
+			      /*timeout*/ 50000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_TOC_HDR;
+		xpt_action(start_ccb);
+		break;
+	}
+	case CD_STATE_MEDIA_TOC_FULL: {
+
+		bzero(&softc->toc, sizeof(softc->toc));
+
+		scsi_read_toc(&start_ccb->csio,
+			      /*retries*/ cd_retry_count,
+			      /*cbfcnp*/ cddone,
+			      /*tag_action*/ MSG_SIMPLE_Q_TAG,
+			      /*byte1_flags*/ 0,
+			      /*format*/ SRTOC_FORMAT_TOC,
+			      /*track*/ 0,
+			      /*data_ptr*/ (uint8_t *)&softc->toc,
+			      /*dxfer_len*/ softc->toc_read_len ?
+					    softc->toc_read_len :
+					    sizeof(softc->toc),
+			      /*sense_len*/ SSD_FULL_SIZE,
+			      /*timeout*/ 50000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_TOC_FULL;
+		xpt_action(start_ccb);
+		break;
+	}
+	case CD_STATE_MEDIA_TOC_LEAD: {
+		struct cd_toc_single *leadout;
+
+		leadout = &softc->leadout;
+		bzero(leadout, sizeof(*leadout));
+
+		scsi_read_toc(&start_ccb->csio,
+			      /*retries*/ cd_retry_count,
+			      /*cbfcnp*/ cddone,
+			      /*tag_action*/ MSG_SIMPLE_Q_TAG,
+			      /*byte1_flags*/ CD_MSF,
+			      /*format*/ SRTOC_FORMAT_TOC,
+			      /*track*/ LEADOUT,
+			      /*data_ptr*/ (uint8_t *)leadout, 
+			      /*dxfer_len*/ sizeof(*leadout),
+			      /*sense_len*/ SSD_FULL_SIZE,
+			      /*timeout*/ 50000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = CD_CCB_MEDIA_TOC_LEAD;
+		xpt_action(start_ccb);
+		break;
+	}
+	}
 }
 
 static void
@@ -1251,6 +1384,293 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
 		cam_periph_release_locked(periph);
 		return;
 	}
+	case CD_CCB_MEDIA_ALLOW: 
+	case CD_CCB_MEDIA_PREVENT:
+	{
+		int error;
+		int is_prevent;
+
+		error = 0;
+
+		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+			error = cderror(done_ccb, CAM_RETRY_SELTO,
+			    SF_RETRY_UA | SF_NO_PRINT);
+		}
+		if (error == 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);
+
+		/*
+		 * Note that just like the original cdcheckmedia(), we do
+		 * a prevent without failing the whole operation if the
+		 * prevent fails.  We try, but keep going if it doesn't
+		 * work.
+		 */
+
+		if ((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
+		     CD_CCB_MEDIA_PREVENT)
+			is_prevent = 1;
+		else
+			is_prevent = 0;
+
+		xpt_release_ccb(done_ccb);
+
+		if (is_prevent != 0) {
+			if (error == 0)
+				softc->flags |= CD_FLAG_DISC_LOCKED;
+			else
+				softc->flags &= ~CD_FLAG_DISC_LOCKED;
+			softc->state = CD_STATE_MEDIA_SIZE;
+			xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+		} else {
+			if (error == 0)
+				softc->flags &= ~CD_FLAG_DISC_LOCKED;
+			softc->state = CD_STATE_NORMAL;
+			if (bioq_first(&softc->bio_queue) != NULL)
+				xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+		}
+		return;
+	}
+	case CD_CCB_MEDIA_SIZE:
+	{
+		struct scsi_read_capacity_data *rdcap;
+		int error;
+
+		error = 0;
+		if ((csio->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+			error = cderror(done_ccb, CAM_RETRY_SELTO,
+			    SF_RETRY_UA | SF_NO_PRINT);
+		}
+		if (error == 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);
+		rdcap = (struct scsi_read_capacity_data *)csio->data_ptr;
+
+		if (error == 0) {
+			softc->params.disksize =scsi_4btoul(rdcap->addr) + 1;
+			softc->params.blksize  = scsi_4btoul(rdcap->length);
+
+			/* Make sure we got at least some block size. */
+			if (softc->params.blksize == 0)
+				error = EIO;
+			/*
+			 * SCSI-3 mandates that the reported blocksize shall be
+			 * 2048.  Older drives sometimes report funny values,
+			 * trim it down to 2048, or other parts of the kernel
+			 * will get confused.
+			 *
+			 * XXX we leave drives alone that might report 512
+			 * bytes, as well as drives reporting more weird
+			 * sizes like perhaps 4K.
+			 */
+			if (softc->params.blksize > 2048
+			 && softc->params.blksize <= 2352)
+				softc->params.blksize = 2048;
+		}
+		free(rdcap, M_SCSICD);
+
+		if (error == 0) {
+			softc->disk->d_sectorsize = softc->params.blksize;
+			softc->disk->d_mediasize =
+			    (off_t)softc->params.blksize *
+			    softc->params.disksize;
+			softc->flags |= CD_FLAG_SAW_MEDIA | CD_FLAG_VALID_MEDIA;
+			softc->state = CD_STATE_MEDIA_TOC_HDR;
+		} else {
+			softc->flags &= ~(CD_FLAG_VALID_MEDIA |
+					  CD_FLAG_VALID_TOC);
+			bioq_flush(&softc->bio_queue, NULL, EINVAL);
+			softc->state = CD_STATE_MEDIA_ALLOW;
+			cdmediaprobedone(periph);
+		}
+		xpt_release_ccb(done_ccb);
+		xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+		return;
+	}
+	case CD_CCB_MEDIA_TOC_HDR:
+	case CD_CCB_MEDIA_TOC_FULL:
+	case CD_CCB_MEDIA_TOC_LEAD:
+	{
+		int error;
+		struct ioc_toc_header *toch;
+		int num_entries;
+		int cdindex;
+
+		error = 0;
+
+		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+			error = cderror(done_ccb, CAM_RETRY_SELTO,
+			    SF_RETRY_UA | SF_NO_PRINT);
+		}
+		if (error == 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);
+
+		/*
+		 * We will get errors here for media that doesn't have a table
+		 * of contents.  According to the MMC-3 spec: "When a Read 
+		 * TOC/PMA/ATIP command is presented for a DDCD/CD-R/RW media,
+		 * where the first TOC has not been recorded (no complete
+		 * session) and the Format codes 0000b, 0001b, or 0010b are
+		 * specified, this command shall be rejected with an INVALID 
+		 * FIELD IN CDB.  Devices that are not capable of reading an 
+		 * incomplete session on DDC/CD-R/RW media shall report
+		 * CANNOT READ MEDIUM - INCOMPATIBLE FORMAT."
+		 *
+		 * So this isn't fatal if we can't read the table of contents,
+		 * it just means that the user won't be able to issue the
+		 * play tracks ioctl, and likely lots of other stuff won't
+		 * work either.  They need to burn the CD before we can do
+		 * a whole lot with it.  So we don't print anything here if
+		 * we get an error back.
+		 *
+		 * We also bail out if the drive doesn't at least give us
+		 * the full TOC header.
+		 */
+		if ((error != 0)
+		 || ((csio->dxfer_len - csio->resid) <
+		      sizeof(struct ioc_toc_header))) {
+			softc->flags &= ~CD_FLAG_VALID_TOC;
+			bzero(&softc->toc, sizeof(softc->toc));
+			/*
+			 * Failing the TOC read is not an error.
+			 */
+			softc->state = CD_STATE_NORMAL;
+			xpt_release_ccb(done_ccb);
+
+			cdmediaprobedone(periph);
+
+			/*
+			 * Go ahead and schedule I/O execution if there is
+			 * anything in the queue.  It'll probably get
+			 * kicked out with an error.
+			 */
+			if (bioq_first(&softc->bio_queue) != NULL)
+				xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+			return;
+		}
+
+		/*
+		 * Note that this is NOT the storage location used for the
+		 * leadout!
+		 */
+		toch = &softc->toc.header;
+
+		if (softc->quirks & CD_Q_BCD_TRACKS) {
+			toch->starting_track = bcd2bin(toch->starting_track);
+			toch->ending_track = bcd2bin(toch->ending_track);
+		}
+
+		/* Number of TOC entries, plus leadout */
+		num_entries = (toch->ending_track - toch->starting_track) + 2;
+		cdindex = toch->starting_track + num_entries -1;
+
+		if ((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
+		     CD_CCB_MEDIA_TOC_HDR) {
+			if (num_entries <= 0) {
+				softc->flags &= ~CD_FLAG_VALID_TOC;
+				bzero(&softc->toc, sizeof(softc->toc));
+				/*
+				 * Failing the TOC read is not an error.
+				 */
+				softc->state = CD_STATE_NORMAL;
+				xpt_release_ccb(done_ccb);
+
+				cdmediaprobedone(periph);
+
+				/*
+				 * Go ahead and schedule I/O execution if
+				 * there is anything in the queue.  It'll
+				 * probably get kicked out with an error.
+				 */
+				if (bioq_first(&softc->bio_queue) != NULL)
+					xpt_schedule(periph,
+					    CAM_PRIORITY_NORMAL);
+			} else {
+				softc->toc_read_len = num_entries *
+				    sizeof(struct cd_toc_entry);
+				softc->toc_read_len += sizeof(*toch);
+
+				softc->state = CD_STATE_MEDIA_TOC_FULL;
+				xpt_release_ccb(done_ccb);
+				xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+			}
+
+			return;
+		} else if ((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
+			    CD_CCB_MEDIA_TOC_LEAD) {
+			struct cd_toc_single *leadout;
+
+			leadout = (struct cd_toc_single *)csio->data_ptr;
+			softc->toc.entries[cdindex - toch->starting_track] =
+			    leadout->entry;
+		} else if (((done_ccb->ccb_h.ccb_state & CD_CCB_TYPE_MASK) ==
+			    CD_CCB_MEDIA_TOC_FULL)
+			&& (cdindex == toch->ending_track + 1)) {
+			/*
+			 * XXX KDM is this necessary?  Probably only if the
+			 * drive doesn't return leadout information with the
+			 * table of contents.
+			 */
+			softc->state = CD_STATE_MEDIA_TOC_LEAD;
+			xpt_release_ccb(done_ccb);
+			xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+			return;
+		}
+
+		if (softc->quirks & CD_Q_BCD_TRACKS) {
+			for (cdindex = 0; cdindex < num_entries - 1; cdindex++){
+				softc->toc.entries[cdindex].track =
+				    bcd2bin(softc->toc.entries[cdindex].track);
+			}
+		}
+
+		softc->flags |= CD_FLAG_VALID_TOC;
+		/* If the first track is audio, correct sector size. */
+		if ((softc->toc.entries[0].control & 4) == 0) {
+			softc->disk->d_sectorsize =softc->params.blksize = 2352;
+			softc->disk->d_mediasize =
+			    (off_t)softc->params.blksize *
+			    softc->params.disksize;
+		}
+		softc->state = CD_STATE_NORMAL;
+
+		/*
+		 * We unconditionally (re)set the blocksize each time the
+		 * CD device is opened.  This is because the CD can change,
+		 * and therefore the blocksize might change.
+		 * XXX problems here if some slice or partition is still
+		 * open with the old size?
+		 */
+		if ((softc->disk->d_devstat->flags & DEVSTAT_BS_UNAVAILABLE)!=0)
+			softc->disk->d_devstat->flags &=
+			    ~DEVSTAT_BS_UNAVAILABLE;
+		softc->disk->d_devstat->block_size = softc->params.blksize;
+
+		xpt_release_ccb(done_ccb);
+
+		cdmediaprobedone(periph);
+
+		if (bioq_first(&softc->bio_queue) != NULL)
+			xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+		return;
+	}
 	default:
 		break;
 	}
@@ -1343,7 +1763,7 @@ cdioctl(struct disk *dp, u_long cmd, void *addr, int f
 	 && ((cmd != CDIOCCLOSE)
 	  && (cmd != CDIOCEJECT))
 	 && (IOCGROUP(cmd) == 'c')) {
-		error = cdcheckmedia(periph);
+		error = cdcheckmedia(periph, /*do_wait*/ 1);
 		if (error != 0) {
 			cam_periph_unhold(periph);
 			cam_periph_unlock(periph);
@@ -2227,11 +2647,66 @@ cdprevent(struct cam_periph *periph, int action)
 	}
 }
 
+static void
+cdmediaprobedone(struct cam_periph *periph)
+{
+	struct cd_softc *softc;
+
+	softc = (struct cd_softc *)periph->softc;
+
+	softc->flags &= ~CD_FLAG_MEDIA_SCAN_ACT;
+
+	if ((softc->flags & CD_FLAG_MEDIA_WAIT) != 0) {
+		softc->flags &= ~CD_FLAG_MEDIA_WAIT;
+		wakeup(&softc->toc);
+	}
+}
+
 /*
  * XXX: the disk media and sector size is only really able to change
  * XXX: while the device is closed.
  */
+
 static int
+cdcheckmedia(struct cam_periph *periph, int do_wait)
+{
+	struct cd_softc *softc;
+	int error;
+
+	softc = (struct cd_softc *)periph->softc;
+	error = 0;
+
+	if ((do_wait != 0)
+	 && ((softc->flags & CD_FLAG_MEDIA_WAIT) == 0)) {
+		softc->flags |= CD_FLAG_MEDIA_WAIT;
+	}
+	if ((softc->flags & CD_FLAG_MEDIA_SCAN_ACT) == 0) {
+		softc->state = CD_STATE_MEDIA_PREVENT;
+		softc->flags |= CD_FLAG_MEDIA_SCAN_ACT;
+		xpt_schedule(periph, CAM_PRIORITY_NORMAL);
+	}
+
+	if (do_wait == 0)
+		goto bailout;
+
+	error = msleep(&softc->toc, cam_periph_mtx(periph), PRIBIO,"cdmedia",0);
+	
+	if (error != 0)
+		goto bailout;
+
+	/*
+	 * Check to see whether we have a valid size from the media.  We
+	 * may or may not have a valid TOC.
+	 */
+	if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0)
+		error = EINVAL;
+bailout:
+
+	return (error);
+}
+
+#if 0
+static int
 cdcheckmedia(struct cam_periph *periph)
 {
 	struct cd_softc *softc;
@@ -2371,6 +2846,7 @@ bailout:
 
 	return (error);
 }
+#endif
 
 static int
 cdsize(struct cam_periph *periph, u_int32_t *size)
@@ -2650,7 +3126,6 @@ static int 
 cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, 
 	  u_int8_t *data, u_int32_t len, u_int32_t sense_flags)
 {
-	struct scsi_read_toc *scsi_cmd;
 	u_int32_t ntoc;
         struct ccb_scsiio *csio;
 	union ccb *ccb;
@@ -2663,29 +3138,18 @@ cdreadtoc(struct cam_periph *periph, u_int32_t mode, u
 
 	csio = &ccb->csio;
 
-	cam_fill_csio(csio, 
+	scsi_read_toc(csio,
 		      /* retries */ cd_retry_count, 
 		      /* cbfcnp */ NULL,
-		      /* flags */ CAM_DIR_IN,
 		      /* tag_action */ MSG_SIMPLE_Q_TAG,
+		      /* byte1_flags */ (mode == CD_MSF_FORMAT) ? CD_MSF : 0,
+		      /* format */ SRTOC_FORMAT_TOC,
+		      /* track*/ start,
 		      /* data_ptr */ data,
 		      /* dxfer_len */ len,
 		      /* sense_len */ SSD_FULL_SIZE,
-		      sizeof(struct scsi_read_toc),
- 		      /* timeout */ 50000);
+		      /* timeout */ 50000);
 
-	scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes;
-	bzero (scsi_cmd, sizeof(*scsi_cmd));
-
-	if (mode == CD_MSF_FORMAT)
-		scsi_cmd->byte2 |= CD_MSF;
-	scsi_cmd->from_track = start;
-	/* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */
-	scsi_cmd->data_len[0] = (ntoc) >> 8;
-	scsi_cmd->data_len[1] = (ntoc) & 0xff;
-
-	scsi_cmd->op_code = READ_TOC;
-
 	error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
 			 /*sense_flags*/SF_RETRY_UA | sense_flags);
 
@@ -3742,4 +4206,39 @@ scsi_read_dvd_structure(struct ccb_scsiio *csio, u_int
 		      sense_len,
 		      sizeof(*scsi_cmd),
 		      timeout);
+}
+
+void
+scsi_read_toc(struct ccb_scsiio *csio, uint32_t retries,
+	      void (*cbfcnp)(struct cam_periph *, union ccb *),
+	      uint8_t tag_action, uint8_t byte1_flags, uint8_t format,
+	      uint8_t track, uint8_t *data_ptr, uint32_t dxfer_len,
+	      int sense_len, int timeout)
+{
+	struct scsi_read_toc *scsi_cmd;
+
+	scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes;
+	bzero(scsi_cmd, sizeof(*scsi_cmd));
+	scsi_cmd->op_code = READ_TOC;
+
+	/*
+	 * The structure is counting from 1, the function counting from 0.
+	 * The spec counts from 0.  In MMC-6, there is only one flag, the
+	 * MSF flag.  But we put the whole byte in for a bit a future-proofing.
+	 */
+	scsi_cmd->byte2 = byte1_flags;
+	scsi_cmd->format = format;
+	scsi_cmd->from_track = track;
+	scsi_ulto2b(dxfer_len, scsi_cmd->data_len);
+
+	cam_fill_csio(csio, 
+		      /* retries */ retries, 
+		      /* cbfcnp */ cbfcnp,
+		      /* flags */ CAM_DIR_IN,
+		      /* tag_action */ tag_action,
+		      /* data_ptr */ data_ptr,
+		      /* dxfer_len */ dxfer_len,
+		      /* sense_len */ sense_len,
+		      sizeof(*scsi_cmd),
+ 		      /* timeout */ timeout);
 }

Modified: head/sys/cam/scsi/scsi_cd.h
==============================================================================
--- head/sys/cam/scsi/scsi_cd.h	Thu Aug 29 07:50:25 2019	(r351598)
+++ head/sys/cam/scsi/scsi_cd.h	Thu Aug 29 07:51:11 2019	(r351599)
@@ -231,6 +231,12 @@ struct scsi_read_toc
 	u_int8_t op_code;
 	u_int8_t byte2;
 	u_int8_t format;
+#define	SRTOC_FORMAT_TOC	0x00
+#define	SRTOC_FORMAT_LAST_ADDR	0x01
+#define	SRTOC_FORMAT_QSUB_TOC	0x02
+#define	SRTOC_FORMAT_QSUB_PMA	0x03
+#define	SRTOC_FORMAT_ATIP	0x04
+#define	SRTOC_FORMAT_CD_TEXT	0x05
 	u_int8_t unused[3];
 	u_int8_t from_track;
 	u_int8_t data_len[2];
@@ -870,6 +876,12 @@ void scsi_read_dvd_structure(struct ccb_scsiio *csio, 
 			     u_int8_t agid, u_int8_t *data_ptr,
 			     u_int32_t dxfer_len, u_int8_t sense_len,
 			     u_int32_t timeout);
+
+void scsi_read_toc(struct ccb_scsiio *csio, uint32_t retries,
+		   void (*cbfcnp)(struct cam_periph *, union ccb *),
+		   uint8_t tag_action, uint8_t byte1_flags, uint8_t format,
+		   uint8_t track, uint8_t *data_ptr, uint32_t dxfer_len,
+		   int sense_len, int timeout);
 
 __END_DECLS
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201908290751.x7T7pB2X077563>