Whamcloud - gitweb
LU-82 Remove useless clio locks
[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 (c) 2008, 2010, Oracle and/or its affiliates. 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 /**
62  * Asserts a condition for a given lu_ref. Must be called with
63  * lu_ref::lf_guard held.
64  */
65 #define REFASSERT(ref, expr)                            \
66   do {                                                  \
67           struct lu_ref *__ref = (ref);                 \
68                                                         \
69           if (unlikely(!(expr))) {                      \
70                   lu_ref_print(__ref);                  \
71                   cfs_spin_unlock(&__ref->lf_guard);    \
72                   lu_ref_print_all();                   \
73                   LASSERT(0);                           \
74                   cfs_spin_lock(&__ref->lf_guard);      \
75           }                                             \
76   } while (0)
77
78 struct lu_ref_link {
79         struct lu_ref    *ll_ref;
80         cfs_list_t        ll_linkage;
81         const char       *ll_scope;
82         const void       *ll_source;
83 };
84
85 static cfs_mem_cache_t *lu_ref_link_kmem;
86
87 static struct lu_kmem_descr lu_ref_caches[] = {
88         {
89                 .ckd_cache = &lu_ref_link_kmem,
90                 .ckd_name  = "lu_ref_link_kmem",
91                 .ckd_size  = sizeof (struct lu_ref_link)
92         },
93         {
94                 .ckd_cache = NULL
95         }
96 };
97
98 /**
99  * Global list of active (initialized, but not finalized) lu_ref's.
100  *
101  * Protected by lu_ref_refs_guard.
102  */
103 static CFS_LIST_HEAD(lu_ref_refs);
104 static cfs_spinlock_t lu_ref_refs_guard;
105 static struct lu_ref lu_ref_marker = {
106         .lf_guard   = CFS_SPIN_LOCK_UNLOCKED,
107         .lf_list    = CFS_LIST_HEAD_INIT(lu_ref_marker.lf_list),
108         .lf_linkage = CFS_LIST_HEAD_INIT(lu_ref_marker.lf_linkage)
109 };
110
111 void lu_ref_print(const struct lu_ref *ref)
112 {
113         struct lu_ref_link *link;
114
115         CERROR("lu_ref: %p %d %d %s:%d\n",
116                ref, ref->lf_refs, ref->lf_failed, ref->lf_func, ref->lf_line);
117         cfs_list_for_each_entry(link, &ref->lf_list, ll_linkage) {
118                 CERROR("     link: %s %p\n", link->ll_scope, link->ll_source);
119         }
120 }
121 EXPORT_SYMBOL(lu_ref_print);
122
123 static int lu_ref_is_marker(const struct lu_ref *ref)
124 {
125         return (ref == &lu_ref_marker);
126 }
127
128 void lu_ref_print_all(void)
129 {
130         struct lu_ref *ref;
131
132         cfs_spin_lock(&lu_ref_refs_guard);
133         cfs_list_for_each_entry(ref, &lu_ref_refs, lf_linkage) {
134                 if (lu_ref_is_marker(ref))
135                         continue;
136
137                 cfs_spin_lock(&ref->lf_guard);
138                 lu_ref_print(ref);
139                 cfs_spin_unlock(&ref->lf_guard);
140         }
141         cfs_spin_unlock(&lu_ref_refs_guard);
142 }
143 EXPORT_SYMBOL(lu_ref_print_all);
144
145 void lu_ref_init_loc(struct lu_ref *ref, const char *func, const int line)
146 {
147         ref->lf_refs = 0;
148         ref->lf_func = func;
149         ref->lf_line = line;
150         cfs_spin_lock_init(&ref->lf_guard);
151         CFS_INIT_LIST_HEAD(&ref->lf_list);
152         cfs_spin_lock(&lu_ref_refs_guard);
153         cfs_list_add(&ref->lf_linkage, &lu_ref_refs);
154         cfs_spin_unlock(&lu_ref_refs_guard);
155 }
156 EXPORT_SYMBOL(lu_ref_init_loc);
157
158 void lu_ref_fini(struct lu_ref *ref)
159 {
160         REFASSERT(ref, cfs_list_empty(&ref->lf_list));
161         REFASSERT(ref, ref->lf_refs == 0);
162         cfs_spin_lock(&lu_ref_refs_guard);
163         cfs_list_del_init(&ref->lf_linkage);
164         cfs_spin_unlock(&lu_ref_refs_guard);
165 }
166 EXPORT_SYMBOL(lu_ref_fini);
167
168 static struct lu_ref_link *lu_ref_add_context(struct lu_ref *ref,
169                                               enum cfs_alloc_flags flags,
170                                               const char *scope,
171                                               const void *source)
172 {
173         struct lu_ref_link *link;
174
175         link = NULL;
176         if (lu_ref_link_kmem != NULL) {
177                 OBD_SLAB_ALLOC_PTR_GFP(link, lu_ref_link_kmem, flags);
178                 if (link != NULL) {
179                         link->ll_ref    = ref;
180                         link->ll_scope  = scope;
181                         link->ll_source = source;
182                         cfs_spin_lock(&ref->lf_guard);
183                         cfs_list_add_tail(&link->ll_linkage, &ref->lf_list);
184                         ref->lf_refs++;
185                         cfs_spin_unlock(&ref->lf_guard);
186                 }
187         }
188
189         if (link == NULL) {
190                 cfs_spin_lock(&ref->lf_guard);
191                 ref->lf_failed++;
192                 cfs_spin_unlock(&ref->lf_guard);
193                 link = ERR_PTR(-ENOMEM);
194         }
195
196         return link;
197 }
198
199 struct lu_ref_link *lu_ref_add(struct lu_ref *ref, const char *scope,
200                                const void *source)
201 {
202         cfs_might_sleep();
203         return lu_ref_add_context(ref, CFS_ALLOC_STD, scope, source);
204 }
205 EXPORT_SYMBOL(lu_ref_add);
206
207 /**
208  * Version of lu_ref_add() to be used in non-blockable contexts.
209  */
210 struct lu_ref_link *lu_ref_add_atomic(struct lu_ref *ref, const char *scope,
211                                       const void *source)
212 {
213         return lu_ref_add_context(ref, CFS_ALLOC_ATOMIC, scope, source);
214 }
215 EXPORT_SYMBOL(lu_ref_add_atomic);
216
217 static inline int lu_ref_link_eq(const struct lu_ref_link *link,
218                                  const char *scope, const void *source)
219 {
220         return link->ll_source == source && !strcmp(link->ll_scope, scope);
221 }
222
223 /**
224  * Maximal chain length seen so far.
225  */
226 static unsigned lu_ref_chain_max_length = 127;
227
228 /**
229  * Searches for a lu_ref_link with given [scope, source] within given lu_ref.
230  */
231 static struct lu_ref_link *lu_ref_find(struct lu_ref *ref, const char *scope,
232                                        const void *source)
233 {
234         struct lu_ref_link *link;
235         unsigned            iterations;
236
237         iterations = 0;
238         cfs_list_for_each_entry(link, &ref->lf_list, ll_linkage) {
239                 ++iterations;
240                 if (lu_ref_link_eq(link, scope, source)) {
241                         if (iterations > lu_ref_chain_max_length) {
242                                 CWARN("Long lu_ref chain %d \"%s\":%p\n",
243                                       iterations, scope, source);
244                                 lu_ref_chain_max_length = iterations * 3 / 2;
245                         }
246                         return link;
247                 }
248         }
249         return NULL;
250 }
251
252 void lu_ref_del(struct lu_ref *ref, const char *scope, const void *source)
253 {
254         struct lu_ref_link *link;
255
256         cfs_spin_lock(&ref->lf_guard);
257         link = lu_ref_find(ref, scope, source);
258         if (link != NULL) {
259                 cfs_list_del(&link->ll_linkage);
260                 ref->lf_refs--;
261                 cfs_spin_unlock(&ref->lf_guard);
262                 OBD_SLAB_FREE(link, lu_ref_link_kmem, sizeof(*link));
263         } else {
264                 REFASSERT(ref, ref->lf_failed > 0);
265                 ref->lf_failed--;
266                 cfs_spin_unlock(&ref->lf_guard);
267         }
268 }
269 EXPORT_SYMBOL(lu_ref_del);
270
271 void lu_ref_set_at(struct lu_ref *ref, struct lu_ref_link *link,
272                    const char *scope,
273                    const void *source0, const void *source1)
274 {
275         cfs_spin_lock(&ref->lf_guard);
276         if (link != ERR_PTR(-ENOMEM)) {
277                 REFASSERT(ref, link->ll_ref == ref);
278                 REFASSERT(ref, lu_ref_link_eq(link, scope, source0));
279                 link->ll_source = source1;
280         } else {
281                 REFASSERT(ref, ref->lf_failed > 0);
282         }
283         cfs_spin_unlock(&ref->lf_guard);
284 }
285 EXPORT_SYMBOL(lu_ref_set_at);
286
287 void lu_ref_del_at(struct lu_ref *ref, struct lu_ref_link *link,
288                    const char *scope, const void *source)
289 {
290         if (link != ERR_PTR(-ENOMEM)) {
291                 cfs_spin_lock(&ref->lf_guard);
292                 REFASSERT(ref, link->ll_ref == ref);
293                 REFASSERT(ref, lu_ref_link_eq(link, scope, source));
294                 cfs_list_del(&link->ll_linkage);
295                 ref->lf_refs--;
296                 cfs_spin_unlock(&ref->lf_guard);
297                 OBD_SLAB_FREE(link, lu_ref_link_kmem, sizeof(*link));
298         } else {
299                 cfs_spin_lock(&ref->lf_guard);
300                 REFASSERT(ref, ref->lf_failed > 0);
301                 ref->lf_failed--;
302                 cfs_spin_unlock(&ref->lf_guard);
303         }
304 }
305 EXPORT_SYMBOL(lu_ref_del_at);
306
307 #if defined(__KERNEL__) && defined(LPROCFS)
308
309 static void *lu_ref_seq_start(struct seq_file *seq, loff_t *pos)
310 {
311         struct lu_ref *ref = seq->private;
312
313         cfs_spin_lock(&lu_ref_refs_guard);
314         if (cfs_list_empty(&ref->lf_linkage))
315                 ref = NULL;
316         cfs_spin_unlock(&lu_ref_refs_guard);
317
318         return ref;
319 }
320
321 static void *lu_ref_seq_next(struct seq_file *seq, void *p, loff_t *pos)
322 {
323         struct lu_ref *ref = p;
324         struct lu_ref *next;
325
326         LASSERT(seq->private == p);
327         LASSERT(!cfs_list_empty(&ref->lf_linkage));
328
329         cfs_spin_lock(&lu_ref_refs_guard);
330         next = cfs_list_entry(ref->lf_linkage.next, struct lu_ref, lf_linkage);
331         if (&next->lf_linkage == &lu_ref_refs) {
332                 p = NULL;
333         } else {
334                 (*pos)++;
335                 cfs_list_move(&ref->lf_linkage, &next->lf_linkage);
336         }
337         cfs_spin_unlock(&lu_ref_refs_guard);
338         return p;
339 }
340
341 static void lu_ref_seq_stop(struct seq_file *seq, void *p)
342 {
343         /* Nothing to do */
344 }
345
346
347 static int lu_ref_seq_show(struct seq_file *seq, void *p)
348 {
349         struct lu_ref *ref  = p;
350         struct lu_ref *next; 
351
352         cfs_spin_lock(&lu_ref_refs_guard);
353         next = cfs_list_entry(ref->lf_linkage.next, struct lu_ref, lf_linkage);
354         if ((&next->lf_linkage == &lu_ref_refs) || lu_ref_is_marker(next)) {
355                 cfs_spin_unlock(&lu_ref_refs_guard);
356                 return 0;
357         }
358
359         /* print the entry */
360
361         cfs_spin_lock(&next->lf_guard);
362         seq_printf(seq, "lu_ref: %p %d %d %s:%d\n",
363                    next, next->lf_refs, next->lf_failed,
364                    next->lf_func, next->lf_line);
365         if (next->lf_refs > 64) {
366                 seq_printf(seq, "  too many references, skip\n");
367         } else {
368                 struct lu_ref_link *link;
369                 int i = 0;
370
371                 cfs_list_for_each_entry(link, &next->lf_list, ll_linkage)
372                         seq_printf(seq, "  #%d link: %s %p\n",
373                                    i++, link->ll_scope, link->ll_source);
374         }
375         cfs_spin_unlock(&next->lf_guard);
376         cfs_spin_unlock(&lu_ref_refs_guard);
377
378         return 0;
379 }
380
381 static struct seq_operations lu_ref_seq_ops = {
382         .start = lu_ref_seq_start,
383         .stop  = lu_ref_seq_stop,
384         .next  = lu_ref_seq_next,
385         .show  = lu_ref_seq_show
386 };
387
388 static int lu_ref_seq_open(struct inode *inode, struct file *file)
389 {
390         struct lu_ref *marker = &lu_ref_marker;
391         int result = 0;
392
393         result = seq_open(file, &lu_ref_seq_ops);
394         if (result == 0) {
395                 cfs_spin_lock(&lu_ref_refs_guard);
396                 if (!cfs_list_empty(&marker->lf_linkage))
397                         result = -EAGAIN;
398                 else
399                         cfs_list_add(&marker->lf_linkage, &lu_ref_refs);
400                 cfs_spin_unlock(&lu_ref_refs_guard);
401
402                 if (result == 0) {
403                         struct seq_file *f = file->private_data;
404                         f->private = marker;
405                 } else {
406                         seq_release(inode, file);
407                 }
408         }
409
410         return result;
411 }
412
413 static int lu_ref_seq_release(struct inode *inode, struct file *file)
414 {
415         struct lu_ref *ref = ((struct seq_file *)file->private_data)->private;
416
417         cfs_spin_lock(&lu_ref_refs_guard);
418         cfs_list_del_init(&ref->lf_linkage);
419         cfs_spin_unlock(&lu_ref_refs_guard);
420
421         return seq_release(inode, file);
422 }
423
424 static struct file_operations lu_ref_dump_fops = {
425         .owner   = THIS_MODULE,
426         .open    = lu_ref_seq_open,
427         .read    = seq_read,
428         .llseek  = seq_lseek,
429         .release = lu_ref_seq_release
430 };
431
432 #endif
433
434 int lu_ref_global_init(void)
435 {
436         int result;
437
438         CDEBUG(D_CONSOLE,
439                "lu_ref tracking is enabled. Performance isn't.\n");
440
441
442         cfs_spin_lock_init(&lu_ref_refs_guard);
443         result = lu_kmem_init(lu_ref_caches);
444
445 #if defined(__KERNEL__) && defined(LPROCFS)
446         if (result == 0) {
447                 result = lprocfs_seq_create(proc_lustre_root, "lu_refs",
448                                             0444, &lu_ref_dump_fops, NULL);
449                 if (result)
450                         lu_kmem_fini(lu_ref_caches);
451         }
452 #endif
453
454         return result;
455 }
456
457 void lu_ref_global_fini(void)
458 {
459 #if defined(__KERNEL__) && defined(LPROCFS)
460         lprocfs_remove_proc_entry("lu_refs", proc_lustre_root);
461 #endif
462         lu_kmem_fini(lu_ref_caches);
463 }
464
465 #endif /* USE_LU_REF */