825 lines · 18.5 KB · C
RawDownload
/*****************************************************************************************************************************************************************  Copyright (C) 2005-2009 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/init.h>#include <linux/ctype.h>#include <linux/debugfs.h>#include <linux/slab.h> #include "dlm_internal.h"#include "lock.h" #define DLM_DEBUG_BUF_LEN 4096static char debug_buf[DLM_DEBUG_BUF_LEN];static struct mutex debug_buf_lock; static struct dentry *dlm_root; 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_format1_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_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_puts(s, "\n");} static void print_format1(struct dlm_rsb *res, struct seq_file *s){	struct dlm_lkb *lkb;	int i, lvblen = res->res_ls->ls_lvblen, recover_list, root_list; 	lock_rsb(res); 	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_puts(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);	if (seq_has_overflowed(s))		goto out; 	/* Print the LVB: */	if (res->res_lvbptr) {		seq_puts(s, "LVB: ");		for (i = 0; i < lvblen; i++) {			if (i == lvblen / 2)				seq_puts(s, "\n     ");			seq_printf(s, "%02x ",				   (unsigned char) res->res_lvbptr[i]);		}		if (rsb_flag(res, RSB_VALNOTVALID))			seq_puts(s, " (INVALID)");		seq_puts(s, "\n");		if (seq_has_overflowed(s))			goto out;	} 	root_list = !list_empty(&res->res_root_list);	recover_list = !list_empty(&res->res_recover_list); 	if (root_list || recover_list) {		seq_printf(s, "Recovery: root %d recover %d flags %lx count %d\n",			   root_list, recover_list,			   res->res_flags, res->res_recover_locks_count);	} 	/* Print the locks attached to this resource */	seq_puts(s, "Granted Queue\n");	list_for_each_entry(lkb, &res->res_grantqueue, lkb_statequeue) {		print_format1_lock(s, lkb, res);		if (seq_has_overflowed(s))			goto out;	} 	seq_puts(s, "Conversion Queue\n");	list_for_each_entry(lkb, &res->res_convertqueue, lkb_statequeue) {		print_format1_lock(s, lkb, res);		if (seq_has_overflowed(s))			goto out;	} 	seq_puts(s, "Waiting Queue\n");	list_for_each_entry(lkb, &res->res_waitqueue, lkb_statequeue) {		print_format1_lock(s, lkb, res);		if (seq_has_overflowed(s))			goto out;	} 	if (list_empty(&res->res_lookup))		goto out; 	seq_puts(s, "Lookup Queue\n");	list_for_each_entry(lkb, &res->res_lookup, lkb_rsb_lookup) {		seq_printf(s, "%08x %s",			   lkb->lkb_id, print_lockmode(lkb->lkb_rqmode));		if (lkb->lkb_wait_type)			seq_printf(s, " wait_type: %d", lkb->lkb_wait_type);		seq_puts(s, "\n");		if (seq_has_overflowed(s))			goto out;	} out:	unlock_rsb(res);} static void print_format2_lock(struct seq_file *s, struct dlm_lkb *lkb,			       struct dlm_rsb *r){	u64 xid = 0;	u64 us; 	if (lkb->lkb_flags & DLM_IFL_USER) {		if (lkb->lkb_ua)			xid = lkb->lkb_ua->xid;	} 	/* microseconds since lkb was added to current queue */	us = ktime_to_us(ktime_sub(ktime_get(), lkb->lkb_timestamp)); 	/* id nodeid remid pid xid exflags flags sts grmode rqmode time_us	   r_nodeid r_len r_name */ 	seq_printf(s, "%x %d %x %u %llu %x %x %d %d %d %llu %u %d \"%s\"\n",		   lkb->lkb_id,		   lkb->lkb_nodeid,		   lkb->lkb_remid,		   lkb->lkb_ownpid,		   (unsigned long long)xid,		   lkb->lkb_exflags,		   lkb->lkb_flags,		   lkb->lkb_status,		   lkb->lkb_grmode,		   lkb->lkb_rqmode,		   (unsigned long long)us,		   r->res_nodeid,		   r->res_length,		   r->res_name);} static void print_format2(struct dlm_rsb *r, struct seq_file *s){	struct dlm_lkb *lkb; 	lock_rsb(r); 	list_for_each_entry(lkb, &r->res_grantqueue, lkb_statequeue) {		print_format2_lock(s, lkb, r);		if (seq_has_overflowed(s))			goto out;	} 	list_for_each_entry(lkb, &r->res_convertqueue, lkb_statequeue) {		print_format2_lock(s, lkb, r);		if (seq_has_overflowed(s))			goto out;	} 	list_for_each_entry(lkb, &r->res_waitqueue, lkb_statequeue) {		print_format2_lock(s, lkb, r);		if (seq_has_overflowed(s))			goto out;	} out:	unlock_rsb(r);} static void print_format3_lock(struct seq_file *s, struct dlm_lkb *lkb,			      int rsb_lookup){	u64 xid = 0; 	if (lkb->lkb_flags & DLM_IFL_USER) {		if (lkb->lkb_ua)			xid = lkb->lkb_ua->xid;	} 	seq_printf(s, "lkb %x %d %x %u %llu %x %x %d %d %d %d %d %d %u %llu %llu\n",		   lkb->lkb_id,		   lkb->lkb_nodeid,		   lkb->lkb_remid,		   lkb->lkb_ownpid,		   (unsigned long long)xid,		   lkb->lkb_exflags,		   lkb->lkb_flags,		   lkb->lkb_status,		   lkb->lkb_grmode,		   lkb->lkb_rqmode,		   lkb->lkb_last_bast.mode,		   rsb_lookup,		   lkb->lkb_wait_type,		   lkb->lkb_lvbseq,		   (unsigned long long)ktime_to_ns(lkb->lkb_timestamp),		   (unsigned long long)ktime_to_ns(lkb->lkb_last_bast_time));} static void print_format3(struct dlm_rsb *r, struct seq_file *s){	struct dlm_lkb *lkb;	int i, lvblen = r->res_ls->ls_lvblen;	int print_name = 1; 	lock_rsb(r); 	seq_printf(s, "rsb %p %d %x %lx %d %d %u %d ",		   r,		   r->res_nodeid,		   r->res_first_lkid,		   r->res_flags,		   !list_empty(&r->res_root_list),		   !list_empty(&r->res_recover_list),		   r->res_recover_locks_count,		   r->res_length);	if (seq_has_overflowed(s))		goto out; 	for (i = 0; i < r->res_length; i++) {		if (!isascii(r->res_name[i]) || !isprint(r->res_name[i]))			print_name = 0;	} 	seq_puts(s, print_name ? "str " : "hex"); 	for (i = 0; i < r->res_length; i++) {		if (print_name)			seq_printf(s, "%c", r->res_name[i]);		else			seq_printf(s, " %02x", (unsigned char)r->res_name[i]);	}	seq_puts(s, "\n");	if (seq_has_overflowed(s))		goto out; 	if (!r->res_lvbptr)		goto do_locks; 	seq_printf(s, "lvb %u %d", r->res_lvbseq, lvblen); 	for (i = 0; i < lvblen; i++)		seq_printf(s, " %02x", (unsigned char)r->res_lvbptr[i]);	seq_puts(s, "\n");	if (seq_has_overflowed(s))		goto out;  do_locks:	list_for_each_entry(lkb, &r->res_grantqueue, lkb_statequeue) {		print_format3_lock(s, lkb, 0);		if (seq_has_overflowed(s))			goto out;	} 	list_for_each_entry(lkb, &r->res_convertqueue, lkb_statequeue) {		print_format3_lock(s, lkb, 0);		if (seq_has_overflowed(s))			goto out;	} 	list_for_each_entry(lkb, &r->res_waitqueue, lkb_statequeue) {		print_format3_lock(s, lkb, 0);		if (seq_has_overflowed(s))			goto out;	} 	list_for_each_entry(lkb, &r->res_lookup, lkb_rsb_lookup) {		print_format3_lock(s, lkb, 1);		if (seq_has_overflowed(s))			goto out;	} out:	unlock_rsb(r);} static void print_format4(struct dlm_rsb *r, struct seq_file *s){	int our_nodeid = dlm_our_nodeid();	int print_name = 1;	int i; 	lock_rsb(r); 	seq_printf(s, "rsb %p %d %d %d %d %lu %lx %d ",		   r,		   r->res_nodeid,		   r->res_master_nodeid,		   r->res_dir_nodeid,		   our_nodeid,		   r->res_toss_time,		   r->res_flags,		   r->res_length); 	for (i = 0; i < r->res_length; i++) {		if (!isascii(r->res_name[i]) || !isprint(r->res_name[i]))			print_name = 0;	} 	seq_puts(s, print_name ? "str " : "hex"); 	for (i = 0; i < r->res_length; i++) {		if (print_name)			seq_printf(s, "%c", r->res_name[i]);		else			seq_printf(s, " %02x", (unsigned char)r->res_name[i]);	}	seq_puts(s, "\n"); 	unlock_rsb(r);} struct rsbtbl_iter {	struct dlm_rsb *rsb;	unsigned bucket;	int format;	int header;}; /* * If the buffer is full, seq_printf can be called again, but it * does nothing.  So, the these printing routines periodically check * seq_has_overflowed to avoid wasting too much time trying to print to * a full buffer. */ static int table_seq_show(struct seq_file *seq, void *iter_ptr){	struct rsbtbl_iter *ri = iter_ptr; 	switch (ri->format) {	case 1:		print_format1(ri->rsb, seq);		break;	case 2:		if (ri->header) {			seq_puts(seq, "id nodeid remid pid xid exflags flags sts grmode rqmode time_ms r_nodeid r_len r_name\n");			ri->header = 0;		}		print_format2(ri->rsb, seq);		break;	case 3:		if (ri->header) {			seq_puts(seq, "version rsb 1.1 lvb 1.1 lkb 1.1\n");			ri->header = 0;		}		print_format3(ri->rsb, seq);		break;	case 4:		if (ri->header) {			seq_puts(seq, "version 4 rsb 2\n");			ri->header = 0;		}		print_format4(ri->rsb, seq);		break;	} 	return 0;} static const struct seq_operations format1_seq_ops;static const struct seq_operations format2_seq_ops;static const struct seq_operations format3_seq_ops;static const struct seq_operations format4_seq_ops; static void *table_seq_start(struct seq_file *seq, loff_t *pos){	struct rb_root *tree;	struct rb_node *node;	struct dlm_ls *ls = seq->private;	struct rsbtbl_iter *ri;	struct dlm_rsb *r;	loff_t n = *pos;	unsigned bucket, entry;	int toss = (seq->op == &format4_seq_ops); 	bucket = n >> 32;	entry = n & ((1LL << 32) - 1); 	if (bucket >= ls->ls_rsbtbl_size)		return NULL; 	ri = kzalloc(sizeof(struct rsbtbl_iter), GFP_NOFS);	if (!ri)		return NULL;	if (n == 0)		ri->header = 1;	if (seq->op == &format1_seq_ops)		ri->format = 1;	if (seq->op == &format2_seq_ops)		ri->format = 2;	if (seq->op == &format3_seq_ops)		ri->format = 3;	if (seq->op == &format4_seq_ops)		ri->format = 4; 	tree = toss ? &ls->ls_rsbtbl[bucket].toss : &ls->ls_rsbtbl[bucket].keep; 	spin_lock(&ls->ls_rsbtbl[bucket].lock);	if (!RB_EMPTY_ROOT(tree)) {		for (node = rb_first(tree); node; node = rb_next(node)) {			r = rb_entry(node, struct dlm_rsb, res_hashnode);			if (!entry--) {				dlm_hold_rsb(r);				ri->rsb = r;				ri->bucket = bucket;				spin_unlock(&ls->ls_rsbtbl[bucket].lock);				return ri;			}		}	}	spin_unlock(&ls->ls_rsbtbl[bucket].lock); 	/*	 * move to the first rsb in the next non-empty bucket	 */ 	/* zero the entry */	n &= ~((1LL << 32) - 1); 	while (1) {		bucket++;		n += 1LL << 32; 		if (bucket >= ls->ls_rsbtbl_size) {			kfree(ri);			return NULL;		}		tree = toss ? &ls->ls_rsbtbl[bucket].toss : &ls->ls_rsbtbl[bucket].keep; 		spin_lock(&ls->ls_rsbtbl[bucket].lock);		if (!RB_EMPTY_ROOT(tree)) {			node = rb_first(tree);			r = rb_entry(node, struct dlm_rsb, res_hashnode);			dlm_hold_rsb(r);			ri->rsb = r;			ri->bucket = bucket;			spin_unlock(&ls->ls_rsbtbl[bucket].lock);			*pos = n;			return ri;		}		spin_unlock(&ls->ls_rsbtbl[bucket].lock);	}}

Lines 1–500 of 825.