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