Whamcloud - gitweb
3a4925a7eb8ca2fa6b90d1dae0dd298ec9b3791c
[fs/lustre-release.git] / lustre / lod / lod_dev.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License version 2 for more details.  A copy is
14  * included in the COPYING file that accompanied this code.
15
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * GPL HEADER END
21  */
22 /*
23  * Copyright  2009 Sun Microsystems, Inc. All rights reserved
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2011, 2012, Intel, Inc.
27  *
28  */
29 /*
30  * This file is part of Lustre, http://www.lustre.org/
31  * Lustre is a trademark of Sun Microsystems, Inc.
32  *
33  * lustre/lod/lod_dev.c
34  *
35  * Lustre Logical Object Device
36  *
37  * Author: Alex Zhuravlev <alexey.zhuravlev@intel.com>
38  * Author: Mikhail Pershin <mike.pershin@intel.com>
39  */
40
41 #ifndef EXPORT_SYMTAB
42 # define EXPORT_SYMTAB
43 #endif
44 #define DEBUG_SUBSYSTEM S_MDS
45
46 #include <obd_class.h>
47 #include <lustre_param.h>
48
49 #include "lod_internal.h"
50
51 /* Slab for OSD object allocation */
52 cfs_mem_cache_t *lod_object_kmem;
53
54 static struct lu_kmem_descr lod_caches[] = {
55         {
56                 .ckd_cache = &lod_object_kmem,
57                 .ckd_name  = "lod_obj",
58                 .ckd_size  = sizeof(struct lod_object)
59         },
60         {
61                 .ckd_cache = NULL
62         }
63 };
64
65 static struct lu_device *lod_device_fini(const struct lu_env *env,
66                                          struct lu_device *d);
67
68 static int lod_process_config(const struct lu_env *env,
69                               struct lu_device *dev,
70                               struct lustre_cfg *lcfg)
71 {
72         struct lod_device *lod = lu2lod_dev(dev);
73         struct lu_device  *next = &lod->lod_child->dd_lu_dev;
74         char              *arg1;
75         int                rc, i;
76         ENTRY;
77
78         switch(lcfg->lcfg_command) {
79
80         case LCFG_LOV_DEL_OBD:
81         case LCFG_LOV_ADD_INA:
82         case LCFG_LOV_ADD_OBD: {
83                 __u32 index;
84                 int gen;
85                 /* lov_modify_tgts add  0:lov_mdsA  1:osp  2:0  3:1 */
86                 arg1 = lustre_cfg_string(lcfg, 1);
87
88                 if (sscanf(lustre_cfg_buf(lcfg, 2), "%d", &index) != 1)
89                         GOTO(out, rc = -EINVAL);
90                 if (sscanf(lustre_cfg_buf(lcfg, 3), "%d", &gen) != 1)
91                         GOTO(out, rc = -EINVAL);
92
93                 if (lcfg->lcfg_command == LCFG_LOV_ADD_OBD)
94                         rc = lod_add_device(env, lod, arg1, index, gen, 1);
95                 else if (lcfg->lcfg_command == LCFG_LOV_ADD_INA)
96                         rc = lod_add_device(env, lod, arg1, index, gen, 0);
97                 else
98                         rc = lod_del_device(env, lod, arg1, index, gen);
99
100                 break;
101         }
102
103         case LCFG_PARAM: {
104                 struct lprocfs_static_vars  v = { 0 };
105                 struct obd_device         *obd = lod2obd(lod);
106
107                 lprocfs_lod_init_vars(&v);
108
109                 rc = class_process_proc_param(PARAM_LOV, v.obd_vars, lcfg, obd);
110                 if (rc > 0)
111                         rc = 0;
112                 GOTO(out, rc);
113          }
114
115         case LCFG_CLEANUP:
116                 lu_dev_del_linkage(dev->ld_site, dev);
117                 lod_getref(lod);
118                 lod_foreach_ost(lod, i) {
119                         struct lod_ost_desc *ost;
120                         ost = OST_TGT(lod, i);
121                         LASSERT(ost && ost->ltd_ost);
122                         next = &ost->ltd_ost->dd_lu_dev;
123                         rc = next->ld_ops->ldo_process_config(env, next, lcfg);
124                         if (rc)
125                                 CERROR("%s: can't process %u: %d\n",
126                                        lod2obd(lod)->obd_name,
127                                        lcfg->lcfg_command, rc);
128                 }
129                 lod_putref(lod);
130
131                 /*
132                  * do cleanup on underlying storage only when
133                  * all OSPs are cleaned up, as they use that OSD as well
134                  */
135                 next = &lod->lod_child->dd_lu_dev;
136                 rc = next->ld_ops->ldo_process_config(env, next, lcfg);
137                 if (rc)
138                         CERROR("%s: can't process %u: %d\n",
139                                lod2obd(lod)->obd_name, lcfg->lcfg_command, rc);
140
141                 rc = obd_disconnect(lod->lod_child_exp);
142                 if (rc)
143                         CERROR("error in disconnect from storage: %d\n", rc);
144                 break;
145
146         default:
147                CERROR("%s: unknown command %u\n", lod2obd(lod)->obd_name,
148                       lcfg->lcfg_command);
149                rc = -EINVAL;
150                break;
151         }
152
153 out:
154         RETURN(rc);
155 }
156
157 static int lod_recovery_complete(const struct lu_env *env,
158                                  struct lu_device *dev)
159 {
160         struct lod_device   *lod = lu2lod_dev(dev);
161         struct lu_device    *next = &lod->lod_child->dd_lu_dev;
162         struct lod_ost_desc *ost;
163         int                  i, rc;
164         ENTRY;
165
166         LASSERT(lod->lod_recovery_completed == 0);
167         lod->lod_recovery_completed = 1;
168
169         rc = next->ld_ops->ldo_recovery_complete(env, next);
170
171         lod_getref(lod);
172         lod_foreach_ost(lod, i) {
173                 ost = OST_TGT(lod, i);
174                 LASSERT(ost && ost->ltd_ost);
175                 next = &ost->ltd_ost->dd_lu_dev;
176                 rc = next->ld_ops->ldo_recovery_complete(env, next);
177                 if (rc)
178                         CERROR("%s: can't complete recovery on #%d: %d\n",
179                                lod2obd(lod)->obd_name, i, rc);
180         }
181         lod_putref(lod);
182
183         RETURN(rc);
184 }
185
186 static int lod_prepare(const struct lu_env *env, struct lu_device *pdev,
187                        struct lu_device *cdev)
188 {
189         struct lod_device   *lod = lu2lod_dev(cdev);
190         struct lu_device    *next = &lod->lod_child->dd_lu_dev;
191         int                  rc;
192         ENTRY;
193
194         rc = next->ld_ops->ldo_prepare(env, pdev, next);
195
196         RETURN(rc);
197 }
198
199 const struct lu_device_operations lod_lu_ops = {
200         .ldo_process_config     = lod_process_config,
201         .ldo_recovery_complete  = lod_recovery_complete,
202         .ldo_prepare            = lod_prepare,
203 };
204
205 static int lod_root_get(const struct lu_env *env,
206                         struct dt_device *dev, struct lu_fid *f)
207 {
208         return dt_root_get(env, dt2lod_dev(dev)->lod_child, f);
209 }
210
211 static int lod_statfs(const struct lu_env *env,
212                       struct dt_device *dev, struct obd_statfs *sfs)
213 {
214         return dt_statfs(env, dt2lod_dev(dev)->lod_child, sfs);
215 }
216
217 static struct thandle *lod_trans_create(const struct lu_env *env,
218                                         struct dt_device *dev)
219 {
220         return dt_trans_create(env, dt2lod_dev(dev)->lod_child);
221 }
222
223 static int lod_trans_start(const struct lu_env *env, struct dt_device *dev,
224                            struct thandle *th)
225 {
226         return dt_trans_start(env, dt2lod_dev(dev)->lod_child, th);
227 }
228
229 static int lod_trans_stop(const struct lu_env *env, struct thandle *th)
230 {
231         /* XXX: we don't know next device, will be fixed with DNE */
232         return dt_trans_stop(env, th->th_dev, th);
233 }
234
235 static void lod_conf_get(const struct lu_env *env,
236                          const struct dt_device *dev,
237                          struct dt_device_param *param)
238 {
239         dt_conf_get(env, dt2lod_dev((struct dt_device *)dev)->lod_child, param);
240 }
241
242 static int lod_sync(const struct lu_env *env, struct dt_device *dev)
243 {
244         struct lod_device   *lod = dt2lod_dev(dev);
245         struct lod_ost_desc *ost;
246         int                  rc = 0, i;
247         ENTRY;
248
249         lod_getref(lod);
250         lod_foreach_ost(lod, i) {
251                 ost = OST_TGT(lod, i);
252                 LASSERT(ost && ost->ltd_ost);
253                 rc = dt_sync(env, ost->ltd_ost);
254                 if (rc) {
255                         CERROR("%s: can't sync %u: %d\n",
256                                lod2obd(lod)->obd_name, i, rc);
257                         break;
258                 }
259         }
260         lod_putref(lod);
261         if (rc == 0)
262                 rc = dt_sync(env, lod->lod_child);
263
264         RETURN(rc);
265 }
266
267 static int lod_ro(const struct lu_env *env, struct dt_device *dev)
268 {
269         return dt_ro(env, dt2lod_dev(dev)->lod_child);
270 }
271
272 static int lod_commit_async(const struct lu_env *env, struct dt_device *dev)
273 {
274         return dt_commit_async(env, dt2lod_dev(dev)->lod_child);
275 }
276
277 static int lod_init_capa_ctxt(const struct lu_env *env, struct dt_device *dev,
278                               int mode, unsigned long timeout,
279                               __u32 alg, struct lustre_capa_key *keys)
280 {
281         struct dt_device *next = dt2lod_dev(dev)->lod_child;
282         return dt_init_capa_ctxt(env, next, mode, timeout, alg, keys);
283 }
284
285 static const struct dt_device_operations lod_dt_ops = {
286         .dt_root_get         = lod_root_get,
287         .dt_statfs           = lod_statfs,
288         .dt_trans_create     = lod_trans_create,
289         .dt_trans_start      = lod_trans_start,
290         .dt_trans_stop       = lod_trans_stop,
291         .dt_conf_get         = lod_conf_get,
292         .dt_sync             = lod_sync,
293         .dt_ro               = lod_ro,
294         .dt_commit_async     = lod_commit_async,
295         .dt_init_capa_ctxt   = lod_init_capa_ctxt,
296 };
297
298 static int lod_connect_to_osd(const struct lu_env *env, struct lod_device *lod,
299                               struct lustre_cfg *cfg)
300 {
301         struct obd_connect_data *data = NULL;
302         struct obd_device       *obd;
303         char                    *nextdev = NULL, *p, *s;
304         int                      rc, len = 0;
305         ENTRY;
306
307         LASSERT(cfg);
308         LASSERT(lod->lod_child_exp == NULL);
309
310         /* compatibility hack: we still use old config logs
311          * which specify LOV, but we need to learn underlying
312          * OSD device, which is supposed to be:
313          *  <fsname>-MDTxxxx-osd
314          *
315          * 2.x MGS generates lines like the following:
316          *   #03 (176)lov_setup 0:lustre-MDT0000-mdtlov  1:(struct lov_desc)
317          * 1.8 MGS generates lines like the following:
318          *   #03 (168)lov_setup 0:lustre-mdtlov  1:(struct lov_desc)
319          *
320          * we use "-MDT" to differentiate 2.x from 1.8 */
321
322         if ((p = lustre_cfg_string(cfg, 0)) && strstr(p, "-mdtlov")) {
323                 len = strlen(p) + 1;
324                 OBD_ALLOC(nextdev, len);
325                 if (nextdev == NULL)
326                         GOTO(out, rc = -ENOMEM);
327
328                 strcpy(nextdev, p);
329                 s = strstr(nextdev, "-mdtlov");
330                 if (unlikely(s == NULL)) {
331                         CERROR("unable to parse device name %s\n",
332                                lustre_cfg_string(cfg, 0));
333                         GOTO(out, rc = -EINVAL);
334                 }
335
336                 if (strstr(nextdev, "-MDT")) {
337                         /* 2.x config */
338                         strcpy(s, "-osd");
339                 } else {
340                         /* 1.8 config */
341                         strcpy(s, "-MDT0000-osd");
342                 }
343         } else {
344                 CERROR("unable to parse device name %s\n",
345                        lustre_cfg_string(cfg, 0));
346                 GOTO(out, rc = -EINVAL);
347         }
348
349         OBD_ALLOC_PTR(data);
350         if (data == NULL)
351                 GOTO(out, rc = -ENOMEM);
352
353         obd = class_name2obd(nextdev);
354         if (obd == NULL) {
355                 CERROR("can not locate next device: %s\n", nextdev);
356                 GOTO(out, rc = -ENOTCONN);
357         }
358
359         data->ocd_connect_flags = OBD_CONNECT_VERSION;
360         data->ocd_version = LUSTRE_VERSION_CODE;
361
362         rc = obd_connect(env, &lod->lod_child_exp, obd, &obd->obd_uuid,
363                          data, NULL);
364         if (rc) {
365                 CERROR("cannot connect to next dev %s (%d)\n", nextdev, rc);
366                 GOTO(out, rc);
367         }
368
369         lod->lod_dt_dev.dd_lu_dev.ld_site =
370                 lod->lod_child_exp->exp_obd->obd_lu_dev->ld_site;
371         LASSERT(lod->lod_dt_dev.dd_lu_dev.ld_site);
372         lod->lod_child = lu2dt_dev(lod->lod_child_exp->exp_obd->obd_lu_dev);
373
374 out:
375         if (data)
376                 OBD_FREE_PTR(data);
377         if (nextdev)
378                 OBD_FREE(nextdev, len);
379         RETURN(rc);
380 }
381
382 static int lod_init0(const struct lu_env *env, struct lod_device *lod,
383                      struct lu_device_type *ldt, struct lustre_cfg *cfg)
384 {
385         struct dt_device_param ddp;
386         struct proc_dir_entry *lov_proc_dir;
387         struct obd_device     *obd;
388         int                    rc;
389         ENTRY;
390
391         obd = class_name2obd(lustre_cfg_string(cfg, 0));
392         if (obd == NULL) {
393                 CERROR("Cannot find obd with name %s\n",
394                        lustre_cfg_string(cfg, 0));
395                 RETURN(-ENODEV);
396         }
397
398         obd->obd_lu_dev = &lod->lod_dt_dev.dd_lu_dev;
399         lod->lod_dt_dev.dd_lu_dev.ld_obd = obd;
400         lod->lod_dt_dev.dd_lu_dev.ld_ops = &lod_lu_ops;
401         lod->lod_dt_dev.dd_ops = &lod_dt_ops;
402
403         rc = lod_connect_to_osd(env, lod, cfg);
404         if (rc)
405                 RETURN(rc);
406
407         dt_conf_get(env, &lod->lod_dt_dev, &ddp);
408         lod->lod_osd_max_easize = ddp.ddp_max_ea_size;
409
410         /* setup obd to be used with old lov code */
411         rc = lod_pools_init(lod, cfg);
412         if (rc)
413                 GOTO(out_disconnect, rc);
414
415         /* for compatibility we link old procfs's OSC entries to osp ones */
416         lov_proc_dir = lprocfs_srch(proc_lustre_root, "lov");
417         if (lov_proc_dir) {
418                 cfs_proc_dir_entry_t *symlink = NULL;
419                 char *name;
420                 OBD_ALLOC(name, strlen(obd->obd_name) + 1);
421                 if (name) {
422                         strcpy(name, obd->obd_name);
423                         if (strstr(name, "lov"))
424                                 symlink = lprocfs_add_symlink(name,
425                                                 lov_proc_dir,
426                                                 "../lod/%s",
427                                                 obd->obd_name);
428                         OBD_FREE(name, strlen(obd->obd_name) + 1);
429                         lod->lod_symlink = symlink;
430                 }
431         }
432
433         cfs_mutex_init(&lod->lod_mutex);
434         cfs_init_rwsem(&lod->lod_rw_sem);
435         cfs_spin_lock_init(&lod->lod_desc_lock);
436
437         RETURN(0);
438
439 out_disconnect:
440         obd_disconnect(lod->lod_child_exp);
441         RETURN(rc);
442 }
443
444 static struct lu_device *lod_device_free(const struct lu_env *env,
445                                          struct lu_device *lu)
446 {
447         struct lod_device *lod = lu2lod_dev(lu);
448         struct lu_device  *next = &lod->lod_child->dd_lu_dev;
449         ENTRY;
450
451         LASSERT(cfs_atomic_read(&lu->ld_ref) == 0);
452         dt_device_fini(&lod->lod_dt_dev);
453         OBD_FREE_PTR(lod);
454         RETURN(next);
455 }
456
457 static struct lu_device *lod_device_alloc(const struct lu_env *env,
458                                           struct lu_device_type *type,
459                                           struct lustre_cfg *lcfg)
460 {
461         struct lod_device *lod;
462         struct lu_device  *lu_dev;
463
464         OBD_ALLOC_PTR(lod);
465         if (lod == NULL) {
466                 lu_dev = ERR_PTR(-ENOMEM);
467         } else {
468                 int rc;
469
470                 lu_dev = lod2lu_dev(lod);
471                 dt_device_init(&lod->lod_dt_dev, type);
472                 rc = lod_init0(env, lod, type, lcfg);
473                 if (rc != 0) {
474                         lod_device_free(env, lu_dev);
475                         lu_dev = ERR_PTR(rc);
476                 }
477         }
478
479         return lu_dev;
480 }
481
482 static struct lu_device *lod_device_fini(const struct lu_env *env,
483                                          struct lu_device *d)
484 {
485         struct lod_device *lod = lu2lod_dev(d);
486         ENTRY;
487
488         if (lod->lod_symlink)
489                 lprocfs_remove(&lod->lod_symlink);
490
491         lod_pools_fini(lod);
492
493         RETURN(NULL);
494 }
495
496 /*
497  * we use exports to track all LOD users
498  */
499 static int lod_obd_connect(const struct lu_env *env, struct obd_export **exp,
500                            struct obd_device *obd, struct obd_uuid *cluuid,
501                            struct obd_connect_data *data, void *localdata)
502 {
503         struct lod_device    *lod = lu2lod_dev(obd->obd_lu_dev);
504         struct lustre_handle  conn;
505         int                   rc;
506         ENTRY;
507
508         CDEBUG(D_CONFIG, "connect #%d\n", lod->lod_connects);
509
510         rc = class_connect(&conn, obd, cluuid);
511         if (rc)
512                 RETURN(rc);
513
514         *exp = class_conn2export(&conn);
515
516         cfs_mutex_lock(&lod->lod_mutex);
517         lod->lod_connects++;
518         /* at the moment we expect the only user */
519         LASSERT(lod->lod_connects == 1);
520         cfs_mutex_unlock(&lod->lod_mutex);
521
522         RETURN(0);
523 }
524
525 /*
526  * once last export (we don't count self-export) disappeared
527  * lod can be released
528  */
529 static int lod_obd_disconnect(struct obd_export *exp)
530 {
531         struct obd_device *obd = exp->exp_obd;
532         struct lod_device *lod = lu2lod_dev(obd->obd_lu_dev);
533         int                rc, release = 0;
534         ENTRY;
535
536         /* Only disconnect the underlying layers on the final disconnect. */
537         cfs_mutex_lock(&lod->lod_mutex);
538         lod->lod_connects--;
539         if (lod->lod_connects != 0) {
540                 /* why should there be more than 1 connect? */
541                 cfs_mutex_unlock(&lod->lod_mutex);
542                 CERROR("%s: disconnect #%d\n", exp->exp_obd->obd_name,
543                        lod->lod_connects);
544                 goto out;
545         }
546         cfs_mutex_unlock(&lod->lod_mutex);
547
548         /* the last user of lod has gone, let's release the device */
549         release = 1;
550
551 out:
552         rc = class_disconnect(exp); /* bz 9811 */
553
554         if (rc == 0 && release)
555                 class_manual_cleanup(obd);
556         RETURN(rc);
557 }
558
559 LU_KEY_INIT(lod, struct lod_thread_info);
560
561 static void lod_key_fini(const struct lu_context *ctx,
562                 struct lu_context_key *key, void *data)
563 {
564         struct lod_thread_info *info = data;
565         /* allocated in lod_get_lov_ea
566          * XXX: this is overload, a tread may have such store but used only
567          * once. Probably better would be pool of such stores per LOD.
568          */
569         if (info->lti_ea_store) {
570                 OBD_FREE_LARGE(info->lti_ea_store, info->lti_ea_store_size);
571                 info->lti_ea_store = NULL;
572                 info->lti_ea_store_size = 0;
573         }
574         OBD_FREE_PTR(info);
575 }
576
577 /* context key: lod_thread_key */
578 LU_CONTEXT_KEY_DEFINE(lod, LCT_MD_THREAD);
579
580 LU_TYPE_INIT_FINI(lod, &lod_thread_key);
581
582 static struct lu_device_type_operations lod_device_type_ops = {
583         .ldto_init           = lod_type_init,
584         .ldto_fini           = lod_type_fini,
585
586         .ldto_start          = lod_type_start,
587         .ldto_stop           = lod_type_stop,
588
589         .ldto_device_alloc   = lod_device_alloc,
590         .ldto_device_free    = lod_device_free,
591
592         .ldto_device_fini    = lod_device_fini
593 };
594
595 static struct lu_device_type lod_device_type = {
596         .ldt_tags     = LU_DEVICE_DT,
597         .ldt_name     = LUSTRE_LOD_NAME,
598         .ldt_ops      = &lod_device_type_ops,
599         .ldt_ctx_tags = LCT_MD_THREAD,
600 };
601
602 static int lod_obd_health_check(const struct lu_env *env,
603                 struct obd_device *obd)
604 {
605         struct lod_device   *d = lu2lod_dev(obd->obd_lu_dev);
606         struct lod_ost_desc *ost;
607         int                  i, rc = 1;
608         ENTRY;
609
610         LASSERT(d);
611         lod_getref(d);
612         lod_foreach_ost(d, i) {
613                 ost = OST_TGT(d, i);
614                 LASSERT(ost && ost->ltd_ost);
615                 rc = obd_health_check(env, ost->ltd_exp->exp_obd);
616                 /* one healthy device is enough */
617                 if (rc == 0)
618                         break;
619         }
620         lod_putref(d);
621         RETURN(rc);
622 }
623
624 static struct obd_ops lod_obd_device_ops = {
625         .o_owner        = THIS_MODULE,
626         .o_connect      = lod_obd_connect,
627         .o_disconnect   = lod_obd_disconnect,
628         .o_health_check = lod_obd_health_check,
629         .o_pool_new     = lod_pool_new,
630         .o_pool_rem     = lod_pool_remove,
631         .o_pool_add     = lod_pool_add,
632         .o_pool_del     = lod_pool_del,
633 };
634
635 static int __init lod_mod_init(void)
636 {
637         struct lprocfs_static_vars  lvars = { 0 };
638         cfs_proc_dir_entry_t       *lov_proc_dir;
639         int                         rc;
640
641         rc = lu_kmem_init(lod_caches);
642         if (rc)
643                 return rc;
644
645         lprocfs_lod_init_vars(&lvars);
646
647         rc = class_register_type(&lod_obd_device_ops, NULL, lvars.module_vars,
648                                  LUSTRE_LOD_NAME, &lod_device_type);
649         if (rc) {
650                 lu_kmem_fini(lod_caches);
651                 return rc;
652         }
653
654         /* create "lov" entry in procfs for compatibility purposes */
655         lov_proc_dir = lprocfs_srch(proc_lustre_root, "lov");
656         if (lov_proc_dir == NULL) {
657                 lov_proc_dir = lprocfs_register("lov", proc_lustre_root,
658                                                 NULL, NULL);
659                 if (IS_ERR(lov_proc_dir))
660                         CERROR("lod: can't create compat entry \"lov\": %d\n",
661                                (int)PTR_ERR(lov_proc_dir));
662         }
663
664         return rc;
665 }
666
667 static void __exit lod_mod_exit(void)
668 {
669
670         lprocfs_try_remove_proc_entry("lov", proc_lustre_root);
671
672         class_unregister_type(LUSTRE_LOD_NAME);
673         lu_kmem_fini(lod_caches);
674 }
675
676 MODULE_AUTHOR("Whamcloud, Inc. <http://www.whamcloud.com/>");
677 MODULE_DESCRIPTION("Lustre Logical Object Device ("LUSTRE_LOD_NAME")");
678 MODULE_LICENSE("GPL");
679
680 module_init(lod_mod_init);
681 module_exit(lod_mod_exit);
682