From owner-svn-src-head@freebsd.org Thu Aug 29 07:51:12 2019 Return-Path: Delivered-To: svn-src-head@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id 3D1C0CD810; Thu, 29 Aug 2019 07:51:12 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46Jvtm1ZZmz4M4d; Thu, 29 Aug 2019 07:51:12 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 18A891CC9E; Thu, 29 Aug 2019 07:51:12 +0000 (UTC) (envelope-from avg@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x7T7pCVG077565; Thu, 29 Aug 2019 07:51:12 GMT (envelope-from avg@FreeBSD.org) Received: (from avg@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x7T7pB2X077563; Thu, 29 Aug 2019 07:51:11 GMT (envelope-from avg@FreeBSD.org) Message-Id: <201908290751.x7T7pB2X077563@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: avg set sender to avg@FreeBSD.org using -f From: Andriy Gapon Date: Thu, 29 Aug 2019 07:51:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r351599 - head/sys/cam/scsi X-SVN-Group: head X-SVN-Commit-Author: avg X-SVN-Commit-Paths: head/sys/cam/scsi X-SVN-Commit-Revision: 351599 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 29 Aug 2019 07:51:12 -0000 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