lustre_hash_t *lh;
int i;
ENTRY;
-
+
LASSERT(name != NULL);
LASSERT(ops != NULL);
LASSERT(cur_bits > 0);
LASSERT(max_bits >= cur_bits);
LASSERT(max_bits < 31);
-
+
OBD_ALLOC_PTR(lh);
if (!lh)
RETURN(NULL);
-
+
strncpy(lh->lh_name, name, sizeof(lh->lh_name));
+ lh->lh_name[sizeof(lh->lh_name) - 1] = '\0';
atomic_set(&lh->lh_rehash_count, 0);
atomic_set(&lh->lh_count, 0);
rwlock_init(&lh->lh_rwlock);
OBD_FREE_PTR(lh);
RETURN(NULL);
}
-
+
for (i = 0; i <= lh->lh_cur_mask; i++) {
INIT_HLIST_HEAD(&lh->lh_buckets[i].lhb_head);
rwlock_init(&lh->lh_buckets[i].lhb_rwlock);
atomic_set(&lh->lh_buckets[i].lhb_count, 0);
}
-
+
return lh;
}
EXPORT_SYMBOL(lustre_hash_init);
-
+
/**
* Cleanup lustre hash @lh.
*/
ENTRY;
LASSERT(lh != NULL);
-
+
write_lock(&lh->lh_rwlock);
-
+
lh_for_each_bucket(lh, lhb, i) {
write_lock(&lhb->lhb_rwlock);
hlist_for_each_safe(hnode, pos, &(lhb->lhb_head)) {
__lustre_hash_bucket_del(lh, lhb, hnode);
lh_exit(lh, hnode);
}
-
+
LASSERT(hlist_empty(&(lhb->lhb_head)));
LASSERT(atomic_read(&lhb->lhb_count) == 0);
write_unlock(&lhb->lhb_rwlock);
}
-
- OBD_VFREE(lh->lh_buckets, sizeof(*lh->lh_buckets) << lh->lh_cur_bits);
+
LASSERT(atomic_read(&lh->lh_count) == 0);
write_unlock(&lh->lh_rwlock);
-
+
+ OBD_VFREE(lh->lh_buckets, sizeof(*lh->lh_buckets) << lh->lh_cur_bits);
OBD_FREE_PTR(lh);
EXIT;
}
if (!(lh->lh_flags & LH_REHASH))
return 0;
- /* XXX: need to handle case with max_theta != 2.0
+ /* XXX: need to handle case with max_theta != 2.0
* and the case with min_theta != 0.5 */
if ((lh->lh_cur_bits < lh->lh_max_bits) &&
(__lustre_hash_theta(lh) > lh->lh_max_theta))
return 0;
}
-
+
/**
* Add item @hnode to lustre hash @lh using @key. The registered
* ops->lh_get function will be called when the item is added.
int bits;
unsigned i;
ENTRY;
-
+
__lustre_hash_key_validate(lh, key, hnode);
read_lock(&lh->lh_rwlock);
read_unlock(&lh->lh_rwlock);
if (bits)
lustre_hash_rehash(lh, bits);
-
+
EXIT;
}
EXPORT_SYMBOL(lustre_hash_add);
lustre_hash_bucket_t *lhb;
unsigned i;
ENTRY;
-
+
__lustre_hash_key_validate(lh, key, hnode);
-
+
read_lock(&lh->lh_rwlock);
i = lh_hash(lh, key, lh->lh_cur_mask);
lhb = &lh->lh_buckets[i];
read_unlock(&lh->lh_rwlock);
if (bits)
lustre_hash_rehash(lh, bits);
-
+
RETURN(ehnode);
}
-
+
/**
* Add item @hnode to lustre hash @lh using @key. The registered
* ops->lh_get function will be called if the item was added.
{
struct hlist_node *ehnode;
ENTRY;
-
+
ehnode = lustre_hash_findadd_unique_hnode(lh, key, hnode);
if (ehnode != hnode) {
lh_put(lh, ehnode);
RETURN(0);
}
EXPORT_SYMBOL(lustre_hash_add_unique);
-
+
/**
* Add item @hnode to lustre hash @lh using @key. If this @key
* already exists in the hash then ops->lh_get will be called on the
struct hlist_node *ehnode;
void *obj;
ENTRY;
-
+
ehnode = lustre_hash_findadd_unique_hnode(lh, key, hnode);
obj = lh_get(lh, ehnode);
lh_put(lh, ehnode);
RETURN(obj);
}
EXPORT_SYMBOL(lustre_hash_findadd_unique);
-
+
/**
* Delete item @hnode from the lustre hash @lh using @key. The @key
* is required to ensure the correct hash bucket is locked since there
unsigned i;
void *obj;
ENTRY;
-
+
__lustre_hash_key_validate(lh, key, hnode);
-
+
read_lock(&lh->lh_rwlock);
i = lh_hash(lh, key, lh->lh_cur_mask);
lhb = &lh->lh_buckets[i];
obj = __lustre_hash_bucket_del(lh, lhb, hnode);
write_unlock(&lhb->lhb_rwlock);
read_unlock(&lh->lh_rwlock);
-
+
RETURN(obj);
}
EXPORT_SYMBOL(lustre_hash_del);
-
+
/**
* Delete item given @key in lustre hash @lh. The first @key found in
* the hash will be removed, if the key exists multiple times in the hash
unsigned i;
void *obj = NULL;
ENTRY;
-
+
read_lock(&lh->lh_rwlock);
i = lh_hash(lh, key, lh->lh_cur_mask);
lhb = &lh->lh_buckets[i];
write_unlock(&lhb->lhb_rwlock);
read_unlock(&lh->lh_rwlock);
-
+
RETURN(obj);
}
EXPORT_SYMBOL(lustre_hash_del_key);
-
+
/**
* Lookup an item using @key in the lustre hash @lh and return it.
* If the @key is found in the hash lh->lh_get() is called and the
unsigned i;
void *obj = NULL;
ENTRY;
-
+
read_lock(&lh->lh_rwlock);
i = lh_hash(lh, key, lh->lh_cur_mask);
lhb = &lh->lh_buckets[i];
hnode = __lustre_hash_bucket_lookup(lh, lhb, key);
if (hnode)
obj = lh_get(lh, hnode);
-
+
read_unlock(&lhb->lhb_rwlock);
read_unlock(&lh->lh_rwlock);
-
+
RETURN(obj);
}
EXPORT_SYMBOL(lustre_hash_lookup);
-
+
/**
* For each item in the lustre hash @lh call the passed callback @func
* and pass to it as an argument each hash item and the private @data.
void *obj;
int i;
ENTRY;
-
+
read_lock(&lh->lh_rwlock);
lh_for_each_bucket(lh, lhb, i) {
read_lock(&lhb->lhb_rwlock);
EXIT;
}
EXPORT_SYMBOL(lustre_hash_for_each);
-
+
/**
* For each item in the lustre hash @lh call the passed callback @func
* and pass to it as an argument each hash item and the private @data.
void *obj;
int i;
ENTRY;
-
+
read_lock(&lh->lh_rwlock);
lh_for_each_bucket(lh, lhb, i) {
read_lock(&lhb->lhb_rwlock);
EXIT;
}
EXPORT_SYMBOL(lustre_hash_for_each_safe);
-
+
/**
* For each hash bucket in the lustre hash @lh call the passed callback
* @func until all the hash buckets are empty. The passed callback @func
void *obj;
int i;
ENTRY;
-
+
restart:
read_lock(&lh->lh_rwlock);
lh_for_each_bucket(lh, lhb, i) {
lustre_hash_bucket_t *lhb;
unsigned i;
ENTRY;
-
+
read_lock(&lh->lh_rwlock);
i = lh_hash(lh, key, lh->lh_cur_mask);
lhb = &lh->lh_buckets[i];
LASSERT(i <= lh->lh_cur_mask);
-
+
read_lock(&lhb->lhb_rwlock);
hlist_for_each(hnode, &(lhb->lhb_head)) {
__lustre_hash_bucket_validate(lh, lhb, hnode);
-
+
if (!lh_compare(lh, key, hnode))
continue;
-
+
func(lh_get(lh, hnode), data);
(void)lh_put(lh, hnode);
}
-
+
read_unlock(&lhb->lhb_rwlock);
read_unlock(&lh->lh_rwlock);
-
+
EXIT;
}
EXPORT_SYMBOL(lustre_hash_for_each_key);
-
+
/**
* Rehash the lustre hash @lh to the given @bits. This can be used
* to grow the hash size when excessive chaining is detected, or to
int mask = (1 << bits) - 1;
void *key;
ENTRY;
-
+
LASSERT(!in_interrupt());
LASSERT(mask > 0);
OBD_VMALLOC(rehash_buckets, sizeof(*rehash_buckets) << bits);
if (!rehash_buckets)
RETURN(-ENOMEM);
-
+
for (i = 0; i <= mask; i++) {
INIT_HLIST_HEAD(&rehash_buckets[i].lhb_head);
rwlock_init(&rehash_buckets[i].lhb_rwlock);
atomic_set(&rehash_buckets[i].lhb_count, 0);
}
-
+
write_lock(&lh->lh_rwlock);
- /*
+ /*
* Early return for multiple concurrent racing callers,
- * ensure we only trigger the rehash if it is still needed.
+ * ensure we only trigger the rehash if it is still needed.
*/
theta = __lustre_hash_theta(lh);
if ((theta >= lh->lh_min_theta) && (theta <= lh->lh_max_theta)) {
write_unlock(&lh->lh_rwlock);
RETURN(-EALREADY);
}
-
+
lh_bits = lh->lh_cur_bits;
lh_buckets = lh->lh_buckets;
lh_mask = (1 << lh_bits) - 1;
-
+
lh->lh_cur_bits = bits;
lh->lh_cur_mask = (1 << bits) - 1;
lh->lh_buckets = rehash_buckets;
key = lh_key(lh, hnode);
LASSERT(key);
- /*
+ /*
* Validate hnode is in the correct bucket.
*/
if (unlikely(lh->lh_flags & LH_DEBUG))
LASSERT(lh_hash(lh, key, lh_mask) == i);
- /*
+ /*
* Delete from old hash bucket.
*/
hlist_del(hnode);
LASSERT(atomic_read(&lh_lhb->lhb_count) > 0);
atomic_dec(&lh_lhb->lhb_count);
- /*
- * Add to rehash bucket, ops->lh_key must be defined.
+ /*
+ * Add to rehash bucket, ops->lh_key must be defined.
*/
rehash_lhb = &rehash_buckets[lh_hash(lh, key, mask)];
hlist_add_head(hnode, &(rehash_lhb->lhb_head));
atomic_inc(&rehash_lhb->lhb_count);
}
-
+
LASSERT(hlist_empty(&(lh_lhb->lhb_head)));
LASSERT(atomic_read(&lh_lhb->lhb_count) == 0);
write_unlock(&lh_lhb->lhb_rwlock);
}
-
+
OBD_VFREE(lh_buckets, sizeof(*lh_buckets) << lh_bits);
write_unlock(&lh->lh_rwlock);
-
+
RETURN(0);
}
EXPORT_SYMBOL(lustre_hash_rehash);
-
+
/**
* Rehash the object referenced by @hnode in the lustre hash @lh. The
* @old_key must be provided to locate the objects previous location
lustre_hash_bucket_t *old_lhb;
lustre_hash_bucket_t *new_lhb;
unsigned i;
- int j;
+ unsigned j;
ENTRY;
-
+
__lustre_hash_key_validate(lh, new_key, hnode);
LASSERT(!hlist_unhashed(hnode));
-
+
read_lock(&lh->lh_rwlock);
-
+
i = lh_hash(lh, old_key, lh->lh_cur_mask);
old_lhb = &lh->lh_buckets[i];
LASSERT(i <= lh->lh_cur_mask);
new_lhb = &lh->lh_buckets[j];
LASSERT(j <= lh->lh_cur_mask);
- write_lock(&old_lhb->lhb_rwlock);
- write_lock(&new_lhb->lhb_rwlock);
+ if (i < j) { /* write_lock ordering */
+ write_lock(&old_lhb->lhb_rwlock);
+ write_lock(&new_lhb->lhb_rwlock);
+ } else if (i > j) {
+ write_lock(&new_lhb->lhb_rwlock);
+ write_lock(&old_lhb->lhb_rwlock);
+ } else { /* do nothing */
+ read_unlock(&lh->lh_rwlock);
+ EXIT;
+ return;
+ }
- /*
+ /*
* Migrate item between hash buckets without calling
- * the lh_get() and lh_put() callback functions.
+ * the lh_get() and lh_put() callback functions.
*/
hlist_del(hnode);
LASSERT(atomic_read(&old_lhb->lhb_count) > 0);
write_unlock(&new_lhb->lhb_rwlock);
write_unlock(&old_lhb->lhb_rwlock);
read_unlock(&lh->lh_rwlock);
-
+
EXIT;
}
EXPORT_SYMBOL(lustre_hash_rehash_key);
-
+
int lustre_hash_debug_header(char *str, int size)
{
return snprintf(str, size,
- "%-36s%6s%6s%6s%6s%6s%6s%6s%7s%6s%s\n",
+ "%-*s%6s%6s%6s%6s%6s%6s%6s%7s%6s%s\n", LUSTRE_MAX_HASH_NAME,
"name", "cur", "min", "max", "theta", "t-min", "t-max",
"flags", "rehash", "count", " distribution");
}
read_lock(&lh->lh_rwlock);
theta = __lustre_hash_theta(lh);
- c += snprintf(str + c, size - c, "%-36s ", lh->lh_name);
+ c += snprintf(str + c, size - c, "%-*s ",
+ LUSTRE_MAX_HASH_NAME, lh->lh_name);
c += snprintf(str + c, size - c, "%5d ", 1 << lh->lh_cur_bits);
c += snprintf(str + c, size - c, "%5d ", 1 << lh->lh_min_bits);
c += snprintf(str + c, size - c, "%5d ", 1 << lh->lh_max_bits);
c += snprintf(str + c, size - c, "%5d ",
atomic_read(&lh->lh_count));
- /*
+ /*
* The distribution is a summary of the chained hash depth in
* each of the lustre hash buckets. Each buckets lhb_count is
* divided by the hash theta value and used to generate a
for (i = 0; i < 8; i++)
c += snprintf(str + c, size - c, "%d%c", dist[i],
(i == 7) ? '\n' : '/');
-
+
read_unlock(&lh->lh_rwlock);
-
+
return c;
}
EXPORT_SYMBOL(lustre_hash_debug_str);