Commit 22ae8ce8 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe

block: simplify bdev/disk lookup in blkdev_get

To simplify block device lookup and a few other upcoming areas, make sure
that we always have a struct block_device available for each disk and
each partition, and only find existing block devices in bdget.  The only
downside of this is that each device and partition uses a little more
memory.  The upside will be that a lot of code can be simplified.

With that all we need to look up the block device is to lookup the inode
and do a few sanity checks on the gendisk, instead of the separate lookup
for the gendisk.  For blk-cgroup which wants to access a gendisk without
opening it, a new blkdev_{get,put}_no_open low-level interface is added
to replace the previous get_gendisk use.

Note that the change to look up block device directly instead of the two
step lookup using struct gendisk causes a subtile change in behavior:
accessing a non-existing partition on an existing block device can now
cause a call to request_module.  That call is harmless, and in practice
no recent system will access these nodes as they aren't created by udev
and static /dev/ setups are unusual.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 4e7b5671
...@@ -556,22 +556,22 @@ static struct blkcg_gq *blkg_lookup_check(struct blkcg *blkcg, ...@@ -556,22 +556,22 @@ static struct blkcg_gq *blkg_lookup_check(struct blkcg *blkcg,
} }
/** /**
* blkg_conf_prep - parse and prepare for per-blkg config update * blkcg_conf_open_bdev - parse and open bdev for per-blkg config update
* @inputp: input string pointer * @inputp: input string pointer
* *
* Parse the device node prefix part, MAJ:MIN, of per-blkg config update * Parse the device node prefix part, MAJ:MIN, of per-blkg config update
* from @input and get and return the matching gendisk. *@inputp is * from @input and get and return the matching bdev. *@inputp is
* updated to point past the device node prefix. Returns an ERR_PTR() * updated to point past the device node prefix. Returns an ERR_PTR()
* value on error. * value on error.
* *
* Use this function iff blkg_conf_prep() can't be used for some reason. * Use this function iff blkg_conf_prep() can't be used for some reason.
*/ */
struct gendisk *blkcg_conf_get_disk(char **inputp) struct block_device *blkcg_conf_open_bdev(char **inputp)
{ {
char *input = *inputp; char *input = *inputp;
unsigned int major, minor; unsigned int major, minor;
struct gendisk *disk; struct block_device *bdev;
int key_len, part; int key_len;
if (sscanf(input, "%u:%u%n", &major, &minor, &key_len) != 2) if (sscanf(input, "%u:%u%n", &major, &minor, &key_len) != 2)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -581,16 +581,16 @@ struct gendisk *blkcg_conf_get_disk(char **inputp) ...@@ -581,16 +581,16 @@ struct gendisk *blkcg_conf_get_disk(char **inputp)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
input = skip_spaces(input); input = skip_spaces(input);
disk = get_gendisk(MKDEV(major, minor), &part); bdev = blkdev_get_no_open(MKDEV(major, minor));
if (!disk) if (!bdev)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
if (part) { if (bdev_is_partition(bdev)) {
put_disk_and_module(disk); blkdev_put_no_open(bdev);
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
*inputp = input; *inputp = input;
return disk; return bdev;
} }
/** /**
...@@ -607,18 +607,18 @@ struct gendisk *blkcg_conf_get_disk(char **inputp) ...@@ -607,18 +607,18 @@ struct gendisk *blkcg_conf_get_disk(char **inputp)
*/ */
int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
char *input, struct blkg_conf_ctx *ctx) char *input, struct blkg_conf_ctx *ctx)
__acquires(rcu) __acquires(&disk->queue->queue_lock) __acquires(rcu) __acquires(&bdev->bd_disk->queue->queue_lock)
{ {
struct gendisk *disk; struct block_device *bdev;
struct request_queue *q; struct request_queue *q;
struct blkcg_gq *blkg; struct blkcg_gq *blkg;
int ret; int ret;
disk = blkcg_conf_get_disk(&input); bdev = blkcg_conf_open_bdev(&input);
if (IS_ERR(disk)) if (IS_ERR(bdev))
return PTR_ERR(disk); return PTR_ERR(bdev);
q = disk->queue; q = bdev->bd_disk->queue;
rcu_read_lock(); rcu_read_lock();
spin_lock_irq(&q->queue_lock); spin_lock_irq(&q->queue_lock);
...@@ -689,7 +689,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, ...@@ -689,7 +689,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
goto success; goto success;
} }
success: success:
ctx->disk = disk; ctx->bdev = bdev;
ctx->blkg = blkg; ctx->blkg = blkg;
ctx->body = input; ctx->body = input;
return 0; return 0;
...@@ -700,7 +700,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, ...@@ -700,7 +700,7 @@ int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
spin_unlock_irq(&q->queue_lock); spin_unlock_irq(&q->queue_lock);
rcu_read_unlock(); rcu_read_unlock();
fail: fail:
put_disk_and_module(disk); blkdev_put_no_open(bdev);
/* /*
* If queue was bypassing, we should retry. Do so after a * If queue was bypassing, we should retry. Do so after a
* short msleep(). It isn't strictly necessary but queue * short msleep(). It isn't strictly necessary but queue
...@@ -723,11 +723,11 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep); ...@@ -723,11 +723,11 @@ EXPORT_SYMBOL_GPL(blkg_conf_prep);
* with blkg_conf_prep(). * with blkg_conf_prep().
*/ */
void blkg_conf_finish(struct blkg_conf_ctx *ctx) void blkg_conf_finish(struct blkg_conf_ctx *ctx)
__releases(&ctx->disk->queue->queue_lock) __releases(rcu) __releases(&ctx->bdev->bd_disk->queue->queue_lock) __releases(rcu)
{ {
spin_unlock_irq(&ctx->disk->queue->queue_lock); spin_unlock_irq(&ctx->bdev->bd_disk->queue->queue_lock);
rcu_read_unlock(); rcu_read_unlock();
put_disk_and_module(ctx->disk); blkdev_put_no_open(ctx->bdev);
} }
EXPORT_SYMBOL_GPL(blkg_conf_finish); EXPORT_SYMBOL_GPL(blkg_conf_finish);
......
...@@ -3120,23 +3120,23 @@ static const match_table_t qos_tokens = { ...@@ -3120,23 +3120,23 @@ static const match_table_t qos_tokens = {
static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input, static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input,
size_t nbytes, loff_t off) size_t nbytes, loff_t off)
{ {
struct gendisk *disk; struct block_device *bdev;
struct ioc *ioc; struct ioc *ioc;
u32 qos[NR_QOS_PARAMS]; u32 qos[NR_QOS_PARAMS];
bool enable, user; bool enable, user;
char *p; char *p;
int ret; int ret;
disk = blkcg_conf_get_disk(&input); bdev = blkcg_conf_open_bdev(&input);
if (IS_ERR(disk)) if (IS_ERR(bdev))
return PTR_ERR(disk); return PTR_ERR(bdev);
ioc = q_to_ioc(disk->queue); ioc = q_to_ioc(bdev->bd_disk->queue);
if (!ioc) { if (!ioc) {
ret = blk_iocost_init(disk->queue); ret = blk_iocost_init(bdev->bd_disk->queue);
if (ret) if (ret)
goto err; goto err;
ioc = q_to_ioc(disk->queue); ioc = q_to_ioc(bdev->bd_disk->queue);
} }
spin_lock_irq(&ioc->lock); spin_lock_irq(&ioc->lock);
...@@ -3231,12 +3231,12 @@ static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input, ...@@ -3231,12 +3231,12 @@ static ssize_t ioc_qos_write(struct kernfs_open_file *of, char *input,
ioc_refresh_params(ioc, true); ioc_refresh_params(ioc, true);
spin_unlock_irq(&ioc->lock); spin_unlock_irq(&ioc->lock);
put_disk_and_module(disk); blkdev_put_no_open(bdev);
return nbytes; return nbytes;
einval: einval:
ret = -EINVAL; ret = -EINVAL;
err: err:
put_disk_and_module(disk); blkdev_put_no_open(bdev);
return ret; return ret;
} }
...@@ -3287,23 +3287,23 @@ static const match_table_t i_lcoef_tokens = { ...@@ -3287,23 +3287,23 @@ static const match_table_t i_lcoef_tokens = {
static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input, static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
size_t nbytes, loff_t off) size_t nbytes, loff_t off)
{ {
struct gendisk *disk; struct block_device *bdev;
struct ioc *ioc; struct ioc *ioc;
u64 u[NR_I_LCOEFS]; u64 u[NR_I_LCOEFS];
bool user; bool user;
char *p; char *p;
int ret; int ret;
disk = blkcg_conf_get_disk(&input); bdev = blkcg_conf_open_bdev(&input);
if (IS_ERR(disk)) if (IS_ERR(bdev))
return PTR_ERR(disk); return PTR_ERR(bdev);
ioc = q_to_ioc(disk->queue); ioc = q_to_ioc(bdev->bd_disk->queue);
if (!ioc) { if (!ioc) {
ret = blk_iocost_init(disk->queue); ret = blk_iocost_init(bdev->bd_disk->queue);
if (ret) if (ret)
goto err; goto err;
ioc = q_to_ioc(disk->queue); ioc = q_to_ioc(bdev->bd_disk->queue);
} }
spin_lock_irq(&ioc->lock); spin_lock_irq(&ioc->lock);
...@@ -3356,13 +3356,13 @@ static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input, ...@@ -3356,13 +3356,13 @@ static ssize_t ioc_cost_model_write(struct kernfs_open_file *of, char *input,
ioc_refresh_params(ioc, true); ioc_refresh_params(ioc, true);
spin_unlock_irq(&ioc->lock); spin_unlock_irq(&ioc->lock);
put_disk_and_module(disk); blkdev_put_no_open(bdev);
return nbytes; return nbytes;
einval: einval:
ret = -EINVAL; ret = -EINVAL;
err: err:
put_disk_and_module(disk); blkdev_put_no_open(bdev);
return ret; return ret;
} }
......
...@@ -352,7 +352,6 @@ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector); ...@@ -352,7 +352,6 @@ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector);
int blk_alloc_devt(struct hd_struct *part, dev_t *devt); int blk_alloc_devt(struct hd_struct *part, dev_t *devt);
void blk_free_devt(dev_t devt); void blk_free_devt(dev_t devt);
void blk_invalidate_devt(dev_t devt);
char *disk_name(struct gendisk *hd, int partno, char *buf); char *disk_name(struct gendisk *hd, int partno, char *buf);
#define ADDPART_FLAG_NONE 0 #define ADDPART_FLAG_NONE 0
#define ADDPART_FLAG_RAID 1 #define ADDPART_FLAG_RAID 1
...@@ -384,6 +383,7 @@ static inline void hd_free_part(struct hd_struct *part) ...@@ -384,6 +383,7 @@ static inline void hd_free_part(struct hd_struct *part)
{ {
free_percpu(part->dkstats); free_percpu(part->dkstats);
kfree(part->info); kfree(part->info);
bdput(part->bdev);
percpu_ref_exit(&part->ref); percpu_ref_exit(&part->ref);
} }
......
...@@ -27,17 +27,11 @@ ...@@ -27,17 +27,11 @@
static struct kobject *block_depr; static struct kobject *block_depr;
static DEFINE_XARRAY(bdev_map); DECLARE_RWSEM(bdev_lookup_sem);
static DEFINE_MUTEX(bdev_map_lock);
/* for extended dynamic devt allocation, currently only one major is used */ /* for extended dynamic devt allocation, currently only one major is used */
#define NR_EXT_DEVT (1 << MINORBITS) #define NR_EXT_DEVT (1 << MINORBITS)
static DEFINE_IDA(ext_devt_ida);
/* For extended devt allocation. ext_devt_lock prevents look up
* results from going away underneath its user.
*/
static DEFINE_SPINLOCK(ext_devt_lock);
static DEFINE_IDR(ext_devt_idr);
static void disk_check_events(struct disk_events *ev, static void disk_check_events(struct disk_events *ev,
unsigned int *clearing_ptr); unsigned int *clearing_ptr);
...@@ -580,14 +574,7 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) ...@@ -580,14 +574,7 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
return 0; return 0;
} }
/* allocate ext devt */ idx = ida_alloc_range(&ext_devt_ida, 0, NR_EXT_DEVT, GFP_KERNEL);
idr_preload(GFP_KERNEL);
spin_lock_bh(&ext_devt_lock);
idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_NOWAIT);
spin_unlock_bh(&ext_devt_lock);
idr_preload_end();
if (idx < 0) if (idx < 0)
return idx == -ENOSPC ? -EBUSY : idx; return idx == -ENOSPC ? -EBUSY : idx;
...@@ -606,26 +593,8 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) ...@@ -606,26 +593,8 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
*/ */
void blk_free_devt(dev_t devt) void blk_free_devt(dev_t devt)
{ {
if (devt == MKDEV(0, 0)) if (MAJOR(devt) == BLOCK_EXT_MAJOR)
return; ida_free(&ext_devt_ida, blk_mangle_minor(MINOR(devt)));
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);
}
}
/*
* We invalidate devt by assigning NULL pointer for devt in idr.
*/
void blk_invalidate_devt(dev_t devt)
{
if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
spin_lock_bh(&ext_devt_lock);
idr_replace(&ext_devt_idr, NULL, blk_mangle_minor(MINOR(devt)));
spin_unlock_bh(&ext_devt_lock);
}
} }
static char *bdevt_str(dev_t devt, char *buf) static char *bdevt_str(dev_t devt, char *buf)
...@@ -640,28 +609,6 @@ static char *bdevt_str(dev_t devt, char *buf) ...@@ -640,28 +609,6 @@ static char *bdevt_str(dev_t devt, char *buf)
return buf; return buf;
} }
static void blk_register_region(struct gendisk *disk)
{
int i;
mutex_lock(&bdev_map_lock);
for (i = 0; i < disk->minors; i++) {
if (xa_insert(&bdev_map, disk_devt(disk) + i, disk, GFP_KERNEL))
WARN_ON_ONCE(1);
}
mutex_unlock(&bdev_map_lock);
}
static void blk_unregister_region(struct gendisk *disk)
{
int i;
mutex_lock(&bdev_map_lock);
for (i = 0; i < disk->minors; i++)
xa_erase(&bdev_map, disk_devt(disk) + i);
mutex_unlock(&bdev_map_lock);
}
static void disk_scan_partitions(struct gendisk *disk) static void disk_scan_partitions(struct gendisk *disk)
{ {
struct block_device *bdev; struct block_device *bdev;
...@@ -805,7 +752,7 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk, ...@@ -805,7 +752,7 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk,
ret = bdi_register(bdi, "%u:%u", MAJOR(devt), MINOR(devt)); ret = bdi_register(bdi, "%u:%u", MAJOR(devt), MINOR(devt));
WARN_ON(ret); WARN_ON(ret);
bdi_set_owner(bdi, dev); bdi_set_owner(bdi, dev);
blk_register_region(disk); bdev_add(disk->part0.bdev, devt);
} }
register_disk(parent, disk, groups); register_disk(parent, disk, groups);
if (register_queue) if (register_queue)
...@@ -847,8 +794,8 @@ static void invalidate_partition(struct gendisk *disk, int partno) ...@@ -847,8 +794,8 @@ static void invalidate_partition(struct gendisk *disk, int partno)
__invalidate_device(bdev, true); __invalidate_device(bdev, true);
/* /*
* Unhash the bdev inode for this device so that it gets evicted as soon * Unhash the bdev inode for this device so that it can't be looked
* as last inode reference is dropped. * up any more even if openers still hold references to it.
*/ */
remove_inode_hash(bdev->bd_inode); remove_inode_hash(bdev->bd_inode);
bdput(bdev); bdput(bdev);
...@@ -890,7 +837,8 @@ void del_gendisk(struct gendisk *disk) ...@@ -890,7 +837,8 @@ void del_gendisk(struct gendisk *disk)
* Block lookups of the disk until all bdevs are unhashed and the * Block lookups of the disk until all bdevs are unhashed and the
* disk is marked as dead (GENHD_FL_UP cleared). * disk is marked as dead (GENHD_FL_UP cleared).
*/ */
down_write(&disk->lookup_sem); down_write(&bdev_lookup_sem);
/* invalidate stuff */ /* invalidate stuff */
disk_part_iter_init(&piter, disk, disk_part_iter_init(&piter, disk,
DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE); DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE);
...@@ -903,7 +851,7 @@ void del_gendisk(struct gendisk *disk) ...@@ -903,7 +851,7 @@ void del_gendisk(struct gendisk *disk)
invalidate_partition(disk, 0); invalidate_partition(disk, 0);
set_capacity(disk, 0); set_capacity(disk, 0);
disk->flags &= ~GENHD_FL_UP; disk->flags &= ~GENHD_FL_UP;
up_write(&disk->lookup_sem); up_write(&bdev_lookup_sem);
if (!(disk->flags & GENHD_FL_HIDDEN)) { if (!(disk->flags & GENHD_FL_HIDDEN)) {
sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
...@@ -916,16 +864,6 @@ void del_gendisk(struct gendisk *disk) ...@@ -916,16 +864,6 @@ void del_gendisk(struct gendisk *disk)
} }
blk_unregister_queue(disk); blk_unregister_queue(disk);
if (!(disk->flags & GENHD_FL_HIDDEN))
blk_unregister_region(disk);
/*
* Remove gendisk pointer from idr so that it cannot be looked up
* while RCU period before freeing gendisk is running to prevent
* use-after-free issues. Note that the device number stays
* "in-use" until we really free the gendisk.
*/
blk_invalidate_devt(disk_devt(disk));
kobject_put(disk->part0.holder_dir); kobject_put(disk->part0.holder_dir);
kobject_put(disk->slave_dir); kobject_put(disk->slave_dir);
...@@ -964,7 +902,7 @@ static ssize_t disk_badblocks_store(struct device *dev, ...@@ -964,7 +902,7 @@ static ssize_t disk_badblocks_store(struct device *dev,
return badblocks_store(disk->bb, page, len, 0); return badblocks_store(disk->bb, page, len, 0);
} }
static void request_gendisk_module(dev_t devt) void blk_request_module(dev_t devt)
{ {
unsigned int major = MAJOR(devt); unsigned int major = MAJOR(devt);
struct blk_major_name **n; struct blk_major_name **n;
...@@ -984,84 +922,6 @@ static void request_gendisk_module(dev_t devt) ...@@ -984,84 +922,6 @@ static void request_gendisk_module(dev_t devt)
request_module("block-major-%d", MAJOR(devt)); request_module("block-major-%d", MAJOR(devt));
} }
static bool get_disk_and_module(struct gendisk *disk)
{
struct module *owner;
if (!disk->fops)
return false;
owner = disk->fops->owner;
if (owner && !try_module_get(owner))
return false;
if (!kobject_get_unless_zero(&disk_to_dev(disk)->kobj)) {
module_put(owner);
return false;
}
return true;
}
/**
* get_gendisk - get partitioning information for a given device
* @devt: device to get partitioning information for
* @partno: returned partition index
*
* This function gets the structure containing partitioning
* information for the given device @devt.
*
* Context: can sleep
*/
struct gendisk *get_gendisk(dev_t devt, int *partno)
{
struct gendisk *disk = NULL;
might_sleep();
if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
mutex_lock(&bdev_map_lock);
disk = xa_load(&bdev_map, devt);
if (!disk) {
mutex_unlock(&bdev_map_lock);
request_gendisk_module(devt);
mutex_lock(&bdev_map_lock);
disk = xa_load(&bdev_map, devt);
}
if (disk && !get_disk_and_module(disk))
disk = NULL;
if (disk)
*partno = devt - disk_devt(disk);
mutex_unlock(&bdev_map_lock);
} else {
struct hd_struct *part;
spin_lock_bh(&ext_devt_lock);
part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
if (part && get_disk_and_module(part_to_disk(part))) {
*partno = part->partno;
disk = part_to_disk(part);
}
spin_unlock_bh(&ext_devt_lock);
}
if (!disk)
return NULL;
/*
* Synchronize with del_gendisk() to not return disk that is being
* destroyed.
*/
down_read(&disk->lookup_sem);
if (unlikely((disk->flags & GENHD_FL_HIDDEN) ||
!(disk->flags & GENHD_FL_UP))) {
up_read(&disk->lookup_sem);
put_disk_and_module(disk);
disk = NULL;
} else {
up_read(&disk->lookup_sem);
}
return disk;
}
/** /**
* bdget_disk - do bdget() by gendisk and partition number * bdget_disk - do bdget() by gendisk and partition number
* @disk: gendisk of interest * @disk: gendisk of interest
...@@ -1559,11 +1419,6 @@ int disk_expand_part_tbl(struct gendisk *disk, int partno) ...@@ -1559,11 +1419,6 @@ int disk_expand_part_tbl(struct gendisk *disk, int partno)
* *
* This function releases all allocated resources of the gendisk. * This function releases all allocated resources of the gendisk.
* *
* The struct gendisk refcount is incremented with get_gendisk() or
* get_disk_and_module(), and its refcount is decremented with
* put_disk_and_module() or put_disk(). Once the refcount reaches 0 this
* function is called.
*
* Drivers which used __device_add_disk() have a gendisk with a request_queue * Drivers which used __device_add_disk() have a gendisk with a request_queue
* assigned. Since the request_queue sits on top of the gendisk for these * assigned. Since the request_queue sits on top of the gendisk for these
* drivers we also call blk_put_queue() for them, and we expect the * drivers we also call blk_put_queue() for them, and we expect the
...@@ -1748,16 +1603,17 @@ struct gendisk *__alloc_disk_node(int minors, int node_id) ...@@ -1748,16 +1603,17 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
if (!disk) if (!disk)
return NULL; return NULL;
disk->part0.bdev = bdev_alloc(disk, 0);
if (!disk->part0.bdev)
goto out_free_disk;
disk->part0.dkstats = alloc_percpu(struct disk_stats); disk->part0.dkstats = alloc_percpu(struct disk_stats);
if (!disk->part0.dkstats) if (!disk->part0.dkstats)
goto out_free_disk; goto out_bdput;
init_rwsem(&disk->lookup_sem);
disk->node_id = node_id; disk->node_id = node_id;
if (disk_expand_part_tbl(disk, 0)) { if (disk_expand_part_tbl(disk, 0))
free_percpu(disk->part0.dkstats); goto out_free_bdstats;
goto out_free_disk;
}
ptbl = rcu_dereference_protected(disk->part_tbl, 1); ptbl = rcu_dereference_protected(disk->part_tbl, 1);
rcu_assign_pointer(ptbl->part[0], &disk->part0); rcu_assign_pointer(ptbl->part[0], &disk->part0);
...@@ -1773,7 +1629,7 @@ struct gendisk *__alloc_disk_node(int minors, int node_id) ...@@ -1773,7 +1629,7 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
*/ */
hd_sects_seq_init(&disk->part0); hd_sects_seq_init(&disk->part0);
if (hd_ref_init(&disk->part0)) if (hd_ref_init(&disk->part0))
goto out_free_part0; goto out_free_bdstats;
disk->minors = minors; disk->minors = minors;
rand_initialize_disk(disk); rand_initialize_disk(disk);
...@@ -1782,8 +1638,10 @@ struct gendisk *__alloc_disk_node(int minors, int node_id) ...@@ -1782,8 +1638,10 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
device_initialize(disk_to_dev(disk)); device_initialize(disk_to_dev(disk));
return disk; return disk;
out_free_part0: out_free_bdstats:
hd_free_part(&disk->part0); free_percpu(disk->part0.dkstats);
out_bdput:
bdput(disk->part0.bdev);
out_free_disk: out_free_disk:
kfree(disk); kfree(disk);
return NULL; return NULL;
...@@ -1807,26 +1665,6 @@ void put_disk(struct gendisk *disk) ...@@ -1807,26 +1665,6 @@ void put_disk(struct gendisk *disk)
} }
EXPORT_SYMBOL(put_disk); EXPORT_SYMBOL(put_disk);
/**
* put_disk_and_module - decrements the module and gendisk refcount
* @disk: the struct gendisk to decrement the refcount for
*
* This is a counterpart of get_disk_and_module() and thus also of
* get_gendisk().
*
* Context: Any context, but the last reference must not be dropped from
* atomic context.
*/
void put_disk_and_module(struct gendisk *disk)
{
if (disk) {
struct module *owner = disk->fops->owner;
put_disk(disk);
module_put(owner);
}
}
static void set_disk_ro_uevent(struct gendisk *gd, int ro) static void set_disk_ro_uevent(struct gendisk *gd, int ro)
{ {
char event[] = "DISK_RO=1"; char event[] = "DISK_RO=1";
......
...@@ -340,12 +340,11 @@ void delete_partition(struct hd_struct *part) ...@@ -340,12 +340,11 @@ void delete_partition(struct hd_struct *part)
device_del(part_to_dev(part)); device_del(part_to_dev(part));
/* /*
* Remove gendisk pointer from idr so that it cannot be looked up * Remove the block device from the inode hash, so that it cannot be
* while RCU period before freeing gendisk is running to prevent * looked up any more even when openers still hold references.
* use-after-free issues. Note that the device number stays
* "in-use" until we really free the gendisk.
*/ */
blk_invalidate_devt(part_devt(part)); remove_inode_hash(part->bdev->bd_inode);
percpu_ref_kill(&part->ref); percpu_ref_kill(&part->ref);
} }
...@@ -368,6 +367,7 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno, ...@@ -368,6 +367,7 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
dev_t devt = MKDEV(0, 0); dev_t devt = MKDEV(0, 0);
struct device *ddev = disk_to_dev(disk); struct device *ddev = disk_to_dev(disk);
struct device *pdev; struct device *pdev;
struct block_device *bdev;
struct disk_part_tbl *ptbl; struct disk_part_tbl *ptbl;
const char *dname; const char *dname;
int err; int err;
...@@ -402,11 +402,15 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno, ...@@ -402,11 +402,15 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
if (!p) if (!p)
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
err = -ENOMEM;
p->dkstats = alloc_percpu(struct disk_stats); p->dkstats = alloc_percpu(struct disk_stats);
if (!p->dkstats) { if (!p->dkstats)
err = -ENOMEM;
goto out_free; goto out_free;
}
bdev = bdev_alloc(disk, partno);
if (!bdev)
goto out_free_stats;
p->bdev = bdev;
hd_sects_seq_init(p); hd_sects_seq_init(p);
pdev = part_to_dev(p); pdev = part_to_dev(p);
...@@ -420,10 +424,8 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno, ...@@ -420,10 +424,8 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
struct partition_meta_info *pinfo; struct partition_meta_info *pinfo;
pinfo = kzalloc_node(sizeof(*pinfo), GFP_KERNEL, disk->node_id); pinfo = kzalloc_node(sizeof(*pinfo), GFP_KERNEL, disk->node_id);
if (!pinfo) { if (!pinfo)
err = -ENOMEM; goto out_bdput;
goto out_free_stats;
}
memcpy(pinfo, info, sizeof(*info)); memcpy(pinfo, info, sizeof(*info));
p->info = pinfo; p->info = pinfo;
} }
...@@ -470,6 +472,7 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno, ...@@ -470,6 +472,7 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
} }
/* everything is up and running, commence */ /* everything is up and running, commence */
bdev_add(bdev, devt);
rcu_assign_pointer(ptbl->part[partno], p); rcu_assign_pointer(ptbl->part[partno], p);
/* suppress uevent if the disk suppresses it */ /* suppress uevent if the disk suppresses it */
...@@ -479,6 +482,8 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno, ...@@ -479,6 +482,8 @@ static struct hd_struct *add_partition(struct gendisk *disk, int partno,
out_free_info: out_free_info:
kfree(p->info); kfree(p->info);
out_bdput:
bdput(bdev);
out_free_stats: out_free_stats:
free_percpu(p->dkstats); free_percpu(p->dkstats);
out_free: out_free:
......
...@@ -863,31 +863,46 @@ void __init bdev_cache_init(void) ...@@ -863,31 +863,46 @@ void __init bdev_cache_init(void)
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */ blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
} }
static struct block_device *bdget(dev_t dev) struct block_device *bdev_alloc(struct gendisk *disk, u8 partno)
{ {
struct block_device *bdev; struct block_device *bdev;
struct inode *inode; struct inode *inode;
inode = iget_locked(blockdev_superblock, dev); inode = new_inode(blockdev_superblock);
if (!inode) if (!inode)
return NULL; return NULL;
inode->i_mode = S_IFBLK;
inode->i_rdev = 0;
inode->i_data.a_ops = &def_blk_aops;
mapping_set_gfp_mask(&inode->i_data, GFP_USER);
bdev = I_BDEV(inode);
spin_lock_init(&bdev->bd_size_lock);
bdev->bd_disk = disk;
bdev->bd_partno = partno;
bdev->bd_contains = NULL;
bdev->bd_super = NULL;
bdev->bd_inode = inode;
bdev->bd_part_count = 0;
return bdev;
}
bdev = &BDEV_I(inode)->bdev; void bdev_add(struct block_device *bdev, dev_t dev)
{
bdev->bd_dev = dev;
bdev->bd_inode->i_rdev = dev;
bdev->bd_inode->i_ino = dev;
insert_inode_hash(bdev->bd_inode);
}
if (inode->i_state & I_NEW) { static struct block_device *bdget(dev_t dev)
spin_lock_init(&bdev->bd_size_lock); {
bdev->bd_contains = NULL; struct inode *inode;
bdev->bd_super = NULL;
bdev->bd_inode = inode; inode = ilookup(blockdev_superblock, dev);
bdev->bd_part_count = 0; if (!inode)
bdev->bd_dev = dev; return NULL;
inode->i_mode = S_IFBLK; return &BDEV_I(inode)->bdev;
inode->i_rdev = dev;
inode->i_data.a_ops = &def_blk_aops;
mapping_set_gfp_mask(&inode->i_data, GFP_USER);
unlock_new_inode(inode);
}
return bdev;
} }
/** /**
...@@ -1004,27 +1019,6 @@ int bd_prepare_to_claim(struct block_device *bdev, struct block_device *whole, ...@@ -1004,27 +1019,6 @@ int bd_prepare_to_claim(struct block_device *bdev, struct block_device *whole,
} }
EXPORT_SYMBOL_GPL(bd_prepare_to_claim); /* only for the loop driver */ EXPORT_SYMBOL_GPL(bd_prepare_to_claim); /* only for the loop driver */
static struct gendisk *bdev_get_gendisk(struct block_device *bdev, int *partno)
{
struct gendisk *disk = get_gendisk(bdev->bd_dev, partno);
if (!disk)
return NULL;
/*
* Now that we hold gendisk reference we make sure bdev we looked up is
* not stale. If it is, it means device got removed and created before
* we looked up gendisk and we fail open in such case. Associating
* unhashed bdev with newly created gendisk could lead to two bdevs
* (and thus two independent caches) being associated with one device
* which is bad.
*/
if (inode_unhashed(bdev->bd_inode)) {
put_disk_and_module(disk);
return NULL;
}
return disk;
}
static void bd_clear_claiming(struct block_device *whole, void *holder) static void bd_clear_claiming(struct block_device *whole, void *holder)
{ {
lockdep_assert_held(&bdev_lock); lockdep_assert_held(&bdev_lock);
...@@ -1347,19 +1341,17 @@ EXPORT_SYMBOL_GPL(bdev_disk_changed); ...@@ -1347,19 +1341,17 @@ EXPORT_SYMBOL_GPL(bdev_disk_changed);
* mutex_lock(part->bd_mutex) * mutex_lock(part->bd_mutex)
* mutex_lock_nested(whole->bd_mutex, 1) * mutex_lock_nested(whole->bd_mutex, 1)
*/ */
static int __blkdev_get(struct block_device *bdev, struct gendisk *disk, static int __blkdev_get(struct block_device *bdev, fmode_t mode)
int partno, fmode_t mode)
{ {
struct gendisk *disk = bdev->bd_disk;
int ret; int ret;
if (!bdev->bd_openers) { if (!bdev->bd_openers) {
bdev->bd_disk = disk;
bdev->bd_contains = bdev; bdev->bd_contains = bdev;
bdev->bd_partno = partno;
if (!partno) { if (!bdev->bd_partno) {
ret = -ENXIO; ret = -ENXIO;
bdev->bd_part = disk_get_part(disk, partno); bdev->bd_part = disk_get_part(disk, 0);
if (!bdev->bd_part) if (!bdev->bd_part)
goto out_clear; goto out_clear;
...@@ -1388,7 +1380,7 @@ static int __blkdev_get(struct block_device *bdev, struct gendisk *disk, ...@@ -1388,7 +1380,7 @@ static int __blkdev_get(struct block_device *bdev, struct gendisk *disk,
struct block_device *whole = bdget_disk(disk, 0); struct block_device *whole = bdget_disk(disk, 0);
mutex_lock_nested(&whole->bd_mutex, 1); mutex_lock_nested(&whole->bd_mutex, 1);
ret = __blkdev_get(whole, disk, 0, mode); ret = __blkdev_get(whole, mode);
if (ret) { if (ret) {
mutex_unlock(&whole->bd_mutex); mutex_unlock(&whole->bd_mutex);
bdput(whole); bdput(whole);
...@@ -1398,7 +1390,7 @@ static int __blkdev_get(struct block_device *bdev, struct gendisk *disk, ...@@ -1398,7 +1390,7 @@ static int __blkdev_get(struct block_device *bdev, struct gendisk *disk,
mutex_unlock(&whole->bd_mutex); mutex_unlock(&whole->bd_mutex);
bdev->bd_contains = whole; bdev->bd_contains = whole;
bdev->bd_part = disk_get_part(disk, partno); bdev->bd_part = disk_get_part(disk, bdev->bd_partno);
if (!(disk->flags & GENHD_FL_UP) || if (!(disk->flags & GENHD_FL_UP) ||
!bdev->bd_part || !bdev->bd_part->nr_sects) { !bdev->bd_part || !bdev->bd_part->nr_sects) {
__blkdev_put(whole, mode, 1); __blkdev_put(whole, mode, 1);
...@@ -1430,12 +1422,53 @@ static int __blkdev_get(struct block_device *bdev, struct gendisk *disk, ...@@ -1430,12 +1422,53 @@ static int __blkdev_get(struct block_device *bdev, struct gendisk *disk,
out_clear: out_clear:
disk_put_part(bdev->bd_part); disk_put_part(bdev->bd_part);
bdev->bd_disk = NULL;
bdev->bd_part = NULL; bdev->bd_part = NULL;
bdev->bd_contains = NULL; bdev->bd_contains = NULL;
return ret; return ret;
} }
struct block_device *blkdev_get_no_open(dev_t dev)
{
struct block_device *bdev;
struct gendisk *disk;
down_read(&bdev_lookup_sem);
bdev = bdget(dev);
if (!bdev) {
up_read(&bdev_lookup_sem);
blk_request_module(dev);
down_read(&bdev_lookup_sem);
bdev = bdget(dev);
if (!bdev)
goto unlock;
}
disk = bdev->bd_disk;
if (!kobject_get_unless_zero(&disk_to_dev(disk)->kobj))
goto bdput;
if ((disk->flags & (GENHD_FL_UP | GENHD_FL_HIDDEN)) != GENHD_FL_UP)
goto put_disk;
if (!try_module_get(bdev->bd_disk->fops->owner))
goto put_disk;
up_read(&bdev_lookup_sem);
return bdev;
put_disk:
put_disk(disk);
bdput:
bdput(bdev);
unlock:
up_read(&bdev_lookup_sem);
return NULL;
}
void blkdev_put_no_open(struct block_device *bdev)
{
module_put(bdev->bd_disk->fops->owner);
put_disk(bdev->bd_disk);
bdput(bdev);
}
/** /**
* blkdev_get_by_dev - open a block device by device number * blkdev_get_by_dev - open a block device by device number
* @dev: device number of block device to open * @dev: device number of block device to open
...@@ -1463,7 +1496,6 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) ...@@ -1463,7 +1496,6 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
bool unblock_events = true; bool unblock_events = true;
struct block_device *bdev; struct block_device *bdev;
struct gendisk *disk; struct gendisk *disk;
int partno;
int ret; int ret;
ret = devcgroup_check_permission(DEVCG_DEV_BLOCK, ret = devcgroup_check_permission(DEVCG_DEV_BLOCK,
...@@ -1473,18 +1505,14 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) ...@@ -1473,18 +1505,14 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
if (ret) if (ret)
return ERR_PTR(ret); return ERR_PTR(ret);
bdev = bdget(dev);
if (!bdev)
return ERR_PTR(-ENOMEM);
/* /*
* If we lost a race with 'disk' being deleted, try again. See md.c. * If we lost a race with 'disk' being deleted, try again. See md.c.
*/ */
retry: retry:
ret = -ENXIO; bdev = blkdev_get_no_open(dev);
disk = bdev_get_gendisk(bdev, &partno); if (!bdev)
if (!disk) return ERR_PTR(-ENXIO);
goto bdput; disk = bdev->bd_disk;
if (mode & FMODE_EXCL) { if (mode & FMODE_EXCL) {
WARN_ON_ONCE(!holder); WARN_ON_ONCE(!holder);
...@@ -1492,7 +1520,7 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) ...@@ -1492,7 +1520,7 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
ret = -ENOMEM; ret = -ENOMEM;
claiming = bdget_disk(disk, 0); claiming = bdget_disk(disk, 0);
if (!claiming) if (!claiming)
goto put_disk; goto put_blkdev;
ret = bd_prepare_to_claim(bdev, claiming, holder); ret = bd_prepare_to_claim(bdev, claiming, holder);
if (ret) if (ret)
goto put_claiming; goto put_claiming;
...@@ -1501,12 +1529,10 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) ...@@ -1501,12 +1529,10 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
disk_block_events(disk); disk_block_events(disk);
mutex_lock(&bdev->bd_mutex); mutex_lock(&bdev->bd_mutex);
ret =__blkdev_get(bdev, disk, partno, mode); ret =__blkdev_get(bdev, mode);
if (!(mode & FMODE_EXCL)) { if (ret)
; /* nothing to do here */ goto abort_claiming;
} else if (ret) { if (mode & FMODE_EXCL) {
bd_abort_claiming(bdev, claiming, holder);
} else {
bd_finish_claiming(bdev, claiming, holder); bd_finish_claiming(bdev, claiming, holder);
/* /*
...@@ -1526,21 +1552,23 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder) ...@@ -1526,21 +1552,23 @@ struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
if (unblock_events) if (unblock_events)
disk_unblock_events(disk); disk_unblock_events(disk);
if (mode & FMODE_EXCL)
bdput(claiming);
return bdev;
abort_claiming:
if (mode & FMODE_EXCL)
bd_abort_claiming(bdev, claiming, holder);
mutex_unlock(&bdev->bd_mutex);
disk_unblock_events(disk);
put_claiming: put_claiming:
if (mode & FMODE_EXCL) if (mode & FMODE_EXCL)
bdput(claiming); bdput(claiming);
put_disk: put_blkdev:
if (ret) blkdev_put_no_open(bdev);
put_disk_and_module(disk);
if (ret == -ERESTARTSYS) if (ret == -ERESTARTSYS)
goto retry; goto retry;
bdput: return ERR_PTR(ret);
if (ret) {
bdput(bdev);
return ERR_PTR(ret);
}
return bdev;
} }
EXPORT_SYMBOL(blkdev_get_by_dev); EXPORT_SYMBOL(blkdev_get_by_dev);
...@@ -1641,7 +1669,6 @@ static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part) ...@@ -1641,7 +1669,6 @@ static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part)
disk_put_part(bdev->bd_part); disk_put_part(bdev->bd_part);
bdev->bd_part = NULL; bdev->bd_part = NULL;
bdev->bd_disk = NULL;
if (bdev_is_partition(bdev)) if (bdev_is_partition(bdev))
victim = bdev->bd_contains; victim = bdev->bd_contains;
bdev->bd_contains = NULL; bdev->bd_contains = NULL;
...@@ -1699,12 +1726,10 @@ void blkdev_put(struct block_device *bdev, fmode_t mode) ...@@ -1699,12 +1726,10 @@ void blkdev_put(struct block_device *bdev, fmode_t mode)
* from userland - e.g. eject(1). * from userland - e.g. eject(1).
*/ */
disk_flush_events(disk, DISK_EVENT_MEDIA_CHANGE); disk_flush_events(disk, DISK_EVENT_MEDIA_CHANGE);
mutex_unlock(&bdev->bd_mutex); mutex_unlock(&bdev->bd_mutex);
__blkdev_put(bdev, mode, 0); __blkdev_put(bdev, mode, 0);
bdput(bdev); blkdev_put_no_open(bdev);
put_disk_and_module(disk);
} }
EXPORT_SYMBOL(blkdev_put); EXPORT_SYMBOL(blkdev_put);
......
...@@ -197,12 +197,12 @@ void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg, ...@@ -197,12 +197,12 @@ void blkcg_print_blkgs(struct seq_file *sf, struct blkcg *blkcg,
u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v); u64 __blkg_prfill_u64(struct seq_file *sf, struct blkg_policy_data *pd, u64 v);
struct blkg_conf_ctx { struct blkg_conf_ctx {
struct gendisk *disk; struct block_device *bdev;
struct blkcg_gq *blkg; struct blkcg_gq *blkg;
char *body; char *body;
}; };
struct gendisk *blkcg_conf_get_disk(char **inputp); struct block_device *blkcg_conf_open_bdev(char **inputp);
int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol, int blkg_conf_prep(struct blkcg *blkcg, const struct blkcg_policy *pol,
char *input, struct blkg_conf_ctx *ctx); char *input, struct blkg_conf_ctx *ctx);
void blkg_conf_finish(struct blkg_conf_ctx *ctx); void blkg_conf_finish(struct blkg_conf_ctx *ctx);
......
...@@ -1994,6 +1994,12 @@ void bd_abort_claiming(struct block_device *bdev, struct block_device *whole, ...@@ -1994,6 +1994,12 @@ void bd_abort_claiming(struct block_device *bdev, struct block_device *whole,
void *holder); void *holder);
void blkdev_put(struct block_device *bdev, fmode_t mode); void blkdev_put(struct block_device *bdev, fmode_t mode);
/* just for blk-cgroup, don't use elsewhere */
struct block_device *blkdev_get_no_open(dev_t dev);
void blkdev_put_no_open(struct block_device *bdev);
struct block_device *bdev_alloc(struct gendisk *disk, u8 partno);
void bdev_add(struct block_device *bdev, dev_t dev);
struct block_device *I_BDEV(struct inode *inode); struct block_device *I_BDEV(struct inode *inode);
struct block_device *bdget_part(struct hd_struct *part); struct block_device *bdget_part(struct hd_struct *part);
struct block_device *bdgrab(struct block_device *bdev); struct block_device *bdgrab(struct block_device *bdev);
......
...@@ -65,6 +65,7 @@ struct hd_struct { ...@@ -65,6 +65,7 @@ struct hd_struct {
struct disk_stats __percpu *dkstats; struct disk_stats __percpu *dkstats;
struct percpu_ref ref; struct percpu_ref ref;
struct block_device *bdev;
struct device __dev; struct device __dev;
struct kobject *holder_dir; struct kobject *holder_dir;
int policy, partno; int policy, partno;
...@@ -193,7 +194,6 @@ struct gendisk { ...@@ -193,7 +194,6 @@ struct gendisk {
int flags; int flags;
unsigned long state; unsigned long state;
#define GD_NEED_PART_SCAN 0 #define GD_NEED_PART_SCAN 0
struct rw_semaphore lookup_sem;
struct kobject *slave_dir; struct kobject *slave_dir;
struct timer_rand_state *random; struct timer_rand_state *random;
...@@ -300,7 +300,6 @@ static inline void add_disk_no_queue_reg(struct gendisk *disk) ...@@ -300,7 +300,6 @@ static inline void add_disk_no_queue_reg(struct gendisk *disk)
} }
extern void del_gendisk(struct gendisk *gp); extern void del_gendisk(struct gendisk *gp);
extern struct gendisk *get_gendisk(dev_t dev, int *partno);
extern struct block_device *bdget_disk(struct gendisk *disk, int partno); extern struct block_device *bdget_disk(struct gendisk *disk, int partno);
extern void set_disk_ro(struct gendisk *disk, int flag); extern void set_disk_ro(struct gendisk *disk, int flag);
...@@ -338,7 +337,6 @@ int blk_drop_partitions(struct block_device *bdev); ...@@ -338,7 +337,6 @@ int blk_drop_partitions(struct block_device *bdev);
extern struct gendisk *__alloc_disk_node(int minors, int node_id); extern struct gendisk *__alloc_disk_node(int minors, int node_id);
extern void put_disk(struct gendisk *disk); extern void put_disk(struct gendisk *disk);
extern void put_disk_and_module(struct gendisk *disk);
#define alloc_disk_node(minors, node_id) \ #define alloc_disk_node(minors, node_id) \
({ \ ({ \
...@@ -388,7 +386,10 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev, ...@@ -388,7 +386,10 @@ static inline void bd_unlink_disk_holder(struct block_device *bdev,
} }
#endif /* CONFIG_SYSFS */ #endif /* CONFIG_SYSFS */
extern struct rw_semaphore bdev_lookup_sem;
dev_t blk_lookup_devt(const char *name, int partno); dev_t blk_lookup_devt(const char *name, int partno);
void blk_request_module(dev_t devt);
#ifdef CONFIG_BLOCK #ifdef CONFIG_BLOCK
void printk_all_partitions(void); void printk_all_partitions(void);
#else /* CONFIG_BLOCK */ #else /* CONFIG_BLOCK */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment