Whamcloud - gitweb
b=16098
[fs/lustre-release.git] / lustre / obdclass / class_obd.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
37 #define DEBUG_SUBSYSTEM S_CLASS
38 #ifndef EXPORT_SYMTAB
39 # define EXPORT_SYMTAB
40 #endif
41 #ifndef __KERNEL__
42 # include <liblustre.h>
43 #else
44 # include <asm/atomic.h>
45 #endif
46
47 #include <obd_support.h>
48 #include <obd_class.h>
49 #include <lustre_debug.h>
50 #include <lprocfs_status.h>
51 #include <lustre/lustre_build_version.h>
52 #include <libcfs/list.h>
53 #include "llog_internal.h"
54
55 #ifndef __KERNEL__
56 /* liblustre workaround */
57 atomic_t libcfs_kmemory = {0};
58 #endif
59
60 struct obd_device *obd_devs[MAX_OBD_DEVICES];
61 struct list_head obd_types;
62 spinlock_t obd_dev_lock = SPIN_LOCK_UNLOCKED;
63
64 #ifndef __KERNEL__
65 __u64 obd_max_pages = 0;
66 __u64 obd_max_alloc = 0;
67 __u64 obd_alloc;
68 __u64 obd_pages;
69 #endif
70
71 /* The following are visible and mutable through /proc/sys/lustre/. */
72 unsigned int obd_debug_peer_on_timeout;
73 unsigned int obd_dump_on_timeout;
74 unsigned int obd_dump_on_eviction;
75 unsigned int obd_timeout = OBD_TIMEOUT_DEFAULT;   /* seconds */
76 unsigned int ldlm_timeout = LDLM_TIMEOUT_DEFAULT; /* seconds */
77 unsigned int obd_max_dirty_pages = 256;
78 atomic_t obd_dirty_pages;
79
80 cfs_waitq_t obd_race_waitq;
81 int obd_race_state;
82
83 #ifdef __KERNEL__
84 unsigned long obd_print_fail_loc(void)
85 {
86         CWARN("obd_fail_loc = %lx\n", obd_fail_loc);
87         return obd_fail_loc;
88 }
89
90 void obd_set_fail_loc(unsigned int fl)
91 {
92         obd_fail_loc = fl;
93 }
94
95 /*  opening /dev/obd */
96 static int obd_class_open(unsigned long flags, void *args)
97 {
98         ENTRY;
99
100         PORTAL_MODULE_USE;
101         RETURN(0);
102 }
103
104 /*  closing /dev/obd */
105 static int obd_class_release(unsigned long flags, void *args)
106 {
107         ENTRY;
108
109         PORTAL_MODULE_UNUSE;
110         RETURN(0);
111 }
112 #endif
113
114 static inline void obd_data2conn(struct lustre_handle *conn,
115                                  struct obd_ioctl_data *data)
116 {
117         memset(conn, 0, sizeof *conn);
118         conn->cookie = data->ioc_cookie;
119 }
120
121 static inline void obd_conn2data(struct obd_ioctl_data *data,
122                                  struct lustre_handle *conn)
123 {
124         data->ioc_cookie = conn->cookie;
125 }
126
127 int class_resolve_dev_name(__u32 len, const char *name)
128 {
129         int rc;
130         int dev;
131
132         ENTRY;
133         if (!len || !name) {
134                 CERROR("No name passed,!\n");
135                 GOTO(out, rc = -EINVAL);
136         }
137         if (name[len - 1] != 0) {
138                 CERROR("Name not nul terminated!\n");
139                 GOTO(out, rc = -EINVAL);
140         }
141
142         CDEBUG(D_IOCTL, "device name %s\n", name);
143         dev = class_name2dev(name);
144         if (dev == -1) {
145                 CDEBUG(D_IOCTL, "No device for name %s!\n", name);
146                 GOTO(out, rc = -EINVAL);
147         }
148
149         CDEBUG(D_IOCTL, "device name %s, dev %d\n", name, dev);
150         rc = dev;
151
152 out:
153         RETURN(rc);
154 }
155
156 int class_handle_ioctl(unsigned int cmd, unsigned long arg)
157 {
158         char *buf = NULL;
159         struct obd_ioctl_data *data;
160         struct libcfs_debug_ioctl_data *debug_data;
161         struct obd_device *obd = NULL;
162         int err = 0, len = 0;
163         ENTRY;
164
165         /* only for debugging */
166         if (cmd == LIBCFS_IOC_DEBUG_MASK) {
167                 debug_data = (struct libcfs_debug_ioctl_data*)arg;
168                 libcfs_subsystem_debug = debug_data->subs;
169                 libcfs_debug = debug_data->debug;
170                 return 0;
171         }
172
173         CDEBUG(D_IOCTL, "cmd = %x\n", cmd);
174         if (obd_ioctl_getdata(&buf, &len, (void *)arg)) {
175                 CERROR("OBD ioctl: data error\n");
176                 GOTO(out, err = -EINVAL);
177         }
178         data = (struct obd_ioctl_data *)buf;
179
180         switch (cmd) {
181         case OBD_IOC_PROCESS_CFG: {
182                 struct lustre_cfg *lcfg;
183
184                 if (!data->ioc_plen1 || !data->ioc_pbuf1) {
185                         CERROR("No config buffer passed!\n");
186                         GOTO(out, err = -EINVAL);
187                 }
188                 OBD_ALLOC(lcfg, data->ioc_plen1);
189                 if (lcfg == NULL)
190                         GOTO(out, err = -ENOMEM);
191                 err = copy_from_user(lcfg, data->ioc_pbuf1, data->ioc_plen1);
192                 if (!err)
193                         err = lustre_cfg_sanity_check(lcfg, data->ioc_plen1);
194                 if (!err)
195                         err = class_process_config(lcfg);
196
197                 OBD_FREE(lcfg, data->ioc_plen1);
198                 GOTO(out, err);
199         }
200
201         case OBD_GET_VERSION:
202                 if (!data->ioc_inlbuf1) {
203                         CERROR("No buffer passed in ioctl\n");
204                         GOTO(out, err = -EINVAL);
205                 }
206
207                 if (strlen(BUILD_VERSION) + 1 > data->ioc_inllen1) {
208                         CERROR("ioctl buffer too small to hold version\n");
209                         GOTO(out, err = -EINVAL);
210                 }
211
212                 memcpy(data->ioc_bulk, BUILD_VERSION,
213                        strlen(BUILD_VERSION) + 1);
214
215                 err = obd_ioctl_popdata((void *)arg, data, len);
216                 if (err)
217                         err = -EFAULT;
218                 GOTO(out, err);
219
220         case OBD_IOC_NAME2DEV: {
221                 /* Resolve a device name.  This does not change the
222                  * currently selected device.
223                  */
224                 int dev;
225
226                 dev = class_resolve_dev_name(data->ioc_inllen1,
227                                              data->ioc_inlbuf1);
228                 data->ioc_dev = dev;
229                 if (dev < 0)
230                         GOTO(out, err = -EINVAL);
231
232                 err = obd_ioctl_popdata((void *)arg, data, sizeof(*data));
233                 if (err)
234                         err = -EFAULT;
235                 GOTO(out, err);
236         }
237
238         case OBD_IOC_UUID2DEV: {
239                 /* Resolve a device uuid.  This does not change the
240                  * currently selected device.
241                  */
242                 int dev;
243                 struct obd_uuid uuid;
244
245                 if (!data->ioc_inllen1 || !data->ioc_inlbuf1) {
246                         CERROR("No UUID passed!\n");
247                         GOTO(out, err = -EINVAL);
248                 }
249                 if (data->ioc_inlbuf1[data->ioc_inllen1 - 1] != 0) {
250                         CERROR("UUID not NUL terminated!\n");
251                         GOTO(out, err = -EINVAL);
252                 }
253
254                 CDEBUG(D_IOCTL, "device name %s\n", data->ioc_inlbuf1);
255                 obd_str2uuid(&uuid, data->ioc_inlbuf1);
256                 dev = class_uuid2dev(&uuid);
257                 data->ioc_dev = dev;
258                 if (dev == -1) {
259                         CDEBUG(D_IOCTL, "No device for UUID %s!\n",
260                                data->ioc_inlbuf1);
261                         GOTO(out, err = -EINVAL);
262                 }
263
264                 CDEBUG(D_IOCTL, "device name %s, dev %d\n", data->ioc_inlbuf1,
265                        dev);
266                 err = obd_ioctl_popdata((void *)arg, data, sizeof(*data));
267                 if (err)
268                         err = -EFAULT;
269                 GOTO(out, err);
270         }
271
272         case OBD_IOC_CLOSE_UUID: {
273                 CDEBUG(D_IOCTL, "closing all connections to uuid %s (NOOP)\n",
274                        data->ioc_inlbuf1);
275                 GOTO(out, err = 0);
276         }
277
278         case OBD_IOC_GETDEVICE: {
279                 int     index = data->ioc_count;
280                 char    *status, *str;
281
282                 if (!data->ioc_inlbuf1) {
283                         CERROR("No buffer passed in ioctl\n");
284                         GOTO(out, err = -EINVAL);
285                 }
286                 if (data->ioc_inllen1 < 128) {
287                         CERROR("ioctl buffer too small to hold version\n");
288                         GOTO(out, err = -EINVAL);
289                 }
290
291                 obd = class_num2obd(index);
292                 if (!obd)
293                         GOTO(out, err = -ENOENT);
294
295                 if (obd->obd_stopping)
296                         status = "ST";
297                 else if (obd->obd_set_up)
298                         status = "UP";
299                 else if (obd->obd_attached)
300                         status = "AT";
301                 else
302                         status = "--";
303                 str = (char *)data->ioc_bulk;
304                 snprintf(str, len - sizeof(*data), "%3d %s %s %s %s %d",
305                          (int)index, status, obd->obd_type->typ_name,
306                          obd->obd_name, obd->obd_uuid.uuid,
307                          atomic_read(&obd->obd_refcount));
308                 err = obd_ioctl_popdata((void *)arg, data, len);
309
310                 GOTO(out, err = 0);
311         }
312
313         }
314
315         if (data->ioc_dev >= class_devno_max()) {
316                 CERROR("OBD ioctl: No device\n");
317                 GOTO(out, err = -EINVAL);
318         }
319
320         obd = class_num2obd(data->ioc_dev);
321         if (obd == NULL) {
322                 CERROR("OBD ioctl : No Device %d\n", data->ioc_dev);
323                 GOTO(out, err = -EINVAL);
324         }
325         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
326
327         if (!obd->obd_set_up || obd->obd_stopping) {
328                 CERROR("OBD ioctl: device not setup %d \n", data->ioc_dev);
329                 GOTO(out, err = -EINVAL);
330         }
331
332         switch(cmd) {
333         case OBD_IOC_NO_TRANSNO: {
334                 if (!obd->obd_attached) {
335                         CERROR("Device %d not attached\n", obd->obd_minor);
336                         GOTO(out, err = -ENODEV);
337                 }
338                 CDEBUG(D_HA, "%s: disabling committed-transno notification\n",
339                        obd->obd_name);
340                 obd->obd_no_transno = 1;
341                 GOTO(out, err = 0);
342         }
343
344         default: {
345                 err = obd_iocontrol(cmd, obd->obd_self_export, len, data, NULL);
346                 if (err)
347                         GOTO(out, err);
348
349                 err = obd_ioctl_popdata((void *)arg, data, len);
350                 if (err)
351                         err = -EFAULT;
352                 GOTO(out, err);
353         }
354         }
355
356  out:
357         if (buf)
358                 obd_ioctl_freedata(buf, len);
359         RETURN(err);
360 } /* class_handle_ioctl */
361
362
363
364 #define OBD_MINOR 241
365 #ifdef __KERNEL__
366 /* to control /dev/obd */
367 static int obd_class_ioctl (struct cfs_psdev_file *pfile, unsigned long cmd,
368                             void *arg)
369 {
370         return class_handle_ioctl(cmd, (unsigned long)arg);
371 }
372
373 /* declare character device */
374 struct cfs_psdev_ops obd_psdev_ops = {
375         /* .p_open    = */ obd_class_open,      /* open */
376         /* .p_close   = */ obd_class_release,   /* release */
377         /* .p_read    = */ NULL,
378         /* .p_write   = */ NULL,
379         /* .p_ioctl   = */ obd_class_ioctl     /* ioctl */
380 };
381
382 extern cfs_psdev_t obd_psdev;
383 #else
384 void *obd_psdev = NULL;
385 #endif
386
387 EXPORT_SYMBOL(obd_devs);
388 EXPORT_SYMBOL(obd_print_fail_loc);
389 EXPORT_SYMBOL(obd_race_waitq);
390 EXPORT_SYMBOL(obd_race_state);
391 EXPORT_SYMBOL(obd_debug_peer_on_timeout);
392 EXPORT_SYMBOL(obd_dump_on_timeout);
393 EXPORT_SYMBOL(obd_dump_on_eviction);
394 EXPORT_SYMBOL(obd_timeout);
395 EXPORT_SYMBOL(ldlm_timeout);
396 EXPORT_SYMBOL(obd_max_dirty_pages);
397 EXPORT_SYMBOL(obd_dirty_pages);
398 EXPORT_SYMBOL(ptlrpc_put_connection_superhack);
399
400 EXPORT_SYMBOL(proc_lustre_root);
401
402 EXPORT_SYMBOL(class_register_type);
403 EXPORT_SYMBOL(class_unregister_type);
404 EXPORT_SYMBOL(class_get_type);
405 EXPORT_SYMBOL(class_put_type);
406 EXPORT_SYMBOL(class_name2dev);
407 EXPORT_SYMBOL(class_name2obd);
408 EXPORT_SYMBOL(class_uuid2dev);
409 EXPORT_SYMBOL(class_uuid2obd);
410 EXPORT_SYMBOL(class_find_client_obd);
411 EXPORT_SYMBOL(class_find_client_notype);
412 EXPORT_SYMBOL(class_devices_in_group);
413 EXPORT_SYMBOL(class_conn2export);
414 EXPORT_SYMBOL(class_exp2obd);
415 EXPORT_SYMBOL(class_conn2obd);
416 EXPORT_SYMBOL(class_exp2cliimp);
417 EXPORT_SYMBOL(class_conn2cliimp);
418 EXPORT_SYMBOL(class_disconnect);
419 EXPORT_SYMBOL(class_num2obd);
420
421 /* uuid.c */
422 EXPORT_SYMBOL(class_uuid_unparse);
423 EXPORT_SYMBOL(lustre_uuid_to_peer);
424
425 EXPORT_SYMBOL(class_handle_hash);
426 EXPORT_SYMBOL(class_handle_unhash);
427 EXPORT_SYMBOL(class_handle_hash_back);
428 EXPORT_SYMBOL(class_handle2object);
429 EXPORT_SYMBOL(class_handle_free_cb);
430
431 /* obd_config.c */
432 EXPORT_SYMBOL(class_incref);
433 EXPORT_SYMBOL(class_decref);
434 EXPORT_SYMBOL(class_get_profile);
435 EXPORT_SYMBOL(class_del_profile);
436 EXPORT_SYMBOL(class_del_profiles);
437 EXPORT_SYMBOL(class_process_config);
438 EXPORT_SYMBOL(class_process_proc_param);
439 EXPORT_SYMBOL(class_config_parse_llog);
440 EXPORT_SYMBOL(class_config_dump_llog);
441 EXPORT_SYMBOL(class_attach);
442 EXPORT_SYMBOL(class_setup);
443 EXPORT_SYMBOL(class_cleanup);
444 EXPORT_SYMBOL(class_detach);
445 EXPORT_SYMBOL(class_manual_cleanup);
446
447 /* mea.c */
448 EXPORT_SYMBOL(mea_name2idx);
449 EXPORT_SYMBOL(raw_name2idx);
450
451 #define OBD_INIT_CHECK
452 #ifdef OBD_INIT_CHECK
453 int obd_init_checks(void)
454 {
455         __u64 u64val, div64val;
456         char buf[64];
457         int len, ret = 0;
458
459         CDEBUG(D_INFO, "LPU64=%s, LPD64=%s, LPX64=%s, LPSZ=%s, LPSSZ=%s\n",
460                LPU64, LPD64, LPX64, LPSZ, LPSSZ);
461
462         CDEBUG(D_INFO, "OBD_OBJECT_EOF = "LPX64"\n", (__u64)OBD_OBJECT_EOF);
463
464         u64val = OBD_OBJECT_EOF;
465         CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = "LPX64"\n", u64val);
466         if (u64val != OBD_OBJECT_EOF) {
467                 CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n",
468                        u64val, (int)sizeof(u64val));
469                 ret = -EINVAL;
470         }
471         len = snprintf(buf, sizeof(buf), LPX64, u64val);
472         if (len != 18) {
473                 CWARN("LPX64 wrong length! strlen(%s)=%d != 18\n", buf, len);
474                 ret = -EINVAL;
475         }
476
477         div64val = OBD_OBJECT_EOF;
478         CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = "LPX64"\n", u64val);
479         if (u64val != OBD_OBJECT_EOF) {
480                 CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n",
481                        u64val, (int)sizeof(u64val));
482                 ret = -EOVERFLOW;
483         }
484         if (u64val >> 8 != OBD_OBJECT_EOF >> 8) {
485                 CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n",
486                        u64val, (int)sizeof(u64val));
487                 return -EOVERFLOW;
488         }
489         if (do_div(div64val, 256) != (u64val & 255)) {
490                 CERROR("do_div("LPX64",256) != "LPU64"\n", u64val, u64val &255);
491                 return -EOVERFLOW;
492         }
493         if (u64val >> 8 != div64val) {
494                 CERROR("do_div("LPX64",256) "LPU64" != "LPU64"\n",
495                        u64val, div64val, u64val >> 8);
496                 return -EOVERFLOW;
497         }
498         len = snprintf(buf, sizeof(buf), LPX64, u64val);
499         if (len != 18) {
500                 CWARN("LPX64 wrong length! strlen(%s)=%d != 18\n", buf, len);
501                 ret = -EINVAL;
502         }
503         len = snprintf(buf, sizeof(buf), LPU64, u64val);
504         if (len != 20) {
505                 CWARN("LPU64 wrong length! strlen(%s)=%d != 20\n", buf, len);
506                 ret = -EINVAL;
507         }
508         len = snprintf(buf, sizeof(buf), LPD64, u64val);
509         if (len != 2) {
510                 CWARN("LPD64 wrong length! strlen(%s)=%d != 2\n", buf, len);
511                 ret = -EINVAL;
512         }
513         if ((u64val & ~CFS_PAGE_MASK) >= CFS_PAGE_SIZE) {
514                 CWARN("mask failed: u64val "LPU64" >= "LPU64"\n", u64val,
515                       (__u64)CFS_PAGE_SIZE);
516                 ret = -EINVAL;
517         }
518
519         return ret;
520 }
521 #else
522 #define obd_init_checks() do {} while(0)
523 #endif
524
525 extern spinlock_t obd_types_lock;
526 extern int class_procfs_init(void);
527 extern int class_procfs_clean(void);
528
529 #ifdef __KERNEL__
530 static int __init init_obdclass(void)
531 #else
532 int init_obdclass(void)
533 #endif
534 {
535         int i, err;
536 #ifdef __KERNEL__
537         int lustre_register_fs(void);
538
539         printk(KERN_INFO "Lustre: OBD class driver, info@clusterfs.com\n");
540         printk(KERN_INFO "        Lustre Version: "LUSTRE_VERSION_STRING"\n");
541         printk(KERN_INFO "        Build Version: "BUILD_VERSION"\n");
542
543         for (i = CAPA_SITE_CLIENT; i < CAPA_SITE_MAX; i++)
544                 CFS_INIT_LIST_HEAD(&capa_list[i]);
545 #else
546         CDEBUG(D_INFO, "Lustre: OBD class driver, info@clusterfs.com\n");
547         CDEBUG(D_INFO, "        Lustre Version: "LUSTRE_VERSION_STRING"\n");
548         CDEBUG(D_INFO, "        Build Version: "BUILD_VERSION"\n");
549 #endif
550
551         spin_lock_init(&obd_types_lock);
552         cfs_waitq_init(&obd_race_waitq);
553         obd_zombie_impexp_init();
554 #ifdef LPROCFS
555         obd_memory = lprocfs_alloc_stats(OBD_STATS_NUM,
556                                          LPROCFS_STATS_FLAG_PERCPU);
557         if (obd_memory == NULL) {
558                 CERROR("kmalloc of 'obd_memory' failed\n");
559                 RETURN(-ENOMEM);
560         }
561
562         lprocfs_counter_init(obd_memory, OBD_MEMORY_STAT,
563                              LPROCFS_CNTR_AVGMINMAX,
564                              "memused", "bytes");
565         lprocfs_counter_init(obd_memory, OBD_MEMORY_PAGES_STAT,
566                              LPROCFS_CNTR_AVGMINMAX,
567                              "pagesused", "pages");
568 #endif
569         err = obd_init_checks();
570         if (err == -EOVERFLOW)
571                 return err;
572
573         class_init_uuidlist();
574         err = class_handle_init();
575         if (err)
576                 return err;
577
578         spin_lock_init(&obd_dev_lock);
579         CFS_INIT_LIST_HEAD(&obd_types);
580
581         err = cfs_psdev_register(&obd_psdev);
582         if (err) {
583                 CERROR("cannot register %d err %d\n", OBD_MINOR, err);
584                 return err;
585         }
586
587         /* This struct is already zerod for us (static global) */
588         for (i = 0; i < class_devno_max(); i++)
589                 obd_devs[i] = NULL;
590
591         /* Default the dirty page cache cap to 1/2 of system memory.
592          * For clients with less memory, a larger fraction is needed
593          * for other purposes (mostly for BGL). */
594         if (num_physpages <= 512 << (20 - CFS_PAGE_SHIFT))
595                 obd_max_dirty_pages = num_physpages / 4;
596         else
597                 obd_max_dirty_pages = num_physpages / 2;
598
599         err = obd_init_caches();
600         if (err)
601                 return err;
602 #ifdef __KERNEL__
603         err = lu_global_init();
604         if (err)
605                 return err;
606         err = class_procfs_init();
607         if (err)
608                 return err;
609         err = lustre_register_fs();
610 #endif
611
612         return err;
613 }
614
615 /* liblustre doesn't call cleanup_obdclass, apparently.  we carry on in this
616  * ifdef to the end of the file to cover module and versioning goo.*/
617 #ifdef __KERNEL__
618 static void cleanup_obdclass(void)
619 {
620         int i;
621         int lustre_unregister_fs(void);
622         __u64 memory_leaked, pages_leaked;
623         __u64 memory_max, pages_max;
624         ENTRY;
625
626         lustre_unregister_fs();
627
628         cfs_psdev_deregister(&obd_psdev);
629         for (i = 0; i < class_devno_max(); i++) {
630                 struct obd_device *obd = class_num2obd(i);
631                 if (obd && obd->obd_set_up &&
632                     OBT(obd) && OBP(obd, detach)) {
633                         /* XXX should this call generic detach otherwise? */
634                         LASSERT(obd->obd_magic == OBD_DEVICE_MAGIC);
635                         OBP(obd, detach)(obd);
636                 }
637         }
638         lu_global_fini();
639
640         obd_cleanup_caches();
641         obd_sysctl_clean();
642
643         class_procfs_clean();
644
645         class_handle_cleanup();
646         class_exit_uuidlist();
647         obd_zombie_impexp_stop();
648
649         memory_leaked = obd_memory_sum();
650         pages_leaked = obd_pages_sum();
651
652         memory_max = obd_memory_max();
653         pages_max = obd_pages_max();
654
655         lprocfs_free_stats(&obd_memory);
656         if (memory_leaked > 0) {
657                 CWARN("Memory leaks detected (max "LPU64", leaked "LPD64")\n",
658                       memory_max, memory_leaked);
659         }
660         if (pages_leaked > 0) {
661                 CWARN("Page leaks detected (max "LPU64", leaked "LPU64")\n",
662                       pages_max, pages_leaked);
663         }
664
665         EXIT;
666 }
667
668 MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
669 MODULE_DESCRIPTION("Lustre Class Driver Build Version: " BUILD_VERSION);
670 MODULE_LICENSE("GPL");
671
672 cfs_module(obdclass, LUSTRE_VERSION_STRING, init_obdclass, cleanup_obdclass);
673 #endif