dlm: convert rsb list to rb_tree
Change the linked lists to rb_tree's in the rsb hash table to speed up searches. Slow rsb searches were having a large impact on gfs2 performance due to the large number of dlm locks gfs2 uses. Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: David Teigland <teigland@redhat.com>
This commit is contained in:

committed by
David Teigland

parent
c3b92c8787
commit
9beb3bf5a9
@@ -56,6 +56,7 @@
|
||||
L: receive_xxxx_reply() <- R: send_xxxx_reply()
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/slab.h>
|
||||
#include "dlm_internal.h"
|
||||
#include <linux/dlm_device.h>
|
||||
@@ -380,6 +381,8 @@ static int get_rsb_struct(struct dlm_ls *ls, char *name, int len,
|
||||
|
||||
r = list_first_entry(&ls->ls_new_rsb, struct dlm_rsb, res_hashchain);
|
||||
list_del(&r->res_hashchain);
|
||||
/* Convert the empty list_head to a NULL rb_node for tree usage: */
|
||||
memset(&r->res_hashnode, 0, sizeof(struct rb_node));
|
||||
ls->ls_new_rsb_count--;
|
||||
spin_unlock(&ls->ls_new_rsb_spin);
|
||||
|
||||
@@ -388,7 +391,6 @@ static int get_rsb_struct(struct dlm_ls *ls, char *name, int len,
|
||||
memcpy(r->res_name, name, len);
|
||||
mutex_init(&r->res_mutex);
|
||||
|
||||
INIT_LIST_HEAD(&r->res_hashchain);
|
||||
INIT_LIST_HEAD(&r->res_lookup);
|
||||
INIT_LIST_HEAD(&r->res_grantqueue);
|
||||
INIT_LIST_HEAD(&r->res_convertqueue);
|
||||
@@ -400,14 +402,31 @@ static int get_rsb_struct(struct dlm_ls *ls, char *name, int len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int search_rsb_list(struct list_head *head, char *name, int len,
|
||||
static int rsb_cmp(struct dlm_rsb *r, const char *name, int nlen)
|
||||
{
|
||||
char maxname[DLM_RESNAME_MAXLEN];
|
||||
|
||||
memset(maxname, 0, DLM_RESNAME_MAXLEN);
|
||||
memcpy(maxname, name, nlen);
|
||||
return memcmp(r->res_name, maxname, DLM_RESNAME_MAXLEN);
|
||||
}
|
||||
|
||||
static int search_rsb_tree(struct rb_root *tree, char *name, int len,
|
||||
unsigned int flags, struct dlm_rsb **r_ret)
|
||||
{
|
||||
struct rb_node *node = tree->rb_node;
|
||||
struct dlm_rsb *r;
|
||||
int error = 0;
|
||||
int rc;
|
||||
|
||||
list_for_each_entry(r, head, res_hashchain) {
|
||||
if (len == r->res_length && !memcmp(name, r->res_name, len))
|
||||
while (node) {
|
||||
r = rb_entry(node, struct dlm_rsb, res_hashnode);
|
||||
rc = rsb_cmp(r, name, len);
|
||||
if (rc < 0)
|
||||
node = node->rb_left;
|
||||
else if (rc > 0)
|
||||
node = node->rb_right;
|
||||
else
|
||||
goto found;
|
||||
}
|
||||
*r_ret = NULL;
|
||||
@@ -420,22 +439,54 @@ static int search_rsb_list(struct list_head *head, char *name, int len,
|
||||
return error;
|
||||
}
|
||||
|
||||
static int rsb_insert(struct dlm_rsb *rsb, struct rb_root *tree)
|
||||
{
|
||||
struct rb_node **newn = &tree->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
int rc;
|
||||
|
||||
while (*newn) {
|
||||
struct dlm_rsb *cur = rb_entry(*newn, struct dlm_rsb,
|
||||
res_hashnode);
|
||||
|
||||
parent = *newn;
|
||||
rc = rsb_cmp(cur, rsb->res_name, rsb->res_length);
|
||||
if (rc < 0)
|
||||
newn = &parent->rb_left;
|
||||
else if (rc > 0)
|
||||
newn = &parent->rb_right;
|
||||
else {
|
||||
log_print("rsb_insert match");
|
||||
dlm_dump_rsb(rsb);
|
||||
dlm_dump_rsb(cur);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
rb_link_node(&rsb->res_hashnode, parent, newn);
|
||||
rb_insert_color(&rsb->res_hashnode, tree);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _search_rsb(struct dlm_ls *ls, char *name, int len, int b,
|
||||
unsigned int flags, struct dlm_rsb **r_ret)
|
||||
{
|
||||
struct dlm_rsb *r;
|
||||
int error;
|
||||
|
||||
error = search_rsb_list(&ls->ls_rsbtbl[b].list, name, len, flags, &r);
|
||||
error = search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, flags, &r);
|
||||
if (!error) {
|
||||
kref_get(&r->res_ref);
|
||||
goto out;
|
||||
}
|
||||
error = search_rsb_list(&ls->ls_rsbtbl[b].toss, name, len, flags, &r);
|
||||
error = search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, flags, &r);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
list_move(&r->res_hashchain, &ls->ls_rsbtbl[b].list);
|
||||
rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
||||
error = rsb_insert(r, &ls->ls_rsbtbl[b].keep);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (dlm_no_directory(ls))
|
||||
goto out;
|
||||
@@ -527,8 +578,7 @@ static int find_rsb(struct dlm_ls *ls, char *name, int namelen,
|
||||
nodeid = 0;
|
||||
r->res_nodeid = nodeid;
|
||||
}
|
||||
list_add(&r->res_hashchain, &ls->ls_rsbtbl[bucket].list);
|
||||
error = 0;
|
||||
error = rsb_insert(r, &ls->ls_rsbtbl[bucket].keep);
|
||||
out_unlock:
|
||||
spin_unlock(&ls->ls_rsbtbl[bucket].lock);
|
||||
out:
|
||||
@@ -556,7 +606,8 @@ static void toss_rsb(struct kref *kref)
|
||||
|
||||
DLM_ASSERT(list_empty(&r->res_root_list), dlm_print_rsb(r););
|
||||
kref_init(&r->res_ref);
|
||||
list_move(&r->res_hashchain, &ls->ls_rsbtbl[r->res_bucket].toss);
|
||||
rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[r->res_bucket].keep);
|
||||
rsb_insert(r, &ls->ls_rsbtbl[r->res_bucket].toss);
|
||||
r->res_toss_time = jiffies;
|
||||
if (r->res_lvbptr) {
|
||||
dlm_free_lvb(r->res_lvbptr);
|
||||
@@ -1082,19 +1133,19 @@ static void dir_remove(struct dlm_rsb *r)
|
||||
r->res_name, r->res_length);
|
||||
}
|
||||
|
||||
/* FIXME: shouldn't this be able to exit as soon as one non-due rsb is
|
||||
found since they are in order of newest to oldest? */
|
||||
/* FIXME: make this more efficient */
|
||||
|
||||
static int shrink_bucket(struct dlm_ls *ls, int b)
|
||||
{
|
||||
struct rb_node *n;
|
||||
struct dlm_rsb *r;
|
||||
int count = 0, found;
|
||||
|
||||
for (;;) {
|
||||
found = 0;
|
||||
spin_lock(&ls->ls_rsbtbl[b].lock);
|
||||
list_for_each_entry_reverse(r, &ls->ls_rsbtbl[b].toss,
|
||||
res_hashchain) {
|
||||
for (n = rb_first(&ls->ls_rsbtbl[b].toss); n; n = rb_next(n)) {
|
||||
r = rb_entry(n, struct dlm_rsb, res_hashnode);
|
||||
if (!time_after_eq(jiffies, r->res_toss_time +
|
||||
dlm_config.ci_toss_secs * HZ))
|
||||
continue;
|
||||
@@ -1108,7 +1159,7 @@ static int shrink_bucket(struct dlm_ls *ls, int b)
|
||||
}
|
||||
|
||||
if (kref_put(&r->res_ref, kill_rsb)) {
|
||||
list_del(&r->res_hashchain);
|
||||
rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
||||
spin_unlock(&ls->ls_rsbtbl[b].lock);
|
||||
|
||||
if (is_master(r))
|
||||
@@ -4441,10 +4492,12 @@ int dlm_purge_locks(struct dlm_ls *ls)
|
||||
|
||||
static struct dlm_rsb *find_purged_rsb(struct dlm_ls *ls, int bucket)
|
||||
{
|
||||
struct rb_node *n;
|
||||
struct dlm_rsb *r, *r_ret = NULL;
|
||||
|
||||
spin_lock(&ls->ls_rsbtbl[bucket].lock);
|
||||
list_for_each_entry(r, &ls->ls_rsbtbl[bucket].list, res_hashchain) {
|
||||
for (n = rb_first(&ls->ls_rsbtbl[bucket].keep); n; n = rb_next(n)) {
|
||||
r = rb_entry(n, struct dlm_rsb, res_hashnode);
|
||||
if (!rsb_flag(r, RSB_LOCKS_PURGED))
|
||||
continue;
|
||||
hold_rsb(r);
|
||||
|
Reference in New Issue
Block a user