diff --git a/block/genhd.c b/block/genhd.c index fcd6d4fae657..08f3e709a8d1 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -396,6 +396,19 @@ static int blk_mangle_minor(int minor) return minor; } +static void release_blk_ext_minor(int minor) +{ + spin_lock_bh(&ext_devt_lock); + idr_remove(&ext_devt_idr, blk_mangle_minor(minor)); + spin_unlock_bh(&ext_devt_lock); +} + +static void blk_disk_devt_release(struct disk_devt *disk_devt) +{ + release_blk_ext_minor(disk_devt->first_minor); + kfree(disk_devt); +} + /** * blk_alloc_devt - allocate a dev_t for a partition * @part: partition to allocate dev_t for @@ -413,6 +426,7 @@ static int blk_mangle_minor(int minor) int blk_alloc_devt(struct hd_struct *part, dev_t *devt) { struct gendisk *disk = part_to_disk(part); + struct disk_devt *disk_devt = NULL; int idx; /* in consecutive minor range? */ @@ -421,6 +435,13 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) return 0; } + if (part == &disk->part0) { + WARN_ON(disk->disk_devt); + disk_devt = kzalloc(sizeof(*disk_devt), GFP_KERNEL); + if (!disk_devt) + return -ENOMEM; + } + /* allocate ext devt */ idr_preload(GFP_KERNEL); @@ -429,8 +450,17 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) spin_unlock_bh(&ext_devt_lock); idr_preload_end(); - if (idx < 0) + if (idx < 0) { + kfree(disk_devt); return idx == -ENOSPC ? -EBUSY : idx; + } + + if (disk_devt) { + disk_devt->major = BLOCK_EXT_MAJOR; + disk_devt->first_minor = blk_mangle_minor(idx); + disk_devt->release = blk_disk_devt_release; + kref_init(&disk_devt->kref); + } *devt = MKDEV(BLOCK_EXT_MAJOR, blk_mangle_minor(idx)); return 0; @@ -450,11 +480,8 @@ void blk_free_devt(dev_t devt) if (devt == MKDEV(0, 0)) return; - if (MAJOR(devt) == BLOCK_EXT_MAJOR) { - spin_lock_bh(&ext_devt_lock); - idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); - spin_unlock_bh(&ext_devt_lock); - } + if (MAJOR(devt) == BLOCK_EXT_MAJOR) + release_blk_ext_minor(MINOR(devt)); } static char *bdevt_str(dev_t devt, char *buf) @@ -588,6 +615,13 @@ void device_add_disk(struct device *parent, struct gendisk *disk) dev_t devt; int retval; + /* temporary while we convert drivers to use struct disk_devt */ + if (disk->disk_devt) { + disk->minors = disk->disk_devt->minors; + disk->first_minor = disk->disk_devt->first_minor; + disk->major = disk->disk_devt->major; + } + /* minors == 0 indicates to use ext devt from part0 and should * be accompanied with EXT_DEVT flag. Make sure all * parameters make sense. @@ -1137,7 +1171,7 @@ static void disk_release(struct device *dev) { struct gendisk *disk = dev_to_disk(dev); - blk_free_devt(dev->devt); + put_disk_devt(disk->disk_devt); disk_release_events(disk); kfree(disk->random); disk_replace_part_tbl(disk, NULL); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index d3e852ad5aa3..f7fb4f42a0b6 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2961,6 +2961,8 @@ static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen) static void sd_probe_async(void *data, async_cookie_t cookie) { struct scsi_disk *sdkp = data; + struct scsi_disk_devt *sdevt; + struct disk_devt *disk_devt; struct scsi_device *sdp; struct gendisk *gd; u32 index; @@ -2968,12 +2970,14 @@ static void sd_probe_async(void *data, async_cookie_t cookie) sdp = sdkp->device; gd = sdkp->disk; - index = sdkp->index; + disk_devt = gd->disk_devt; + sdevt = to_sdevt(disk_devt); + index = sdevt->index;; dev = &sdp->sdev_gendev; - gd->major = sd_major((index & 0xf0) >> 4); - gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); - gd->minors = SD_MINORS; + disk_devt->major = sd_major((index & 0xf0) >> 4); + disk_devt->first_minor = ((index & 0xf) << 4) | (index & 0xfff00); + disk_devt->minors = SD_MINORS; gd->fops = &sd_fops; gd->private_data = &sdkp->driver; @@ -3012,6 +3016,16 @@ static void sd_probe_async(void *data, async_cookie_t cookie) put_device(&sdkp->dev); } +static void scsi_devt_release(struct disk_devt *disk_devt) +{ + struct scsi_disk_devt *sdevt = to_sdevt(disk_devt); + + spin_lock(&sd_index_lock); + ida_remove(&sd_index_ida, sdevt->index); + spin_unlock(&sd_index_lock); + kfree(sdevt); +} + /** * sd_probe - called during driver initialization and whenever a * new scsi device is attached to the system. It is called once @@ -3033,6 +3047,8 @@ static void sd_probe_async(void *data, async_cookie_t cookie) static int sd_probe(struct device *dev) { struct scsi_device *sdp = to_scsi_device(dev); + struct scsi_disk_devt *sdevt; + struct disk_devt *disk_devt; struct scsi_disk *sdkp; struct gendisk *gd; int index; @@ -3055,9 +3071,13 @@ static int sd_probe(struct device *dev) if (!gd) goto out_free; + sdevt = kzalloc(sizeof(*sdevt), GFP_KERNEL); + if (!sdevt) + goto out_put; + do { if (!ida_pre_get(&sd_index_ida, GFP_KERNEL)) - goto out_put; + goto out_free_sdevt; spin_lock(&sd_index_lock); error = ida_get_new(&sd_index_ida, &index); @@ -3066,7 +3086,7 @@ static int sd_probe(struct device *dev) if (error) { sdev_printk(KERN_WARNING, sdp, "sd_probe: memory exhausted.\n"); - goto out_put; + goto out_free_sdevt; } error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN); @@ -3075,10 +3095,16 @@ static int sd_probe(struct device *dev) goto out_free_index; } + sdevt->index = index; + disk_devt = &sdevt->disk_devt; + disk_devt->release = scsi_devt_release; + kref_init(&disk_devt->kref); + gd->disk_devt = disk_devt; + get_disk_devt(disk_devt); + sdkp->device = sdp; sdkp->driver = &sd_template; sdkp->disk = gd; - sdkp->index = index; atomic_set(&sdkp->openers, 0); atomic_set(&sdkp->device->ioerr_cnt, 0); @@ -3111,6 +3137,8 @@ static int sd_probe(struct device *dev) spin_lock(&sd_index_lock); ida_remove(&sd_index_ida, index); spin_unlock(&sd_index_lock); + out_free_sdevt: + kfree(sdevt); out_put: put_disk(gd); out_free: @@ -3171,10 +3199,7 @@ static void scsi_disk_release(struct device *dev) struct scsi_disk *sdkp = to_scsi_disk(dev); struct gendisk *disk = sdkp->disk; - spin_lock(&sd_index_lock); - ida_remove(&sd_index_ida, sdkp->index); - spin_unlock(&sd_index_lock); - + put_disk_devt(disk->disk_devt); disk->private_data = NULL; put_disk(disk); put_device(&sdkp->device->sdev_gendev); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 765a6f1ac1b7..863ade230f3f 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -59,6 +59,16 @@ enum { SD_LBP_DISABLE, /* Discard disabled due to failed cmd */ }; +struct scsi_disk_devt { + struct disk_devt disk_devt; + u32 index; +}; + +static inline struct scsi_disk_devt *to_sdevt(struct disk_devt *disk_devt) +{ + return container_of(disk_devt, struct scsi_disk_devt, disk_devt); +} + struct scsi_disk { struct scsi_driver *driver; /* always &sd_template */ struct scsi_device *device; @@ -72,7 +82,6 @@ struct scsi_disk { u32 max_unmap_blocks; u32 unmap_granularity; u32 unmap_alignment; - u32 index; unsigned int physical_block_size; unsigned int max_medium_access_timeouts; unsigned int medium_access_timed_out; diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 1dbf52f9c24b..8ba8db005ed6 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -177,15 +177,51 @@ struct blk_integrity { #endif /* CONFIG_BLK_DEV_INTEGRITY */ -struct gendisk { - /* major, first_minor and minors are input parameters only, - * don't use directly. Use disk_devt() and disk_max_parts(). - */ +struct disk_devt { int major; /* major number of driver */ int first_minor; int minors; /* maximum number of minors, =1 for * disks that can't be partitioned. */ + struct kref kref; + void (*release)(struct disk_devt *); +}; + +static inline void disk_devt_release(struct kref *kref) +{ + struct disk_devt *disk_devt; + + disk_devt = container_of(kref, struct disk_devt, kref); + disk_devt->release(disk_devt); +} + +static inline void put_disk_devt(struct disk_devt *disk_devt) +{ + if (disk_devt) + kref_put(&disk_devt->kref, disk_devt_release); +} +static inline struct disk_devt *get_disk_devt(struct disk_devt *disk_devt) +{ + if (disk_devt) + kref_get(&disk_devt->kref); + return disk_devt; +} + +struct gendisk { + /* + * major, first_minor and minors are deprecated, use ->disk_devt to + * communicate the devt to the block core and use disk_devt() and + * disk_max_parts() to retrieve these values. + */ + int major; + int first_minor; + int minors; + + /* + * disk_devt is an optional input parameter for drivers that do not use + * a devt allocated by the block core. + */ + struct disk_devt *disk_devt; char disk_name[DISK_NAME_LEN]; /* name of major driver */ char *(*devnode)(struct gendisk *gd, umode_t *mode);