Whamcloud - gitweb
lu_ref is a debugging module allowing to track references to a given
[fs/lustre-release.git] / lustre / obdclass / lu_ref.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/obdclass/lu_ref.c
37  *
38  * Lustre reference.
39  *
40  *   Author: Nikita Danilov <nikita.danilov@sun.com>
41  */
42
43 #define DEBUG_SUBSYSTEM S_CLASS
44 #ifndef EXPORT_SYMTAB
45 # define EXPORT_SYMTAB
46 #endif
47
48 #ifdef __KERNEL__
49 # include <libcfs/libcfs.h>
50 #else
51 # include <liblustre.h>
52 #endif
53
54 #include <obd.h>
55 #include <obd_class.h>
56 #include <obd_support.h>
57 #include <lu_ref.h>
58
59 #ifdef USE_LU_REF
60
61 struct lu_ref_link {
62         struct lu_ref    *ll_ref;
63         struct list_head  ll_linkage;
64         const char       *ll_scope;
65         const void       *ll_source;
66 };
67
68 static cfs_mem_cache_t *lu_ref_link_kmem;
69
70 static struct lu_kmem_descr lu_ref_caches[] = {
71         {
72                 .ckd_cache = &lu_ref_link_kmem,
73                 .ckd_name  = "lu_ref_link_kmem",
74                 .ckd_size  = sizeof (struct lu_ref_link)
75         },
76         {
77                 .ckd_cache = NULL
78         }
79 };
80
81 void lu_ref_print(const struct lu_ref *ref)
82 {
83         struct lu_ref_link *link;
84
85         CERROR("lu_ref: %p %d\n", ref, ref->lf_failed);
86         list_for_each_entry(link, &ref->lf_list, ll_linkage) {
87                 CERROR("     link: %s %p\n", link->ll_scope, link->ll_source);
88         }
89 }
90 EXPORT_SYMBOL(lu_ref_print);
91
92 void lu_ref_init(struct lu_ref *ref)
93 {
94         spin_lock_init(&ref->lf_guard);
95         CFS_INIT_LIST_HEAD(&ref->lf_list);
96 }
97 EXPORT_SYMBOL(lu_ref_init);
98
99 void lu_ref_fini(struct lu_ref *ref)
100 {
101         if (!list_empty(&ref->lf_list)) {
102                 spin_lock(&ref->lf_guard);
103                 lu_ref_print(ref);
104                 spin_unlock(&ref->lf_guard);
105         }
106         LASSERT(list_empty(&ref->lf_list));
107 }
108 EXPORT_SYMBOL(lu_ref_fini);
109 int lu_ref_global_init(void);
110
111 static struct lu_ref_link *lu_ref_add_context(struct lu_ref *ref,
112                                               enum cfs_alloc_flags flags,
113                                               const char *scope,
114                                               const void *source)
115 {
116         struct lu_ref_link *link;
117
118         /* this can be called so early in lustre initialization, that
119          * lu_ref_link_kmem slab is not yet created. */
120         lu_ref_global_init();
121
122         link = NULL;
123         if (lu_ref_link_kmem != NULL) {
124                 OBD_SLAB_ALLOC(link, lu_ref_link_kmem, flags, sizeof(*link));
125                 if (link != NULL) {
126                         link->ll_ref    = ref;
127                         link->ll_scope  = scope;
128                         link->ll_source = source;
129                         spin_lock(&ref->lf_guard);
130                         list_add_tail(&link->ll_linkage, &ref->lf_list);
131                         spin_unlock(&ref->lf_guard);
132                 }
133         }
134
135         if (link == NULL) {
136                 spin_lock(&ref->lf_guard);
137                 ref->lf_failed++;
138                 spin_unlock(&ref->lf_guard);
139                 link = ERR_PTR(-ENOMEM);
140         }
141         return link;
142 }
143
144 struct lu_ref_link *lu_ref_add(struct lu_ref *ref, const char *scope,
145                                const void *source)
146 {
147         might_sleep();
148         return lu_ref_add_context(ref, CFS_ALLOC_STD, scope, source);
149 }
150 EXPORT_SYMBOL(lu_ref_add);
151
152 /**
153  * Version of lu_ref_add() to be used in non-blockable contexts.
154  */
155 struct lu_ref_link *lu_ref_add_atomic(struct lu_ref *ref, const char *scope,
156                                       const void *source)
157 {
158         return lu_ref_add_context(ref, CFS_ALLOC_ATOMIC, scope, source);
159 }
160 EXPORT_SYMBOL(lu_ref_add_atomic);
161
162 static inline int lu_ref_link_eq(const struct lu_ref_link *link,
163                                  const char *scope, const void *source)
164 {
165         return link->ll_source == source && !strcmp(link->ll_scope, scope);
166 }
167
168 /**
169  * Maximal chain length seen so far.
170  */
171 static unsigned lu_ref_chain_max_length = 127;
172
173 /**
174  * Searches for a lu_ref_link with given [scope, source] within given lu_ref.
175  */
176 static struct lu_ref_link *lu_ref_find(struct lu_ref *ref, const char *scope,
177                                        const void *source)
178 {
179         struct lu_ref_link *link;
180         unsigned            iterations;
181
182         iterations = 0;
183         list_for_each_entry(link, &ref->lf_list, ll_linkage) {
184                 ++iterations;
185                 if (lu_ref_link_eq(link, scope, source)) {
186                         if (iterations > lu_ref_chain_max_length) {
187                                 CWARN("Long lu_ref chain %i \"%s\":%p\n",
188                                       iterations, scope, source);
189                                 lu_ref_chain_max_length = iterations * 3 / 2;
190                         }
191                         return link;
192                 }
193         }
194         return NULL;
195 }
196
197 void lu_ref_del(struct lu_ref *ref, const char *scope, const void *source)
198 {
199         struct lu_ref_link *link;
200
201         spin_lock(&ref->lf_guard);
202         link = lu_ref_find(ref, scope, source);
203         if (link != NULL) {
204                 list_del(&link->ll_linkage);
205                 spin_unlock(&ref->lf_guard);
206                 OBD_SLAB_FREE(link, lu_ref_link_kmem, sizeof(*link));
207         } else {
208                 LASSERT(ref->lf_failed > 0);
209                 ref->lf_failed--;
210                 spin_unlock(&ref->lf_guard);
211         }
212 }
213 EXPORT_SYMBOL(lu_ref_del);
214
215 void lu_ref_set_at(struct lu_ref *ref, struct lu_ref_link *link,
216                    const char *scope,
217                    const void *source0, const void *source1)
218 {
219         spin_lock(&ref->lf_guard);
220         if (link != ERR_PTR(-ENOMEM)) {
221                 LASSERT(link->ll_ref == ref);
222                 LASSERT(lu_ref_link_eq(link, scope, source0));
223                 link->ll_source = source1;
224         } else {
225                 LASSERT(ref->lf_failed > 0);
226         }
227         spin_unlock(&ref->lf_guard);
228 }
229 EXPORT_SYMBOL(lu_ref_set_at);
230
231 void lu_ref_del_at(struct lu_ref *ref, struct lu_ref_link *link,
232                    const char *scope, const void *source)
233 {
234         if (link != ERR_PTR(-ENOMEM)) {
235                 LASSERT(link->ll_ref == ref);
236                 LASSERT(lu_ref_link_eq(link, scope, source));
237                 spin_lock(&ref->lf_guard);
238                 list_del(&link->ll_linkage);
239                 spin_unlock(&ref->lf_guard);
240                 OBD_SLAB_FREE(link, lu_ref_link_kmem, sizeof(*link));
241         } else {
242                 LASSERT(ref->lf_failed > 0);
243                 spin_lock(&ref->lf_guard);
244                 ref->lf_failed--;
245                 spin_unlock(&ref->lf_guard);
246         }
247 }
248 EXPORT_SYMBOL(lu_ref_del_at);
249
250 static int lu_ref_initialized = 0;
251 int lu_ref_global_init(void)
252 {
253         int result;
254
255         if (lu_ref_initialized == 0) {
256                 lu_ref_initialized = 1;
257                 CDEBUG(D_CONSOLE,
258                        "lu_ref tracking is enabled. Performance isn't.\n");
259                 result = lu_kmem_init(lu_ref_caches);
260         } else
261                 result = 0;
262         return result;
263 }
264
265 void lu_ref_global_fini(void)
266 {
267         lu_kmem_fini(lu_ref_caches);
268 }
269
270 #endif /* USE_LU_REF */