* based on reference counting in drivers/scsi/{sd,sr}.c * fixes race between ->open() and ->cleanup() (ide_unregister_subdriver() tests for drive->usage != 0 but there is no protection against new users) * struct kref and pointer to a drive are added to struct ide_cdrom_info * pointer to drive's struct ide_cdrom_info is stored in disk->private_data * ide_cd_{get,put}() is used to {get,put} reference to struct ide_cdrom_info * ide_cd_release() is a release method for struct ide_cdrom_info
diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c --- a/drivers/ide/ide-cd.c 2005-01-21 23:40:52 +01:00 +++ b/drivers/ide/ide-cd.c 2005-01-21 23:40:52 +01:00 @@ -324,6 +324,33 @@ #include "ide-cd.h" +static DECLARE_MUTEX(idecd_ref_sem); + +#define to_ide_cd(obj) container_of(obj, struct cdrom_info, kref) + +#define ide_cd_g(disk) ((disk)->private_data) + +static struct cdrom_info *ide_cd_get(struct gendisk *disk) +{ + struct cdrom_info *cd = NULL; + + down(&idecd_ref_sem); + cd = ide_cd_g(disk); + if (cd) + kref_get(&cd->kref); + up(&idecd_ref_sem); + return cd; +} + +static void ide_cd_release(struct kref *); + +static void ide_cd_put(struct cdrom_info *cd) +{ + down(&idecd_ref_sem); + kref_put(&cd->kref, ide_cd_release); + up(&idecd_ref_sem); +} + /**************************************************************************** * Generic packet command support and error handling routines. */ @@ -3225,14 +3252,27 @@ int ide_cdrom_cleanup(ide_drive_t *drive) { struct cdrom_info *info = drive->driver_data; - struct cdrom_device_info *devinfo = &info->devinfo; - struct gendisk *g = drive->disk; if (ide_unregister_subdriver(drive)) { printk(KERN_ERR "%s: %s: failed to ide_unregister_subdriver\n", __FUNCTION__, drive->name); return 1; } + + del_gendisk(drive->disk); + + ide_cd_put(info); + + return 0; +} + +static void ide_cd_release(struct kref *kref) +{ + struct cdrom_info *info = to_ide_cd(kref); + struct cdrom_device_info *devinfo = &info->devinfo; + ide_drive_t *drive = info->drive; + struct gendisk *g = drive->disk; + if (info->buffer != NULL) kfree(info->buffer); if (info->toc != NULL) @@ -3240,13 +3280,13 @@ if (info->changer_info != NULL) kfree(info->changer_info); if (devinfo->handle == drive && unregister_cdrom(devinfo)) - printk(KERN_ERR "%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); - kfree(info); + printk(KERN_ERR "%s: %s failed to unregister device from the cdrom " + "driver.\n", __FUNCTION__, drive->name); drive->driver_data = NULL; blk_queue_prep_rq(drive->queue, NULL); - del_gendisk(g); + g->private_data = NULL; g->fops = ide_fops; - return 0; + kfree(info); } static int ide_cdrom_attach (ide_drive_t *drive); @@ -3289,9 +3329,16 @@ static int idecd_open(struct inode * inode, struct file * file) { - ide_drive_t *drive = inode->i_bdev->bd_disk->private_data; - struct cdrom_info *info = drive->driver_data; + struct gendisk *disk = inode->i_bdev->bd_disk; + struct cdrom_info *info; + ide_drive_t *drive; int rc = -ENOMEM; + + if (!(info = ide_cd_get(disk))) + return -ENXIO; + + drive = info->drive; + drive->usage++; if (!info->buffer) @@ -3299,16 +3346,24 @@ GFP_KERNEL|__GFP_REPEAT); if (!info->buffer || (rc = cdrom_open(&info->devinfo, inode, file))) drive->usage--; + + if (rc < 0) + ide_cd_put(info); + return rc; } static int idecd_release(struct inode * inode, struct file * file) { - ide_drive_t *drive = inode->i_bdev->bd_disk->private_data; - struct cdrom_info *info = drive->driver_data; + struct gendisk *disk = inode->i_bdev->bd_disk; + struct cdrom_info *info = ide_cd_g(disk); + ide_drive_t *drive = info->drive; cdrom_release (&info->devinfo, file); drive->usage--; + + ide_cd_put(info); + return 0; } @@ -3316,27 +3371,27 @@ unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - ide_drive_t *drive = bdev->bd_disk->private_data; - int err = generic_ide_ioctl(drive, file, bdev, cmd, arg); - if (err == -EINVAL) { - struct cdrom_info *info = drive->driver_data; + struct cdrom_info *info = ide_cd_g(bdev->bd_disk); + int err; + + err = generic_ide_ioctl(info->drive, file, bdev, cmd, arg); + if (err == -EINVAL) err = cdrom_ioctl(file, &info->devinfo, inode, cmd, arg); - } + return err; } static int idecd_media_changed(struct gendisk *disk) { - ide_drive_t *drive = disk->private_data; - struct cdrom_info *info = drive->driver_data; + struct cdrom_info *info = ide_cd_g(disk); return cdrom_media_changed(&info->devinfo); } static int idecd_revalidate_disk(struct gendisk *disk) { - ide_drive_t *drive = disk->private_data; + struct cdrom_info *info = ide_cd_g(disk); struct request_sense sense; - cdrom_read_toc(drive, &sense); + cdrom_read_toc(info->drive, &sense); return 0; } @@ -3390,7 +3445,12 @@ goto failed; } memset(info, 0, sizeof (struct cdrom_info)); + + kref_init(&info->kref); + + info->drive = drive; drive->driver_data = info; + DRIVER(drive)->busy++; g->minors = 1; snprintf(g->devfs_name, sizeof(g->devfs_name), @@ -3417,6 +3477,7 @@ cdrom_read_toc(drive, &sense); g->fops = &idecd_ops; + g->private_data = info; g->flags |= GENHD_FL_REMOVABLE; add_disk(g); return 0; diff -Nru a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h --- a/drivers/ide/ide-cd.h 2005-01-21 23:40:52 +01:00 +++ b/drivers/ide/ide-cd.h 2005-01-21 23:40:52 +01:00 @@ -460,6 +460,8 @@ /* Extra per-device info for cdrom drives. */ struct cdrom_info { + ide_drive_t *drive; + struct kref kref; /* Buffer for table of contents. NULL if we haven't allocated a TOC buffer for this device yet. */ - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/