[DLM] The core of the DLM for GFS2/CLVM
This is the core of the distributed lock manager which is required to use GFS2 as a cluster filesystem. It is also used by CLVM and can be used as a standalone lock manager independantly of either of these two projects. It implements VAX-style locking modes. Signed-off-by: David Teigland <teigland@redhat.com> Signed-off-by: Steve Whitehouse <swhiteho@redhat.com>
This commit is contained in:
parent
e473142070
commit
e7fd41792f
39 changed files with 12178 additions and 0 deletions
|
|
@ -1831,6 +1831,7 @@ source "fs/partitions/Kconfig"
|
|||
endmenu
|
||||
|
||||
source "fs/nls/Kconfig"
|
||||
source "fs/dlm/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ obj-$(CONFIG_SYSFS) += sysfs/
|
|||
obj-y += devpts/
|
||||
|
||||
obj-$(CONFIG_PROFILING) += dcookies.o
|
||||
obj-$(CONFIG_DLM) += dlm/
|
||||
|
||||
# Do not add any filesystems before this line
|
||||
obj-$(CONFIG_REISERFS_FS) += reiserfs/
|
||||
|
|
|
|||
30
fs/dlm/Kconfig
Normal file
30
fs/dlm/Kconfig
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
menu "Distributed Lock Manager"
|
||||
depends on INET && EXPERIMENTAL
|
||||
|
||||
config DLM
|
||||
tristate "Distributed Lock Manager (DLM)"
|
||||
depends on SYSFS
|
||||
depends on IPV6 || IPV6=n
|
||||
select IP_SCTP
|
||||
select CONFIGFS_FS
|
||||
help
|
||||
A general purpose distributed lock manager for kernel or userspace
|
||||
applications.
|
||||
|
||||
config DLM_DEVICE
|
||||
tristate "DLM device for userspace access"
|
||||
depends on DLM
|
||||
help
|
||||
This module creates a misc device through which the dlm lockspace
|
||||
and locking functions become available to userspace applications
|
||||
(usually through the libdlm library).
|
||||
|
||||
config DLM_DEBUG
|
||||
bool "DLM debugging"
|
||||
depends on DLM
|
||||
help
|
||||
Under the debugfs mount point, the name of each lockspace will
|
||||
appear as a file in the "dlm" directory. The output is the
|
||||
list of resource and locks the local node knows about.
|
||||
|
||||
endmenu
|
||||
21
fs/dlm/Makefile
Normal file
21
fs/dlm/Makefile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
obj-$(CONFIG_DLM) += dlm.o
|
||||
obj-$(CONFIG_DLM_DEVICE) += dlm_device.o
|
||||
|
||||
dlm-y := ast.o \
|
||||
config.o \
|
||||
dir.o \
|
||||
lock.o \
|
||||
lockspace.o \
|
||||
lowcomms.o \
|
||||
main.o \
|
||||
member.o \
|
||||
memory.o \
|
||||
midcomms.o \
|
||||
rcom.o \
|
||||
recover.o \
|
||||
recoverd.o \
|
||||
requestqueue.o \
|
||||
util.o
|
||||
dlm-$(CONFIG_DLM_DEBUG) += debug_fs.o
|
||||
|
||||
dlm_device-y := device.o
|
||||
167
fs/dlm/ast.c
Normal file
167
fs/dlm/ast.c
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#include "dlm_internal.h"
|
||||
#include "lock.h"
|
||||
#include "ast.h"
|
||||
|
||||
#define WAKE_ASTS 0
|
||||
|
||||
static struct list_head ast_queue;
|
||||
static spinlock_t ast_queue_lock;
|
||||
static struct task_struct * astd_task;
|
||||
static unsigned long astd_wakeflags;
|
||||
static struct semaphore astd_running;
|
||||
|
||||
|
||||
void dlm_del_ast(struct dlm_lkb *lkb)
|
||||
{
|
||||
spin_lock(&ast_queue_lock);
|
||||
if (lkb->lkb_ast_type & (AST_COMP | AST_BAST))
|
||||
list_del(&lkb->lkb_astqueue);
|
||||
spin_unlock(&ast_queue_lock);
|
||||
}
|
||||
|
||||
void dlm_add_ast(struct dlm_lkb *lkb, int type)
|
||||
{
|
||||
spin_lock(&ast_queue_lock);
|
||||
if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
|
||||
kref_get(&lkb->lkb_ref);
|
||||
list_add_tail(&lkb->lkb_astqueue, &ast_queue);
|
||||
}
|
||||
lkb->lkb_ast_type |= type;
|
||||
spin_unlock(&ast_queue_lock);
|
||||
|
||||
set_bit(WAKE_ASTS, &astd_wakeflags);
|
||||
wake_up_process(astd_task);
|
||||
}
|
||||
|
||||
static void process_asts(void)
|
||||
{
|
||||
struct dlm_ls *ls = NULL;
|
||||
struct dlm_rsb *r = NULL;
|
||||
struct dlm_lkb *lkb;
|
||||
void (*cast) (long param);
|
||||
void (*bast) (long param, int mode);
|
||||
int type = 0, found, bmode;
|
||||
|
||||
for (;;) {
|
||||
found = FALSE;
|
||||
spin_lock(&ast_queue_lock);
|
||||
list_for_each_entry(lkb, &ast_queue, lkb_astqueue) {
|
||||
r = lkb->lkb_resource;
|
||||
ls = r->res_ls;
|
||||
|
||||
if (dlm_locking_stopped(ls))
|
||||
continue;
|
||||
|
||||
list_del(&lkb->lkb_astqueue);
|
||||
type = lkb->lkb_ast_type;
|
||||
lkb->lkb_ast_type = 0;
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
spin_unlock(&ast_queue_lock);
|
||||
|
||||
if (!found)
|
||||
break;
|
||||
|
||||
cast = lkb->lkb_astaddr;
|
||||
bast = lkb->lkb_bastaddr;
|
||||
bmode = lkb->lkb_bastmode;
|
||||
|
||||
if ((type & AST_COMP) && cast)
|
||||
cast(lkb->lkb_astparam);
|
||||
|
||||
/* FIXME: Is it safe to look at lkb_grmode here
|
||||
without doing a lock_rsb() ?
|
||||
Look at other checks in v1 to avoid basts. */
|
||||
|
||||
if ((type & AST_BAST) && bast)
|
||||
if (!dlm_modes_compat(lkb->lkb_grmode, bmode))
|
||||
bast(lkb->lkb_astparam, bmode);
|
||||
|
||||
/* this removes the reference added by dlm_add_ast
|
||||
and may result in the lkb being freed */
|
||||
dlm_put_lkb(lkb);
|
||||
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
static inline int no_asts(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock(&ast_queue_lock);
|
||||
ret = list_empty(&ast_queue);
|
||||
spin_unlock(&ast_queue_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dlm_astd(void *data)
|
||||
{
|
||||
while (!kthread_should_stop()) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (!test_bit(WAKE_ASTS, &astd_wakeflags))
|
||||
schedule();
|
||||
set_current_state(TASK_RUNNING);
|
||||
|
||||
down(&astd_running);
|
||||
if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
|
||||
process_asts();
|
||||
up(&astd_running);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dlm_astd_wake(void)
|
||||
{
|
||||
if (!no_asts()) {
|
||||
set_bit(WAKE_ASTS, &astd_wakeflags);
|
||||
wake_up_process(astd_task);
|
||||
}
|
||||
}
|
||||
|
||||
int dlm_astd_start(void)
|
||||
{
|
||||
struct task_struct *p;
|
||||
int error = 0;
|
||||
|
||||
INIT_LIST_HEAD(&ast_queue);
|
||||
spin_lock_init(&ast_queue_lock);
|
||||
init_MUTEX(&astd_running);
|
||||
|
||||
p = kthread_run(dlm_astd, NULL, "dlm_astd");
|
||||
if (IS_ERR(p))
|
||||
error = PTR_ERR(p);
|
||||
else
|
||||
astd_task = p;
|
||||
return error;
|
||||
}
|
||||
|
||||
void dlm_astd_stop(void)
|
||||
{
|
||||
kthread_stop(astd_task);
|
||||
}
|
||||
|
||||
void dlm_astd_suspend(void)
|
||||
{
|
||||
down(&astd_running);
|
||||
}
|
||||
|
||||
void dlm_astd_resume(void)
|
||||
{
|
||||
up(&astd_running);
|
||||
}
|
||||
|
||||
26
fs/dlm/ast.h
Normal file
26
fs/dlm/ast.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) 2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __ASTD_DOT_H__
|
||||
#define __ASTD_DOT_H__
|
||||
|
||||
void dlm_add_ast(struct dlm_lkb *lkb, int type);
|
||||
void dlm_del_ast(struct dlm_lkb *lkb);
|
||||
|
||||
void dlm_astd_wake(void);
|
||||
int dlm_astd_start(void);
|
||||
void dlm_astd_stop(void);
|
||||
void dlm_astd_suspend(void);
|
||||
void dlm_astd_resume(void);
|
||||
|
||||
#endif
|
||||
|
||||
787
fs/dlm/config.c
Normal file
787
fs/dlm/config.c
Normal file
|
|
@ -0,0 +1,787 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/configfs.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/*
|
||||
* /config/dlm/<cluster>/spaces/<space>/nodes/<node>/nodeid
|
||||
* /config/dlm/<cluster>/spaces/<space>/nodes/<node>/weight
|
||||
* /config/dlm/<cluster>/comms/<comm>/nodeid
|
||||
* /config/dlm/<cluster>/comms/<comm>/local
|
||||
* /config/dlm/<cluster>/comms/<comm>/addr
|
||||
* The <cluster> level is useless, but I haven't figured out how to avoid it.
|
||||
*/
|
||||
|
||||
static struct config_group *space_list;
|
||||
static struct config_group *comm_list;
|
||||
static struct comm *local_comm;
|
||||
|
||||
struct clusters;
|
||||
struct cluster;
|
||||
struct spaces;
|
||||
struct space;
|
||||
struct comms;
|
||||
struct comm;
|
||||
struct nodes;
|
||||
struct node;
|
||||
|
||||
static struct config_group *make_cluster(struct config_group *, const char *);
|
||||
static void drop_cluster(struct config_group *, struct config_item *);
|
||||
static void release_cluster(struct config_item *);
|
||||
static struct config_group *make_space(struct config_group *, const char *);
|
||||
static void drop_space(struct config_group *, struct config_item *);
|
||||
static void release_space(struct config_item *);
|
||||
static struct config_item *make_comm(struct config_group *, const char *);
|
||||
static void drop_comm(struct config_group *, struct config_item *);
|
||||
static void release_comm(struct config_item *);
|
||||
static struct config_item *make_node(struct config_group *, const char *);
|
||||
static void drop_node(struct config_group *, struct config_item *);
|
||||
static void release_node(struct config_item *);
|
||||
|
||||
static ssize_t show_comm(struct config_item *i, struct configfs_attribute *a,
|
||||
char *buf);
|
||||
static ssize_t store_comm(struct config_item *i, struct configfs_attribute *a,
|
||||
const char *buf, size_t len);
|
||||
static ssize_t show_node(struct config_item *i, struct configfs_attribute *a,
|
||||
char *buf);
|
||||
static ssize_t store_node(struct config_item *i, struct configfs_attribute *a,
|
||||
const char *buf, size_t len);
|
||||
|
||||
static ssize_t comm_nodeid_read(struct comm *cm, char *buf);
|
||||
static ssize_t comm_nodeid_write(struct comm *cm, const char *buf, size_t len);
|
||||
static ssize_t comm_local_read(struct comm *cm, char *buf);
|
||||
static ssize_t comm_local_write(struct comm *cm, const char *buf, size_t len);
|
||||
static ssize_t comm_addr_write(struct comm *cm, const char *buf, size_t len);
|
||||
static ssize_t node_nodeid_read(struct node *nd, char *buf);
|
||||
static ssize_t node_nodeid_write(struct node *nd, const char *buf, size_t len);
|
||||
static ssize_t node_weight_read(struct node *nd, char *buf);
|
||||
static ssize_t node_weight_write(struct node *nd, const char *buf, size_t len);
|
||||
|
||||
enum {
|
||||
COMM_ATTR_NODEID = 0,
|
||||
COMM_ATTR_LOCAL,
|
||||
COMM_ATTR_ADDR,
|
||||
};
|
||||
|
||||
struct comm_attribute {
|
||||
struct configfs_attribute attr;
|
||||
ssize_t (*show)(struct comm *, char *);
|
||||
ssize_t (*store)(struct comm *, const char *, size_t);
|
||||
};
|
||||
|
||||
static struct comm_attribute comm_attr_nodeid = {
|
||||
.attr = { .ca_owner = THIS_MODULE,
|
||||
.ca_name = "nodeid",
|
||||
.ca_mode = S_IRUGO | S_IWUSR },
|
||||
.show = comm_nodeid_read,
|
||||
.store = comm_nodeid_write,
|
||||
};
|
||||
|
||||
static struct comm_attribute comm_attr_local = {
|
||||
.attr = { .ca_owner = THIS_MODULE,
|
||||
.ca_name = "local",
|
||||
.ca_mode = S_IRUGO | S_IWUSR },
|
||||
.show = comm_local_read,
|
||||
.store = comm_local_write,
|
||||
};
|
||||
|
||||
static struct comm_attribute comm_attr_addr = {
|
||||
.attr = { .ca_owner = THIS_MODULE,
|
||||
.ca_name = "addr",
|
||||
.ca_mode = S_IRUGO | S_IWUSR },
|
||||
.store = comm_addr_write,
|
||||
};
|
||||
|
||||
static struct configfs_attribute *comm_attrs[] = {
|
||||
[COMM_ATTR_NODEID] = &comm_attr_nodeid.attr,
|
||||
[COMM_ATTR_LOCAL] = &comm_attr_local.attr,
|
||||
[COMM_ATTR_ADDR] = &comm_attr_addr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
enum {
|
||||
NODE_ATTR_NODEID = 0,
|
||||
NODE_ATTR_WEIGHT,
|
||||
};
|
||||
|
||||
struct node_attribute {
|
||||
struct configfs_attribute attr;
|
||||
ssize_t (*show)(struct node *, char *);
|
||||
ssize_t (*store)(struct node *, const char *, size_t);
|
||||
};
|
||||
|
||||
static struct node_attribute node_attr_nodeid = {
|
||||
.attr = { .ca_owner = THIS_MODULE,
|
||||
.ca_name = "nodeid",
|
||||
.ca_mode = S_IRUGO | S_IWUSR },
|
||||
.show = node_nodeid_read,
|
||||
.store = node_nodeid_write,
|
||||
};
|
||||
|
||||
static struct node_attribute node_attr_weight = {
|
||||
.attr = { .ca_owner = THIS_MODULE,
|
||||
.ca_name = "weight",
|
||||
.ca_mode = S_IRUGO | S_IWUSR },
|
||||
.show = node_weight_read,
|
||||
.store = node_weight_write,
|
||||
};
|
||||
|
||||
static struct configfs_attribute *node_attrs[] = {
|
||||
[NODE_ATTR_NODEID] = &node_attr_nodeid.attr,
|
||||
[NODE_ATTR_WEIGHT] = &node_attr_weight.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct clusters {
|
||||
struct configfs_subsystem subsys;
|
||||
};
|
||||
|
||||
struct cluster {
|
||||
struct config_group group;
|
||||
};
|
||||
|
||||
struct spaces {
|
||||
struct config_group ss_group;
|
||||
};
|
||||
|
||||
struct space {
|
||||
struct config_group group;
|
||||
struct list_head members;
|
||||
struct semaphore members_lock;
|
||||
int members_count;
|
||||
};
|
||||
|
||||
struct comms {
|
||||
struct config_group cs_group;
|
||||
};
|
||||
|
||||
struct comm {
|
||||
struct config_item item;
|
||||
int nodeid;
|
||||
int local;
|
||||
int addr_count;
|
||||
struct sockaddr_storage *addr[DLM_MAX_ADDR_COUNT];
|
||||
};
|
||||
|
||||
struct nodes {
|
||||
struct config_group ns_group;
|
||||
};
|
||||
|
||||
struct node {
|
||||
struct config_item item;
|
||||
struct list_head list; /* space->members */
|
||||
int nodeid;
|
||||
int weight;
|
||||
};
|
||||
|
||||
static struct configfs_group_operations clusters_ops = {
|
||||
.make_group = make_cluster,
|
||||
.drop_item = drop_cluster,
|
||||
};
|
||||
|
||||
static struct configfs_item_operations cluster_ops = {
|
||||
.release = release_cluster,
|
||||
};
|
||||
|
||||
static struct configfs_group_operations spaces_ops = {
|
||||
.make_group = make_space,
|
||||
.drop_item = drop_space,
|
||||
};
|
||||
|
||||
static struct configfs_item_operations space_ops = {
|
||||
.release = release_space,
|
||||
};
|
||||
|
||||
static struct configfs_group_operations comms_ops = {
|
||||
.make_item = make_comm,
|
||||
.drop_item = drop_comm,
|
||||
};
|
||||
|
||||
static struct configfs_item_operations comm_ops = {
|
||||
.release = release_comm,
|
||||
.show_attribute = show_comm,
|
||||
.store_attribute = store_comm,
|
||||
};
|
||||
|
||||
static struct configfs_group_operations nodes_ops = {
|
||||
.make_item = make_node,
|
||||
.drop_item = drop_node,
|
||||
};
|
||||
|
||||
static struct configfs_item_operations node_ops = {
|
||||
.release = release_node,
|
||||
.show_attribute = show_node,
|
||||
.store_attribute = store_node,
|
||||
};
|
||||
|
||||
static struct config_item_type clusters_type = {
|
||||
.ct_group_ops = &clusters_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type cluster_type = {
|
||||
.ct_item_ops = &cluster_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type spaces_type = {
|
||||
.ct_group_ops = &spaces_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type space_type = {
|
||||
.ct_item_ops = &space_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type comms_type = {
|
||||
.ct_group_ops = &comms_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type comm_type = {
|
||||
.ct_item_ops = &comm_ops,
|
||||
.ct_attrs = comm_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type nodes_type = {
|
||||
.ct_group_ops = &nodes_ops,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct config_item_type node_type = {
|
||||
.ct_item_ops = &node_ops,
|
||||
.ct_attrs = node_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct cluster *to_cluster(struct config_item *i)
|
||||
{
|
||||
return i ? container_of(to_config_group(i), struct cluster, group):NULL;
|
||||
}
|
||||
|
||||
static struct space *to_space(struct config_item *i)
|
||||
{
|
||||
return i ? container_of(to_config_group(i), struct space, group) : NULL;
|
||||
}
|
||||
|
||||
static struct comm *to_comm(struct config_item *i)
|
||||
{
|
||||
return i ? container_of(i, struct comm, item) : NULL;
|
||||
}
|
||||
|
||||
static struct node *to_node(struct config_item *i)
|
||||
{
|
||||
return i ? container_of(i, struct node, item) : NULL;
|
||||
}
|
||||
|
||||
static struct config_group *make_cluster(struct config_group *g,
|
||||
const char *name)
|
||||
{
|
||||
struct cluster *cl = NULL;
|
||||
struct spaces *sps = NULL;
|
||||
struct comms *cms = NULL;
|
||||
void *gps = NULL;
|
||||
|
||||
cl = kzalloc(sizeof(struct cluster), GFP_KERNEL);
|
||||
gps = kcalloc(3, sizeof(struct config_group *), GFP_KERNEL);
|
||||
sps = kzalloc(sizeof(struct spaces), GFP_KERNEL);
|
||||
cms = kzalloc(sizeof(struct comms), GFP_KERNEL);
|
||||
|
||||
if (!cl || !gps || !sps || !cms)
|
||||
goto fail;
|
||||
|
||||
config_group_init_type_name(&cl->group, name, &cluster_type);
|
||||
config_group_init_type_name(&sps->ss_group, "spaces", &spaces_type);
|
||||
config_group_init_type_name(&cms->cs_group, "comms", &comms_type);
|
||||
|
||||
cl->group.default_groups = gps;
|
||||
cl->group.default_groups[0] = &sps->ss_group;
|
||||
cl->group.default_groups[1] = &cms->cs_group;
|
||||
cl->group.default_groups[2] = NULL;
|
||||
|
||||
space_list = &sps->ss_group;
|
||||
comm_list = &cms->cs_group;
|
||||
return &cl->group;
|
||||
|
||||
fail:
|
||||
kfree(cl);
|
||||
kfree(gps);
|
||||
kfree(sps);
|
||||
kfree(cms);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drop_cluster(struct config_group *g, struct config_item *i)
|
||||
{
|
||||
struct cluster *cl = to_cluster(i);
|
||||
struct config_item *tmp;
|
||||
int j;
|
||||
|
||||
for (j = 0; cl->group.default_groups[j]; j++) {
|
||||
tmp = &cl->group.default_groups[j]->cg_item;
|
||||
cl->group.default_groups[j] = NULL;
|
||||
config_item_put(tmp);
|
||||
}
|
||||
|
||||
space_list = NULL;
|
||||
comm_list = NULL;
|
||||
|
||||
config_item_put(i);
|
||||
}
|
||||
|
||||
static void release_cluster(struct config_item *i)
|
||||
{
|
||||
struct cluster *cl = to_cluster(i);
|
||||
kfree(cl->group.default_groups);
|
||||
kfree(cl);
|
||||
}
|
||||
|
||||
static struct config_group *make_space(struct config_group *g, const char *name)
|
||||
{
|
||||
struct space *sp = NULL;
|
||||
struct nodes *nds = NULL;
|
||||
void *gps = NULL;
|
||||
|
||||
sp = kzalloc(sizeof(struct space), GFP_KERNEL);
|
||||
gps = kcalloc(2, sizeof(struct config_group *), GFP_KERNEL);
|
||||
nds = kzalloc(sizeof(struct nodes), GFP_KERNEL);
|
||||
|
||||
if (!sp || !gps || !nds)
|
||||
goto fail;
|
||||
|
||||
config_group_init_type_name(&sp->group, name, &space_type);
|
||||
config_group_init_type_name(&nds->ns_group, "nodes", &nodes_type);
|
||||
|
||||
sp->group.default_groups = gps;
|
||||
sp->group.default_groups[0] = &nds->ns_group;
|
||||
sp->group.default_groups[1] = NULL;
|
||||
|
||||
INIT_LIST_HEAD(&sp->members);
|
||||
init_MUTEX(&sp->members_lock);
|
||||
sp->members_count = 0;
|
||||
return &sp->group;
|
||||
|
||||
fail:
|
||||
kfree(sp);
|
||||
kfree(gps);
|
||||
kfree(nds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drop_space(struct config_group *g, struct config_item *i)
|
||||
{
|
||||
struct space *sp = to_space(i);
|
||||
struct config_item *tmp;
|
||||
int j;
|
||||
|
||||
/* assert list_empty(&sp->members) */
|
||||
|
||||
for (j = 0; sp->group.default_groups[j]; j++) {
|
||||
tmp = &sp->group.default_groups[j]->cg_item;
|
||||
sp->group.default_groups[j] = NULL;
|
||||
config_item_put(tmp);
|
||||
}
|
||||
|
||||
config_item_put(i);
|
||||
}
|
||||
|
||||
static void release_space(struct config_item *i)
|
||||
{
|
||||
struct space *sp = to_space(i);
|
||||
kfree(sp->group.default_groups);
|
||||
kfree(sp);
|
||||
}
|
||||
|
||||
static struct config_item *make_comm(struct config_group *g, const char *name)
|
||||
{
|
||||
struct comm *cm;
|
||||
|
||||
cm = kzalloc(sizeof(struct comm), GFP_KERNEL);
|
||||
if (!cm)
|
||||
return NULL;
|
||||
|
||||
config_item_init_type_name(&cm->item, name, &comm_type);
|
||||
cm->nodeid = -1;
|
||||
cm->local = 0;
|
||||
cm->addr_count = 0;
|
||||
return &cm->item;
|
||||
}
|
||||
|
||||
static void drop_comm(struct config_group *g, struct config_item *i)
|
||||
{
|
||||
struct comm *cm = to_comm(i);
|
||||
if (local_comm == cm)
|
||||
local_comm = NULL;
|
||||
while (cm->addr_count--)
|
||||
kfree(cm->addr[cm->addr_count]);
|
||||
config_item_put(i);
|
||||
}
|
||||
|
||||
static void release_comm(struct config_item *i)
|
||||
{
|
||||
struct comm *cm = to_comm(i);
|
||||
kfree(cm);
|
||||
}
|
||||
|
||||
static struct config_item *make_node(struct config_group *g, const char *name)
|
||||
{
|
||||
struct space *sp = to_space(g->cg_item.ci_parent);
|
||||
struct node *nd;
|
||||
|
||||
nd = kzalloc(sizeof(struct node), GFP_KERNEL);
|
||||
if (!nd)
|
||||
return NULL;
|
||||
|
||||
config_item_init_type_name(&nd->item, name, &node_type);
|
||||
nd->nodeid = -1;
|
||||
nd->weight = 1; /* default weight of 1 if none is set */
|
||||
|
||||
down(&sp->members_lock);
|
||||
list_add(&nd->list, &sp->members);
|
||||
sp->members_count++;
|
||||
up(&sp->members_lock);
|
||||
|
||||
return &nd->item;
|
||||
}
|
||||
|
||||
static void drop_node(struct config_group *g, struct config_item *i)
|
||||
{
|
||||
struct space *sp = to_space(g->cg_item.ci_parent);
|
||||
struct node *nd = to_node(i);
|
||||
|
||||
down(&sp->members_lock);
|
||||
list_del(&nd->list);
|
||||
sp->members_count--;
|
||||
up(&sp->members_lock);
|
||||
|
||||
config_item_put(i);
|
||||
}
|
||||
|
||||
static void release_node(struct config_item *i)
|
||||
{
|
||||
struct node *nd = to_node(i);
|
||||
kfree(nd);
|
||||
}
|
||||
|
||||
static struct clusters clusters_root = {
|
||||
.subsys = {
|
||||
.su_group = {
|
||||
.cg_item = {
|
||||
.ci_namebuf = "dlm",
|
||||
.ci_type = &clusters_type,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
int dlm_config_init(void)
|
||||
{
|
||||
config_group_init(&clusters_root.subsys.su_group);
|
||||
init_MUTEX(&clusters_root.subsys.su_sem);
|
||||
return configfs_register_subsystem(&clusters_root.subsys);
|
||||
}
|
||||
|
||||
void dlm_config_exit(void)
|
||||
{
|
||||
configfs_unregister_subsystem(&clusters_root.subsys);
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for user space to read/write attributes
|
||||
*/
|
||||
|
||||
static ssize_t show_comm(struct config_item *i, struct configfs_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
struct comm *cm = to_comm(i);
|
||||
struct comm_attribute *cma =
|
||||
container_of(a, struct comm_attribute, attr);
|
||||
return cma->show ? cma->show(cm, buf) : 0;
|
||||
}
|
||||
|
||||
static ssize_t store_comm(struct config_item *i, struct configfs_attribute *a,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct comm *cm = to_comm(i);
|
||||
struct comm_attribute *cma =
|
||||
container_of(a, struct comm_attribute, attr);
|
||||
return cma->store ? cma->store(cm, buf, len) : -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t comm_nodeid_read(struct comm *cm, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", cm->nodeid);
|
||||
}
|
||||
|
||||
static ssize_t comm_nodeid_write(struct comm *cm, const char *buf, size_t len)
|
||||
{
|
||||
cm->nodeid = simple_strtol(buf, NULL, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t comm_local_read(struct comm *cm, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", cm->local);
|
||||
}
|
||||
|
||||
static ssize_t comm_local_write(struct comm *cm, const char *buf, size_t len)
|
||||
{
|
||||
cm->local= simple_strtol(buf, NULL, 0);
|
||||
if (cm->local && !local_comm)
|
||||
local_comm = cm;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t comm_addr_write(struct comm *cm, const char *buf, size_t len)
|
||||
{
|
||||
struct sockaddr_storage *addr;
|
||||
|
||||
if (len != sizeof(struct sockaddr_storage))
|
||||
return -EINVAL;
|
||||
|
||||
if (cm->addr_count >= DLM_MAX_ADDR_COUNT)
|
||||
return -ENOSPC;
|
||||
|
||||
addr = kzalloc(sizeof(*addr), GFP_KERNEL);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(addr, buf, len);
|
||||
cm->addr[cm->addr_count++] = addr;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t show_node(struct config_item *i, struct configfs_attribute *a,
|
||||
char *buf)
|
||||
{
|
||||
struct node *nd = to_node(i);
|
||||
struct node_attribute *nda =
|
||||
container_of(a, struct node_attribute, attr);
|
||||
return nda->show ? nda->show(nd, buf) : 0;
|
||||
}
|
||||
|
||||
static ssize_t store_node(struct config_item *i, struct configfs_attribute *a,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct node *nd = to_node(i);
|
||||
struct node_attribute *nda =
|
||||
container_of(a, struct node_attribute, attr);
|
||||
return nda->store ? nda->store(nd, buf, len) : -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t node_nodeid_read(struct node *nd, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", nd->nodeid);
|
||||
}
|
||||
|
||||
static ssize_t node_nodeid_write(struct node *nd, const char *buf, size_t len)
|
||||
{
|
||||
nd->nodeid = simple_strtol(buf, NULL, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t node_weight_read(struct node *nd, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", nd->weight);
|
||||
}
|
||||
|
||||
static ssize_t node_weight_write(struct node *nd, const char *buf, size_t len)
|
||||
{
|
||||
nd->weight = simple_strtol(buf, NULL, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions for the dlm to get the info that's been configured
|
||||
*/
|
||||
|
||||
static struct space *get_space(char *name)
|
||||
{
|
||||
if (!space_list)
|
||||
return NULL;
|
||||
return to_space(config_group_find_obj(space_list, name));
|
||||
}
|
||||
|
||||
static void put_space(struct space *sp)
|
||||
{
|
||||
config_item_put(&sp->group.cg_item);
|
||||
}
|
||||
|
||||
static struct comm *get_comm(int nodeid, struct sockaddr_storage *addr)
|
||||
{
|
||||
struct config_item *i;
|
||||
struct comm *cm = NULL;
|
||||
int found = 0;
|
||||
|
||||
if (!comm_list)
|
||||
return NULL;
|
||||
|
||||
down(&clusters_root.subsys.su_sem);
|
||||
|
||||
list_for_each_entry(i, &comm_list->cg_children, ci_entry) {
|
||||
cm = to_comm(i);
|
||||
|
||||
if (nodeid) {
|
||||
if (cm->nodeid != nodeid)
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
} else {
|
||||
if (!cm->addr_count ||
|
||||
memcmp(cm->addr[0], addr, sizeof(*addr)))
|
||||
continue;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
up(&clusters_root.subsys.su_sem);
|
||||
|
||||
if (found)
|
||||
config_item_get(i);
|
||||
else
|
||||
cm = NULL;
|
||||
return cm;
|
||||
}
|
||||
|
||||
static void put_comm(struct comm *cm)
|
||||
{
|
||||
config_item_put(&cm->item);
|
||||
}
|
||||
|
||||
/* caller must free mem */
|
||||
int dlm_nodeid_list(char *lsname, int **ids_out)
|
||||
{
|
||||
struct space *sp;
|
||||
struct node *nd;
|
||||
int i = 0, rv = 0;
|
||||
int *ids;
|
||||
|
||||
sp = get_space(lsname);
|
||||
if (!sp)
|
||||
return -EEXIST;
|
||||
|
||||
down(&sp->members_lock);
|
||||
if (!sp->members_count) {
|
||||
rv = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ids = kcalloc(sp->members_count, sizeof(int), GFP_KERNEL);
|
||||
if (!ids) {
|
||||
rv = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = sp->members_count;
|
||||
list_for_each_entry(nd, &sp->members, list)
|
||||
ids[i++] = nd->nodeid;
|
||||
|
||||
if (rv != i)
|
||||
printk("bad nodeid count %d %d\n", rv, i);
|
||||
|
||||
*ids_out = ids;
|
||||
out:
|
||||
up(&sp->members_lock);
|
||||
put_space(sp);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int dlm_node_weight(char *lsname, int nodeid)
|
||||
{
|
||||
struct space *sp;
|
||||
struct node *nd;
|
||||
int w = -EEXIST;
|
||||
|
||||
sp = get_space(lsname);
|
||||
if (!sp)
|
||||
goto out;
|
||||
|
||||
down(&sp->members_lock);
|
||||
list_for_each_entry(nd, &sp->members, list) {
|
||||
if (nd->nodeid != nodeid)
|
||||
continue;
|
||||
w = nd->weight;
|
||||
break;
|
||||
}
|
||||
up(&sp->members_lock);
|
||||
put_space(sp);
|
||||
out:
|
||||
return w;
|
||||
}
|
||||
|
||||
int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr)
|
||||
{
|
||||
struct comm *cm = get_comm(nodeid, NULL);
|
||||
if (!cm)
|
||||
return -EEXIST;
|
||||
if (!cm->addr_count)
|
||||
return -ENOENT;
|
||||
memcpy(addr, cm->addr[0], sizeof(*addr));
|
||||
put_comm(cm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid)
|
||||
{
|
||||
struct comm *cm = get_comm(0, addr);
|
||||
if (!cm)
|
||||
return -EEXIST;
|
||||
*nodeid = cm->nodeid;
|
||||
put_comm(cm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dlm_our_nodeid(void)
|
||||
{
|
||||
return local_comm ? local_comm->nodeid : 0;
|
||||
}
|
||||
|
||||
/* num 0 is first addr, num 1 is second addr */
|
||||
int dlm_our_addr(struct sockaddr_storage *addr, int num)
|
||||
{
|
||||
if (!local_comm)
|
||||
return -1;
|
||||
if (num + 1 > local_comm->addr_count)
|
||||
return -1;
|
||||
memcpy(addr, local_comm->addr[num], sizeof(*addr));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Config file defaults */
|
||||
#define DEFAULT_TCP_PORT 21064
|
||||
#define DEFAULT_BUFFER_SIZE 4096
|
||||
#define DEFAULT_RSBTBL_SIZE 256
|
||||
#define DEFAULT_LKBTBL_SIZE 1024
|
||||
#define DEFAULT_DIRTBL_SIZE 512
|
||||
#define DEFAULT_RECOVER_TIMER 5
|
||||
#define DEFAULT_TOSS_SECS 10
|
||||
#define DEFAULT_SCAN_SECS 5
|
||||
|
||||
struct dlm_config_info dlm_config = {
|
||||
.tcp_port = DEFAULT_TCP_PORT,
|
||||
.buffer_size = DEFAULT_BUFFER_SIZE,
|
||||
.rsbtbl_size = DEFAULT_RSBTBL_SIZE,
|
||||
.lkbtbl_size = DEFAULT_LKBTBL_SIZE,
|
||||
.dirtbl_size = DEFAULT_DIRTBL_SIZE,
|
||||
.recover_timer = DEFAULT_RECOVER_TIMER,
|
||||
.toss_secs = DEFAULT_TOSS_SECS,
|
||||
.scan_secs = DEFAULT_SCAN_SECS
|
||||
};
|
||||
|
||||
42
fs/dlm/config.h
Normal file
42
fs/dlm/config.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __CONFIG_DOT_H__
|
||||
#define __CONFIG_DOT_H__
|
||||
|
||||
#define DLM_MAX_ADDR_COUNT 3
|
||||
|
||||
struct dlm_config_info {
|
||||
int tcp_port;
|
||||
int buffer_size;
|
||||
int rsbtbl_size;
|
||||
int lkbtbl_size;
|
||||
int dirtbl_size;
|
||||
int recover_timer;
|
||||
int toss_secs;
|
||||
int scan_secs;
|
||||
};
|
||||
|
||||
extern struct dlm_config_info dlm_config;
|
||||
|
||||
int dlm_config_init(void);
|
||||
void dlm_config_exit(void);
|
||||
int dlm_node_weight(char *lsname, int nodeid);
|
||||
int dlm_nodeid_list(char *lsname, int **ids_out);
|
||||
int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr);
|
||||
int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid);
|
||||
int dlm_our_nodeid(void);
|
||||
int dlm_our_addr(struct sockaddr_storage *addr, int num);
|
||||
|
||||
#endif /* __CONFIG_DOT_H__ */
|
||||
|
||||
310
fs/dlm/debug_fs.c
Normal file
310
fs/dlm/debug_fs.c
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) 2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "dlm_internal.h"
|
||||
|
||||
|
||||
static struct dentry *dlm_root;
|
||||
|
||||
struct rsb_iter {
|
||||
int entry;
|
||||
struct dlm_ls *ls;
|
||||
struct list_head *next;
|
||||
struct dlm_rsb *rsb;
|
||||
};
|
||||
|
||||
static char *print_lockmode(int mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case DLM_LOCK_IV:
|
||||
return "--";
|
||||
case DLM_LOCK_NL:
|
||||
return "NL";
|
||||
case DLM_LOCK_CR:
|
||||
return "CR";
|
||||
case DLM_LOCK_CW:
|
||||
return "CW";
|
||||
case DLM_LOCK_PR:
|
||||
return "PR";
|
||||
case DLM_LOCK_PW:
|
||||
return "PW";
|
||||
case DLM_LOCK_EX:
|
||||
return "EX";
|
||||
default:
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_lock(struct seq_file *s, struct dlm_lkb *lkb,
|
||||
struct dlm_rsb *res)
|
||||
{
|
||||
seq_printf(s, "%08x %s", lkb->lkb_id, print_lockmode(lkb->lkb_grmode));
|
||||
|
||||
if (lkb->lkb_status == DLM_LKSTS_CONVERT
|
||||
|| lkb->lkb_status == DLM_LKSTS_WAITING)
|
||||
seq_printf(s, " (%s)", print_lockmode(lkb->lkb_rqmode));
|
||||
|
||||
if (lkb->lkb_range) {
|
||||
/* FIXME: this warns on Alpha */
|
||||
if (lkb->lkb_status == DLM_LKSTS_CONVERT
|
||||
|| lkb->lkb_status == DLM_LKSTS_GRANTED)
|
||||
seq_printf(s, " %" PRIx64 "-%" PRIx64,
|
||||
lkb->lkb_range[GR_RANGE_START],
|
||||
lkb->lkb_range[GR_RANGE_END]);
|
||||
if (lkb->lkb_status == DLM_LKSTS_CONVERT
|
||||
|| lkb->lkb_status == DLM_LKSTS_WAITING)
|
||||
seq_printf(s, " (%" PRIx64 "-%" PRIx64 ")",
|
||||
lkb->lkb_range[RQ_RANGE_START],
|
||||
lkb->lkb_range[RQ_RANGE_END]);
|
||||
}
|
||||
|
||||
if (lkb->lkb_nodeid) {
|
||||
if (lkb->lkb_nodeid != res->res_nodeid)
|
||||
seq_printf(s, " Remote: %3d %08x", lkb->lkb_nodeid,
|
||||
lkb->lkb_remid);
|
||||
else
|
||||
seq_printf(s, " Master: %08x", lkb->lkb_remid);
|
||||
}
|
||||
|
||||
if (lkb->lkb_wait_type)
|
||||
seq_printf(s, " wait_type: %d", lkb->lkb_wait_type);
|
||||
|
||||
seq_printf(s, "\n");
|
||||
}
|
||||
|
||||
static int print_resource(struct dlm_rsb *res, struct seq_file *s)
|
||||
{
|
||||
struct dlm_lkb *lkb;
|
||||
int i, lvblen = res->res_ls->ls_lvblen;
|
||||
|
||||
seq_printf(s, "\nResource %p Name (len=%d) \"", res, res->res_length);
|
||||
for (i = 0; i < res->res_length; i++) {
|
||||
if (isprint(res->res_name[i]))
|
||||
seq_printf(s, "%c", res->res_name[i]);
|
||||
else
|
||||
seq_printf(s, "%c", '.');
|
||||
}
|
||||
if (res->res_nodeid > 0)
|
||||
seq_printf(s, "\" \nLocal Copy, Master is node %d\n",
|
||||
res->res_nodeid);
|
||||
else if (res->res_nodeid == 0)
|
||||
seq_printf(s, "\" \nMaster Copy\n");
|
||||
else if (res->res_nodeid == -1)
|
||||
seq_printf(s, "\" \nLooking up master (lkid %x)\n",
|
||||
res->res_first_lkid);
|
||||
else
|
||||
seq_printf(s, "\" \nInvalid master %d\n", res->res_nodeid);
|
||||
|
||||
/* Print the LVB: */
|
||||
if (res->res_lvbptr) {
|
||||
seq_printf(s, "LVB: ");
|
||||
for (i = 0; i < lvblen; i++) {
|
||||
if (i == lvblen / 2)
|
||||
seq_printf(s, "\n ");
|
||||
seq_printf(s, "%02x ",
|
||||
(unsigned char) res->res_lvbptr[i]);
|
||||
}
|
||||
if (rsb_flag(res, RSB_VALNOTVALID))
|
||||
seq_printf(s, " (INVALID)");
|
||||
seq_printf(s, "\n");
|
||||
}
|
||||
|
||||
/* Print the locks attached to this resource */
|
||||
seq_printf(s, "Granted Queue\n");
|
||||
list_for_each_entry(lkb, &res->res_grantqueue, lkb_statequeue)
|
||||
print_lock(s, lkb, res);
|
||||
|
||||
seq_printf(s, "Conversion Queue\n");
|
||||
list_for_each_entry(lkb, &res->res_convertqueue, lkb_statequeue)
|
||||
print_lock(s, lkb, res);
|
||||
|
||||
seq_printf(s, "Waiting Queue\n");
|
||||
list_for_each_entry(lkb, &res->res_waitqueue, lkb_statequeue)
|
||||
print_lock(s, lkb, res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsb_iter_next(struct rsb_iter *ri)
|
||||
{
|
||||
struct dlm_ls *ls = ri->ls;
|
||||
int i;
|
||||
|
||||
if (!ri->next) {
|
||||
top:
|
||||
/* Find the next non-empty hash bucket */
|
||||
for (i = ri->entry; i < ls->ls_rsbtbl_size; i++) {
|
||||
read_lock(&ls->ls_rsbtbl[i].lock);
|
||||
if (!list_empty(&ls->ls_rsbtbl[i].list)) {
|
||||
ri->next = ls->ls_rsbtbl[i].list.next;
|
||||
read_unlock(&ls->ls_rsbtbl[i].lock);
|
||||
break;
|
||||
}
|
||||
read_unlock(&ls->ls_rsbtbl[i].lock);
|
||||
}
|
||||
ri->entry = i;
|
||||
|
||||
if (ri->entry >= ls->ls_rsbtbl_size)
|
||||
return 1;
|
||||
} else {
|
||||
i = ri->entry;
|
||||
read_lock(&ls->ls_rsbtbl[i].lock);
|
||||
ri->next = ri->next->next;
|
||||
if (ri->next->next == ls->ls_rsbtbl[i].list.next) {
|
||||
/* End of list - move to next bucket */
|
||||
ri->next = NULL;
|
||||
ri->entry++;
|
||||
read_unlock(&ls->ls_rsbtbl[i].lock);
|
||||
goto top;
|
||||
}
|
||||
read_unlock(&ls->ls_rsbtbl[i].lock);
|
||||
}
|
||||
ri->rsb = list_entry(ri->next, struct dlm_rsb, res_hashchain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rsb_iter_free(struct rsb_iter *ri)
|
||||
{
|
||||
kfree(ri);
|
||||
}
|
||||
|
||||
static struct rsb_iter *rsb_iter_init(struct dlm_ls *ls)
|
||||
{
|
||||
struct rsb_iter *ri;
|
||||
|
||||
ri = kmalloc(sizeof *ri, GFP_KERNEL);
|
||||
if (!ri)
|
||||
return NULL;
|
||||
|
||||
ri->ls = ls;
|
||||
ri->entry = 0;
|
||||
ri->next = NULL;
|
||||
|
||||
if (rsb_iter_next(ri)) {
|
||||
rsb_iter_free(ri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ri;
|
||||
}
|
||||
|
||||
static void *seq_start(struct seq_file *file, loff_t *pos)
|
||||
{
|
||||
struct rsb_iter *ri;
|
||||
loff_t n = *pos;
|
||||
|
||||
ri = rsb_iter_init(file->private);
|
||||
if (!ri)
|
||||
return NULL;
|
||||
|
||||
while (n--) {
|
||||
if (rsb_iter_next(ri)) {
|
||||
rsb_iter_free(ri);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ri;
|
||||
}
|
||||
|
||||
static void *seq_next(struct seq_file *file, void *iter_ptr, loff_t *pos)
|
||||
{
|
||||
struct rsb_iter *ri = iter_ptr;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (rsb_iter_next(ri)) {
|
||||
rsb_iter_free(ri);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ri;
|
||||
}
|
||||
|
||||
static void seq_stop(struct seq_file *file, void *iter_ptr)
|
||||
{
|
||||
/* nothing for now */
|
||||
}
|
||||
|
||||
static int seq_show(struct seq_file *file, void *iter_ptr)
|
||||
{
|
||||
struct rsb_iter *ri = iter_ptr;
|
||||
|
||||
print_resource(ri->rsb, file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations dlm_seq_ops = {
|
||||
.start = seq_start,
|
||||
.next = seq_next,
|
||||
.stop = seq_stop,
|
||||
.show = seq_show,
|
||||
};
|
||||
|
||||
static int do_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq;
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &dlm_seq_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
seq = file->private_data;
|
||||
seq->private = inode->u.generic_ip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct file_operations dlm_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = do_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release
|
||||
};
|
||||
|
||||
int dlm_create_debug_file(struct dlm_ls *ls)
|
||||
{
|
||||
ls->ls_debug_dentry = debugfs_create_file(ls->ls_name,
|
||||
S_IFREG | S_IRUGO,
|
||||
dlm_root,
|
||||
ls,
|
||||
&dlm_fops);
|
||||
return ls->ls_debug_dentry ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void dlm_delete_debug_file(struct dlm_ls *ls)
|
||||
{
|
||||
if (ls->ls_debug_dentry)
|
||||
debugfs_remove(ls->ls_debug_dentry);
|
||||
}
|
||||
|
||||
int dlm_register_debugfs(void)
|
||||
{
|
||||
dlm_root = debugfs_create_dir("dlm", NULL);
|
||||
return dlm_root ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void dlm_unregister_debugfs(void)
|
||||
{
|
||||
debugfs_remove(dlm_root);
|
||||
}
|
||||
|
||||
1084
fs/dlm/device.c
Normal file
1084
fs/dlm/device.c
Normal file
File diff suppressed because it is too large
Load diff
423
fs/dlm/dir.c
Normal file
423
fs/dlm/dir.c
Normal file
|
|
@ -0,0 +1,423 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#include "dlm_internal.h"
|
||||
#include "lockspace.h"
|
||||
#include "member.h"
|
||||
#include "lowcomms.h"
|
||||
#include "rcom.h"
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "recover.h"
|
||||
#include "util.h"
|
||||
#include "lock.h"
|
||||
#include "dir.h"
|
||||
|
||||
|
||||
static void put_free_de(struct dlm_ls *ls, struct dlm_direntry *de)
|
||||
{
|
||||
spin_lock(&ls->ls_recover_list_lock);
|
||||
list_add(&de->list, &ls->ls_recover_list);
|
||||
spin_unlock(&ls->ls_recover_list_lock);
|
||||
}
|
||||
|
||||
static struct dlm_direntry *get_free_de(struct dlm_ls *ls, int len)
|
||||
{
|
||||
int found = FALSE;
|
||||
struct dlm_direntry *de;
|
||||
|
||||
spin_lock(&ls->ls_recover_list_lock);
|
||||
list_for_each_entry(de, &ls->ls_recover_list, list) {
|
||||
if (de->length == len) {
|
||||
list_del(&de->list);
|
||||
de->master_nodeid = 0;
|
||||
memset(de->name, 0, len);
|
||||
found = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&ls->ls_recover_list_lock);
|
||||
|
||||
if (!found)
|
||||
de = allocate_direntry(ls, len);
|
||||
return de;
|
||||
}
|
||||
|
||||
void dlm_clear_free_entries(struct dlm_ls *ls)
|
||||
{
|
||||
struct dlm_direntry *de;
|
||||
|
||||
spin_lock(&ls->ls_recover_list_lock);
|
||||
while (!list_empty(&ls->ls_recover_list)) {
|
||||
de = list_entry(ls->ls_recover_list.next, struct dlm_direntry,
|
||||
list);
|
||||
list_del(&de->list);
|
||||
free_direntry(de);
|
||||
}
|
||||
spin_unlock(&ls->ls_recover_list_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* We use the upper 16 bits of the hash value to select the directory node.
|
||||
* Low bits are used for distribution of rsb's among hash buckets on each node.
|
||||
*
|
||||
* To give the exact range wanted (0 to num_nodes-1), we apply a modulus of
|
||||
* num_nodes to the hash value. This value in the desired range is used as an
|
||||
* offset into the sorted list of nodeid's to give the particular nodeid.
|
||||
*/
|
||||
|
||||
int dlm_hash2nodeid(struct dlm_ls *ls, uint32_t hash)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct dlm_member *memb = NULL;
|
||||
uint32_t node, n = 0;
|
||||
int nodeid;
|
||||
|
||||
if (ls->ls_num_nodes == 1) {
|
||||
nodeid = dlm_our_nodeid();
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ls->ls_node_array) {
|
||||
node = (hash >> 16) % ls->ls_total_weight;
|
||||
nodeid = ls->ls_node_array[node];
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* make_member_array() failed to kmalloc ls_node_array... */
|
||||
|
||||
node = (hash >> 16) % ls->ls_num_nodes;
|
||||
|
||||
list_for_each(tmp, &ls->ls_nodes) {
|
||||
if (n++ != node)
|
||||
continue;
|
||||
memb = list_entry(tmp, struct dlm_member, list);
|
||||
break;
|
||||
}
|
||||
|
||||
DLM_ASSERT(memb , printk("num_nodes=%u n=%u node=%u\n",
|
||||
ls->ls_num_nodes, n, node););
|
||||
nodeid = memb->nodeid;
|
||||
out:
|
||||
return nodeid;
|
||||
}
|
||||
|
||||
int dlm_dir_nodeid(struct dlm_rsb *r)
|
||||
{
|
||||
return dlm_hash2nodeid(r->res_ls, r->res_hash);
|
||||
}
|
||||
|
||||
static inline uint32_t dir_hash(struct dlm_ls *ls, char *name, int len)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = jhash(name, len, 0);
|
||||
val &= (ls->ls_dirtbl_size - 1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static void add_entry_to_hash(struct dlm_ls *ls, struct dlm_direntry *de)
|
||||
{
|
||||
uint32_t bucket;
|
||||
|
||||
bucket = dir_hash(ls, de->name, de->length);
|
||||
list_add_tail(&de->list, &ls->ls_dirtbl[bucket].list);
|
||||
}
|
||||
|
||||
static struct dlm_direntry *search_bucket(struct dlm_ls *ls, char *name,
|
||||
int namelen, uint32_t bucket)
|
||||
{
|
||||
struct dlm_direntry *de;
|
||||
|
||||
list_for_each_entry(de, &ls->ls_dirtbl[bucket].list, list) {
|
||||
if (de->length == namelen && !memcmp(name, de->name, namelen))
|
||||
goto out;
|
||||
}
|
||||
de = NULL;
|
||||
out:
|
||||
return de;
|
||||
}
|
||||
|
||||
void dlm_dir_remove_entry(struct dlm_ls *ls, int nodeid, char *name, int namelen)
|
||||
{
|
||||
struct dlm_direntry *de;
|
||||
uint32_t bucket;
|
||||
|
||||
bucket = dir_hash(ls, name, namelen);
|
||||
|
||||
write_lock(&ls->ls_dirtbl[bucket].lock);
|
||||
|
||||
de = search_bucket(ls, name, namelen, bucket);
|
||||
|
||||
if (!de) {
|
||||
log_error(ls, "remove fr %u none", nodeid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (de->master_nodeid != nodeid) {
|
||||
log_error(ls, "remove fr %u ID %u", nodeid, de->master_nodeid);
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_del(&de->list);
|
||||
free_direntry(de);
|
||||
out:
|
||||
write_unlock(&ls->ls_dirtbl[bucket].lock);
|
||||
}
|
||||
|
||||
void dlm_dir_clear(struct dlm_ls *ls)
|
||||
{
|
||||
struct list_head *head;
|
||||
struct dlm_direntry *de;
|
||||
int i;
|
||||
|
||||
DLM_ASSERT(list_empty(&ls->ls_recover_list), );
|
||||
|
||||
for (i = 0; i < ls->ls_dirtbl_size; i++) {
|
||||
write_lock(&ls->ls_dirtbl[i].lock);
|
||||
head = &ls->ls_dirtbl[i].list;
|
||||
while (!list_empty(head)) {
|
||||
de = list_entry(head->next, struct dlm_direntry, list);
|
||||
list_del(&de->list);
|
||||
put_free_de(ls, de);
|
||||
}
|
||||
write_unlock(&ls->ls_dirtbl[i].lock);
|
||||
}
|
||||
}
|
||||
|
||||
int dlm_recover_directory(struct dlm_ls *ls)
|
||||
{
|
||||
struct dlm_member *memb;
|
||||
struct dlm_direntry *de;
|
||||
char *b, *last_name = NULL;
|
||||
int error = -ENOMEM, last_len, count = 0;
|
||||
uint16_t namelen;
|
||||
|
||||
log_debug(ls, "dlm_recover_directory");
|
||||
|
||||
if (dlm_no_directory(ls))
|
||||
goto out_status;
|
||||
|
||||
dlm_dir_clear(ls);
|
||||
|
||||
last_name = kmalloc(DLM_RESNAME_MAXLEN, GFP_KERNEL);
|
||||
if (!last_name)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(memb, &ls->ls_nodes, list) {
|
||||
memset(last_name, 0, DLM_RESNAME_MAXLEN);
|
||||
last_len = 0;
|
||||
|
||||
for (;;) {
|
||||
error = dlm_recovery_stopped(ls);
|
||||
if (error)
|
||||
goto out_free;
|
||||
|
||||
error = dlm_rcom_names(ls, memb->nodeid,
|
||||
last_name, last_len);
|
||||
if (error)
|
||||
goto out_free;
|
||||
|
||||
schedule();
|
||||
|
||||
/*
|
||||
* pick namelen/name pairs out of received buffer
|
||||
*/
|
||||
|
||||
b = ls->ls_recover_buf + sizeof(struct dlm_rcom);
|
||||
|
||||
for (;;) {
|
||||
memcpy(&namelen, b, sizeof(uint16_t));
|
||||
namelen = be16_to_cpu(namelen);
|
||||
b += sizeof(uint16_t);
|
||||
|
||||
/* namelen of 0xFFFFF marks end of names for
|
||||
this node; namelen of 0 marks end of the
|
||||
buffer */
|
||||
|
||||
if (namelen == 0xFFFF)
|
||||
goto done;
|
||||
if (!namelen)
|
||||
break;
|
||||
|
||||
error = -ENOMEM;
|
||||
de = get_free_de(ls, namelen);
|
||||
if (!de)
|
||||
goto out_free;
|
||||
|
||||
de->master_nodeid = memb->nodeid;
|
||||
de->length = namelen;
|
||||
last_len = namelen;
|
||||
memcpy(de->name, b, namelen);
|
||||
memcpy(last_name, b, namelen);
|
||||
b += namelen;
|
||||
|
||||
add_entry_to_hash(ls, de);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
done:
|
||||
;
|
||||
}
|
||||
|
||||
out_status:
|
||||
error = 0;
|
||||
dlm_set_recover_status(ls, DLM_RS_DIR);
|
||||
log_debug(ls, "dlm_recover_directory %d entries", count);
|
||||
out_free:
|
||||
kfree(last_name);
|
||||
out:
|
||||
dlm_clear_free_entries(ls);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int get_entry(struct dlm_ls *ls, int nodeid, char *name,
|
||||
int namelen, int *r_nodeid)
|
||||
{
|
||||
struct dlm_direntry *de, *tmp;
|
||||
uint32_t bucket;
|
||||
|
||||
bucket = dir_hash(ls, name, namelen);
|
||||
|
||||
write_lock(&ls->ls_dirtbl[bucket].lock);
|
||||
de = search_bucket(ls, name, namelen, bucket);
|
||||
if (de) {
|
||||
*r_nodeid = de->master_nodeid;
|
||||
write_unlock(&ls->ls_dirtbl[bucket].lock);
|
||||
if (*r_nodeid == nodeid)
|
||||
return -EEXIST;
|
||||
return 0;
|
||||
}
|
||||
|
||||
write_unlock(&ls->ls_dirtbl[bucket].lock);
|
||||
|
||||
de = allocate_direntry(ls, namelen);
|
||||
if (!de)
|
||||
return -ENOMEM;
|
||||
|
||||
de->master_nodeid = nodeid;
|
||||
de->length = namelen;
|
||||
memcpy(de->name, name, namelen);
|
||||
|
||||
write_lock(&ls->ls_dirtbl[bucket].lock);
|
||||
tmp = search_bucket(ls, name, namelen, bucket);
|
||||
if (tmp) {
|
||||
free_direntry(de);
|
||||
de = tmp;
|
||||
} else {
|
||||
list_add_tail(&de->list, &ls->ls_dirtbl[bucket].list);
|
||||
}
|
||||
*r_nodeid = de->master_nodeid;
|
||||
write_unlock(&ls->ls_dirtbl[bucket].lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dlm_dir_lookup(struct dlm_ls *ls, int nodeid, char *name, int namelen,
|
||||
int *r_nodeid)
|
||||
{
|
||||
return get_entry(ls, nodeid, name, namelen, r_nodeid);
|
||||
}
|
||||
|
||||
/* Copy the names of master rsb's into the buffer provided.
|
||||
Only select names whose dir node is the given nodeid. */
|
||||
|
||||
void dlm_copy_master_names(struct dlm_ls *ls, char *inbuf, int inlen,
|
||||
char *outbuf, int outlen, int nodeid)
|
||||
{
|
||||
struct list_head *list;
|
||||
struct dlm_rsb *start_r = NULL, *r = NULL;
|
||||
int offset = 0, start_namelen, error, dir_nodeid;
|
||||
char *start_name;
|
||||
uint16_t be_namelen;
|
||||
|
||||
/*
|
||||
* Find the rsb where we left off (or start again)
|
||||
*/
|
||||
|
||||
start_namelen = inlen;
|
||||
start_name = inbuf;
|
||||
|
||||
if (start_namelen > 1) {
|
||||
/*
|
||||
* We could also use a find_rsb_root() function here that
|
||||
* searched the ls_root_list.
|
||||
*/
|
||||
error = dlm_find_rsb(ls, start_name, start_namelen, R_MASTER,
|
||||
&start_r);
|
||||
DLM_ASSERT(!error && start_r,
|
||||
printk("error %d\n", error););
|
||||
DLM_ASSERT(!list_empty(&start_r->res_root_list),
|
||||
dlm_print_rsb(start_r););
|
||||
dlm_put_rsb(start_r);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send rsb names for rsb's we're master of and whose directory node
|
||||
* matches the requesting node.
|
||||
*/
|
||||
|
||||
down_read(&ls->ls_root_sem);
|
||||
if (start_r)
|
||||
list = start_r->res_root_list.next;
|
||||
else
|
||||
list = ls->ls_root_list.next;
|
||||
|
||||
for (offset = 0; list != &ls->ls_root_list; list = list->next) {
|
||||
r = list_entry(list, struct dlm_rsb, res_root_list);
|
||||
if (r->res_nodeid)
|
||||
continue;
|
||||
|
||||
dir_nodeid = dlm_dir_nodeid(r);
|
||||
if (dir_nodeid != nodeid)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The block ends when we can't fit the following in the
|
||||
* remaining buffer space:
|
||||
* namelen (uint16_t) +
|
||||
* name (r->res_length) +
|
||||
* end-of-block record 0x0000 (uint16_t)
|
||||
*/
|
||||
|
||||
if (offset + sizeof(uint16_t)*2 + r->res_length > outlen) {
|
||||
/* Write end-of-block record */
|
||||
be_namelen = 0;
|
||||
memcpy(outbuf + offset, &be_namelen, sizeof(uint16_t));
|
||||
offset += sizeof(uint16_t);
|
||||
goto out;
|
||||
}
|
||||
|
||||
be_namelen = cpu_to_be16(r->res_length);
|
||||
memcpy(outbuf + offset, &be_namelen, sizeof(uint16_t));
|
||||
offset += sizeof(uint16_t);
|
||||
memcpy(outbuf + offset, r->res_name, r->res_length);
|
||||
offset += r->res_length;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've reached the end of the list (and there's room) write a
|
||||
* terminating record.
|
||||
*/
|
||||
|
||||
if ((list == &ls->ls_root_list) &&
|
||||
(offset + sizeof(uint16_t) <= outlen)) {
|
||||
be_namelen = 0xFFFF;
|
||||
memcpy(outbuf + offset, &be_namelen, sizeof(uint16_t));
|
||||
offset += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
out:
|
||||
up_read(&ls->ls_root_sem);
|
||||
}
|
||||
|
||||
30
fs/dlm/dir.h
Normal file
30
fs/dlm/dir.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __DIR_DOT_H__
|
||||
#define __DIR_DOT_H__
|
||||
|
||||
|
||||
int dlm_dir_nodeid(struct dlm_rsb *rsb);
|
||||
int dlm_hash2nodeid(struct dlm_ls *ls, uint32_t hash);
|
||||
void dlm_dir_remove_entry(struct dlm_ls *ls, int nodeid, char *name, int len);
|
||||
void dlm_dir_clear(struct dlm_ls *ls);
|
||||
void dlm_clear_free_entries(struct dlm_ls *ls);
|
||||
int dlm_recover_directory(struct dlm_ls *ls);
|
||||
int dlm_dir_lookup(struct dlm_ls *ls, int nodeid, char *name, int namelen,
|
||||
int *r_nodeid);
|
||||
void dlm_copy_master_names(struct dlm_ls *ls, char *inbuf, int inlen,
|
||||
char *outbuf, int outlen, int nodeid);
|
||||
|
||||
#endif /* __DIR_DOT_H__ */
|
||||
|
||||
518
fs/dlm/dlm_internal.h
Normal file
518
fs/dlm/dlm_internal.h
Normal file
|
|
@ -0,0 +1,518 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __DLM_INTERNAL_DOT_H__
|
||||
#define __DLM_INTERNAL_DOT_H__
|
||||
|
||||
/*
|
||||
* This is the main header file to be included in each DLM source file.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <linux/dlm.h>
|
||||
|
||||
#define DLM_LOCKSPACE_LEN 64
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#if (BITS_PER_LONG == 64)
|
||||
#define PRIx64 "lx"
|
||||
#else
|
||||
#define PRIx64 "Lx"
|
||||
#endif
|
||||
|
||||
/* Size of the temp buffer midcomms allocates on the stack.
|
||||
We try to make this large enough so most messages fit.
|
||||
FIXME: should sctp make this unnecessary? */
|
||||
|
||||
#define DLM_INBUF_LEN 148
|
||||
|
||||
struct dlm_ls;
|
||||
struct dlm_lkb;
|
||||
struct dlm_rsb;
|
||||
struct dlm_member;
|
||||
struct dlm_lkbtable;
|
||||
struct dlm_rsbtable;
|
||||
struct dlm_dirtable;
|
||||
struct dlm_direntry;
|
||||
struct dlm_recover;
|
||||
struct dlm_header;
|
||||
struct dlm_message;
|
||||
struct dlm_rcom;
|
||||
struct dlm_mhandle;
|
||||
|
||||
#define log_print(fmt, args...) \
|
||||
printk(KERN_ERR "dlm: "fmt"\n" , ##args)
|
||||
#define log_error(ls, fmt, args...) \
|
||||
printk(KERN_ERR "dlm: %s: " fmt "\n", (ls)->ls_name , ##args)
|
||||
|
||||
#ifdef DLM_LOG_DEBUG
|
||||
#define log_debug(ls, fmt, args...) log_error(ls, fmt, ##args)
|
||||
#else
|
||||
#define log_debug(ls, fmt, args...)
|
||||
#endif
|
||||
|
||||
#define DLM_ASSERT(x, do) \
|
||||
{ \
|
||||
if (!(x)) \
|
||||
{ \
|
||||
printk(KERN_ERR "\nDLM: Assertion failed on line %d of file %s\n" \
|
||||
"DLM: assertion: \"%s\"\n" \
|
||||
"DLM: time = %lu\n", \
|
||||
__LINE__, __FILE__, #x, jiffies); \
|
||||
{do} \
|
||||
printk("\n"); \
|
||||
BUG(); \
|
||||
panic("DLM: Record message above and reboot.\n"); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
struct dlm_direntry {
|
||||
struct list_head list;
|
||||
uint32_t master_nodeid;
|
||||
uint16_t length;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct dlm_dirtable {
|
||||
struct list_head list;
|
||||
rwlock_t lock;
|
||||
};
|
||||
|
||||
struct dlm_rsbtable {
|
||||
struct list_head list;
|
||||
struct list_head toss;
|
||||
rwlock_t lock;
|
||||
};
|
||||
|
||||
struct dlm_lkbtable {
|
||||
struct list_head list;
|
||||
rwlock_t lock;
|
||||
uint16_t counter;
|
||||
};
|
||||
|
||||
/*
|
||||
* Lockspace member (per node in a ls)
|
||||
*/
|
||||
|
||||
struct dlm_member {
|
||||
struct list_head list;
|
||||
int nodeid;
|
||||
int weight;
|
||||
};
|
||||
|
||||
/*
|
||||
* Save and manage recovery state for a lockspace.
|
||||
*/
|
||||
|
||||
struct dlm_recover {
|
||||
struct list_head list;
|
||||
int *nodeids;
|
||||
int node_count;
|
||||
uint64_t seq;
|
||||
};
|
||||
|
||||
/*
|
||||
* Pass input args to second stage locking function.
|
||||
*/
|
||||
|
||||
struct dlm_args {
|
||||
uint32_t flags;
|
||||
void *astaddr;
|
||||
long astparam;
|
||||
void *bastaddr;
|
||||
int mode;
|
||||
struct dlm_lksb *lksb;
|
||||
struct dlm_range *range;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Lock block
|
||||
*
|
||||
* A lock can be one of three types:
|
||||
*
|
||||
* local copy lock is mastered locally
|
||||
* (lkb_nodeid is zero and DLM_LKF_MSTCPY is not set)
|
||||
* process copy lock is mastered on a remote node
|
||||
* (lkb_nodeid is non-zero and DLM_LKF_MSTCPY is not set)
|
||||
* master copy master node's copy of a lock owned by remote node
|
||||
* (lkb_nodeid is non-zero and DLM_LKF_MSTCPY is set)
|
||||
*
|
||||
* lkb_exflags: a copy of the most recent flags arg provided to dlm_lock or
|
||||
* dlm_unlock. The dlm does not modify these or use any private flags in
|
||||
* this field; it only contains DLM_LKF_ flags from dlm.h. These flags
|
||||
* are sent as-is to the remote master when the lock is remote.
|
||||
*
|
||||
* lkb_flags: internal dlm flags (DLM_IFL_ prefix) from dlm_internal.h.
|
||||
* Some internal flags are shared between the master and process nodes;
|
||||
* these shared flags are kept in the lower two bytes. One of these
|
||||
* flags set on the master copy will be propagated to the process copy
|
||||
* and v.v. Other internal flags are private to the master or process
|
||||
* node (e.g. DLM_IFL_MSTCPY). These are kept in the high two bytes.
|
||||
*
|
||||
* lkb_sbflags: status block flags. These flags are copied directly into
|
||||
* the caller's lksb.sb_flags prior to the dlm_lock/dlm_unlock completion
|
||||
* ast. All defined in dlm.h with DLM_SBF_ prefix.
|
||||
*
|
||||
* lkb_status: the lock status indicates which rsb queue the lock is
|
||||
* on, grant, convert, or wait. DLM_LKSTS_ WAITING/GRANTED/CONVERT
|
||||
*
|
||||
* lkb_wait_type: the dlm message type (DLM_MSG_ prefix) for which a
|
||||
* reply is needed. Only set when the lkb is on the lockspace waiters
|
||||
* list awaiting a reply from a remote node.
|
||||
*
|
||||
* lkb_nodeid: when the lkb is a local copy, nodeid is 0; when the lkb
|
||||
* is a master copy, nodeid specifies the remote lock holder, when the
|
||||
* lkb is a process copy, the nodeid specifies the lock master.
|
||||
*/
|
||||
|
||||
/* lkb_ast_type */
|
||||
|
||||
#define AST_COMP 1
|
||||
#define AST_BAST 2
|
||||
|
||||
/* lkb_range[] */
|
||||
|
||||
#define GR_RANGE_START 0
|
||||
#define GR_RANGE_END 1
|
||||
#define RQ_RANGE_START 2
|
||||
#define RQ_RANGE_END 3
|
||||
|
||||
/* lkb_status */
|
||||
|
||||
#define DLM_LKSTS_WAITING 1
|
||||
#define DLM_LKSTS_GRANTED 2
|
||||
#define DLM_LKSTS_CONVERT 3
|
||||
|
||||
/* lkb_flags */
|
||||
|
||||
#define DLM_IFL_MSTCPY 0x00010000
|
||||
#define DLM_IFL_RESEND 0x00020000
|
||||
#define DLM_IFL_RANGE 0x00000001
|
||||
|
||||
struct dlm_lkb {
|
||||
struct dlm_rsb *lkb_resource; /* the rsb */
|
||||
struct kref lkb_ref;
|
||||
int lkb_nodeid; /* copied from rsb */
|
||||
int lkb_ownpid; /* pid of lock owner */
|
||||
uint32_t lkb_id; /* our lock ID */
|
||||
uint32_t lkb_remid; /* lock ID on remote partner */
|
||||
uint32_t lkb_exflags; /* external flags from caller */
|
||||
uint32_t lkb_sbflags; /* lksb flags */
|
||||
uint32_t lkb_flags; /* internal flags */
|
||||
uint32_t lkb_lvbseq; /* lvb sequence number */
|
||||
|
||||
int8_t lkb_status; /* granted, waiting, convert */
|
||||
int8_t lkb_rqmode; /* requested lock mode */
|
||||
int8_t lkb_grmode; /* granted lock mode */
|
||||
int8_t lkb_bastmode; /* requested mode */
|
||||
int8_t lkb_highbast; /* highest mode bast sent for */
|
||||
|
||||
int8_t lkb_wait_type; /* type of reply waiting for */
|
||||
int8_t lkb_ast_type; /* type of ast queued for */
|
||||
|
||||
struct list_head lkb_idtbl_list; /* lockspace lkbtbl */
|
||||
struct list_head lkb_statequeue; /* rsb g/c/w list */
|
||||
struct list_head lkb_rsb_lookup; /* waiting for rsb lookup */
|
||||
struct list_head lkb_wait_reply; /* waiting for remote reply */
|
||||
struct list_head lkb_astqueue; /* need ast to be sent */
|
||||
|
||||
uint64_t *lkb_range; /* array of gr/rq ranges */
|
||||
char *lkb_lvbptr;
|
||||
struct dlm_lksb *lkb_lksb; /* caller's status block */
|
||||
void *lkb_astaddr; /* caller's ast function */
|
||||
void *lkb_bastaddr; /* caller's bast function */
|
||||
long lkb_astparam; /* caller's ast arg */
|
||||
};
|
||||
|
||||
|
||||
struct dlm_rsb {
|
||||
struct dlm_ls *res_ls; /* the lockspace */
|
||||
struct kref res_ref;
|
||||
struct semaphore res_sem;
|
||||
unsigned long res_flags;
|
||||
int res_length; /* length of rsb name */
|
||||
int res_nodeid;
|
||||
uint32_t res_lvbseq;
|
||||
uint32_t res_hash;
|
||||
uint32_t res_bucket; /* rsbtbl */
|
||||
unsigned long res_toss_time;
|
||||
uint32_t res_first_lkid;
|
||||
struct list_head res_lookup; /* lkbs waiting on first */
|
||||
struct list_head res_hashchain; /* rsbtbl */
|
||||
struct list_head res_grantqueue;
|
||||
struct list_head res_convertqueue;
|
||||
struct list_head res_waitqueue;
|
||||
|
||||
struct list_head res_root_list; /* used for recovery */
|
||||
struct list_head res_recover_list; /* used for recovery */
|
||||
int res_recover_locks_count;
|
||||
|
||||
char *res_lvbptr;
|
||||
char res_name[1];
|
||||
};
|
||||
|
||||
/* find_rsb() flags */
|
||||
|
||||
#define R_MASTER 1 /* only return rsb if it's a master */
|
||||
#define R_CREATE 2 /* create/add rsb if not found */
|
||||
|
||||
/* rsb_flags */
|
||||
|
||||
enum rsb_flags {
|
||||
RSB_MASTER_UNCERTAIN,
|
||||
RSB_VALNOTVALID,
|
||||
RSB_VALNOTVALID_PREV,
|
||||
RSB_NEW_MASTER,
|
||||
RSB_NEW_MASTER2,
|
||||
RSB_RECOVER_CONVERT,
|
||||
};
|
||||
|
||||
static inline void rsb_set_flag(struct dlm_rsb *r, enum rsb_flags flag)
|
||||
{
|
||||
__set_bit(flag, &r->res_flags);
|
||||
}
|
||||
|
||||
static inline void rsb_clear_flag(struct dlm_rsb *r, enum rsb_flags flag)
|
||||
{
|
||||
__clear_bit(flag, &r->res_flags);
|
||||
}
|
||||
|
||||
static inline int rsb_flag(struct dlm_rsb *r, enum rsb_flags flag)
|
||||
{
|
||||
return test_bit(flag, &r->res_flags);
|
||||
}
|
||||
|
||||
|
||||
/* dlm_header is first element of all structs sent between nodes */
|
||||
|
||||
#define DLM_HEADER_MAJOR 0x00020000
|
||||
#define DLM_HEADER_MINOR 0x00000001
|
||||
|
||||
#define DLM_MSG 1
|
||||
#define DLM_RCOM 2
|
||||
|
||||
struct dlm_header {
|
||||
uint32_t h_version;
|
||||
uint32_t h_lockspace;
|
||||
uint32_t h_nodeid; /* nodeid of sender */
|
||||
uint16_t h_length;
|
||||
uint8_t h_cmd; /* DLM_MSG, DLM_RCOM */
|
||||
uint8_t h_pad;
|
||||
};
|
||||
|
||||
|
||||
#define DLM_MSG_REQUEST 1
|
||||
#define DLM_MSG_CONVERT 2
|
||||
#define DLM_MSG_UNLOCK 3
|
||||
#define DLM_MSG_CANCEL 4
|
||||
#define DLM_MSG_REQUEST_REPLY 5
|
||||
#define DLM_MSG_CONVERT_REPLY 6
|
||||
#define DLM_MSG_UNLOCK_REPLY 7
|
||||
#define DLM_MSG_CANCEL_REPLY 8
|
||||
#define DLM_MSG_GRANT 9
|
||||
#define DLM_MSG_BAST 10
|
||||
#define DLM_MSG_LOOKUP 11
|
||||
#define DLM_MSG_REMOVE 12
|
||||
#define DLM_MSG_LOOKUP_REPLY 13
|
||||
|
||||
struct dlm_message {
|
||||
struct dlm_header m_header;
|
||||
uint32_t m_type; /* DLM_MSG_ */
|
||||
uint32_t m_nodeid;
|
||||
uint32_t m_pid;
|
||||
uint32_t m_lkid; /* lkid on sender */
|
||||
uint32_t m_remid; /* lkid on receiver */
|
||||
uint32_t m_parent_lkid;
|
||||
uint32_t m_parent_remid;
|
||||
uint32_t m_exflags;
|
||||
uint32_t m_sbflags;
|
||||
uint32_t m_flags;
|
||||
uint32_t m_lvbseq;
|
||||
uint32_t m_hash;
|
||||
int m_status;
|
||||
int m_grmode;
|
||||
int m_rqmode;
|
||||
int m_bastmode;
|
||||
int m_asts;
|
||||
int m_result; /* 0 or -EXXX */
|
||||
uint64_t m_range[2];
|
||||
char m_extra[0]; /* name or lvb */
|
||||
};
|
||||
|
||||
|
||||
#define DLM_RS_NODES 0x00000001
|
||||
#define DLM_RS_NODES_ALL 0x00000002
|
||||
#define DLM_RS_DIR 0x00000004
|
||||
#define DLM_RS_DIR_ALL 0x00000008
|
||||
#define DLM_RS_LOCKS 0x00000010
|
||||
#define DLM_RS_LOCKS_ALL 0x00000020
|
||||
#define DLM_RS_DONE 0x00000040
|
||||
#define DLM_RS_DONE_ALL 0x00000080
|
||||
|
||||
#define DLM_RCOM_STATUS 1
|
||||
#define DLM_RCOM_NAMES 2
|
||||
#define DLM_RCOM_LOOKUP 3
|
||||
#define DLM_RCOM_LOCK 4
|
||||
#define DLM_RCOM_STATUS_REPLY 5
|
||||
#define DLM_RCOM_NAMES_REPLY 6
|
||||
#define DLM_RCOM_LOOKUP_REPLY 7
|
||||
#define DLM_RCOM_LOCK_REPLY 8
|
||||
|
||||
struct dlm_rcom {
|
||||
struct dlm_header rc_header;
|
||||
uint32_t rc_type; /* DLM_RCOM_ */
|
||||
int rc_result; /* multi-purpose */
|
||||
uint64_t rc_id; /* match reply with request */
|
||||
char rc_buf[0];
|
||||
};
|
||||
|
||||
struct rcom_config {
|
||||
uint32_t rf_lvblen;
|
||||
uint32_t rf_lsflags;
|
||||
uint64_t rf_unused;
|
||||
};
|
||||
|
||||
struct rcom_lock {
|
||||
uint32_t rl_ownpid;
|
||||
uint32_t rl_lkid;
|
||||
uint32_t rl_remid;
|
||||
uint32_t rl_parent_lkid;
|
||||
uint32_t rl_parent_remid;
|
||||
uint32_t rl_exflags;
|
||||
uint32_t rl_flags;
|
||||
uint32_t rl_lvbseq;
|
||||
int rl_result;
|
||||
int8_t rl_rqmode;
|
||||
int8_t rl_grmode;
|
||||
int8_t rl_status;
|
||||
int8_t rl_asts;
|
||||
uint16_t rl_wait_type;
|
||||
uint16_t rl_namelen;
|
||||
uint64_t rl_range[4];
|
||||
char rl_name[DLM_RESNAME_MAXLEN];
|
||||
char rl_lvb[0];
|
||||
};
|
||||
|
||||
struct dlm_ls {
|
||||
struct list_head ls_list; /* list of lockspaces */
|
||||
uint32_t ls_global_id; /* global unique lockspace ID */
|
||||
uint32_t ls_exflags;
|
||||
int ls_lvblen;
|
||||
int ls_count; /* reference count */
|
||||
unsigned long ls_flags; /* LSFL_ */
|
||||
struct kobject ls_kobj;
|
||||
|
||||
struct dlm_rsbtable *ls_rsbtbl;
|
||||
uint32_t ls_rsbtbl_size;
|
||||
|
||||
struct dlm_lkbtable *ls_lkbtbl;
|
||||
uint32_t ls_lkbtbl_size;
|
||||
|
||||
struct dlm_dirtable *ls_dirtbl;
|
||||
uint32_t ls_dirtbl_size;
|
||||
|
||||
struct semaphore ls_waiters_sem;
|
||||
struct list_head ls_waiters; /* lkbs needing a reply */
|
||||
|
||||
struct list_head ls_nodes; /* current nodes in ls */
|
||||
struct list_head ls_nodes_gone; /* dead node list, recovery */
|
||||
int ls_num_nodes; /* number of nodes in ls */
|
||||
int ls_low_nodeid;
|
||||
int ls_total_weight;
|
||||
int *ls_node_array;
|
||||
|
||||
struct dlm_rsb ls_stub_rsb; /* for returning errors */
|
||||
struct dlm_lkb ls_stub_lkb; /* for returning errors */
|
||||
struct dlm_message ls_stub_ms; /* for faking a reply */
|
||||
|
||||
struct dentry *ls_debug_dentry; /* debugfs */
|
||||
|
||||
wait_queue_head_t ls_uevent_wait; /* user part of join/leave */
|
||||
int ls_uevent_result;
|
||||
|
||||
/* recovery related */
|
||||
|
||||
struct timer_list ls_timer;
|
||||
struct task_struct *ls_recoverd_task;
|
||||
struct semaphore ls_recoverd_active;
|
||||
spinlock_t ls_recover_lock;
|
||||
uint32_t ls_recover_status; /* DLM_RS_ */
|
||||
uint64_t ls_recover_seq;
|
||||
struct dlm_recover *ls_recover_args;
|
||||
struct rw_semaphore ls_in_recovery; /* block local requests */
|
||||
struct list_head ls_requestqueue;/* queue remote requests */
|
||||
struct semaphore ls_requestqueue_lock;
|
||||
char *ls_recover_buf;
|
||||
struct list_head ls_recover_list;
|
||||
spinlock_t ls_recover_list_lock;
|
||||
int ls_recover_list_count;
|
||||
wait_queue_head_t ls_wait_general;
|
||||
|
||||
struct list_head ls_root_list; /* root resources */
|
||||
struct rw_semaphore ls_root_sem; /* protect root_list */
|
||||
|
||||
int ls_namelen;
|
||||
char ls_name[1];
|
||||
};
|
||||
|
||||
#define LSFL_WORK 0
|
||||
#define LSFL_RUNNING 1
|
||||
#define LSFL_RECOVERY_STOP 2
|
||||
#define LSFL_RCOM_READY 3
|
||||
#define LSFL_UEVENT_WAIT 4
|
||||
|
||||
static inline int dlm_locking_stopped(struct dlm_ls *ls)
|
||||
{
|
||||
return !test_bit(LSFL_RUNNING, &ls->ls_flags);
|
||||
}
|
||||
|
||||
static inline int dlm_recovery_stopped(struct dlm_ls *ls)
|
||||
{
|
||||
return test_bit(LSFL_RECOVERY_STOP, &ls->ls_flags);
|
||||
}
|
||||
|
||||
static inline int dlm_no_directory(struct dlm_ls *ls)
|
||||
{
|
||||
return (ls->ls_exflags & DLM_LSFL_NODIR) ? 1 : 0;
|
||||
}
|
||||
|
||||
#endif /* __DLM_INTERNAL_DOT_H__ */
|
||||
|
||||
3610
fs/dlm/lock.c
Normal file
3610
fs/dlm/lock.c
Normal file
File diff suppressed because it is too large
Load diff
50
fs/dlm/lock.h
Normal file
50
fs/dlm/lock.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) 2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __LOCK_DOT_H__
|
||||
#define __LOCK_DOT_H__
|
||||
|
||||
void dlm_print_rsb(struct dlm_rsb *r);
|
||||
int dlm_receive_message(struct dlm_header *hd, int nodeid, int recovery);
|
||||
int dlm_modes_compat(int mode1, int mode2);
|
||||
int dlm_find_rsb(struct dlm_ls *ls, char *name, int namelen,
|
||||
unsigned int flags, struct dlm_rsb **r_ret);
|
||||
void dlm_put_rsb(struct dlm_rsb *r);
|
||||
void dlm_hold_rsb(struct dlm_rsb *r);
|
||||
int dlm_put_lkb(struct dlm_lkb *lkb);
|
||||
void dlm_scan_rsbs(struct dlm_ls *ls);
|
||||
|
||||
int dlm_purge_locks(struct dlm_ls *ls);
|
||||
void dlm_purge_mstcpy_locks(struct dlm_rsb *r);
|
||||
int dlm_grant_after_purge(struct dlm_ls *ls);
|
||||
int dlm_recover_waiters_post(struct dlm_ls *ls);
|
||||
void dlm_recover_waiters_pre(struct dlm_ls *ls);
|
||||
int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc);
|
||||
int dlm_recover_process_copy(struct dlm_ls *ls, struct dlm_rcom *rc);
|
||||
|
||||
static inline int is_master(struct dlm_rsb *r)
|
||||
{
|
||||
return !r->res_nodeid;
|
||||
}
|
||||
|
||||
static inline void lock_rsb(struct dlm_rsb *r)
|
||||
{
|
||||
down(&r->res_sem);
|
||||
}
|
||||
|
||||
static inline void unlock_rsb(struct dlm_rsb *r)
|
||||
{
|
||||
up(&r->res_sem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
666
fs/dlm/lockspace.c
Normal file
666
fs/dlm/lockspace.c
Normal file
|
|
@ -0,0 +1,666 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#include "dlm_internal.h"
|
||||
#include "lockspace.h"
|
||||
#include "member.h"
|
||||
#include "recoverd.h"
|
||||
#include "ast.h"
|
||||
#include "dir.h"
|
||||
#include "lowcomms.h"
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "lock.h"
|
||||
|
||||
#ifdef CONFIG_DLM_DEBUG
|
||||
int dlm_create_debug_file(struct dlm_ls *ls);
|
||||
void dlm_delete_debug_file(struct dlm_ls *ls);
|
||||
#else
|
||||
static inline int dlm_create_debug_file(struct dlm_ls *ls) { return 0; }
|
||||
static inline void dlm_delete_debug_file(struct dlm_ls *ls) { }
|
||||
#endif
|
||||
|
||||
static int ls_count;
|
||||
static struct semaphore ls_lock;
|
||||
static struct list_head lslist;
|
||||
static spinlock_t lslist_lock;
|
||||
static struct task_struct * scand_task;
|
||||
|
||||
|
||||
static ssize_t dlm_control_store(struct dlm_ls *ls, const char *buf, size_t len)
|
||||
{
|
||||
ssize_t ret = len;
|
||||
int n = simple_strtol(buf, NULL, 0);
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
dlm_ls_stop(ls);
|
||||
break;
|
||||
case 1:
|
||||
dlm_ls_start(ls);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t dlm_event_store(struct dlm_ls *ls, const char *buf, size_t len)
|
||||
{
|
||||
ls->ls_uevent_result = simple_strtol(buf, NULL, 0);
|
||||
set_bit(LSFL_UEVENT_WAIT, &ls->ls_flags);
|
||||
wake_up(&ls->ls_uevent_wait);
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t dlm_id_show(struct dlm_ls *ls, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", ls->ls_global_id);
|
||||
}
|
||||
|
||||
static ssize_t dlm_id_store(struct dlm_ls *ls, const char *buf, size_t len)
|
||||
{
|
||||
ls->ls_global_id = simple_strtoul(buf, NULL, 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
struct dlm_attr {
|
||||
struct attribute attr;
|
||||
ssize_t (*show)(struct dlm_ls *, char *);
|
||||
ssize_t (*store)(struct dlm_ls *, const char *, size_t);
|
||||
};
|
||||
|
||||
static struct dlm_attr dlm_attr_control = {
|
||||
.attr = {.name = "control", .mode = S_IWUSR},
|
||||
.store = dlm_control_store
|
||||
};
|
||||
|
||||
static struct dlm_attr dlm_attr_event = {
|
||||
.attr = {.name = "event_done", .mode = S_IWUSR},
|
||||
.store = dlm_event_store
|
||||
};
|
||||
|
||||
static struct dlm_attr dlm_attr_id = {
|
||||
.attr = {.name = "id", .mode = S_IRUGO | S_IWUSR},
|
||||
.show = dlm_id_show,
|
||||
.store = dlm_id_store
|
||||
};
|
||||
|
||||
static struct attribute *dlm_attrs[] = {
|
||||
&dlm_attr_control.attr,
|
||||
&dlm_attr_event.attr,
|
||||
&dlm_attr_id.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static ssize_t dlm_attr_show(struct kobject *kobj, struct attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct dlm_ls *ls = container_of(kobj, struct dlm_ls, ls_kobj);
|
||||
struct dlm_attr *a = container_of(attr, struct dlm_attr, attr);
|
||||
return a->show ? a->show(ls, buf) : 0;
|
||||
}
|
||||
|
||||
static ssize_t dlm_attr_store(struct kobject *kobj, struct attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct dlm_ls *ls = container_of(kobj, struct dlm_ls, ls_kobj);
|
||||
struct dlm_attr *a = container_of(attr, struct dlm_attr, attr);
|
||||
return a->store ? a->store(ls, buf, len) : len;
|
||||
}
|
||||
|
||||
static struct sysfs_ops dlm_attr_ops = {
|
||||
.show = dlm_attr_show,
|
||||
.store = dlm_attr_store,
|
||||
};
|
||||
|
||||
static struct kobj_type dlm_ktype = {
|
||||
.default_attrs = dlm_attrs,
|
||||
.sysfs_ops = &dlm_attr_ops,
|
||||
};
|
||||
|
||||
static struct kset dlm_kset = {
|
||||
.subsys = &kernel_subsys,
|
||||
.kobj = {.name = "dlm",},
|
||||
.ktype = &dlm_ktype,
|
||||
};
|
||||
|
||||
static int kobject_setup(struct dlm_ls *ls)
|
||||
{
|
||||
char lsname[DLM_LOCKSPACE_LEN];
|
||||
int error;
|
||||
|
||||
memset(lsname, 0, DLM_LOCKSPACE_LEN);
|
||||
snprintf(lsname, DLM_LOCKSPACE_LEN, "%s", ls->ls_name);
|
||||
|
||||
error = kobject_set_name(&ls->ls_kobj, "%s", lsname);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ls->ls_kobj.kset = &dlm_kset;
|
||||
ls->ls_kobj.ktype = &dlm_ktype;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_uevent(struct dlm_ls *ls, int in)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (in)
|
||||
kobject_uevent(&ls->ls_kobj, KOBJ_ONLINE);
|
||||
else
|
||||
kobject_uevent(&ls->ls_kobj, KOBJ_OFFLINE);
|
||||
|
||||
error = wait_event_interruptible(ls->ls_uevent_wait,
|
||||
test_and_clear_bit(LSFL_UEVENT_WAIT, &ls->ls_flags));
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = ls->ls_uevent_result;
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int dlm_lockspace_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
ls_count = 0;
|
||||
init_MUTEX(&ls_lock);
|
||||
INIT_LIST_HEAD(&lslist);
|
||||
spin_lock_init(&lslist_lock);
|
||||
|
||||
error = kset_register(&dlm_kset);
|
||||
if (error)
|
||||
printk("dlm_lockspace_init: cannot register kset %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
void dlm_lockspace_exit(void)
|
||||
{
|
||||
kset_unregister(&dlm_kset);
|
||||
}
|
||||
|
||||
static int dlm_scand(void *data)
|
||||
{
|
||||
struct dlm_ls *ls;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
list_for_each_entry(ls, &lslist, ls_list)
|
||||
dlm_scan_rsbs(ls);
|
||||
schedule_timeout_interruptible(dlm_config.scan_secs * HZ);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dlm_scand_start(void)
|
||||
{
|
||||
struct task_struct *p;
|
||||
int error = 0;
|
||||
|
||||
p = kthread_run(dlm_scand, NULL, "dlm_scand");
|
||||
if (IS_ERR(p))
|
||||
error = PTR_ERR(p);
|
||||
else
|
||||
scand_task = p;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void dlm_scand_stop(void)
|
||||
{
|
||||
kthread_stop(scand_task);
|
||||
}
|
||||
|
||||
static struct dlm_ls *dlm_find_lockspace_name(char *name, int namelen)
|
||||
{
|
||||
struct dlm_ls *ls;
|
||||
|
||||
spin_lock(&lslist_lock);
|
||||
|
||||
list_for_each_entry(ls, &lslist, ls_list) {
|
||||
if (ls->ls_namelen == namelen &&
|
||||
memcmp(ls->ls_name, name, namelen) == 0)
|
||||
goto out;
|
||||
}
|
||||
ls = NULL;
|
||||
out:
|
||||
spin_unlock(&lslist_lock);
|
||||
return ls;
|
||||
}
|
||||
|
||||
struct dlm_ls *dlm_find_lockspace_global(uint32_t id)
|
||||
{
|
||||
struct dlm_ls *ls;
|
||||
|
||||
spin_lock(&lslist_lock);
|
||||
|
||||
list_for_each_entry(ls, &lslist, ls_list) {
|
||||
if (ls->ls_global_id == id) {
|
||||
ls->ls_count++;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ls = NULL;
|
||||
out:
|
||||
spin_unlock(&lslist_lock);
|
||||
return ls;
|
||||
}
|
||||
|
||||
struct dlm_ls *dlm_find_lockspace_local(void *id)
|
||||
{
|
||||
struct dlm_ls *ls = id;
|
||||
|
||||
spin_lock(&lslist_lock);
|
||||
ls->ls_count++;
|
||||
spin_unlock(&lslist_lock);
|
||||
return ls;
|
||||
}
|
||||
|
||||
void dlm_put_lockspace(struct dlm_ls *ls)
|
||||
{
|
||||
spin_lock(&lslist_lock);
|
||||
ls->ls_count--;
|
||||
spin_unlock(&lslist_lock);
|
||||
}
|
||||
|
||||
static void remove_lockspace(struct dlm_ls *ls)
|
||||
{
|
||||
for (;;) {
|
||||
spin_lock(&lslist_lock);
|
||||
if (ls->ls_count == 0) {
|
||||
list_del(&ls->ls_list);
|
||||
spin_unlock(&lslist_lock);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&lslist_lock);
|
||||
ssleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int threads_start(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Thread which process lock requests for all lockspace's */
|
||||
error = dlm_astd_start();
|
||||
if (error) {
|
||||
log_print("cannot start dlm_astd thread %d", error);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = dlm_scand_start();
|
||||
if (error) {
|
||||
log_print("cannot start dlm_scand thread %d", error);
|
||||
goto astd_fail;
|
||||
}
|
||||
|
||||
/* Thread for sending/receiving messages for all lockspace's */
|
||||
error = dlm_lowcomms_start();
|
||||
if (error) {
|
||||
log_print("cannot start dlm lowcomms %d", error);
|
||||
goto scand_fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
scand_fail:
|
||||
dlm_scand_stop();
|
||||
astd_fail:
|
||||
dlm_astd_stop();
|
||||
fail:
|
||||
return error;
|
||||
}
|
||||
|
||||
static void threads_stop(void)
|
||||
{
|
||||
dlm_scand_stop();
|
||||
dlm_lowcomms_stop();
|
||||
dlm_astd_stop();
|
||||
}
|
||||
|
||||
static int new_lockspace(char *name, int namelen, void **lockspace,
|
||||
uint32_t flags, int lvblen)
|
||||
{
|
||||
struct dlm_ls *ls;
|
||||
int i, size, error = -ENOMEM;
|
||||
|
||||
if (namelen > DLM_LOCKSPACE_LEN)
|
||||
return -EINVAL;
|
||||
|
||||
if (!lvblen || (lvblen % 8))
|
||||
return -EINVAL;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -EINVAL;
|
||||
|
||||
ls = dlm_find_lockspace_name(name, namelen);
|
||||
if (ls) {
|
||||
*lockspace = ls;
|
||||
module_put(THIS_MODULE);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
ls = kmalloc(sizeof(struct dlm_ls) + namelen, GFP_KERNEL);
|
||||
if (!ls)
|
||||
goto out;
|
||||
memset(ls, 0, sizeof(struct dlm_ls) + namelen);
|
||||
memcpy(ls->ls_name, name, namelen);
|
||||
ls->ls_namelen = namelen;
|
||||
ls->ls_exflags = flags;
|
||||
ls->ls_lvblen = lvblen;
|
||||
ls->ls_count = 0;
|
||||
ls->ls_flags = 0;
|
||||
|
||||
size = dlm_config.rsbtbl_size;
|
||||
ls->ls_rsbtbl_size = size;
|
||||
|
||||
ls->ls_rsbtbl = kmalloc(sizeof(struct dlm_rsbtable) * size, GFP_KERNEL);
|
||||
if (!ls->ls_rsbtbl)
|
||||
goto out_lsfree;
|
||||
for (i = 0; i < size; i++) {
|
||||
INIT_LIST_HEAD(&ls->ls_rsbtbl[i].list);
|
||||
INIT_LIST_HEAD(&ls->ls_rsbtbl[i].toss);
|
||||
rwlock_init(&ls->ls_rsbtbl[i].lock);
|
||||
}
|
||||
|
||||
size = dlm_config.lkbtbl_size;
|
||||
ls->ls_lkbtbl_size = size;
|
||||
|
||||
ls->ls_lkbtbl = kmalloc(sizeof(struct dlm_lkbtable) * size, GFP_KERNEL);
|
||||
if (!ls->ls_lkbtbl)
|
||||
goto out_rsbfree;
|
||||
for (i = 0; i < size; i++) {
|
||||
INIT_LIST_HEAD(&ls->ls_lkbtbl[i].list);
|
||||
rwlock_init(&ls->ls_lkbtbl[i].lock);
|
||||
ls->ls_lkbtbl[i].counter = 1;
|
||||
}
|
||||
|
||||
size = dlm_config.dirtbl_size;
|
||||
ls->ls_dirtbl_size = size;
|
||||
|
||||
ls->ls_dirtbl = kmalloc(sizeof(struct dlm_dirtable) * size, GFP_KERNEL);
|
||||
if (!ls->ls_dirtbl)
|
||||
goto out_lkbfree;
|
||||
for (i = 0; i < size; i++) {
|
||||
INIT_LIST_HEAD(&ls->ls_dirtbl[i].list);
|
||||
rwlock_init(&ls->ls_dirtbl[i].lock);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&ls->ls_waiters);
|
||||
init_MUTEX(&ls->ls_waiters_sem);
|
||||
|
||||
INIT_LIST_HEAD(&ls->ls_nodes);
|
||||
INIT_LIST_HEAD(&ls->ls_nodes_gone);
|
||||
ls->ls_num_nodes = 0;
|
||||
ls->ls_low_nodeid = 0;
|
||||
ls->ls_total_weight = 0;
|
||||
ls->ls_node_array = NULL;
|
||||
|
||||
memset(&ls->ls_stub_rsb, 0, sizeof(struct dlm_rsb));
|
||||
ls->ls_stub_rsb.res_ls = ls;
|
||||
|
||||
ls->ls_debug_dentry = NULL;
|
||||
|
||||
init_waitqueue_head(&ls->ls_uevent_wait);
|
||||
ls->ls_uevent_result = 0;
|
||||
|
||||
ls->ls_recoverd_task = NULL;
|
||||
init_MUTEX(&ls->ls_recoverd_active);
|
||||
spin_lock_init(&ls->ls_recover_lock);
|
||||
ls->ls_recover_status = 0;
|
||||
ls->ls_recover_seq = 0;
|
||||
ls->ls_recover_args = NULL;
|
||||
init_rwsem(&ls->ls_in_recovery);
|
||||
INIT_LIST_HEAD(&ls->ls_requestqueue);
|
||||
init_MUTEX(&ls->ls_requestqueue_lock);
|
||||
|
||||
ls->ls_recover_buf = kmalloc(dlm_config.buffer_size, GFP_KERNEL);
|
||||
if (!ls->ls_recover_buf)
|
||||
goto out_dirfree;
|
||||
|
||||
INIT_LIST_HEAD(&ls->ls_recover_list);
|
||||
spin_lock_init(&ls->ls_recover_list_lock);
|
||||
ls->ls_recover_list_count = 0;
|
||||
init_waitqueue_head(&ls->ls_wait_general);
|
||||
INIT_LIST_HEAD(&ls->ls_root_list);
|
||||
init_rwsem(&ls->ls_root_sem);
|
||||
|
||||
down_write(&ls->ls_in_recovery);
|
||||
|
||||
error = dlm_recoverd_start(ls);
|
||||
if (error) {
|
||||
log_error(ls, "can't start dlm_recoverd %d", error);
|
||||
goto out_rcomfree;
|
||||
}
|
||||
|
||||
spin_lock(&lslist_lock);
|
||||
list_add(&ls->ls_list, &lslist);
|
||||
spin_unlock(&lslist_lock);
|
||||
|
||||
dlm_create_debug_file(ls);
|
||||
|
||||
error = kobject_setup(ls);
|
||||
if (error)
|
||||
goto out_del;
|
||||
|
||||
error = kobject_register(&ls->ls_kobj);
|
||||
if (error)
|
||||
goto out_del;
|
||||
|
||||
error = do_uevent(ls, 1);
|
||||
if (error)
|
||||
goto out_unreg;
|
||||
|
||||
*lockspace = ls;
|
||||
return 0;
|
||||
|
||||
out_unreg:
|
||||
kobject_unregister(&ls->ls_kobj);
|
||||
out_del:
|
||||
dlm_delete_debug_file(ls);
|
||||
spin_lock(&lslist_lock);
|
||||
list_del(&ls->ls_list);
|
||||
spin_unlock(&lslist_lock);
|
||||
dlm_recoverd_stop(ls);
|
||||
out_rcomfree:
|
||||
kfree(ls->ls_recover_buf);
|
||||
out_dirfree:
|
||||
kfree(ls->ls_dirtbl);
|
||||
out_lkbfree:
|
||||
kfree(ls->ls_lkbtbl);
|
||||
out_rsbfree:
|
||||
kfree(ls->ls_rsbtbl);
|
||||
out_lsfree:
|
||||
kfree(ls);
|
||||
out:
|
||||
module_put(THIS_MODULE);
|
||||
return error;
|
||||
}
|
||||
|
||||
int dlm_new_lockspace(char *name, int namelen, void **lockspace,
|
||||
uint32_t flags, int lvblen)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
down(&ls_lock);
|
||||
if (!ls_count)
|
||||
error = threads_start();
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = new_lockspace(name, namelen, lockspace, flags, lvblen);
|
||||
if (!error)
|
||||
ls_count++;
|
||||
out:
|
||||
up(&ls_lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Return 1 if the lockspace still has active remote locks,
|
||||
* 2 if the lockspace still has active local locks.
|
||||
*/
|
||||
static int lockspace_busy(struct dlm_ls *ls)
|
||||
{
|
||||
int i, lkb_found = 0;
|
||||
struct dlm_lkb *lkb;
|
||||
|
||||
/* NOTE: We check the lockidtbl here rather than the resource table.
|
||||
This is because there may be LKBs queued as ASTs that have been
|
||||
unlinked from their RSBs and are pending deletion once the AST has
|
||||
been delivered */
|
||||
|
||||
for (i = 0; i < ls->ls_lkbtbl_size; i++) {
|
||||
read_lock(&ls->ls_lkbtbl[i].lock);
|
||||
if (!list_empty(&ls->ls_lkbtbl[i].list)) {
|
||||
lkb_found = 1;
|
||||
list_for_each_entry(lkb, &ls->ls_lkbtbl[i].list,
|
||||
lkb_idtbl_list) {
|
||||
if (!lkb->lkb_nodeid) {
|
||||
read_unlock(&ls->ls_lkbtbl[i].lock);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
read_unlock(&ls->ls_lkbtbl[i].lock);
|
||||
}
|
||||
return lkb_found;
|
||||
}
|
||||
|
||||
static int release_lockspace(struct dlm_ls *ls, int force)
|
||||
{
|
||||
struct dlm_lkb *lkb;
|
||||
struct dlm_rsb *rsb;
|
||||
struct list_head *head;
|
||||
int i;
|
||||
int busy = lockspace_busy(ls);
|
||||
|
||||
if (busy > force)
|
||||
return -EBUSY;
|
||||
|
||||
if (force < 3)
|
||||
do_uevent(ls, 0);
|
||||
|
||||
dlm_recoverd_stop(ls);
|
||||
|
||||
remove_lockspace(ls);
|
||||
|
||||
dlm_delete_debug_file(ls);
|
||||
|
||||
dlm_astd_suspend();
|
||||
|
||||
kfree(ls->ls_recover_buf);
|
||||
|
||||
/*
|
||||
* Free direntry structs.
|
||||
*/
|
||||
|
||||
dlm_dir_clear(ls);
|
||||
kfree(ls->ls_dirtbl);
|
||||
|
||||
/*
|
||||
* Free all lkb's on lkbtbl[] lists.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ls->ls_lkbtbl_size; i++) {
|
||||
head = &ls->ls_lkbtbl[i].list;
|
||||
while (!list_empty(head)) {
|
||||
lkb = list_entry(head->next, struct dlm_lkb,
|
||||
lkb_idtbl_list);
|
||||
|
||||
list_del(&lkb->lkb_idtbl_list);
|
||||
|
||||
dlm_del_ast(lkb);
|
||||
|
||||
if (lkb->lkb_lvbptr && lkb->lkb_flags & DLM_IFL_MSTCPY)
|
||||
free_lvb(lkb->lkb_lvbptr);
|
||||
|
||||
free_lkb(lkb);
|
||||
}
|
||||
}
|
||||
dlm_astd_resume();
|
||||
|
||||
kfree(ls->ls_lkbtbl);
|
||||
|
||||
/*
|
||||
* Free all rsb's on rsbtbl[] lists
|
||||
*/
|
||||
|
||||
for (i = 0; i < ls->ls_rsbtbl_size; i++) {
|
||||
head = &ls->ls_rsbtbl[i].list;
|
||||
while (!list_empty(head)) {
|
||||
rsb = list_entry(head->next, struct dlm_rsb,
|
||||
res_hashchain);
|
||||
|
||||
list_del(&rsb->res_hashchain);
|
||||
free_rsb(rsb);
|
||||
}
|
||||
|
||||
head = &ls->ls_rsbtbl[i].toss;
|
||||
while (!list_empty(head)) {
|
||||
rsb = list_entry(head->next, struct dlm_rsb,
|
||||
res_hashchain);
|
||||
list_del(&rsb->res_hashchain);
|
||||
free_rsb(rsb);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(ls->ls_rsbtbl);
|
||||
|
||||
/*
|
||||
* Free structures on any other lists
|
||||
*/
|
||||
|
||||
kfree(ls->ls_recover_args);
|
||||
dlm_clear_free_entries(ls);
|
||||
dlm_clear_members(ls);
|
||||
dlm_clear_members_gone(ls);
|
||||
kfree(ls->ls_node_array);
|
||||
kobject_unregister(&ls->ls_kobj);
|
||||
kfree(ls);
|
||||
|
||||
down(&ls_lock);
|
||||
ls_count--;
|
||||
if (!ls_count)
|
||||
threads_stop();
|
||||
up(&ls_lock);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a system has released all its locks and is not going to use the
|
||||
* lockspace any longer. We free everything we're managing for this lockspace.
|
||||
* Remaining nodes will go through the recovery process as if we'd died. The
|
||||
* lockspace must continue to function as usual, participating in recoveries,
|
||||
* until this returns.
|
||||
*
|
||||
* Force has 4 possible values:
|
||||
* 0 - don't destroy locksapce if it has any LKBs
|
||||
* 1 - destroy lockspace if it has remote LKBs but not if it has local LKBs
|
||||
* 2 - destroy lockspace regardless of LKBs
|
||||
* 3 - destroy lockspace as part of a forced shutdown
|
||||
*/
|
||||
|
||||
int dlm_release_lockspace(void *lockspace, int force)
|
||||
{
|
||||
struct dlm_ls *ls;
|
||||
|
||||
ls = dlm_find_lockspace_local(lockspace);
|
||||
if (!ls)
|
||||
return -EINVAL;
|
||||
dlm_put_lockspace(ls);
|
||||
return release_lockspace(ls, force);
|
||||
}
|
||||
|
||||
24
fs/dlm/lockspace.h
Normal file
24
fs/dlm/lockspace.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __LOCKSPACE_DOT_H__
|
||||
#define __LOCKSPACE_DOT_H__
|
||||
|
||||
int dlm_lockspace_init(void);
|
||||
void dlm_lockspace_exit(void);
|
||||
struct dlm_ls *dlm_find_lockspace_global(uint32_t id);
|
||||
struct dlm_ls *dlm_find_lockspace_local(void *id);
|
||||
void dlm_put_lockspace(struct dlm_ls *ls);
|
||||
|
||||
#endif /* __LOCKSPACE_DOT_H__ */
|
||||
|
||||
1218
fs/dlm/lowcomms.c
Normal file
1218
fs/dlm/lowcomms.c
Normal file
File diff suppressed because it is too large
Load diff
25
fs/dlm/lowcomms.h
Normal file
25
fs/dlm/lowcomms.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
||||
** Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __LOWCOMMS_DOT_H__
|
||||
#define __LOWCOMMS_DOT_H__
|
||||
|
||||
int dlm_lowcomms_init(void);
|
||||
void dlm_lowcomms_exit(void);
|
||||
int dlm_lowcomms_start(void);
|
||||
void dlm_lowcomms_stop(void);
|
||||
void *dlm_lowcomms_get_buffer(int nodeid, int len, int allocation, char **ppc);
|
||||
void dlm_lowcomms_commit_buffer(void *mh);
|
||||
|
||||
#endif /* __LOWCOMMS_DOT_H__ */
|
||||
|
||||
18
fs/dlm/lvb_table.h
Normal file
18
fs/dlm/lvb_table.h
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/******************************************************************************
|
||||
*******************************************************************************
|
||||
**
|
||||
** Copyright (C) 2005 Red Hat, Inc. All rights reserved.
|
||||
**
|
||||
** This copyrighted material is made available to anyone wishing to use,
|
||||
** modify, copy, or redistribute it subject to the terms and conditions
|
||||
** of the GNU General Public License v.2.
|
||||
**
|
||||
*******************************************************************************
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __LVB_TABLE_DOT_H__
|
||||
#define __LVB_TABLE_DOT_H__
|
||||
|
||||
extern const int dlm_lvb_operations[8][8];
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue