From: Domen Puncer <domen@coderock.org>

Use semaphores instead of sleep_on*.
Stolen from patch: http://lkml.org/lkml/2004/12/18/107 from
Ondrej Zary <linux@rainbow-software.org>

Signed-off-by: Domen Puncer <domen@coderock.org>
Acked-by: Ondrej Zary <linux@rainbow-software.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/cdrom/cdu31a.c |  283 ++++++++++++++++++++---------------------
 1 files changed, 140 insertions(+), 143 deletions(-)

diff -puN drivers/cdrom/cdu31a.c~cdrom-cdu31a-locking-fixes drivers/cdrom/cdu31a.c
--- 25/drivers/cdrom/cdu31a.c~cdrom-cdu31a-locking-fixes	2005-03-13 20:14:54.000000000 -0800
+++ 25-akpm/drivers/cdrom/cdu31a.c	2005-03-13 20:16:51.000000000 -0800
@@ -267,7 +267,7 @@ static struct s_sony_subcode last_sony_s
 static volatile int sony_inuse = 0;	/* Is the drive in use?  Only one operation
 					   at a time allowed */
 
-static DECLARE_WAIT_QUEUE_HEAD(sony_wait);	/* Things waiting for the drive */
+static DECLARE_MUTEX(sony_sem);		/* Semaphore for drive hardware access */
 
 static struct task_struct *has_cd_task = NULL;	/* The task that is currently
 						   using the CDROM drive, or
@@ -341,13 +341,16 @@ static int scd_media_changed(struct cdro
  */
 static int scd_drive_status(struct cdrom_device_info *cdi, int slot_nr)
 {
-	if (CDSL_CURRENT != slot_nr) {
+	if (CDSL_CURRENT != slot_nr)
 		/* we have no changer support */
 		return -EINVAL;
-	}
-	if (scd_spinup() == 0) {
+	if (sony_spun_up)
+		return CDS_DISC_OK;
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
+	if (scd_spinup() == 0)
 		sony_spun_up = 1;
-	}
+	up(&sony_sem);
 	return sony_spun_up ? CDS_DISC_OK : CDS_DRIVE_NOT_READY;
 }
 
@@ -442,6 +445,8 @@ static int scd_reset(struct cdrom_device
 {
 	unsigned long retry_count;
 
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	reset_drive();
 
 	retry_count = jiffies + SONY_RESET_TIMEOUT;
@@ -449,6 +454,7 @@ static int scd_reset(struct cdrom_device
 		sony_sleep();
 	}
 
+	up(&sony_sem);
 	return 0;
 }
 
@@ -640,7 +646,10 @@ static int scd_select_speed(struct cdrom
 	else
 		sony_speed = speed - 1;
 
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	set_drive_params(sony_speed);
+	up(&sony_sem);
 	return 0;
 }
 
@@ -655,7 +664,10 @@ static int scd_lock_door(struct cdrom_de
 	} else {
 		is_auto_eject = 0;
 	}
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	set_drive_params(sony_speed);
+	up(&sony_sem);
 	return 0;
 }
 
@@ -848,38 +860,12 @@ do_sony_cd_cmd(unsigned char cmd,
 	       unsigned char *result_buffer, unsigned int *result_size)
 {
 	unsigned long retry_count;
-	int num_retries;
-	int recursive_call;
-	unsigned long flags;
-
-
-	save_flags(flags);
-	cli();
-	if (current != has_cd_task) {	/* Allow recursive calls to this routine */
-		while (sony_inuse) {
-			interruptible_sleep_on(&sony_wait);
-			if (signal_pending(current)) {
-				result_buffer[0] = 0x20;
-				result_buffer[1] = SONY_SIGNAL_OP_ERR;
-				*result_size = 2;
-				restore_flags(flags);
-				return;
-			}
-		}
-		sony_inuse = 1;
-		has_cd_task = current;
-		recursive_call = 0;
-	} else {
-		recursive_call = 1;
-	}
+	int num_retries = 0;
 
-	num_retries = 0;
 retry_cd_operation:
 
 	while (handle_sony_cd_attention());
 
-	sti();
-
 	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
 	while (time_before(jiffies, retry_count) && (is_busy())) {
 		sony_sleep();
@@ -907,14 +893,6 @@ retry_cd_operation:
 		msleep(100);
 		goto retry_cd_operation;
 	}
-
-	if (!recursive_call) {
-		has_cd_task = NULL;
-		sony_inuse = 0;
-		wake_up_interruptible(&sony_wait);
-	}
-
-	restore_flags(flags);
 }
 
 
