diff options
author | Chunwei Chen <[email protected]> | 2016-11-30 13:56:50 -0800 |
---|---|---|
committer | Chunwei Chen <[email protected]> | 2016-12-01 14:53:11 -0800 |
commit | d45e010dceffc3bf86600787f4d116f891b8bf23 (patch) | |
tree | 57de3d8298cde31a09f674181d86f9a0d432903c /module/zfs | |
parent | 57ddcda1647daac93057dd520a9dc5187c643264 (diff) |
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 <[email protected]>
Diffstat (limited to 'module/zfs')
-rw-r--r-- | module/zfs/zvol.c | 114 |
1 files changed, 65 insertions, 49 deletions
diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index ea6997b5b..cddc43c7c 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -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. */ @@ -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 { @@ -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); } /* @@ -147,23 +141,33 @@ zvol_find_by_dev(dev_t dev) } /* - * Find a zvol_state_t given the name provided at zvol_alloc() time. + * Find a zvol_state_t given the name and hash generated by zvol_name_hash. */ 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) +{ + return (zvol_find_by_name_hash(name, zvol_name_hash(name))); +} + /* * Given a path, return TRUE if path is a ZVOL. @@ -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 -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); + ASSERT3U(MINOR(zv->zv_dev) & ZVOL_MINOR_MASK, ==, 0); + 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 @@ -1334,6 +1332,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)); } @@ -1352,10 +1351,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; @@ -1375,15 +1381,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; @@ -1449,6 +1452,7 @@ out: add_disk(zv->zv_disk); } else { mutex_exit(&zvol_state_lock); + ida_simple_remove(&zvol_ida, idx); } return (SET_ERROR(error)); @@ -1933,16 +1937,25 @@ 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, @@ -1950,6 +1963,8 @@ zvol_init(void) 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); @@ -1964,6 +1979,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); |