[patch 4/8] ide-cd: add basic refcounting

From: Bartlomiej Zolnierkiewicz
Date: Fri Jan 21 2005 - 18:35:31 EST



* 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/