#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/kmod.h>
-#include <linux/kobj_map.h>
#include <linux/mutex.h>
#include <linux/idr.h>
#include <linux/log2.h>
#include "blk.h"
-static DEFINE_MUTEX(block_class_lock);
static struct kobject *block_depr;
+static DEFINE_XARRAY(bdev_map);
+static DEFINE_MUTEX(bdev_map_lock);
+
/* for extended dynamic devt allocation, currently only one major is used */
#define NR_EXT_DEVT (1 << MINORBITS)
static void disk_release_events(struct gendisk *disk);
/*
- * Set disk capacity and notify if the size is not currently
- * zero and will not be set to zero
+ * Set disk capacity and notify if the size is not currently zero and will not
+ * be set to zero. Returns true if a uevent was sent, otherwise false.
*/
-bool set_capacity_revalidate_and_notify(struct gendisk *disk, sector_t size,
- bool update_bdev)
+bool set_capacity_and_notify(struct gendisk *disk, sector_t size)
{
sector_t capacity = get_capacity(disk);
set_capacity(disk, size);
- if (update_bdev)
- revalidate_disk_size(disk, true);
+ revalidate_disk_size(disk, true);
if (capacity != size && capacity != 0 && size != 0) {
char *envp[] = { "RESIZE=1", NULL };
return false;
}
-
-EXPORT_SYMBOL_GPL(set_capacity_revalidate_and_notify);
+EXPORT_SYMBOL_GPL(set_capacity_and_notify);
/*
* Format the device name of the indicated disk into the supplied buffer and
int inc, end;
/* put the last partition */
- disk_put_part(piter->part);
- piter->part = NULL;
+ disk_part_iter_exit(piter);
/* get part_tbl */
rcu_read_lock();
struct blk_major_name *next;
int major;
char name[16];
+ void (*probe)(dev_t devt);
} *major_names[BLKDEV_MAJOR_HASH_SIZE];
+static DEFINE_MUTEX(major_names_lock);
/* index in the above - for now: assume no multimajor ranges */
static inline int major_to_index(unsigned major)
{
struct blk_major_name *dp;
- mutex_lock(&block_class_lock);
+ mutex_lock(&major_names_lock);
for (dp = major_names[major_to_index(offset)]; dp; dp = dp->next)
if (dp->major == offset)
seq_printf(seqf, "%3d %s\n", dp->major, dp->name);
- mutex_unlock(&block_class_lock);
+ mutex_unlock(&major_names_lock);
}
#endif /* CONFIG_PROC_FS */
/**
- * register_blkdev - register a new block device
+ * __register_blkdev - register a new block device
*
* @major: the requested major device number [1..BLKDEV_MAJOR_MAX-1]. If
* @major = 0, try to allocate any unused major number.
* @name: the name of the new block device as a zero terminated string
+ * @probe: allback that is called on access to any minor number of @major
*
* The @name must be unique within the system.
*
*
* See Documentation/admin-guide/devices.txt for the list of allocated
* major numbers.
+ *
+ * Use register_blkdev instead for any new code.
*/
-int register_blkdev(unsigned int major, const char *name)
+int __register_blkdev(unsigned int major, const char *name,
+ void (*probe)(dev_t devt))
{
struct blk_major_name **n, *p;
int index, ret = 0;
- mutex_lock(&block_class_lock);
+ mutex_lock(&major_names_lock);
/* temporary */
if (major == 0) {
}
p->major = major;
+ p->probe = probe;
strlcpy(p->name, name, sizeof(p->name));
p->next = NULL;
index = major_to_index(major);
kfree(p);
}
out:
- mutex_unlock(&block_class_lock);
+ mutex_unlock(&major_names_lock);
return ret;
}
-
-EXPORT_SYMBOL(register_blkdev);
+EXPORT_SYMBOL(__register_blkdev);
void unregister_blkdev(unsigned int major, const char *name)
{
struct blk_major_name *p = NULL;
int index = major_to_index(major);
- mutex_lock(&block_class_lock);
+ mutex_lock(&major_names_lock);
for (n = &major_names[index]; *n; n = &(*n)->next)
if ((*n)->major == major)
break;
p = *n;
*n = p->next;
}
- mutex_unlock(&block_class_lock);
+ mutex_unlock(&major_names_lock);
kfree(p);
}
EXPORT_SYMBOL(unregister_blkdev);
-static struct kobj_map *bdev_map;
-
/**
* blk_mangle_minor - scatter minor numbers apart
* @minor: minor number to mangle
return buf;
}
-/*
- * Register device numbers dev..(dev+range-1)
- * range must be nonzero
- * The hash chain is sorted on range, so that subranges can override.
- */
-void blk_register_region(dev_t devt, unsigned long range, struct module *module,
- struct kobject *(*probe)(dev_t, int *, void *),
- int (*lock)(dev_t, void *), void *data)
-{
- kobj_map(bdev_map, devt, range, module, probe, lock, data);
-}
-
-EXPORT_SYMBOL(blk_register_region);
-
-void blk_unregister_region(dev_t devt, unsigned long range)
-{
- kobj_unmap(bdev_map, devt, range);
-}
-
-EXPORT_SYMBOL(blk_unregister_region);
-
-static struct kobject *exact_match(dev_t devt, int *partno, void *data)
+static void blk_register_region(struct gendisk *disk)
{
- struct gendisk *p = data;
+ int i;
- return &disk_to_dev(p)->kobj;
+ 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 int exact_lock(dev_t devt, void *data)
+static void blk_unregister_region(struct gendisk *disk)
{
- struct gendisk *p = data;
+ int i;
- if (!get_disk_and_module(p))
- return -1;
- return 0;
+ 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)
ret = bdi_register(bdi, "%u:%u", MAJOR(devt), MINOR(devt));
WARN_ON(ret);
bdi_set_owner(bdi, dev);
- blk_register_region(disk_devt(disk), disk->minors, NULL,
- exact_match, exact_lock, disk);
+ blk_register_region(disk);
}
register_disk(parent, disk, groups);
if (register_queue)
might_sleep();
+ if (WARN_ON_ONCE(!disk->queue))
+ return;
+
blk_integrity_del(disk);
disk_del_events(disk);
disk->flags &= ~GENHD_FL_UP;
up_write(&disk->lookup_sem);
- if (!(disk->flags & GENHD_FL_HIDDEN))
+ if (!(disk->flags & GENHD_FL_HIDDEN)) {
sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi");
- if (disk->queue) {
+
/*
* Unregister bdi before releasing device numbers (as they can
* get reused and we'd get clashes in sysfs).
*/
- if (!(disk->flags & GENHD_FL_HIDDEN))
- bdi_unregister(disk->queue->backing_dev_info);
- blk_unregister_queue(disk);
- } else {
- WARN_ON(1);
+ bdi_unregister(disk->queue->backing_dev_info);
}
+ blk_unregister_queue(disk);
+
if (!(disk->flags & GENHD_FL_HIDDEN))
- blk_unregister_region(disk_devt(disk), disk->minors);
+ 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
return badblocks_store(disk->bb, page, len, 0);
}
+static void request_gendisk_module(dev_t devt)
+{
+ unsigned int major = MAJOR(devt);
+ struct blk_major_name **n;
+
+ mutex_lock(&major_names_lock);
+ for (n = &major_names[major_to_index(major)]; *n; n = &(*n)->next) {
+ if ((*n)->major == major && (*n)->probe) {
+ (*n)->probe(devt);
+ mutex_unlock(&major_names_lock);
+ return;
+ }
+ }
+ mutex_unlock(&major_names_lock);
+
+ if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
+ /* Make old-style 2.4 aliases work */
+ 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
might_sleep();
if (MAJOR(devt) != BLOCK_EXT_MAJOR) {
- struct kobject *kobj;
-
- kobj = kobj_lookup(bdev_map, devt, partno);
- if (kobj)
- disk = dev_to_disk(kobj_to_dev(kobj));
+ 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;
};
#endif
-
-static struct kobject *base_probe(dev_t devt, int *partno, void *data)
-{
- if (request_module("block-major-%d-%d", MAJOR(devt), MINOR(devt)) > 0)
- /* Make old-style 2.4 aliases work */
- request_module("block-major-%d", MAJOR(devt));
- return NULL;
-}
-
static int __init genhd_device_init(void)
{
int error;
error = class_register(&block_class);
if (unlikely(error))
return error;
- bdev_map = kobj_map_init(base_probe, &block_class_lock);
blk_dev_init();
register_blkdev(BLOCK_EXT_MAJOR, "blkext");
}
EXPORT_SYMBOL(__alloc_disk_node);
-/**
- * get_disk_and_module - increments the gendisk and gendisk fops module refcount
- * @disk: the struct gendisk to increment the refcount for
- *
- * This increments the refcount for the struct gendisk, and the gendisk's
- * fops module owner.
- *
- * Context: Any context.
- */
-struct kobject *get_disk_and_module(struct gendisk *disk)
-{
- struct module *owner;
- struct kobject *kobj;
-
- if (!disk->fops)
- return NULL;
- owner = disk->fops->owner;
- if (owner && !try_module_get(owner))
- return NULL;
- kobj = kobject_get_unless_zero(&disk_to_dev(disk)->kobj);
- if (kobj == NULL) {
- module_put(owner);
- return NULL;
- }
- return kobj;
-
-}
-EXPORT_SYMBOL(get_disk_and_module);
-
/**
* put_disk - decrements the gendisk refcount
* @disk: the struct gendisk to decrement the refcount for
module_put(owner);
}
}
-EXPORT_SYMBOL(put_disk_and_module);
static void set_disk_ro_uevent(struct gendisk *gd, int ro)
{
kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
}
-void set_device_ro(struct block_device *bdev, int flag)
-{
- bdev->bd_part->policy = flag;
-}
-
-EXPORT_SYMBOL(set_device_ro);
-
void set_disk_ro(struct gendisk *disk, int flag)
{
struct disk_part_iter piter;