From: Domen Puncer Use semaphores instead of sleep_on*. Stolen from patch: http://lkml.org/lkml/2004/12/18/107 from Ondrej Zary Signed-off-by: Domen Puncer Acked-by: Ondrej Zary Signed-off-by: Andrew Morton --- 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) _