@@ -1154,13 +1132,9 @@ static void abort_read(void)
    pending read operation. */
 static void handle_abort_timeout(unsigned long data)
 {
-	unsigned long flags;
-
 	pr_debug(PFX "Entering %s\n", __FUNCTION__);
-	save_flags(flags);
-	cli();
 	/* If it is in use, ignore it. */
-	if (!sony_inuse) {
+	if (down_trylock(&sony_sem) == 0) {
 		/* We can't use abort_read(), because it will sleep
 		   or schedule in the timer interrupt.  Just start
 		   the operation, finish it on the next access to
@@ -1171,8 +1145,8 @@ static void handle_abort_timeout(unsigne
 
 		sony_blocks_left = 0;
 		abort_read_started = 1;
+		up(&sony_sem);
 	}
-	restore_flags(flags);
 	pr_debug(PFX "Leaving %s\n", __FUNCTION__);
 }
 
@@ -1313,27 +1287,14 @@ static void do_cdu31a_request(request_qu
 	int block, nblock, num_retries;
 	unsigned char res_reg[12];
 	unsigned int res_size;
-	unsigned long flags;
 
 	pr_debug(PFX "Entering %s\n", __FUNCTION__);
 
-	/*
-	 * Make sure no one else is using the driver; wait for them
-	 * to finish if it is so.
-	 */
-	save_flags(flags);
-	cli();
-	while (sony_inuse) {
-		interruptible_sleep_on(&sony_wait);
-		if (signal_pending(current)) {
-			restore_flags(flags);
-			pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__,
-			       __LINE__);
-			return;
-		}
+	spin_unlock_irq(q->queue_lock);
+	if (down_interruptible(&sony_sem)) {
+		spin_lock_irq(q->queue_lock);
+		return;
 	}
-	sony_inuse = 1;
-	has_cd_task = current;
 
 	/* Get drive status before doing anything. */
 	while (handle_sony_cd_attention());
@@ -1341,10 +1302,6 @@ static void do_cdu31a_request(request_qu
 	/* Make sure we have a valid TOC. */
 	sony_get_toc();
 
-	/*
-	 * jens: driver has lots of races
-	 */
-	spin_unlock_irq(q->queue_lock);
 
 	/* Make sure the timer is cancelled. */
 	del_timer(&cdu31a_abort_timer);
@@ -1457,7 +1414,6 @@ static void do_cdu31a_request(request_qu
 		goto try_read_again;
 	}
       end_do_cdu31a_request:
-	spin_lock_irq(q->queue_lock);
 #if 0
 	/* After finished, cancel any pending operations. */
 	abort_read();
@@ -1468,10 +1424,8 @@ static void do_cdu31a_request(request_qu
 	add_timer(&cdu31a_abort_timer);
 #endif
 
-	has_cd_task = NULL;
-	sony_inuse = 0;
-	wake_up_interruptible(&sony_wait);
-	restore_flags(flags);
+	up(&sony_sem);
+	spin_lock_irq(q->queue_lock);
 	pr_debug(PFX "Leaving %s at %d\n", __FUNCTION__, __LINE__);
 }
 
@@ -1940,8 +1894,12 @@ static int scd_get_last_session(struct c
 	if (ms_info == NULL)
 		return 1;
 
-	if (!sony_toc_read)
+	if (!sony_toc_read) {
+		if (down_interruptible(&sony_sem))
+			return -ERESTARTSYS;
 		sony_get_toc();
+		up(&sony_sem);
+	}
 
 	ms_info->addr_format = CDROM_LBA;
 	ms_info->addr.lba = sony_toc.start_track_lba;
@@ -2019,8 +1977,11 @@ scd_get_mcn(struct cdrom_device_info *cd
 	unsigned int res_size;
 
 	memset(mcn->medium_catalog_number, 0, 14);
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	do_sony_cd_cmd(SONY_REQ_UPC_EAN_CMD,
 		       NULL, 0, resbuffer, &res_size);
+	up(&sony_sem);
 	if ((res_size < 2) || ((resbuffer[0] & 0xf0) == 0x20));
 	else {
 		/* packed bcd to single ASCII digits */
@@ -2234,28 +2195,11 @@ static int read_audio(struct cdrom_read_
 	unsigned char res_reg[12];
 	unsigned int res_size;
 	unsigned int cframe;
-	unsigned long flags;
 
-	/* 
-	 * Make sure no one else is using the driver; wait for them
-	 * to finish if it is so.
-	 */
-	save_flags(flags);
-	cli();
-	while (sony_inuse) {
-		interruptible_sleep_on(&sony_wait);
-		if (signal_pending(current)) {
-			restore_flags(flags);
-			return -EAGAIN;
-		}
-	}
-	sony_inuse = 1;
-	has_cd_task = current;
-	restore_flags(flags);
-
-	if (!sony_spun_up) {
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
+	if (!sony_spun_up)
 		scd_spinup();
-	}
 
 	/* Set the drive to do raw operations. */
 	params[0] = SONY_SD_DECODE_PARAM;
@@ -2265,7 +2209,8 @@ static int read_audio(struct cdrom_read_
 	if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) {
 		printk(KERN_ERR PFX "Unable to set decode params: 0x%2.2x\n",
 		       res_reg[1]);
-		return -EIO;
+		retval = -EIO;
+		goto out_up;
 	}
 
 	/* From here down, we have to goto exit_read_audio instead of returning
@@ -2383,12 +2328,11 @@ static int read_audio(struct cdrom_read_
 	if ((res_size < 2) || ((res_reg[0] & 0xf0) == 0x20)) {
 		printk(KERN_ERR PFX "Unable to reset decode params: 0x%2.2x\n",
 		       res_reg[1]);
-		return -EIO;
+		retval = -EIO;
 	}
 
-	has_cd_task = NULL;
-	sony_inuse = 0;
-	wake_up_interruptible(&sony_wait);
+ out_up:
+	up(&sony_sem);
 
 	return retval;
 }
@@ -2416,6 +2360,10 @@ do_sony_cd_cmd_chk(const char *name,
  */
 static int scd_tray_move(struct cdrom_device_info *cdi, int position)
 {
+	int retval;
+
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	if (position == 1 /* open tray */ ) {
 		unsigned char res_reg[12];
 		unsigned int res_size;
@@ -2426,13 +2374,15 @@ static int scd_tray_move(struct cdrom_de
 			       &res_size);
 
 		sony_audio_status = CDROM_AUDIO_INVALID;
-		return do_sony_cd_cmd_chk("EJECT", SONY_EJECT_CMD, NULL, 0,
+		retval = do_sony_cd_cmd_chk("EJECT", SONY_EJECT_CMD, NULL, 0,
 					  res_reg, &res_size);
 	} else {
 		if (0 == scd_spinup())
 			sony_spun_up = 1;
-		return 0;
+		retval = 0;
 	}
+	up(&sony_sem);
+	return retval;
 }
 
 /*
@@ -2444,12 +2394,13 @@ static int scd_audio_ioctl(struct cdrom_
 	unsigned char res_reg[12];
 	unsigned int res_size;
 	unsigned char params[7];
-	int i;
-
+	int i, retval;
 
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	switch (cmd) {
 	case CDROMSTART:	/* Spin up the drive */
-		return do_sony_cd_cmd_chk("START", SONY_SPIN_UP_CMD, NULL,
+		retval = do_sony_cd_cmd_chk("START", SONY_SPIN_UP_CMD, NULL,
 					  0, res_reg, &res_size);
 		break;
 
@@ -2462,28 +2413,33 @@ static int scd_audio_ioctl(struct cdrom_
 		 * already not spinning.
 		 */
 		sony_audio_status = CDROM_AUDIO_NO_STATUS;
-		return do_sony_cd_cmd_chk("STOP", SONY_SPIN_DOWN_CMD, NULL,
+		retval = do_sony_cd_cmd_chk("STOP", SONY_SPIN_DOWN_CMD, NULL,
 					  0, res_reg, &res_size);
+		break;
 
 	case CDROMPAUSE:	/* Pause the drive */
 		if (do_sony_cd_cmd_chk
 		    ("PAUSE", SONY_AUDIO_STOP_CMD, NULL, 0, res_reg,
-		     &res_size))
-			return -EIO;
+		     &res_size)) {
+			retval = -EIO;
+			break;
+		}
 		/* Get the current position and save it for resuming */
 		if (read_subcode() < 0) {
-			return -EIO;
+			retval = -EIO;
+			break;
 		}
 		cur_pos_msf[0] = last_sony_subcode.abs_msf[0];
 		cur_pos_msf[1] = last_sony_subcode.abs_msf[1];
 		cur_pos_msf[2] = last_sony_subcode.abs_msf[2];
 		sony_audio_status = CDROM_AUDIO_PAUSED;
-		return 0;
+		retval = 0;
 		break;
 
 	case CDROMRESUME:	/* Start the drive after being paused */
 		if (sony_audio_status != CDROM_AUDIO_PAUSED) {
-			return -EINVAL;
+			retval = -EINVAL;
+			break;
 		}
 
 		do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg,
@@ -2499,10 +2455,13 @@ static int scd_audio_ioctl(struct cdrom_
 		params[0] = 0x03;
 		if (do_sony_cd_cmd_chk
 		    ("RESUME", SONY_AUDIO_PLAYBACK_CMD, params, 7, res_reg,
-		     &res_size) < 0)
-			return -EIO;
+		     &res_size) < 0) {
+			retval = -EIO;
+			break;
+		}
 		sony_audio_status = CDROM_AUDIO_PLAY;
-		return 0;
+		retval = 0;
+		break;
 
 	case CDROMPLAYMSF:	/* Play starting at the given MSF address. */
 		do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg,
@@ -2516,15 +2475,18 @@ static int scd_audio_ioctl(struct cdrom_
 		params[0] = 0x03;
 		if (do_sony_cd_cmd_chk
 		    ("PLAYMSF", SONY_AUDIO_PLAYBACK_CMD, params, 7,
-		     res_reg, &res_size) < 0)
-			return -EIO;
+		     res_reg, &res_size) < 0) {
+			retval = -EIO;
+			break;
+		}
 
 		/* Save the final position for pauses and resumes */
 		final_pos_msf[0] = bcd_to_int(params[4]);
 		final_pos_msf[1] = bcd_to_int(params[5]);
 		final_pos_msf[2] = bcd_to_int(params[6]);
 		sony_audio_status = CDROM_AUDIO_PLAY;
-		return 0;
+		retval = 0;
+		break;
 
 	case CDROMREADTOCHDR:	/* Read the table of contents header */
 		{
@@ -2532,14 +2494,16 @@ static int scd_audio_ioctl(struct cdrom_
 
 			sony_get_toc();
 			if (!sony_toc_read) {
-				return -EIO;
+				retval = -EIO;
+				break;
 			}
 
 			hdr = (struct cdrom_tochdr *) arg;
 			hdr->cdth_trk0 = sony_toc.first_track_num;
 			hdr->cdth_trk1 = sony_toc.last_track_num;
 		}
-		return 0;
+		retval = 0;
+		break;
 
 	case CDROMREADTOCENTRY:	/* Read a given table of contents entry */
 		{
@@ -2549,14 +2513,16 @@ static int scd_audio_ioctl(struct cdrom_
 
 			sony_get_toc();
 			if (!sony_toc_read) {
-				return -EIO;
+				retval = -EIO;
+				break;
 			}
 
 			entry = (struct cdrom_tocentry *) arg;
 
 			track_idx = find_track(entry->cdte_track);
 			if (track_idx < 0) {
-				return -EINVAL;
+				retval = -EINVAL;
+				break;
 			}
 
 			entry->cdte_adr =
@@ -2577,7 +2543,7 @@ static int scd_audio_ioctl(struct cdrom_
 				    *(msf_val + 2);
 			}
 		}
-		return 0;
+		retval = 0;
 		break;
 
 	case CDROMPLAYTRKIND:	/* Play a track.  This currently ignores index. */
@@ -2587,18 +2553,21 @@ static int scd_audio_ioctl(struct cdrom_
 
 			sony_get_toc();
 			if (!sony_toc_read) {
-				return -EIO;
+				retval = -EIO;
+				break;
 			}
 
 			if ((ti->cdti_trk0 < sony_toc.first_track_num)
 			    || (ti->cdti_trk0 > sony_toc.last_track_num)
 			    || (ti->cdti_trk1 < ti->cdti_trk0)) {
-				return -EINVAL;
+				retval = -EINVAL;
+				break;
 			}
 
 			track_idx = find_track(ti->cdti_trk0);
 			if (track_idx < 0) {
-				return -EINVAL;
+				retval = -EINVAL;
+				break;
 			}
 			params[1] =
 			    int_to_bcd(sony_toc.tracks[track_idx].
@@ -2620,7 +2589,8 @@ static int scd_audio_ioctl(struct cdrom_
 				track_idx = find_track(ti->cdti_trk1 + 1);
 			}
 			if (track_idx < 0) {
-				return -EINVAL;
+				retval = -EINVAL;
+				break;
 			}
 			params[4] =
 			    int_to_bcd(sony_toc.tracks[track_idx].
@@ -2649,7 +2619,8 @@ static int scd_audio_ioctl(struct cdrom_
 				printk(KERN_ERR PFX
 					"Error %s (CDROMPLAYTRKIND)\n",
 				     translate_error(res_reg[1]));
-				return -EIO;
+				retval = -EIO;
+				break;
 			}
 
 			/* Save the final position for pauses and resumes */
@@ -2657,7 +2628,8 @@ static int scd_audio_ioctl(struct cdrom_
 			final_pos_msf[1] = bcd_to_int(params[5]);
 			final_pos_msf[2] = bcd_to_int(params[6]);
 			sony_audio_status = CDROM_AUDIO_PLAY;
-			return 0;
+			retval = 0;
+			break;
 		}
 
 	case CDROMVOLCTRL:	/* Volume control.  What volume does this change, anyway? */
@@ -2668,24 +2640,32 @@ static int scd_audio_ioctl(struct cdrom_
 			params[0] = SONY_SD_AUDIO_VOLUME;
 			params[1] = volctrl->channel0;
 			params[2] = volctrl->channel1;
-			return do_sony_cd_cmd_chk("VOLCTRL",
+			retval = do_sony_cd_cmd_chk("VOLCTRL",
 						  SONY_SET_DRIVE_PARAM_CMD,
 						  params, 3, res_reg,
 						  &res_size);
+			break;
 		}
 	case CDROMSUBCHNL:	/* Get subchannel info */
-		return sony_get_subchnl_info((struct cdrom_subchnl *) arg);
+		retval = sony_get_subchnl_info((struct cdrom_subchnl *) arg);
+		break;
 
 	default:
-		return -EINVAL;
+		retval = -EINVAL;
+		break;
 	}
+	up(&sony_sem);
+	return retval;
 }
 
 static int scd_dev_ioctl(struct cdrom_device_info *cdi,
 			 unsigned int cmd, unsigned long arg)
 {
 	void __user *argp = (void __user *)arg;
+	int retval;
 
+	if (down_interruptible(&sony_sem))
+		return -ERESTARTSYS;
 	switch (cmd) {
 	case CDROMREADAUDIO:	/* Read 2352 byte audio tracks and 2340 byte
 				   raw data tracks. */
@@ -2695,14 +2675,18 @@ static int scd_dev_ioctl(struct cdrom_de
 
 			sony_get_toc();
 			if (!sony_toc_read) {
-				return -EIO;
+				retval = -EIO;
+				break;
 			}
 
-			if (copy_from_user(&ra, argp, sizeof(ra)))
-				return -EFAULT;
+			if (copy_from_user(&ra, argp, sizeof(ra))) {
+				retval = -EFAULT;
+				break;
+			}
 
 			if (ra.nframes == 0) {
-				return 0;
+				retval = 0;
+				break;
 			}
 
 			if (!access_ok(VERIFY_WRITE, ra.buf,
@@ -2714,13 +2698,15 @@ static int scd_dev_ioctl(struct cdrom_de
 				     sony_toc.lead_out_start_lba)
 				    || (ra.addr.lba + ra.nframes >=
 					sony_toc.lead_out_start_lba)) {
-					return -EINVAL;
+					retval = -EINVAL;
+					break;
 				}
 			} else if (ra.addr_format == CDROM_MSF) {
 				if ((ra.addr.msf.minute >= 75)
 				    || (ra.addr.msf.second >= 60)
 				    || (ra.addr.msf.frame >= 75)) {
-					return -EINVAL;
+					retval = -EINVAL;
+					break;
 				}
 
 				ra.addr.lba = ((ra.addr.msf.minute * 4500)
@@ -2730,7 +2716,8 @@ static int scd_dev_ioctl(struct cdrom_de
 				     sony_toc.lead_out_start_lba)
 				    || (ra.addr.lba + ra.nframes >=
 					sony_toc.lead_out_start_lba)) {
-					return -EINVAL;
+					retval = -EINVAL;
+					break;
 				}
 
 				/* I know, this can go negative on an unsigned.  However,
@@ -2738,16 +2725,21 @@ static int scd_dev_ioctl(struct cdrom_de
 				   so this should compensate and allow direct msf access. */
 				ra.addr.lba -= LOG_START_OFFSET;
 			} else {
-				return -EINVAL;
+				retval = -EINVAL;
+				break;
 			}
 
-			return read_audio(&ra);
+			retval = read_audio(&ra);
+			break;
 		}
-		return 0;
+		retval = 0;
+		break;
 
 	default:
-		return -EINVAL;
+		retval = -EINVAL;
 	}
+	up(&sony_sem);
+	return retval;
 }
 
 static int scd_spinup(void)
@@ -2920,6 +2912,8 @@ static int scd_block_release(struct inod
 static int scd_block_ioctl(struct inode *inode, struct file *file,
 				unsigned cmd, unsigned long arg)
 {
+	int retval;
+
 	/* The eject and close commands should be handled by Uniform CD-ROM
 	 * driver - but I always got hard lockup instead of eject
 	 * until I put this here.
@@ -2927,12 +2921,15 @@ static int scd_block_ioctl(struct inode 
 	switch (cmd) {
 		case CDROMEJECT:
 			scd_lock_door(&scd_info, 0);
-			return scd_tray_move(&scd_info, 1);
+			retval = scd_tray_move(&scd_info, 1);
+			break;
 		case CDROMCLOSETRAY:
-			return scd_tray_move(&scd_info, 0);
+			retval = scd_tray_move(&scd_info, 0);
+			break;
 		default:
-			return cdrom_ioctl(file, &scd_info, inode, cmd, arg);
+			retval = cdrom_ioctl(file, &scd_info, inode, cmd, arg);
 	}
+	return retval;
 }
 
 static int scd_block_media_changed(struct gendisk *disk)
_