Skip to content

Commit

Permalink
zvol: reduce linear list search
Browse files Browse the repository at this point in the history
Use kernel ida to generate minor number, and use hash table to find zvol with
name.

Signed-off-by: Chunwei Chen <[email protected]>
  • Loading branch information
Chunwei Chen committed Nov 30, 2016
1 parent 63d9fd8 commit 0a7c4b7
Showing 1 changed file with 65 additions and 52 deletions.
117 changes: 65 additions & 52 deletions module/zfs/zvol.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ static kmutex_t zvol_state_lock;
static list_t zvol_state_list;
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.
*/
Expand All @@ -81,6 +86,8 @@ typedef struct zvol_state {
struct gendisk *zv_disk; /* generic disk */
struct request_queue *zv_queue; /* request queue */
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;

typedef enum {
Expand All @@ -102,30 +109,17 @@ typedef struct {

#define ZVOL_RDONLY 0x1

/*
* Find the next available range of ZVOL_MINORS minor numbers. The
* 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)
static uint64_t
zvol_name_hash(const char *name)
{
zvol_state_t *zv;

*minor = 0;
ASSERT(MUTEX_HELD(&zvol_state_lock));
for (zv = list_head(&zvol_state_list); zv != NULL;
zv = list_next(&zvol_state_list, zv), *minor += ZVOL_MINORS) {
if (MINOR(zv->zv_dev) != MINOR(*minor))
break;
int i;
uint64_t crc = -1ULL;
uint8_t *p = (uint8_t *)name;
ASSERT(zfs_crc64_table[128] == ZFS_CRC64_POLY);
for (i = 0; i < MAXNAMELEN - 1 && *p; i++, p++) {
crc = (crc >> 8) ^ zfs_crc64_table[(crc ^ (*p)) & 0xFF];
}

/* All minors are in use */
if (*minor >= (1 << MINORBITS))
return (SET_ERROR(ENXIO));

return (0);
return (crc);
}

/*
Expand All @@ -146,24 +140,33 @@ zvol_find_by_dev(dev_t dev)
return (NULL);
}

/*
* Find a zvol_state_t given the name provided at zvol_alloc() time.
*/
static zvol_state_t *
zvol_find_by_name(const char *name)
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));
for (zv = list_head(&zvol_state_list); zv != NULL;
zv = list_next(&zvol_state_list, zv)) {
if (strncmp(zv->zv_name, name, MAXNAMELEN) == 0)
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.
*/
static zvol_state_t *
zvol_find_by_name(const char *name)
{
uint64_t hash = zvol_name_hash(name);

return (zvol_find_by_name_hash(name, hash));
}


/*
* Given a path, return TRUE if path is a ZVOL.
Expand Down Expand Up @@ -921,32 +924,24 @@ 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
zvol_insert(zvol_state_t *zv_insert)
zvol_insert(zvol_state_t *zv)
{
zvol_state_t *zv = NULL;

ASSERT(MUTEX_HELD(&zvol_state_lock));
ASSERT3U(MINOR(zv_insert->zv_dev) & ZVOL_MINOR_MASK, ==, 0);
for (zv = list_head(&zvol_state_list); zv != NULL;
zv = list_next(&zvol_state_list, zv)) {
if (MINOR(zv->zv_dev) > MINOR(zv_insert->zv_dev))
break;
}

list_insert_before(&zvol_state_list, zv, zv_insert);
list_insert_head(&zvol_state_list, zv);
hlist_add_head(&zv->zv_hlink, ZVOL_HT_HEAD(zv->zv_hash));
}

/*
* Simply remove the zvol from to list of zvols.
*/
static void
zvol_remove(zvol_state_t *zv_remove)
zvol_remove(zvol_state_t *zv)
{
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
Expand Down Expand Up @@ -1334,6 +1329,7 @@ zvol_free(zvol_state_t *zv)
blk_cleanup_queue(zv->zv_queue);
put_disk(zv->zv_disk);

ida_simple_remove(&zvol_ida, MINOR(zv->zv_dev) >> ZVOL_MINOR_BITS);
kmem_free(zv, sizeof (zvol_state_t));
}

Expand All @@ -1352,10 +1348,17 @@ zvol_create_minor_impl(const char *name)
uint64_t len;
unsigned minor = 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);

zv = zvol_find_by_name(name);
zv = zvol_find_by_name_hash(name, hash);
if (zv) {
error = SET_ERROR(EEXIST);
goto out;
Expand All @@ -1375,15 +1378,12 @@ zvol_create_minor_impl(const char *name)
if (error)
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);
if (zv == NULL) {
error = SET_ERROR(EAGAIN);
goto out_dmu_objset_disown;
}
zv->zv_hash = hash;

if (dmu_objset_is_snapshot(os))
zv->zv_flags |= ZVOL_RDONLY;
Expand Down Expand Up @@ -1449,6 +1449,7 @@ zvol_create_minor_impl(const char *name)
add_disk(zv->zv_disk);
} else {
mutex_exit(&zvol_state_lock);
ida_simple_remove(&zvol_ida, idx);
}

return (SET_ERROR(error));
Expand Down Expand Up @@ -1933,23 +1934,34 @@ zvol_rename_minors(spa_t *spa, const char *name1, const char *name2,
int
zvol_init(void)
{
int error;
int i, error;

list_create(&zvol_state_list, sizeof (zvol_state_t),
offsetof(zvol_state_t, zv_next));
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);
if (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,
THIS_MODULE, zvol_probe, NULL, NULL);

return (0);

out_free:
kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));
out:
mutex_destroy(&zvol_state_lock);
list_destroy(&zvol_state_list);
Expand All @@ -1964,6 +1976,7 @@ zvol_fini(void)

blk_unregister_region(MKDEV(zvol_major, 0), 1UL << MINORBITS);
unregister_blkdev(zvol_major, ZVOL_DRIVER);
kmem_free(zvol_htable, ZVOL_HT_SIZE * sizeof (struct hlist_head));

list_destroy(&zvol_state_list);
mutex_destroy(&zvol_state_lock);
Expand Down

0 comments on commit 0a7c4b7

Please sign in to comment.