zvol: reduce linear list search
Use kernel ida to generate minor number, and use hash table to find zvol with name. Signed-off-by: Chunwei Chen <david.chen@osnexus.com>
This commit is contained in:
parent
57ddcda164
commit
d45e010dce
|
@ -63,6 +63,11 @@ static kmutex_t zvol_state_lock;
|
||||||
static list_t zvol_state_list;
|
static list_t zvol_state_list;
|
||||||
void *zvol_tag = "zvol_tag";
|
void *zvol_tag = "zvol_tag";
|
||||||
|
|
||||||
|
#define ZVOL_HT_SIZE 1024
|
||||||
|
static struct hlist_head *zvol_htable;
|
||||||
|
#define ZVOL_HT_HEAD(hash) (&zvol_htable[(hash) & (ZVOL_HT_SIZE-1)])
|
||||||
|
static DEFINE_IDA(zvol_ida);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The in-core state of each volume.
|
* The in-core state of each volume.
|
||||||
*/
|
*/
|
||||||
|
@ -81,6 +86,8 @@ typedef struct zvol_state {
|
||||||
struct gendisk *zv_disk; /* generic disk */
|
struct gendisk *zv_disk; /* generic disk */
|
||||||
struct request_queue *zv_queue; /* request queue */
|
struct request_queue *zv_queue; /* request queue */
|
||||||
list_node_t zv_next; /* next zvol_state_t linkage */
|
list_node_t zv_next; /* next zvol_state_t linkage */
|
||||||
|
uint64_t zv_hash; /* name hash */
|
||||||
|
struct hlist_node zv_hlink; /* hash link */
|
||||||
} zvol_state_t;
|
} zvol_state_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -102,30 +109,17 @@ typedef struct {
|
||||||
|
|
||||||
#define ZVOL_RDONLY 0x1
|
#define ZVOL_RDONLY 0x1
|
||||||
|
|
||||||
/*
|
static uint64_t
|
||||||
* Find the next available range of ZVOL_MINORS minor numbers. The
|
zvol_name_hash(const char *name)
|
||||||
* zvol_state_list is kept in ascending minor order so we simply need
|
|
||||||
* to scan the list for the first gap in the sequence. This allows us
|
|
||||||
* to recycle minor number as devices are created and removed.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
zvol_find_minor(unsigned *minor)
|
|
||||||
{
|
{
|
||||||
zvol_state_t *zv;
|
int i;
|
||||||
|
uint64_t crc = -1ULL;
|
||||||
*minor = 0;
|
uint8_t *p = (uint8_t *)name;
|
||||||
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);
|
||||||
for (zv = list_head(&zvol_state_list); zv != NULL;
|
for (i = 0; i < MAXNAMELEN - 1 && *p; i++, p++) {
|
||||||
zv = list_next(&zvol_state_list, zv), *minor += ZVOL_MINORS) {
|
crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ (*p)) & 0xFF];
|
||||||
if (MINOR(zv->zv_dev) != MINOR(*minor))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return (crc);
|
||||||
/* All minors are in use */
|
|
||||||
if (*minor >= (1 << MINORBITS))
|
|
||||||
return (SET_ERROR(ENXIO));
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -146,22 +140,32 @@ zvol_find_by_dev(dev_t dev)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a zvol_state_t given the name and hash generated by zvol_name_hash.
|
||||||
|
*/
|
||||||
|
static zvol_state_t *
|
||||||
|
zvol_find_by_name_hash(const char *name, uint64_t hash)
|
||||||
|
{
|
||||||
|
zvol_state_t *zv;
|
||||||
|
struct hlist_node *p;
|
||||||
|
|
||||||
|
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
||||||
|
hlist_for_each(p, ZVOL_HT_HEAD(hash)) {
|
||||||
|
zv = hlist_entry(p, zvol_state_t, zv_hlink);
|
||||||
|
if (zv->zv_hash == hash &&
|
||||||
|
strncmp(zv->zv_name, name, MAXNAMELEN) == 0)
|
||||||
|
return (zv);
|
||||||
|
}
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find a zvol_state_t given the name provided at zvol_alloc() time.
|
* Find a zvol_state_t given the name provided at zvol_alloc() time.
|
||||||
*/
|
*/
|
||||||
static zvol_state_t *
|
static zvol_state_t *
|
||||||
zvol_find_by_name(const char *name)
|
zvol_find_by_name(const char *name)
|
||||||
{
|
{
|
||||||
zvol_state_t *zv;
|
return (zvol_find_by_name_hash(name, zvol_name_hash(name)));
|
||||||
|
|
||||||
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
|
||||||
for (zv = list_head(&zvol_state_list); zv != NULL;
|
|
||||||
zv = list_next(&zvol_state_list, zv)) {
|
|
||||||
if (strncmp(zv->zv_name, name, MAXNAMELEN) == 0)
|
|
||||||
return (zv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -921,32 +925,26 @@ zvol_get_data(void *arg, lr_write_t *lr, char *buf, zio_t *zio)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The zvol_state_t's are inserted in increasing MINOR(dev_t) order.
|
* The zvol_state_t's are inserted into zvol_state_list and zvol_htable.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
zvol_insert(zvol_state_t *zv_insert)
|
zvol_insert(zvol_state_t *zv)
|
||||||
{
|
{
|
||||||
zvol_state_t *zv = NULL;
|
|
||||||
|
|
||||||
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
||||||
ASSERT3U(MINOR(zv_insert->zv_dev) & ZVOL_MINOR_MASK, ==, 0);
|
ASSERT3U(MINOR(zv->zv_dev) & ZVOL_MINOR_MASK, ==, 0);
|
||||||
for (zv = list_head(&zvol_state_list); zv != NULL;
|
list_insert_head(&zvol_state_list, zv);
|
||||||
zv = list_next(&zvol_state_list, zv)) {
|
hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash));
|
||||||
if (MINOR(zv->zv_dev) > MINOR(zv_insert->zv_dev))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_insert_before(&zvol_state_list, zv, zv_insert);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simply remove the zvol from to list of zvols.
|
* Simply remove the zvol from to list of zvols.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
zvol_remove(zvol_state_t *zv_remove)
|
zvol_remove(zvol_state_t *zv)
|
||||||
{
|
{
|
||||||
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
ASSERT(MUTEX_HELD(&zvol_state_lock));
|
||||||
list_remove(&zvol_state_list, zv_remove);
|
list_remove(&zvol_state_list, zv);
|
||||||
|
hlist_del(&zv->zv_hlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1334,6 +1332,7 @@ zvol_free(zvol_state_t *zv)
|
||||||
blk_cleanup_queue(zv->zv_queue);
|
blk_cleanup_queue(zv->zv_queue);
|
||||||
put_disk(zv->zv_disk);
|
put_disk(zv->zv_disk);
|
||||||
|
|
||||||
|
ida_simple_remove(&zvol_ida, MINOR(zv->zv_dev) >> ZVOL_MINOR_BITS);
|
||||||
kmem_free(zv, sizeof (zvol_state_t));
|
kmem_free(zv, sizeof (zvol_state_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1352,10 +1351,17 @@ zvol_create_minor_impl(const char *name)
|
||||||
uint64_t len;
|
uint64_t len;
|
||||||
unsigned minor = 0;
|
unsigned minor = 0;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
int idx;
|
||||||
|
uint64_t hash = zvol_name_hash(name);
|
||||||
|
|
||||||
|
idx = ida_simple_get(&zvol_ida, 0, 0, kmem_flags_convert(KM_SLEEP));
|
||||||
|
if (idx < 0)
|
||||||
|
return (SET_ERROR(-idx));
|
||||||
|
minor = idx << ZVOL_MINOR_BITS;
|
||||||
|
|
||||||
mutex_enter(&zvol_state_lock);
|
mutex_enter(&zvol_state_lock);
|
||||||
|
|
||||||
zv = zvol_find_by_name(name);
|
zv = zvol_find_by_name_hash(name, hash);
|
||||||
if (zv) {
|
if (zv) {
|
||||||
error = SET_ERROR(EEXIST);
|
error = SET_ERROR(EEXIST);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1375,15 +1381,12 @@ zvol_create_minor_impl(const char *name)
|
||||||
if (error)
|
if (error)
|
||||||
goto out_dmu_objset_disown;
|
goto out_dmu_objset_disown;
|
||||||
|
|
||||||
error = zvol_find_minor(&minor);
|
|
||||||
if (error)
|
|
||||||
goto out_dmu_objset_disown;
|
|
||||||
|
|
||||||
zv = zvol_alloc(MKDEV(zvol_major, minor), name);
|
zv = zvol_alloc(MKDEV(zvol_major, minor), name);
|
||||||
if (zv == NULL) {
|
if (zv == NULL) {
|
||||||
error = SET_ERROR(EAGAIN);
|
error = SET_ERROR(EAGAIN);
|
||||||
goto out_dmu_objset_disown;
|
goto out_dmu_objset_disown;
|
||||||
}
|
}
|
||||||
|
zv->zv_hash = hash;
|
||||||
|
|
||||||
if (dmu_objset_is_snapshot(os))
|
if (dmu_objset_is_snapshot(os))
|
||||||
zv->zv_flags |= ZVOL_RDONLY;
|
zv->zv_flags |= ZVOL_RDONLY;
|
||||||
|
@ -1449,6 +1452,7 @@ out:
|
||||||
add_disk(zv->zv_disk);
|
add_disk(zv->zv_disk);
|
||||||
} else {
|
} else {
|
||||||
mutex_exit(&zvol_state_lock);
|
mutex_exit(&zvol_state_lock);
|
||||||
|
ida_simple_remove(&zvol_ida, idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (SET_ERROR(error));
|
return (SET_ERROR(error));
|
||||||
|
@ -1933,16 +1937,25 @@ zvol_rename_minors(spa_t *spa, const char *name1, const char *name2,
|
||||||
int
|
int
|
||||||
zvol_init(void)
|
zvol_init(void)
|
||||||
{
|
{
|
||||||
int error;
|
int i, error;
|
||||||
|
|
||||||
list_create(&zvol_state_list, sizeof (zvol_state_t),
|
list_create(&zvol_state_list, sizeof (zvol_state_t),
|
||||||
offsetof(zvol_state_t, zv_next));
|
offsetof(zvol_state_t, zv_next));
|
||||||
mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL);
|
mutex_init(&zvol_state_lock, NULL, MUTEX_DEFAULT, NULL);
|
||||||
|
|
||||||
|
zvol_htable = kmem_alloc(ZVOL_HT_SIZE * sizeof (struct hlist_head),
|
||||||
|
KM_SLEEP);
|
||||||
|
if (!zvol_htable) {
|
||||||
|
error = ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
for (i = 0; i < ZVOL_HT_SIZE; i++)
|
||||||
|
INIT_HLIST_HEAD(&zvol_htable[i]);
|
||||||
|
|
||||||
error = register_blkdev(zvol_major, ZVOL_DRIVER);
|
error = register_blkdev(zvol_major, ZVOL_DRIVER);
|
||||||
if (error) {
|
if (error) {
|
||||||
printk(KERN_INFO "ZFS: register_blkdev() failed %d\n", error);
|
printk(KERN_INFO "ZFS: register_blkdev() failed %d\n", error);
|
||||||
goto out;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
blk_register_region(MKDEV(zvol_major, 0), 1UL << MINORBITS,
|
blk_register_region(MKDEV(zvol_major, 0), 1UL << MINORBITS,
|
||||||
|
@ -1950,6 +1963,8 @@ zvol_init(void)
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
|
out_free:
|
||||||
|
kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));
|
||||||
out:
|
out:
|
||||||
mutex_destroy(&zvol_state_lock);
|
mutex_destroy(&zvol_state_lock);
|
||||||
list_destroy(&zvol_state_list);
|
list_destroy(&zvol_state_list);
|
||||||
|
@ -1964,6 +1979,7 @@ zvol_fini(void)
|
||||||
|
|
||||||
blk_unregister_region(MKDEV(zvol_major, 0), 1UL << MINORBITS);
|
blk_unregister_region(MKDEV(zvol_major, 0), 1UL << MINORBITS);
|
||||||
unregister_blkdev(zvol_major, ZVOL_DRIVER);
|
unregister_blkdev(zvol_major, ZVOL_DRIVER);
|
||||||
|
kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));
|
||||||
|
|
||||||
list_destroy(&zvol_state_list);
|
list_destroy(&zvol_state_list);
|
||||||
mutex_destroy(&zvol_state_lock);
|
mutex_destroy(&zvol_state_lock);
|
||||||
|
|
Loading…
Reference in New Issue