Whamcloud - gitweb
LU-413 limit used inodes for performance tests
[fs/lustre-release.git] / lustre / obdecho / echo.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) 2002, 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/obdecho/echo.c
37  *
38  * Author: Peter Braam <braam@clusterfs.com>
39  * Author: Andreas Dilger <adilger@clusterfs.com>
40  */
41
42 #ifndef EXPORT_SYMTAB
43 # define EXPORT_SYMTAB
44 #endif
45
46 #define DEBUG_SUBSYSTEM S_ECHO
47
48 #include <obd_support.h>
49 #include <obd_class.h>
50 #include <obd_echo.h>
51 #include <lustre_debug.h>
52 #include <lustre_dlm.h>
53 #include <lprocfs_status.h>
54
55 /* The echo objid needs to be below 2^32, because regular FID numbers are
56  * limited to 2^32 objects in f_oid for the FID_SEQ_ECHO range. b=23335 */
57 #define ECHO_INIT_OID        0x10000000ULL
58 #define ECHO_HANDLE_MAGIC    0xabcd0123fedc9876ULL
59
60 #define ECHO_PERSISTENT_PAGES (ECHO_PERSISTENT_SIZE >> CFS_PAGE_SHIFT)
61 static cfs_page_t *echo_persistent_pages[ECHO_PERSISTENT_PAGES];
62
63 enum {
64         LPROC_ECHO_READ_BYTES = 1,
65         LPROC_ECHO_WRITE_BYTES = 2,
66         LPROC_ECHO_LAST = LPROC_ECHO_WRITE_BYTES +1
67 };
68
69 static int echo_connect(struct lustre_handle *conn, struct obd_device *obd,
70                         struct obd_uuid *cluuid, struct obd_connect_data *data,
71                         void *localdata)
72 {
73         data->ocd_connect_flags &= ECHO_CONNECT_SUPPORTED;
74         return class_connect(conn, obd, cluuid);
75 }
76
77 static int echo_disconnect(struct obd_export *exp)
78 {
79         LASSERT (exp != NULL);
80
81         return server_disconnect_export(exp);
82 }
83
84 static int echo_init_export(struct obd_export *exp)
85 {
86         return ldlm_init_export(exp);
87 }
88
89 static int echo_destroy_export(struct obd_export *exp)
90 {
91         ENTRY;
92
93         target_destroy_export(exp);
94         ldlm_destroy_export(exp);
95
96         RETURN(0);
97 }
98
99  static __u64 echo_next_id(struct obd_device *obddev)
100 {
101         obd_id id;
102
103         spin_lock(&obddev->u.echo.eo_lock);
104         id = ++obddev->u.echo.eo_lastino;
105         spin_unlock(&obddev->u.echo.eo_lock);
106
107         return id;
108 }
109
110 int echo_create(struct obd_export *exp, struct obdo *oa,
111                 struct lov_stripe_md **ea, struct obd_trans_info *oti)
112 {
113         struct obd_device *obd = class_exp2obd(exp);
114
115         if (!obd) {
116                 CERROR("invalid client cookie "LPX64"\n",
117                        exp->exp_handle.h_cookie);
118                 return -EINVAL;
119         }
120
121         if (!(oa->o_mode && S_IFMT)) {
122                 CERROR("echo obd: no type!\n");
123                 return -ENOENT;
124         }
125
126         if (!(oa->o_valid & OBD_MD_FLTYPE)) {
127                 CERROR("invalid o_valid "LPX64"\n", oa->o_valid);
128                 return -EINVAL;
129         }
130
131         oa->o_id = echo_next_id(obd);
132         oa->o_valid = OBD_MD_FLID;
133
134         return 0;
135 }
136
137 int echo_destroy(struct obd_export *exp, struct obdo *oa,
138                  struct lov_stripe_md *ea, struct obd_trans_info *oti,
139                  struct obd_export *md_exp)
140 {
141         struct obd_device *obd = class_exp2obd(exp);
142
143         ENTRY;
144         if (!obd) {
145                 CERROR("invalid client cookie "LPX64"\n",
146                        exp->exp_handle.h_cookie);
147                 RETURN(-EINVAL);
148         }
149
150         if (!(oa->o_valid & OBD_MD_FLID)) {
151                 CERROR("obdo missing FLID valid flag: "LPX64"\n", oa->o_valid);
152                 RETURN(-EINVAL);
153         }
154
155         if (oa->o_id > obd->u.echo.eo_lastino || oa->o_id < ECHO_INIT_OID) {
156                 CERROR("bad destroy objid: "LPX64"\n", oa->o_id);
157                 RETURN(-EINVAL);
158         }
159
160         RETURN(0);
161 }
162
163 static int echo_getattr(struct obd_export *exp, struct obd_info *oinfo)
164 {
165         struct obd_device *obd = class_exp2obd(exp);
166         obd_id id = oinfo->oi_oa->o_id;
167
168         ENTRY;
169         if (!obd) {
170                 CERROR("invalid client cookie "LPX64"\n",
171                        exp->exp_handle.h_cookie);
172                 RETURN(-EINVAL);
173         }
174
175         if (!(oinfo->oi_oa->o_valid & OBD_MD_FLID)) {
176                 CERROR("obdo missing FLID valid flag: "LPX64"\n",
177                        oinfo->oi_oa->o_valid);
178                 RETURN(-EINVAL);
179         }
180
181         obdo_cpy_md(oinfo->oi_oa, &obd->u.echo.eo_oa, oinfo->oi_oa->o_valid);
182         oinfo->oi_oa->o_id = id;
183
184         RETURN(0);
185 }
186
187 static int echo_setattr(struct obd_export *exp, struct obd_info *oinfo,
188                         struct obd_trans_info *oti)
189 {
190         struct obd_device *obd = class_exp2obd(exp);
191
192         ENTRY;
193         if (!obd) {
194                 CERROR("invalid client cookie "LPX64"\n",
195                        exp->exp_handle.h_cookie);
196                 RETURN(-EINVAL);
197         }
198
199         if (!(oinfo->oi_oa->o_valid & OBD_MD_FLID)) {
200                 CERROR("obdo missing FLID valid flag: "LPX64"\n",
201                        oinfo->oi_oa->o_valid);
202                 RETURN(-EINVAL);
203         }
204
205         memcpy(&obd->u.echo.eo_oa, oinfo->oi_oa, sizeof(*oinfo->oi_oa));
206
207         if (oinfo->oi_oa->o_id & 4) {
208                 /* Save lock to force ACKed reply */
209                 ldlm_lock_addref (&obd->u.echo.eo_nl_lock, LCK_NL);
210                 oti->oti_ack_locks[0].mode = LCK_NL;
211                 oti->oti_ack_locks[0].lock = obd->u.echo.eo_nl_lock;
212         }
213
214         RETURN(0);
215 }
216
217 static void
218 echo_page_debug_setup(cfs_page_t *page, int rw, obd_id id,
219                       __u64 offset, int len)
220 {
221         int   page_offset = offset & ~CFS_PAGE_MASK;
222         char *addr        = ((char *)cfs_kmap(page)) + page_offset;
223
224         if (len % OBD_ECHO_BLOCK_SIZE != 0)
225                 CERROR("Unexpected block size %d\n", len);
226
227         while (len > 0) {
228                 if (rw & OBD_BRW_READ)
229                         block_debug_setup(addr, OBD_ECHO_BLOCK_SIZE,
230                                           offset, id);
231                 else
232                         block_debug_setup(addr, OBD_ECHO_BLOCK_SIZE,
233                                           0xecc0ecc0ecc0ecc0ULL,
234                                           0xecc0ecc0ecc0ecc0ULL);
235
236                 addr   += OBD_ECHO_BLOCK_SIZE;
237                 offset += OBD_ECHO_BLOCK_SIZE;
238                 len    -= OBD_ECHO_BLOCK_SIZE;
239         }
240
241         cfs_kunmap(page);
242 }
243
244 static int
245 echo_page_debug_check(cfs_page_t *page, obd_id id,
246                       __u64 offset, int len)
247 {
248         int   page_offset = offset & ~CFS_PAGE_MASK;
249         char *addr        = ((char *)cfs_kmap(page)) + page_offset;
250         int   rc          = 0;
251         int   rc2;
252
253         if (len % OBD_ECHO_BLOCK_SIZE != 0)
254                 CERROR("Unexpected block size %d\n", len);
255
256         while (len > 0) {
257                 rc2 = block_debug_check("echo", addr, OBD_ECHO_BLOCK_SIZE,
258                                         offset, id);
259
260                 if (rc2 != 0 && rc == 0)
261                         rc = rc2;
262
263                 addr   += OBD_ECHO_BLOCK_SIZE;
264                 offset += OBD_ECHO_BLOCK_SIZE;
265                 len    -= OBD_ECHO_BLOCK_SIZE;
266         }
267
268         cfs_kunmap(page);
269
270         return (rc);
271 }
272
273 /* This allows us to verify that desc_private is passed unmolested */
274 #define DESC_PRIV 0x10293847
275
276 static int echo_map_nb_to_lb(struct obdo *oa, struct obd_ioobj *obj,
277                              struct niobuf_remote *nb, int *pages,
278                              struct niobuf_local *lb, int cmd, int *left)
279 {
280         int gfp_mask = (obj->ioo_id & 1) ? CFS_ALLOC_HIGHUSER : CFS_ALLOC_STD;
281         int ispersistent = obj->ioo_id == ECHO_PERSISTENT_OBJID;
282         int debug_setup = (!ispersistent &&
283                            (oa->o_valid & OBD_MD_FLFLAGS) != 0 &&
284                            (oa->o_flags & OBD_FL_DEBUG_CHECK) != 0);
285         struct niobuf_local *res = lb;
286         obd_off offset = nb->offset;
287         int len = nb->len;
288
289         while (len > 0) {
290                 int plen = CFS_PAGE_SIZE - (offset & (CFS_PAGE_SIZE-1));
291                 if (len < plen)
292                         plen = len;
293
294                 /* check for local buf overflow */
295                 if (*left == 0)
296                         return -EINVAL;
297
298                 res->offset = offset;
299                 res->len = plen;
300                 LASSERT((res->offset & ~CFS_PAGE_MASK) + res->len <= CFS_PAGE_SIZE);
301
302
303                 if (ispersistent &&
304                     (res->offset >> CFS_PAGE_SHIFT) < ECHO_PERSISTENT_PAGES) {
305                         res->page = echo_persistent_pages[res->offset >>
306                                 CFS_PAGE_SHIFT];
307                         /* Take extra ref so __free_pages() can be called OK */
308                         cfs_get_page (res->page);
309                 } else {
310                         OBD_PAGE_ALLOC(res->page, gfp_mask);
311                         if (res->page == NULL) {
312                                 CERROR("can't get page for id " LPU64"\n",
313                                        obj->ioo_id);
314                                 return -ENOMEM;
315                         }
316                 }
317
318                 CDEBUG(D_PAGE, "$$$$ get page %p @ "LPU64" for %d\n",
319                        res->page, res->offset, res->len);
320
321                 if (cmd & OBD_BRW_READ)
322                         res->rc = res->len;
323
324                 if (debug_setup)
325                         echo_page_debug_setup(res->page, cmd, obj->ioo_id,
326                                               res->offset, res->len);
327
328                 offset += plen;
329                 len -= plen;
330                 res++;
331
332                 (*left)--;
333                 (*pages)++;
334         }
335
336         return 0;
337 }
338
339 int echo_preprw(int cmd, struct obd_export *export, struct obdo *oa,
340                 int objcount, struct obd_ioobj *obj, struct niobuf_remote *nb,
341                 int *pages, struct niobuf_local *res,
342                 struct obd_trans_info *oti)
343 {
344         struct obd_device *obd;
345         struct niobuf_local *r = res;
346         int tot_bytes = 0;
347         int rc = 0;
348         int i, left;
349         ENTRY;
350
351         obd = export->exp_obd;
352         if (obd == NULL)
353                 RETURN(-EINVAL);
354
355         /* Temp fix to stop falling foul of osc_announce_cached() */
356         oa->o_valid &= ~(OBD_MD_FLBLOCKS | OBD_MD_FLGRANT);
357
358         memset(res, 0, sizeof(*res) * *pages);
359
360         CDEBUG(D_PAGE, "%s %d obdos with %d IOs\n",
361                cmd == OBD_BRW_READ ? "reading" : "writing", objcount, *pages);
362
363         if (oti)
364                 oti->oti_handle = (void *)DESC_PRIV;
365
366         left = *pages;
367         *pages = 0;
368
369         for (i = 0; i < objcount; i++, obj++) {
370                 int j;
371
372                 for (j = 0 ; j < obj->ioo_bufcnt ; j++, nb++, r++) {
373
374                         rc = echo_map_nb_to_lb(oa, obj, nb, pages,
375                                                res + *pages, cmd, &left);
376                         if (rc)
377                                 GOTO(preprw_cleanup, rc);
378
379                         tot_bytes += nb->len;
380                 }
381         }
382
383         atomic_add(*pages, &obd->u.echo.eo_prep);
384
385         if (cmd & OBD_BRW_READ)
386                 lprocfs_counter_add(obd->obd_stats, LPROC_ECHO_READ_BYTES,
387                                     tot_bytes);
388         else
389                 lprocfs_counter_add(obd->obd_stats, LPROC_ECHO_WRITE_BYTES,
390                                     tot_bytes);
391
392         CDEBUG(D_PAGE, "%d pages allocated after prep\n",
393                atomic_read(&obd->u.echo.eo_prep));
394
395         RETURN(0);
396
397 preprw_cleanup:
398         /* It is possible that we would rather handle errors by  allow
399          * any already-set-up pages to complete, rather than tearing them
400          * all down again.  I believe that this is what the in-kernel
401          * prep/commit operations do.
402          */
403         CERROR("cleaning up %u pages (%d obdos)\n", *pages, objcount);
404         for (i = 0; i < *pages; i++) {
405                 cfs_kunmap(res[i].page);
406                 /* NB if this is a persistent page, __free_pages will just
407                  * lose the extra ref gained above */
408                 OBD_PAGE_FREE(res[i].page);
409                 res[i].page = NULL;
410                 atomic_dec(&obd->u.echo.eo_prep);
411         }
412
413         return rc;
414 }
415
416 int echo_commitrw(int cmd, struct obd_export *export, struct obdo *oa,
417                   int objcount, struct obd_ioobj *obj,
418                   struct niobuf_remote *rb, int niocount,
419                   struct niobuf_local *res, struct obd_trans_info *oti, int rc)
420 {
421         struct obd_device *obd;
422         struct niobuf_local *r = res;
423         struct niobuf_remote *rbc = rb;
424         int i, vrc = 0;
425         ENTRY;
426
427         obd = export->exp_obd;
428         if (obd == NULL)
429                 RETURN(-EINVAL);
430
431         if (rc)
432                 GOTO(commitrw_cleanup, rc);
433
434         if ((cmd & OBD_BRW_RWMASK) == OBD_BRW_READ) {
435                 CDEBUG(D_PAGE, "reading %d obdos with %d IOs\n",
436                        objcount, niocount);
437         } else {
438                 CDEBUG(D_PAGE, "writing %d obdos with %d IOs\n",
439                        objcount, niocount);
440         }
441
442         if (niocount && !r) {
443                 CERROR("NULL res niobuf with niocount %d\n", niocount);
444                 RETURN(-EINVAL);
445         }
446
447         LASSERT(oti == NULL || oti->oti_handle == (void *)DESC_PRIV);
448
449         for (i = 0; i < objcount; i++, obj++) {
450                 int verify = (rc == 0 &&
451                               obj->ioo_id != ECHO_PERSISTENT_OBJID &&
452                               (oa->o_valid & OBD_MD_FLFLAGS) != 0 &&
453                               (oa->o_flags & OBD_FL_DEBUG_CHECK) != 0);
454                 int j;
455
456                 for (j = 0 ; j < obj->ioo_bufcnt ; j++, rbc++) {
457                         long l;
458                         for (l = rbc->len; l>0; l-=r->len, r++) {
459                                 cfs_page_t *page = r->page;
460                                 void *addr;
461
462                                 if (page == NULL) {
463                                         CERROR("null page objid "LPU64":%p, buf %d/%d\n",
464                                         obj->ioo_id, page, j, obj->ioo_bufcnt);
465                                         GOTO(commitrw_cleanup, rc = -EFAULT);
466                                 }
467
468                                 addr = cfs_kmap(page);
469
470                                 CDEBUG(D_PAGE, "$$$$ use page %p, addr %p@"LPU64"\n",
471                                 r->page, addr, r->offset);
472
473                                 if (verify) {
474                                         vrc = echo_page_debug_check(page, obj->ioo_id,
475                                                                 r->offset, r->len);
476                                         /* check all the pages always */
477                                         if (vrc != 0 && rc == 0)
478                                                 rc = vrc;
479                                 }
480
481                                 cfs_kunmap(page);
482                                 /* NB see comment above regarding persistent pages */
483                                 OBD_PAGE_FREE(page);
484                                 atomic_dec(&obd->u.echo.eo_prep);
485                         }
486                 }
487         }
488         CDEBUG(D_PAGE, "%d pages remain after commit\n",
489                atomic_read(&obd->u.echo.eo_prep));
490         RETURN(rc);
491
492 commitrw_cleanup:
493         CERROR("cleaning up %ld pages (%d obdos)\n",
494                niocount - (long)(r - res) - 1, objcount);
495         while (++r < res + niocount) {
496                 cfs_page_t *page = r->page;
497
498                 /* NB see comment above regarding persistent pages */
499                 OBD_PAGE_FREE(page);
500                 atomic_dec(&obd->u.echo.eo_prep);
501         }
502         return rc;
503 }
504
505 static int echo_setup(struct obd_device *obd, obd_count len, void *buf)
506 {
507         struct lprocfs_static_vars lvars;
508         int                        rc;
509         int                        lock_flags = 0;
510         struct ldlm_res_id         res_id = {.name = {1}};
511         char                       ns_name[48];
512         ENTRY;
513
514         spin_lock_init(&obd->u.echo.eo_lock);
515         obd->u.echo.eo_lastino = ECHO_INIT_OID;
516
517         sprintf(ns_name, "echotgt-%s", obd->obd_uuid.uuid);
518         obd->obd_namespace = ldlm_namespace_new(obd, ns_name,
519                                                 LDLM_NAMESPACE_SERVER,
520                                                 LDLM_NAMESPACE_GREEDY);
521         if (obd->obd_namespace == NULL) {
522                 LBUG();
523                 RETURN(-ENOMEM);
524         }
525
526         rc = ldlm_cli_enqueue_local(obd->obd_namespace, &res_id, LDLM_PLAIN,
527                                     NULL, LCK_NL, &lock_flags, NULL,
528                                     ldlm_completion_ast, NULL, NULL,
529                                     0, NULL, &obd->u.echo.eo_nl_lock);
530         LASSERT (rc == ELDLM_OK);
531
532         lprocfs_echo_init_vars(&lvars);
533         if (lprocfs_obd_setup(obd, lvars.obd_vars) == 0 &&
534             lprocfs_alloc_obd_stats(obd, LPROC_ECHO_LAST) == 0) {
535                 lprocfs_counter_init(obd->obd_stats, LPROC_ECHO_READ_BYTES,
536                                      LPROCFS_CNTR_AVGMINMAX,
537                                      "read_bytes", "bytes");
538                 lprocfs_counter_init(obd->obd_stats, LPROC_ECHO_WRITE_BYTES,
539                                      LPROCFS_CNTR_AVGMINMAX,
540                                      "write_bytes", "bytes");
541         }
542
543         ptlrpc_init_client (LDLM_CB_REQUEST_PORTAL, LDLM_CB_REPLY_PORTAL,
544                             "echo_ldlm_cb_client", &obd->obd_ldlm_client);
545         RETURN(0);
546 }
547
548 static int echo_cleanup(struct obd_device *obd)
549 {
550         int leaked;
551         ENTRY;
552
553         lprocfs_obd_cleanup(obd);
554         lprocfs_free_obd_stats(obd);
555
556         ldlm_lock_decref (&obd->u.echo.eo_nl_lock, LCK_NL);
557
558         /* XXX Bug 3413; wait for a bit to ensure the BL callback has
559          * happened before calling ldlm_namespace_free() */
560         cfs_schedule_timeout (CFS_TASK_UNINT, cfs_time_seconds(1));
561
562         ldlm_namespace_free(obd->obd_namespace, NULL, obd->obd_force);
563         obd->obd_namespace = NULL;
564
565         leaked = atomic_read(&obd->u.echo.eo_prep);
566         if (leaked != 0)
567                 CERROR("%d prep/commitrw pages leaked\n", leaked);
568
569         RETURN(0);
570 }
571
572 static struct obd_ops echo_obd_ops = {
573         .o_owner           = THIS_MODULE,
574         .o_connect         = echo_connect,
575         .o_disconnect      = echo_disconnect,
576         .o_init_export     = echo_init_export,
577         .o_destroy_export  = echo_destroy_export,
578         .o_create          = echo_create,
579         .o_destroy         = echo_destroy,
580         .o_getattr         = echo_getattr,
581         .o_setattr         = echo_setattr,
582         .o_preprw          = echo_preprw,
583         .o_commitrw        = echo_commitrw,
584         .o_setup           = echo_setup,
585         .o_cleanup         = echo_cleanup
586 };
587
588 extern int echo_client_init(void);
589 extern void echo_client_exit(void);
590
591 static void
592 echo_persistent_pages_fini (void)
593 {
594         int     i;
595
596         for (i = 0; i < ECHO_PERSISTENT_PAGES; i++)
597                 if (echo_persistent_pages[i] != NULL) {
598                         OBD_PAGE_FREE(echo_persistent_pages[i]);
599                         echo_persistent_pages[i] = NULL;
600                 }
601 }
602
603 static int
604 echo_persistent_pages_init (void)
605 {
606         cfs_page_t *pg;
607         int          i;
608
609         for (i = 0; i < ECHO_PERSISTENT_PAGES; i++) {
610                 int gfp_mask = (i < ECHO_PERSISTENT_PAGES/2) ?
611                         CFS_ALLOC_STD : CFS_ALLOC_HIGHUSER;
612
613                 OBD_PAGE_ALLOC(pg, gfp_mask);
614                 if (pg == NULL) {
615                         echo_persistent_pages_fini ();
616                         return (-ENOMEM);
617                 }
618
619                 memset (cfs_kmap (pg), 0, CFS_PAGE_SIZE);
620                 cfs_kunmap (pg);
621
622                 echo_persistent_pages[i] = pg;
623         }
624
625         return (0);
626 }
627
628 static int __init obdecho_init(void)
629 {
630         struct lprocfs_static_vars lvars;
631         int rc;
632
633         ENTRY;
634         printk(KERN_INFO "Lustre: Echo OBD driver; http://www.lustre.org/\n");
635
636         LASSERT(CFS_PAGE_SIZE % OBD_ECHO_BLOCK_SIZE == 0);
637
638         lprocfs_echo_init_vars(&lvars);
639
640         rc = echo_persistent_pages_init ();
641         if (rc != 0)
642                 goto failed_0;
643
644         rc = class_register_type(&echo_obd_ops, lvars.module_vars,
645                                  LUSTRE_ECHO_NAME);
646         if (rc != 0)
647                 goto failed_1;
648
649         rc = echo_client_init();
650         if (rc == 0)
651                 RETURN (0);
652
653         class_unregister_type(LUSTRE_ECHO_NAME);
654  failed_1:
655         echo_persistent_pages_fini ();
656  failed_0:
657         RETURN(rc);
658 }
659
660 static void /*__exit*/ obdecho_exit(void)
661 {
662         echo_client_exit();
663         class_unregister_type(LUSTRE_ECHO_NAME);
664         echo_persistent_pages_fini ();
665 }
666
667 MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
668 MODULE_DESCRIPTION("Lustre Testing Echo OBD driver");
669 MODULE_LICENSE("GPL");
670
671 cfs_module(obdecho, "1.0.0", obdecho_init, obdecho_exit);