From c0b9c99922da2f059d145e92f9636f37d1fea49e Mon Sep 17 00:00:00 2001 From: nikita Date: Sat, 18 Oct 2008 17:36:17 +0000 Subject: [PATCH 1/1] lu_ref is a debugging module allowing to track references to a given object. It is quite cpu expensive, and has to be explicitly enabled with --enable-lu_ref. See usage description within the patch. b=16450 --- lustre/include/lu_ref.h | 203 +++++++++++++++++++++++++++++++++++ lustre/obdclass/lu_ref.c | 270 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 473 insertions(+) create mode 100644 lustre/include/lu_ref.h create mode 100644 lustre/obdclass/lu_ref.c diff --git a/lustre/include/lu_ref.h b/lustre/include/lu_ref.h new file mode 100644 index 0000000..4125930 --- /dev/null +++ b/lustre/include/lu_ref.h @@ -0,0 +1,203 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Author: Nikita Danilov + * + * This file is part of Lustre, http://www.lustre.org. + * + * Lustre is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * Lustre is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Lustre; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef __LUSTRE_LU_REF_H +#define __LUSTRE_LU_REF_H + +#include + +/** \defgroup lu_ref lu_ref + * + * An interface to track references between objects. Mostly for debugging. + * + * Suppose there is a reference counted data-structure struct foo. To track + * who acquired references to instance of struct foo, add lu_ref field to it: + * + * \code + * struct foo { + * atomic_t foo_refcount; + * struct lu_ref foo_reference; + * ... + * }; + * \endcode + * + * foo::foo_reference has to be initialized by calling + * lu_ref_init(). Typically there will be functions or macros to increment and + * decrement foo::foo_refcount, let's say they are foo_get(struct foo *foo) + * and foo_put(struct foo *foo), respectively. + * + * Whenever foo_get() is called to acquire a reference on a foo, lu_ref_add() + * has to be called to insert into foo::foo_reference a record, describing + * acquired reference. Dually, lu_ref_del() removes matching record. Typical + * usages are: + * + * \code + * struct bar *bar; + * + * // bar owns a reference to foo. + * bar->bar_foo = foo_get(foo); + * lu_ref_add(&foo->foo_reference, "bar", bar); + * + * ... + * + * // reference from bar to foo is released. + * lu_ref_del(&foo->foo_reference, "bar", bar); + * foo_put(bar->bar_foo); + * + * + * // current thread acquired a temporary reference to foo. + * foo_get(foo); + * lu_ref_add(&foo->reference, __FUNCTION__, cfs_current()); + * + * ... + * + * // temporary reference is released. + * lu_ref_del(&foo->reference, __FUNCTION__, cfs_current()); + * foo_put(foo); + * \endcode + * + * \e Et \e cetera. Often it makes sense to include lu_ref_add() and + * lu_ref_del() calls into foo_get() and foo_put(). When an instance of struct + * foo is destroyed, lu_ref_fini() has to be called that checks that no + * pending references remain. lu_ref_print() can be used to dump a list of + * pending references, while hunting down a leak. + * + * For objects to which a large number of references can be acquired, + * lu_ref_del() can become cpu consuming, as it has to scan the list of + * references. To work around this, remember result of lu_ref_add() (usually + * in the same place where pointer to struct foo is stored), and use + * lu_ref_del_at(): + * + * \code + * // There is a large number of bar's for a single foo. + * bar->bar_foo = foo_get(foo); + * bar->bar_foo_ref = lu_ref_add(&foo->foo_reference, "bar", bar); + * + * ... + * + * // reference from bar to foo is released. + * lu_ref_del_at(&foo->foo_reference, bar->bar_foo_ref, "bar", bar); + * foo_put(bar->bar_foo); + * \endcode + * + * lu_ref interface degrades gracefully in case of memory shortages. + * + * @{ + */ + +#ifdef USE_LU_REF + +/* An incomplete type (defined locally in lu_ref.c) */ +struct lu_ref_link; + +/** + * Data-structure to keep track of references to a given object. This is used + * for debugging. + * + * lu_ref is embedded into an object which other entities (objects, threads, + * etc.) refer to. + */ +struct lu_ref { + spinlock_t lf_guard; + struct list_head lf_list; + int lf_failed; +}; + +void lu_ref_init(struct lu_ref *ref); +void lu_ref_fini(struct lu_ref *ref); + +struct lu_ref_link *lu_ref_add (struct lu_ref *ref, const char *scope, + const void *source); +struct lu_ref_link *lu_ref_add_atomic(struct lu_ref *ref, const char *scope, + const void *source); +void lu_ref_del (struct lu_ref *ref, const char *scope, + const void *source); +void lu_ref_set_at (struct lu_ref *ref, + struct lu_ref_link *link, + const char *scope, const void *source0, + const void *source1); +void lu_ref_del_at (struct lu_ref *ref, + struct lu_ref_link *link, + const char *scope, const void *source); +void lu_ref_print (const struct lu_ref *ref); +#else /* !USE_LU_REF */ + +struct lu_ref {}; + +static inline void lu_ref_init(struct lu_ref *ref) +{ +} + +static inline void lu_ref_fini(struct lu_ref *ref) +{ +} + +static inline struct lu_ref_link *lu_ref_add(struct lu_ref *ref, + const char *scope, + const void *source) +{ + return NULL; +} + +static inline struct lu_ref_link *lu_ref_add_atomic(struct lu_ref *ref, + const char *scope, + const void *source) +{ + return NULL; +} + +static inline void lu_ref_del(struct lu_ref *ref, const char *scope, + const void *source) +{ +} + +static inline void lu_ref_set_at(struct lu_ref *ref, struct lu_ref_link *link, + const char *scope, const void *source0, + const void *source1) +{ +} + +static inline void lu_ref_del_at(struct lu_ref *ref, struct lu_ref_link *link, + const char *scope, const void *source) +{ +} + +static inline int lu_ref_global_init(void) +{ + return 0; +} + +static inline void lu_ref_global_fini(void) +{ +} + +static inline void lu_ref_print(const struct lu_ref *ref) +{ +} +#endif /* USE_LU_REF */ + +/** @} lu */ + +#endif /* __LUSTRE_LU_REF_H */ diff --git a/lustre/obdclass/lu_ref.c b/lustre/obdclass/lu_ref.c new file mode 100644 index 0000000..1364bcb --- /dev/null +++ b/lustre/obdclass/lu_ref.c @@ -0,0 +1,270 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + * GPL HEADER END + */ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * Lustre is a trademark of Sun Microsystems, Inc. + * + * lustre/obdclass/lu_ref.c + * + * Lustre reference. + * + * Author: Nikita Danilov + */ + +#define DEBUG_SUBSYSTEM S_CLASS +#ifndef EXPORT_SYMTAB +# define EXPORT_SYMTAB +#endif + +#ifdef __KERNEL__ +# include +#else +# include +#endif + +#include +#include +#include +#include + +#ifdef USE_LU_REF + +struct lu_ref_link { + struct lu_ref *ll_ref; + struct list_head ll_linkage; + const char *ll_scope; + const void *ll_source; +}; + +static cfs_mem_cache_t *lu_ref_link_kmem; + +static struct lu_kmem_descr lu_ref_caches[] = { + { + .ckd_cache = &lu_ref_link_kmem, + .ckd_name = "lu_ref_link_kmem", + .ckd_size = sizeof (struct lu_ref_link) + }, + { + .ckd_cache = NULL + } +}; + +void lu_ref_print(const struct lu_ref *ref) +{ + struct lu_ref_link *link; + + CERROR("lu_ref: %p %d\n", ref, ref->lf_failed); + list_for_each_entry(link, &ref->lf_list, ll_linkage) { + CERROR(" link: %s %p\n", link->ll_scope, link->ll_source); + } +} +EXPORT_SYMBOL(lu_ref_print); + +void lu_ref_init(struct lu_ref *ref) +{ + spin_lock_init(&ref->lf_guard); + CFS_INIT_LIST_HEAD(&ref->lf_list); +} +EXPORT_SYMBOL(lu_ref_init); + +void lu_ref_fini(struct lu_ref *ref) +{ + if (!list_empty(&ref->lf_list)) { + spin_lock(&ref->lf_guard); + lu_ref_print(ref); + spin_unlock(&ref->lf_guard); + } + LASSERT(list_empty(&ref->lf_list)); +} +EXPORT_SYMBOL(lu_ref_fini); +int lu_ref_global_init(void); + +static struct lu_ref_link *lu_ref_add_context(struct lu_ref *ref, + enum cfs_alloc_flags flags, + const char *scope, + const void *source) +{ + struct lu_ref_link *link; + + /* this can be called so early in lustre initialization, that + * lu_ref_link_kmem slab is not yet created. */ + lu_ref_global_init(); + + link = NULL; + if (lu_ref_link_kmem != NULL) { + OBD_SLAB_ALLOC(link, lu_ref_link_kmem, flags, sizeof(*link)); + if (link != NULL) { + link->ll_ref = ref; + link->ll_scope = scope; + link->ll_source = source; + spin_lock(&ref->lf_guard); + list_add_tail(&link->ll_linkage, &ref->lf_list); + spin_unlock(&ref->lf_guard); + } + } + + if (link == NULL) { + spin_lock(&ref->lf_guard); + ref->lf_failed++; + spin_unlock(&ref->lf_guard); + link = ERR_PTR(-ENOMEM); + } + return link; +} + +struct lu_ref_link *lu_ref_add(struct lu_ref *ref, const char *scope, + const void *source) +{ + might_sleep(); + return lu_ref_add_context(ref, CFS_ALLOC_STD, scope, source); +} +EXPORT_SYMBOL(lu_ref_add); + +/** + * Version of lu_ref_add() to be used in non-blockable contexts. + */ +struct lu_ref_link *lu_ref_add_atomic(struct lu_ref *ref, const char *scope, + const void *source) +{ + return lu_ref_add_context(ref, CFS_ALLOC_ATOMIC, scope, source); +} +EXPORT_SYMBOL(lu_ref_add_atomic); + +static inline int lu_ref_link_eq(const struct lu_ref_link *link, + const char *scope, const void *source) +{ + return link->ll_source == source && !strcmp(link->ll_scope, scope); +} + +/** + * Maximal chain length seen so far. + */ +static unsigned lu_ref_chain_max_length = 127; + +/** + * Searches for a lu_ref_link with given [scope, source] within given lu_ref. + */ +static struct lu_ref_link *lu_ref_find(struct lu_ref *ref, const char *scope, + const void *source) +{ + struct lu_ref_link *link; + unsigned iterations; + + iterations = 0; + list_for_each_entry(link, &ref->lf_list, ll_linkage) { + ++iterations; + if (lu_ref_link_eq(link, scope, source)) { + if (iterations > lu_ref_chain_max_length) { + CWARN("Long lu_ref chain %i \"%s\":%p\n", + iterations, scope, source); + lu_ref_chain_max_length = iterations * 3 / 2; + } + return link; + } + } + return NULL; +} + +void lu_ref_del(struct lu_ref *ref, const char *scope, const void *source) +{ + struct lu_ref_link *link; + + spin_lock(&ref->lf_guard); + link = lu_ref_find(ref, scope, source); + if (link != NULL) { + list_del(&link->ll_linkage); + spin_unlock(&ref->lf_guard); + OBD_SLAB_FREE(link, lu_ref_link_kmem, sizeof(*link)); + } else { + LASSERT(ref->lf_failed > 0); + ref->lf_failed--; + spin_unlock(&ref->lf_guard); + } +} +EXPORT_SYMBOL(lu_ref_del); + +void lu_ref_set_at(struct lu_ref *ref, struct lu_ref_link *link, + const char *scope, + const void *source0, const void *source1) +{ + spin_lock(&ref->lf_guard); + if (link != ERR_PTR(-ENOMEM)) { + LASSERT(link->ll_ref == ref); + LASSERT(lu_ref_link_eq(link, scope, source0)); + link->ll_source = source1; + } else { + LASSERT(ref->lf_failed > 0); + } + spin_unlock(&ref->lf_guard); +} +EXPORT_SYMBOL(lu_ref_set_at); + +void lu_ref_del_at(struct lu_ref *ref, struct lu_ref_link *link, + const char *scope, const void *source) +{ + if (link != ERR_PTR(-ENOMEM)) { + LASSERT(link->ll_ref == ref); + LASSERT(lu_ref_link_eq(link, scope, source)); + spin_lock(&ref->lf_guard); + list_del(&link->ll_linkage); + spin_unlock(&ref->lf_guard); + OBD_SLAB_FREE(link, lu_ref_link_kmem, sizeof(*link)); + } else { + LASSERT(ref->lf_failed > 0); + spin_lock(&ref->lf_guard); + ref->lf_failed--; + spin_unlock(&ref->lf_guard); + } +} +EXPORT_SYMBOL(lu_ref_del_at); + +static int lu_ref_initialized = 0; +int lu_ref_global_init(void) +{ + int result; + + if (lu_ref_initialized == 0) { + lu_ref_initialized = 1; + CDEBUG(D_CONSOLE, + "lu_ref tracking is enabled. Performance isn't.\n"); + result = lu_kmem_init(lu_ref_caches); + } else + result = 0; + return result; +} + +void lu_ref_global_fini(void) +{ + lu_kmem_fini(lu_ref_caches); +} + +#endif /* USE_LU_REF */ -- 1.8.3.1