Page MenuHomeClusterLabs Projects

proc.c
No OneTemporary

/******************************************************************************
*******************************************************************************
**
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
** Copyright (C) 2004 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/init.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include "dlm_internal.h"
#include "lockspace.h"
#if defined(DLM_DEBUG)
#define DLM_DEBUG_SIZE (1024)
#define MAX_DEBUG_MSG_LEN (64)
#else
#define DLM_DEBUG_SIZE (0)
#define MAX_DEBUG_MSG_LEN (0)
#endif
static char * debug_buf;
static unsigned int debug_size;
static unsigned int debug_point;
static int debug_wrap;
static spinlock_t debug_lock;
static struct proc_dir_entry * debug_proc_entry = NULL;
static char proc_ls_name[255] = "";
#ifdef CONFIG_CLUSTER_DLM_PROCLOCKS
static struct proc_dir_entry * locks_proc_entry = NULL;
static struct seq_operations locks_info_op;
static struct proc_dir_entry * dir_proc_entry = NULL;
static struct seq_operations dir_info_op;
/*
* /proc/cluster/dlm_locks - dump resources and locks
*/
static int locks_open(struct inode *inode, struct file *file)
{
return seq_open(file, &locks_info_op);
}
/* Write simply sets the lockspace to use */
static ssize_t locks_write(struct file *file, const char *buf,
size_t count, loff_t * ppos)
{
int error;
if (count < sizeof(proc_ls_name)) {
error = copy_from_user(proc_ls_name, buf, count);
if (error)
return error;
proc_ls_name[count] = '\0';
/* Remove any trailing LF so that lazy users
can just echo "lsname" > /proc/cluster/dlm_locks */
if (proc_ls_name[count - 1] == '\n')
proc_ls_name[count - 1] = '\0';
return count;
}
return 0;
}
static struct file_operations locks_fops = {
open:locks_open,
write:locks_write,
read:seq_read,
llseek:seq_lseek,
release:seq_release,
};
struct ls_dumpinfo {
int entry;
struct list_head *next;
struct dlm_ls *ls;
struct dlm_rsb *rsb;
struct dlm_direntry *de;
};
static int print_resource(struct dlm_rsb * res, struct seq_file *s);
static struct ls_dumpinfo *next_rsb(struct ls_dumpinfo *di)
{
int i;
if (!di->next) {
/* Find the next non-empty hash bucket */
for (i = di->entry; i < di->ls->ls_rsbtbl_size; i++) {
read_lock(&di->ls->ls_rsbtbl[i].lock);
if (!list_empty(&di->ls->ls_rsbtbl[i].list)) {
di->next = di->ls->ls_rsbtbl[i].list.next;
read_unlock(&di->ls->ls_rsbtbl[i].lock);
break;
}
read_unlock(&di->ls->ls_rsbtbl[i].lock);
}
di->entry = i;
if (di->entry >= di->ls->ls_rsbtbl_size)
return NULL; /* End of hash list */
} else { /* Find the next entry in the list */
i = di->entry;
read_lock(&di->ls->ls_rsbtbl[i].lock);
di->next = di->next->next;
if (di->next->next == di->ls->ls_rsbtbl[i].list.next) {
/* End of list - move to next bucket */
di->next = NULL;
di->entry++;
read_unlock(&di->ls->ls_rsbtbl[i].lock);
return next_rsb(di); /* do the top half of this conditional */
}
read_unlock(&di->ls->ls_rsbtbl[i].lock);
}
di->rsb = list_entry(di->next, struct dlm_rsb, res_hashchain);
return di;
}
static void *s_start(struct seq_file *m, loff_t *pos)
{
struct ls_dumpinfo *di;
struct dlm_ls *ls;
int i;
ls = find_lockspace_by_name(proc_ls_name, strlen(proc_ls_name));
if (!ls)
return NULL;
if (!m->private) {
di = kmalloc(sizeof(struct ls_dumpinfo), GFP_KERNEL);
m->private = di;
if (!di)
return NULL;
}
else
di = m->private;
if (*pos == 0)
seq_printf(m, "DLM lockspace '%s'\n", proc_ls_name);
di->entry = 0;
di->next = NULL;
di->ls = ls;
di->de = NULL;
for (i = 0; i < *pos; i++)
if (next_rsb(di) == NULL)
return NULL;
return next_rsb(di);
}
static void *s_next(struct seq_file *m, void *p, loff_t *pos)
{
struct ls_dumpinfo *di = p;
*pos += 1;
return next_rsb(di);
}
static int s_show(struct seq_file *m, void *p)
{
struct ls_dumpinfo *di = p;
return print_resource(di->rsb, m);
}
static void s_stop(struct seq_file *m, void *p)
{
if (m->private) {
kfree(m->private);
m->private = NULL;
}
}
static struct seq_operations locks_info_op = {
start:s_start,
next:s_next,
stop:s_stop,
show:s_show
};
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 == GDLM_LKSTS_CONVERT
|| lkb->lkb_status == GDLM_LKSTS_WAITING)
seq_printf(s, " (%s)", print_lockmode(lkb->lkb_rqmode));
if (lkb->lkb_range) {
/* This warns on Alpha. Tough. Only I see it */
if (lkb->lkb_status == GDLM_LKSTS_CONVERT
|| lkb->lkb_status == GDLM_LKSTS_GRANTED)
seq_printf(s, " %" PRIx64 "-%" PRIx64,
lkb->lkb_range[GR_RANGE_START],
lkb->lkb_range[GR_RANGE_END]);
if (lkb->lkb_status == GDLM_LKSTS_CONVERT
|| lkb->lkb_status == GDLM_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_status != GDLM_LKSTS_GRANTED)
seq_printf(s, " LQ: %d,0x%x", lkb->lkb_lockqueue_state,
lkb->lkb_lockqueue_flags);
seq_printf(s, "\n");
}
static int print_resource(struct dlm_rsb *res, struct seq_file *s)
{
int i;
struct list_head *locklist;
seq_printf(s, "\nResource %p (parent %p). Name (len=%d) \"", res,
res->res_parent, 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)
seq_printf(s, "\" \nLocal Copy, Master is node %d\n",
res->res_nodeid);
else
seq_printf(s, "\" \nMaster Copy\n");
/* Print the LVB: */
if (res->res_lvbptr) {
seq_printf(s, "LVB: ");
for (i = 0; i < DLM_LVB_LEN; i++) {
if (i == DLM_LVB_LEN / 2)
seq_printf(s, "\n ");
seq_printf(s, "%02x ",
(unsigned char) res->res_lvbptr[i]);
}
if (test_bit(RESFL_VALNOTVALID, &res->res_flags))
seq_printf(s, " (INVALID)");
seq_printf(s, "\n");
}
/* Print the locks attached to this resource */
seq_printf(s, "Granted Queue\n");
list_for_each(locklist, &res->res_grantqueue) {
struct dlm_lkb *this_lkb =
list_entry(locklist, struct dlm_lkb, lkb_statequeue);
print_lock(s, this_lkb, res);
}
seq_printf(s, "Conversion Queue\n");
list_for_each(locklist, &res->res_convertqueue) {
struct dlm_lkb *this_lkb =
list_entry(locklist, struct dlm_lkb, lkb_statequeue);
print_lock(s, this_lkb, res);
}
seq_printf(s, "Waiting Queue\n");
list_for_each(locklist, &res->res_waitqueue) {
struct dlm_lkb *this_lkb =
list_entry(locklist, struct dlm_lkb, lkb_statequeue);
print_lock(s, this_lkb, res);
}
return 0;
}
/*
* /proc/cluster/dlm_dir - dump resource directory
*/
static int print_de(struct dlm_direntry *de, struct seq_file *s)
{
char strname[DLM_RESNAME_MAXLEN+1];
memset(strname, 0, DLM_RESNAME_MAXLEN+1);
memcpy(strname, de->name, de->length);
seq_printf(s, "%s %u\n", strname, de->master_nodeid);
return 0;
}
static int dir_open(struct inode *inode, struct file *file)
{
return seq_open(file, &dir_info_op);
}
static ssize_t dir_write(struct file *file, const char *buf,
size_t count, loff_t *ppos)
{
return locks_write(file, buf, count, ppos);
}
static struct file_operations dir_fops = {
.open = dir_open,
.write = dir_write,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.owner = THIS_MODULE,
};
static struct ls_dumpinfo *next_de(struct ls_dumpinfo *di)
{
int i;
if (!di->next) {
/* Find the next non-empty hash bucket */
for (i = di->entry; i < di->ls->ls_dirtbl_size; i++) {
read_lock(&di->ls->ls_dirtbl[i].lock);
if (!list_empty(&di->ls->ls_dirtbl[i].list)) {
di->next = di->ls->ls_dirtbl[i].list.next;
read_unlock(&di->ls->ls_dirtbl[i].lock);
break;
}
read_unlock(&di->ls->ls_dirtbl[i].lock);
}
di->entry = i;
if (di->entry >= di->ls->ls_dirtbl_size)
return NULL; /* End of hash list */
} else { /* Find the next entry in the list */
i = di->entry;
read_lock(&di->ls->ls_dirtbl[i].lock);
di->next = di->next->next;
if (di->next->next == di->ls->ls_dirtbl[i].list.next) {
/* End of list - move to next bucket */
di->next = NULL;
di->entry++;
read_unlock(&di->ls->ls_dirtbl[i].lock);
return next_de(di); /* do the top half of this conditional */
}
read_unlock(&di->ls->ls_dirtbl[i].lock);
}
di->de = list_entry(di->next, struct dlm_direntry, list);
return di;
}
static void *dir_start(struct seq_file *m, loff_t *pos)
{
struct ls_dumpinfo *di;
struct dlm_ls *ls;
int i;
ls = find_lockspace_by_name(proc_ls_name, strlen(proc_ls_name));
if (!ls)
return NULL;
if (!m->private) {
di = kmalloc(sizeof(struct ls_dumpinfo), GFP_KERNEL);
m->private = di;
if (!di)
return NULL;
}
else
di = m->private;
if (*pos == 0)
seq_printf(m, "DLM lockspace '%s'\n", proc_ls_name);
di->entry = 0;
di->next = NULL;
di->ls = ls;
for (i = 0; i < *pos; i++)
if (next_de(di) == NULL)
return NULL;
return next_de(di);
}
static void *dir_next(struct seq_file *m, void *p, loff_t *pos)
{
struct ls_dumpinfo *di = p;
*pos += 1;
return next_de(di);
}
static int dir_show(struct seq_file *m, void *p)
{
struct ls_dumpinfo *di = p;
return print_de(di->de, m);
}
static void dir_stop(struct seq_file *m, void *p)
{
if (m->private) {
kfree(m->private);
m->private = NULL;
}
}
static struct seq_operations dir_info_op = {
.start = dir_start,
.next = dir_next,
.stop = dir_stop,
.show = dir_show,
};
#endif /* CONFIG_CLUSTER_DLM_PROCLOCKS */
void dlm_debug_log(struct dlm_ls *ls, const char *fmt, ...)
{
va_list va;
int i, n, size, len;
char buf[MAX_DEBUG_MSG_LEN+1];
spin_lock(&debug_lock);
if (!debug_buf)
goto out;
size = MAX_DEBUG_MSG_LEN;
memset(buf, 0, size+1);
n = snprintf(buf, size, "%s ", ls->ls_name);
size -= n;
va_start(va, fmt);
vsnprintf(buf+n, size, fmt, va);
va_end(va);
len = strlen(buf);
if (len > MAX_DEBUG_MSG_LEN-1)
len = MAX_DEBUG_MSG_LEN-1;
buf[len] = '\n';
buf[len+1] = '\0';
for (i = 0; i < strlen(buf); i++) {
debug_buf[debug_point++] = buf[i];
if (debug_point == debug_size) {
debug_point = 0;
debug_wrap = 1;
}
}
out:
spin_unlock(&debug_lock);
}
void dlm_debug_dump(void)
{
int i;
spin_lock(&debug_lock);
if (debug_wrap) {
for (i = debug_point; i < debug_size; i++)
printk("%c", debug_buf[i]);
}
for (i = 0; i < debug_point; i++)
printk("%c", debug_buf[i]);
spin_unlock(&debug_lock);
}
void dlm_debug_setup(int size)
{
char *b = NULL;
if (size > PAGE_SIZE)
size = PAGE_SIZE;
if (size)
b = kmalloc(size, GFP_KERNEL);
spin_lock(&debug_lock);
if (debug_buf)
kfree(debug_buf);
if (!size || !b)
goto out;
debug_size = size;
debug_point = 0;
debug_wrap = 0;
debug_buf = b;
memset(debug_buf, 0, debug_size);
out:
spin_unlock(&debug_lock);
}
static void dlm_debug_init(void)
{
debug_buf = NULL;
debug_size = 0;
debug_point = 0;
debug_wrap = 0;
spin_lock_init(&debug_lock);
dlm_debug_setup(DLM_DEBUG_SIZE);
}
#ifdef CONFIG_PROC_FS
int dlm_debug_info(char *b, char **start, off_t offset, int length)
{
int i, n = 0;
spin_lock(&debug_lock);
if (debug_wrap) {
for (i = debug_point; i < debug_size; i++)
n += sprintf(b + n, "%c", debug_buf[i]);
}
for (i = 0; i < debug_point; i++)
n += sprintf(b + n, "%c", debug_buf[i]);
spin_unlock(&debug_lock);
return n;
}
#endif
#ifdef CONFIG_DLM_STATS
struct dlm_statinfo dlm_stats;
static struct proc_dir_entry *stats_proc_entry = NULL;
static int dlm_stats_info(char *b, char **start, off_t offset, int length)
{
int n=0;
int i;
long lq_locks = 0;
unsigned long lq_time = 0;
n += sprintf(b+n, "DLM stats (HZ=%d)\n\n", HZ);
n += sprintf(b+n, "Lock operations: %7d\n", dlm_stats.lockops);
n += sprintf(b+n, "Unlock operations: %7d\n", dlm_stats.unlockops);
n += sprintf(b+n, "Convert operations: %7d\n", dlm_stats.convertops);
n += sprintf(b+n, "Completion ASTs: %7d\n", dlm_stats.cast);
n += sprintf(b+n, "Blocking ASTs: %7d\n", dlm_stats.bast);
n += sprintf(b+n, "\n");
n += sprintf(b+n, "Lockqueue num waittime ave\n");
for (i=1; i<=4 ; i++) {
char *lq_reason="???";
switch (i){
case 1: lq_reason = "WAIT_RSB ";
break;
case 2: lq_reason = "WAIT_CONV ";
break;
case 3: lq_reason = "WAIT_GRANT ";
break;
case 4: lq_reason = "WAIT_UNLOCK";
break;
}
if (dlm_stats.lockqueue_locks[i])
n += sprintf(b+n, "%s %6lu %7lu %3lu\n",
lq_reason,
dlm_stats.lockqueue_locks[i],
dlm_stats.lockqueue_time[i],
dlm_stats.lockqueue_time[i]/
dlm_stats.lockqueue_locks[i]);
lq_locks += dlm_stats.lockqueue_locks[i];
lq_time += dlm_stats.lockqueue_time[i];
}
if (lq_locks)
n += sprintf(b+n, "Total %6lu %7lu %3lu\n",
lq_locks, lq_time, lq_time/lq_locks);
return n;
}
static int dlm_stats_clear(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
memset(&dlm_stats, 0, sizeof(dlm_stats));
return count;
}
#endif /* CONFIG_DLM_STATS */
void dlm_proc_init(void)
{
#ifdef CONFIG_PROC_FS
debug_proc_entry = create_proc_entry("cluster/dlm_debug", S_IRUGO,
NULL);
if (!debug_proc_entry)
return;
debug_proc_entry->get_info = &dlm_debug_info;
#endif
#ifdef CONFIG_DLM_STATS
stats_proc_entry = create_proc_entry("cluster/dlm_stats",
S_IRUSR | S_IWUSR, NULL);
if (!stats_proc_entry)
return;
stats_proc_entry->get_info = &dlm_stats_info;
stats_proc_entry->write_proc = &dlm_stats_clear;
#endif
dlm_debug_init();
#ifdef CONFIG_CLUSTER_DLM_PROCLOCKS
locks_proc_entry = create_proc_read_entry("cluster/dlm_locks",
S_IFREG | 0400,
NULL, NULL, NULL);
if (!locks_proc_entry)
return;
locks_proc_entry->proc_fops = &locks_fops;
dir_proc_entry = create_proc_read_entry("cluster/dlm_dir",
S_IFREG | 0400,
NULL, NULL, NULL);
if (!dir_proc_entry)
return;
dir_proc_entry->proc_fops = &dir_fops;
#endif
}
void dlm_proc_exit(void)
{
#ifdef CONFIG_PROC_FS
if (debug_proc_entry) {
remove_proc_entry("cluster/dlm_debug", NULL);
dlm_debug_setup(0);
}
#endif
#ifdef CONFIG_DLM_STATS
if (stats_proc_entry)
remove_proc_entry("cluster/dlm_stats", NULL);
#endif
#ifdef CONFIG_CLUSTER_DLM_PROCLOCKS
if (locks_proc_entry)
remove_proc_entry("cluster/dlm_locks", NULL);
if (dir_proc_entry)
remove_proc_entry("cluster/dlm_dir", NULL);
#endif
}

File Metadata

Mime Type
text/x-c
Expires
Tue, Feb 25, 7:54 PM (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464959
Default Alt Text
proc.c (15 KB)

Event Timeline