Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 001aac25 authored by James Bottomley's avatar James Bottomley Committed by James Bottomley
Browse files

[SCSI] sd,sr: add early detection of medium not present



The current scsi_test_unit_ready() is updated to return sense code
information (in struct scsi_sense_hdr).  The sd and sr drivers are
changed to interpret the sense code return asc 0x3a as no media and
adjust the device status accordingly.

Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 4a03d90e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -244,7 +244,7 @@ int scsi_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
		return scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW);
	case SCSI_IOCTL_TEST_UNIT_READY:
		return scsi_test_unit_ready(sdev, IOCTL_NORMAL_TIMEOUT,
					    NORMAL_RETRIES);
					    NORMAL_RETRIES, NULL);
	case SCSI_IOCTL_START_UNIT:
		scsi_cmd[0] = START_STOP;
		scsi_cmd[1] = 0;
+38 −8
Original line number Diff line number Diff line
@@ -1973,27 +1973,57 @@ scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
}
EXPORT_SYMBOL(scsi_mode_sense);

/**
 *	scsi_test_unit_ready - test if unit is ready
 *	@sdev:	scsi device to change the state of.
 *	@timeout: command timeout
 *	@retries: number of retries before failing
 *	@sshdr_external: Optional pointer to struct scsi_sense_hdr for
 *		returning sense. Make sure that this is cleared before passing
 *		in.
 *
 *	Returns zero if unsuccessful or an error if TUR failed.  For
 *	removable media, a return of NOT_READY or UNIT_ATTENTION is
 *	translated to success, with the ->changed flag updated.
 **/
int
scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries)
scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries,
		     struct scsi_sense_hdr *sshdr_external)
{
	char cmd[] = {
		TEST_UNIT_READY, 0, 0, 0, 0, 0,
	};
	struct scsi_sense_hdr sshdr;
	struct scsi_sense_hdr *sshdr;
	int result;

	result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr,
	if (!sshdr_external)
		sshdr = kzalloc(sizeof(*sshdr), GFP_KERNEL);
	else
		sshdr = sshdr_external;

	/* try to eat the UNIT_ATTENTION if there are enough retries */
	do {
		result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, sshdr,
					  timeout, retries);
	} while ((driver_byte(result) & DRIVER_SENSE) &&
		 sshdr && sshdr->sense_key == UNIT_ATTENTION &&
		 --retries);

	if (!sshdr)
		/* could not allocate sense buffer, so can't process it */
		return result;

	if ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) {

		if ((scsi_sense_valid(&sshdr)) &&
		    ((sshdr.sense_key == UNIT_ATTENTION) ||
		     (sshdr.sense_key == NOT_READY))) {
		if ((scsi_sense_valid(sshdr)) &&
		    ((sshdr->sense_key == UNIT_ATTENTION) ||
		     (sshdr->sense_key == NOT_READY))) {
			sdev->changed = 1;
			result = 0;
		}
	}
	if (!sshdr_external)
		kfree(sshdr);
	return result;
}
EXPORT_SYMBOL(scsi_test_unit_ready);
+10 −3
Original line number Diff line number Diff line
@@ -736,6 +736,7 @@ static int sd_media_changed(struct gendisk *disk)
{
	struct scsi_disk *sdkp = scsi_disk(disk);
	struct scsi_device *sdp = sdkp->device;
	struct scsi_sense_hdr *sshdr = NULL;
	int retval;

	SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_media_changed\n"));
@@ -766,8 +767,11 @@ static int sd_media_changed(struct gendisk *disk)
	 */
	retval = -ENODEV;

	if (scsi_block_when_processing_errors(sdp))
		retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES);
	if (scsi_block_when_processing_errors(sdp)) {
		sshdr  = kzalloc(sizeof(*sshdr), GFP_KERNEL);
		retval = scsi_test_unit_ready(sdp, SD_TIMEOUT, SD_MAX_RETRIES,
					      sshdr);
	}

	/*
	 * Unable to test, unit probably not ready.   This usually
@@ -775,7 +779,9 @@ static int sd_media_changed(struct gendisk *disk)
	 * and we will figure it out later once the drive is
	 * available again.
	 */
	if (retval) {
	if (retval || (scsi_sense_valid(sshdr) &&
		       /* 0x3a is medium not present */
		       sshdr->asc == 0x3a)) {
		set_media_not_present(sdkp);
		retval = 1;
		goto out;
@@ -794,6 +800,7 @@ static int sd_media_changed(struct gendisk *disk)
	if (retval != sdkp->previous_state)
		sdev_evt_send_simple(sdp, SDEV_EVT_MEDIA_CHANGE, GFP_KERNEL);
	sdkp->previous_state = retval;
	kfree(sshdr);
	return retval;
}

+13 −6
Original line number Diff line number Diff line
@@ -179,18 +179,24 @@ static int sr_media_change(struct cdrom_device_info *cdi, int slot)
{
	struct scsi_cd *cd = cdi->handle;
	int retval;
	struct scsi_sense_hdr *sshdr;

	if (CDSL_CURRENT != slot) {
		/* no changer support */
		return -EINVAL;
	}

	retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES);
	if (retval) {
		/* Unable to test, unit probably not ready.  This usually
		 * means there is no disc in the drive.  Mark as changed,
		 * and we will figure it out later once the drive is
		 * available again.  */
	sshdr =  kzalloc(sizeof(*sshdr), GFP_KERNEL);
	retval = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES,
				      sshdr);
	if (retval || (scsi_sense_valid(sshdr) &&
		       /* 0x3a is medium not present */
		       sshdr->asc == 0x3a)) {
		/* Media not present or unable to test, unit probably not
		 * ready. This usually means there is no disc in the drive.
		 * Mark as changed, and we will figure it out later once
		 * the drive is available again.
		 */
		cd->device->changed = 1;
		/* This will force a flush, if called from check_disk_change */
		retval = 1;
@@ -213,6 +219,7 @@ static int sr_media_change(struct cdrom_device_info *cdi, int slot)
		sdev_evt_send_simple(cd->device, SDEV_EVT_MEDIA_CHANGE,
				     GFP_KERNEL);
	cd->previous_state = retval;
	kfree(sshdr);

	return retval;
}
+1 −1
Original line number Diff line number Diff line
@@ -295,7 +295,7 @@ extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp,
			    struct scsi_mode_data *data,
			    struct scsi_sense_hdr *);
extern int scsi_test_unit_ready(struct scsi_device *sdev, int timeout,
				int retries);
				int retries, struct scsi_sense_hdr *sshdr);
extern int scsi_device_set_state(struct scsi_device *sdev,
				 enum scsi_device_state state);
extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,