diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index 0c271b9e3c5b..d8d82623ed3e 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -286,6 +286,18 @@
 #include <scsi/scsi_common.h>
 #include <scsi/scsi_request.h>

+/*
+ * For the wait-and-retry loop after possibly having loaded the drive tray.
+ * 10 retries in 20 seconds are hardcoded in sr_do_ioctl() which was used
+ * up to 2008.
+ * But time spans up to 25 seconds were measured by libburn on
+ * drives connected via SATA or USB-SATA bridges.
+ * So 20 retries * 2000 ms = 40 seconds seems more appropriate.
+ */
+#define CD_OPEN_MEDIUM_RETRY_MAX 20
+#define CD_OPEN_MEDIUM_RETRY_MSLEEP 2000
+#include <linux/delay.h>
+
 /* used to tell the module to turn on full debugging messages */
 static bool debug;
 /* default compatibility mode */
@@ -1040,6 +1052,106 @@ static void cdrom_count_tracks(struct cdrom_device_info *cdi, tracktype *tracks)
 	       tracks->cdi, tracks->xa);
 }

+static
+int wait_for_medium_decision(struct cdrom_device_info *cdi)
+{
+	int retry = 0, ret;
+	const struct cdrom_device_ops *cdo = cdi->ops;
+
+	/* Wait until the intermediate drive status CDS_DRIVE_NOT_READY ends */
+	while (1) {
+		ret = cdo->drive_status(cdi, CDSL_CURRENT);
+		if (ret == CDS_DRIVE_NOT_READY &&
+		    retry++ < CD_OPEN_MEDIUM_RETRY_MAX)
+			msleep(CD_OPEN_MEDIUM_RETRY_MSLEEP);
+		else
+			break;
+	}
+	if (ret != CDS_DISC_OK)
+		return ret;
+	/*
+	 * It is hard to test whether very recent readiness can cause race
+	 * conditions with media change events. So wait a while to never
+	 * undercut the average delay between actual readiness and detection
+	 * which was tested without this additional msleep().
+	 */
+	msleep(CD_OPEN_MEDIUM_RETRY_MSLEEP / 2);
+
+	return CDS_DISC_OK;
+}
+
+/*
+ * To be called by expectant callers of cdrom_open(), before they call
+ * check_disk_change() and then cdrom_open().
+ *
+ * If the mode is right, the drive capable, the tray out, and autoclose
+ * enabled, try to move in the tray and wait for the drive's decision about
+ * the medium.
+ * Return 0 if cdrom_open() would not want to know the tray status, or the
+ * drive cannot report its tray status at all, or the decision is CDS_DISC_OK.
+ * Else return a negative error number.
+ * Input parameter mode decides whether cdrom_open() will want to know or
+ * change the tray status at all.
+ * Input parameter leave_open == 1 suppresses the try to close, and rather just
+ * assesses the situation. Submit mode == 0 to not hamper assessment.
+ */
+int cdrom_handle_open_tray(struct cdrom_device_info *cdi, fmode_t mode,
+			   int leave_open)
+{
+	int ret;
+	const struct cdrom_device_ops *cdo = cdi->ops;
+
+	if ((mode & FMODE_NDELAY) && (cdi->options & CDO_USE_FFLAGS))
+		return 0;
+	if (!cdo->drive_status)
+		return 0;
+
+	ret = cdo->drive_status(cdi, CDSL_CURRENT);
+	cd_dbg(CD_OPEN, "drive_status=%d\n", ret);
+	if (ret != CDS_TRAY_OPEN)
+		goto assess_and_return;
+
+	cd_dbg(CD_OPEN, "the tray is open...\n");
+	if (leave_open)
+		return -ENOMEDIUM;
+	/* can/may i close it? */
+	if (CDROM_CAN(CDC_CLOSE_TRAY) && cdi->options & CDO_AUTO_CLOSE) {
+		cd_dbg(CD_OPEN, "trying to close the tray\n");
+		ret = cdo->tray_move(cdi, 0);
+		if (ret) {
+			cd_dbg(CD_OPEN,
+			       "bummer. tried to close the tray but failed.\n");
+			/* Ignore the error from the low
+			 * level driver.  We don't care why it
+			 * couldn't close the tray.  We only care
+			 * that there is no disc in the drive,
+			 * since that is the _REAL_ problem here.
+			 */
+			return -ENOMEDIUM;
+		}
+	} else {
+		if (!CDROM_CAN(CDC_CLOSE_TRAY))
+			cd_dbg(CD_OPEN,
+			       "bummer. this drive can't close the tray.\n");
+		return -ENOMEDIUM;
+	}
+
+	ret = wait_for_medium_decision(cdi);
+	if (ret == CDS_NO_DISC || ret == CDS_TRAY_OPEN) {
+		cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n");
+		cd_dbg(CD_OPEN, "tray might not contain a medium\n");
+		return -ENOMEDIUM;
+	}
+	cd_dbg(CD_OPEN, "the tray is now closed\n");
+
+assess_and_return:
+	ret = cdo->drive_status(cdi, CDSL_CURRENT);
+	if (ret != CDS_DISC_OK)
+		return -ENOMEDIUM;
+	return 0;
+}
+EXPORT_SYMBOL(cdrom_handle_open_tray);
+
 static
 int open_for_data(struct cdrom_device_info *cdi)
 {
@@ -1047,50 +1159,15 @@ int open_for_data(struct cdrom_device_info *cdi)
 	const struct cdrom_device_ops *cdo = cdi->ops;
 	tracktype tracks;
 	cd_dbg(CD_OPEN, "entering open_for_data\n");
-	/* Check if the driver can report drive status.  If it can, we
-	   can do clever things.  If it can't, well, we at least tried! */
-	if (cdo->drive_status != NULL) {
-		ret = cdo->drive_status(cdi, CDSL_CURRENT);
-		cd_dbg(CD_OPEN, "drive_status=%d\n", ret);
-		if (ret == CDS_TRAY_OPEN) {
-			cd_dbg(CD_OPEN, "the tray is open...\n");
-			/* can/may i close it? */
-			if (CDROM_CAN(CDC_CLOSE_TRAY) &&
-			    cdi->options & CDO_AUTO_CLOSE) {
-				cd_dbg(CD_OPEN, "trying to close the tray\n");
-				ret=cdo->tray_move(cdi,0);
-				if (ret) {
-					cd_dbg(CD_OPEN, "bummer. tried to close the tray but failed.\n");
-					/* Ignore the error from the low
-					level driver.  We don't care why it
-					couldn't close the tray.  We only care
-					that there is no disc in the drive,
-					since that is the _REAL_ problem here.*/
-					ret=-ENOMEDIUM;
-					goto clean_up_and_return;
-				}
-			} else {
-				cd_dbg(CD_OPEN, "bummer. this drive can't close the tray.\n");
-				ret=-ENOMEDIUM;
-				goto clean_up_and_return;
-			}
-			/* Ok, the door should be closed now.. Check again */
-			ret = cdo->drive_status(cdi, CDSL_CURRENT);
-			if ((ret == CDS_NO_DISC) || (ret==CDS_TRAY_OPEN)) {
-				cd_dbg(CD_OPEN, "bummer. the tray is still not closed.\n");
-				cd_dbg(CD_OPEN, "tray might not contain a medium\n");
-				ret=-ENOMEDIUM;
-				goto clean_up_and_return;
-			}
-			cd_dbg(CD_OPEN, "the tray is now closed\n");
-		}
-		/* the door should be closed now, check for the disc */
-		ret = cdo->drive_status(cdi, CDSL_CURRENT);
-		if (ret!=CDS_DISC_OK) {
-			ret = -ENOMEDIUM;
-			goto clean_up_and_return;
-		}
-	}
+
+	/*
+	 * Check for open tray, but do not close it. The caller should
+	 * have cared to call cdrom_handle_open_tray(,,0) in advance.
+	 */
+	ret = cdrom_handle_open_tray(cdi, (fmode_t)0, 1);
+	if (ret)
+		goto clean_up_and_return;
+
 	cdrom_count_tracks(cdi, &tracks);
 	if (tracks.error == CDS_NO_DISC) {
 		cd_dbg(CD_OPEN, "bummer. no disc.\n");
diff --git a/include/linux/cdrom.h b/include/linux/cdrom.h
index f48d0a31deae..cf2b5fc9c6fd 100644
--- a/include/linux/cdrom.h
+++ b/include/linux/cdrom.h
@@ -98,6 +98,9 @@ int cdrom_multisession(struct cdrom_device_info *cdi,
 int cdrom_read_tocentry(struct cdrom_device_info *cdi,
 		struct cdrom_tocentry *entry);

+int cdrom_handle_open_tray(struct cdrom_device_info *cdi, fmode_t mode,
+			   int leave_open);
+
 /* the general block_device operations structure: */
 extern int cdrom_open(struct cdrom_device_info *cdi, struct block_device *bdev,
 			fmode_t mode);
