Whamcloud - gitweb
bb766773d33439de1427ea9a773cc608fe0147aa
[fs/lustre-release.git] / lustre / obdclass / class_obd.c
1 /*
2  *              An implementation of a loadable kernel mode driver providing
3  *              multiple kernel/user space bidirectional communications links.
4  *
5  *              Author:         Alan Cox <alan@cymru.net>
6  *
7  *              This program is free software; you can redistribute it and/or
8  *              modify it under the terms of the GNU General Public License
9  *              as published by the Free Software Foundation; either version
10  *              2 of the License, or (at your option) any later version.
11  * 
12  *              Adapted to become the Linux 2.0 Coda pseudo device
13  *              Peter  Braam  <braam@maths.ox.ac.uk> 
14  *              Michael Callahan <mjc@emmy.smith.edu>           
15  *
16  *              Changes for Linux 2.1
17  *              Copyright (c) 1997 Carnegie-Mellon University
18  *
19  *              Redone again for Intermezzo
20  *              Copyright (c) 1998 Peter J. Braam
21  *
22  *              Hacked up again for simulated OBD
23  *              Copyright (c) 1999 Stelias Computing, Inc.
24  *                (authors {pschwan,braam}@stelias.com)
25  *              Copyright (C) 1999 Seagate Technology, Inc.
26  *
27  * 
28  */
29
30 #define EXPORT_SYMTAB
31
32 #include <linux/config.h> /* for CONFIG_PROC_FS */
33 #include <linux/module.h>
34 #include <linux/errno.h>
35 #include <linux/kernel.h>
36 #include <linux/major.h>
37 #include <linux/sched.h>
38 #include <linux/lp.h>
39 #include <linux/malloc.h>
40 #include <linux/ioport.h>
41 #include <linux/fcntl.h>
42 #include <linux/delay.h>
43 #include <linux/skbuff.h>
44 #include <linux/proc_fs.h>
45 #include <linux/vmalloc.h>
46 #include <linux/fs.h>
47 #include <linux/poll.h>
48 #include <linux/init.h>
49 #include <linux/list.h>
50 #include <asm/io.h>
51 #include <asm/segment.h>
52 #include <asm/system.h>
53 #include <asm/poll.h>
54 #include <asm/uaccess.h>
55
56 #include <linux/obd_support.h>
57 #include <linux/obd_class.h>
58
59 int           obd_print_entry = 1;
60 int           obd_debug_level = 4095;
61 struct obd_device obd_dev[MAX_OBD_DEVICES];
62 struct list_head obd_types;
63
64 /* called when opening /dev/obdNNN */
65 static int obd_class_open(struct inode * inode, struct file * file)
66 {
67         int dev;
68         ENTRY;
69
70         if (!inode)
71                 return -EINVAL;
72         dev = MINOR(inode->i_rdev);
73         if (dev >= MAX_OBD_DEVICES)
74                 return -ENODEV;
75         obd_dev[dev].obd_refcnt++;
76         CDEBUG(D_PSDEV, "Refcount now %d\n", obd_dev[dev].obd_refcnt++);
77
78         MOD_INC_USE_COUNT;
79         EXIT;
80         return 0;
81 }
82
83 /* called when closing /dev/obdNNN */
84 static int obd_class_release(struct inode * inode, struct file * file)
85 {
86         int dev;
87         ENTRY;
88
89         if (!inode)
90                 return -EINVAL;
91         dev = MINOR(inode->i_rdev);
92         if (dev >= MAX_OBD_DEVICES)
93                 return -ENODEV;
94         fsync_dev(inode->i_rdev);
95         if (obd_dev[dev].obd_refcnt <= 0)
96                 printk(KERN_ALERT "presto_psdev_release: refcount(%d) <= 0\n",
97                        obd_dev[dev].obd_refcnt);
98         obd_dev[dev].obd_refcnt--;
99
100         CDEBUG(D_PSDEV, "Refcount now %d\n", obd_dev[dev].obd_refcnt++);
101
102         MOD_DEC_USE_COUNT;
103
104         EXIT;
105         return 0;
106 }
107
108 /* support function */
109 static struct obd_type *obd_nm_to_type(char *nm) 
110 {
111         struct list_head *tmp;
112         struct obd_type *type;
113         
114         tmp = &obd_types;
115         while ( (tmp = tmp->next) != &obd_types ) {
116                 type = list_entry(tmp, struct obd_type, typ_chain);
117                 if (strlen(type->typ_name) == strlen(nm) &&
118                     strcmp(type->typ_name, nm) == 0 ) {
119                         return type;
120                 }
121         }
122         return NULL;
123 }
124
125
126 static int getdata(int len, void **data)
127 {
128         void *tmp = NULL;
129
130         if (!len) 
131                 return 0;
132
133         OBD_ALLOC(tmp, void *, len);
134         if ( !tmp )
135                 return -ENOMEM;
136         
137         memset(tmp, 0, len);
138         if ( copy_from_user(tmp, *data, len)) {
139                 OBD_FREE(tmp,len);
140                 return -EFAULT;
141         }
142         *data = tmp;
143
144         return 0;
145 }
146
147 /* to control /dev/obdNNN */
148 static int obd_class_ioctl (struct inode * inode, struct file * filp, 
149                             unsigned int cmd, unsigned long arg)
150 {
151         int err, i_ino, dev;
152         struct obd_device *obddev;
153         struct oic_rw_s rw_s; /* read, write */
154         long int cli_id; /* connect, disconnect */
155
156         struct oic_prealloc_s prealloc; /* preallocate */
157
158         if (!inode)
159                 return -EINVAL;
160
161         dev = MINOR(inode->i_rdev);
162         if (dev > MAX_OBD_DEVICES)
163                 return -ENODEV;
164         obddev = &obd_dev[dev];
165
166         switch (cmd) {
167         case OBD_IOC_ATTACH: {
168                 struct obd_type *type;
169                 struct oic_attach input;
170
171                 /* have we attached a type to this device */
172                 if ( obddev->obd_type || (obddev->obd_flags & OBD_ATTACHED) ){
173                         CDEBUG(D_IOCTL, "OBD Device %d already attached to type %s.\n", dev, obddev->obd_type->typ_name);
174                         return -EINVAL;
175                 }
176
177                 /* get data structures */
178                 err = copy_from_user(&input, (void *) arg, sizeof(input));
179                 if (err)
180                         return err;
181
182                 if ( (err = getdata(input.att_typelen + 1, &input.att_type)) )
183                         return err;
184
185                 /* find the type */
186                 err = -EINVAL;
187                 type = obd_nm_to_type(input.att_type);
188                 OBD_FREE(input.att_type, input.att_typelen + 1);
189                 if ( !type ) {
190                         printk("Unknown obd type dev %d\n", dev);
191                         return err;
192                 }
193                 obddev->obd_type = type;
194                 
195                 /* get the attach data */
196                 if ( (err = getdata(input.att_datalen, &input.att_data)) ) {
197                         return err;
198                 }
199
200                 CDEBUG(D_IOCTL, "Attach %d, type %s\n", 
201                        dev, obddev->obd_type->typ_name);
202                 if (!obddev->obd_type->typ_ops || 
203                     !obddev->obd_type->typ_ops->o_attach ) {
204                         obddev->obd_flags |=  OBD_ATTACHED;
205                         type->typ_refcnt++;
206                         MOD_INC_USE_COUNT;
207                         return 0;
208                 }
209
210                 /* do the attach */
211                 err = obddev->obd_type->typ_ops->o_attach
212                         (obddev,  input.att_datalen, &input.att_data);
213                 OBD_FREE(input.att_data, input.att_datalen);
214
215                 if ( err ) {
216                         obddev->obd_flags &= ~OBD_ATTACHED;
217                         obddev->obd_type = NULL;
218                 } else {
219                         obddev->obd_flags |=  OBD_ATTACHED;
220                         type->typ_refcnt++;
221                         MOD_INC_USE_COUNT;
222                 }
223                 return err;
224         }
225         case OBD_IOC_FORMAT: {
226                 struct ioc_format {
227                         int format_datalen;
228                         void *format_data;
229                 } input;
230
231                 /* have we attached a type to this device */
232                 if ( !obddev->obd_type ) {
233                         CDEBUG(D_IOCTL, "OBD Device %d has no type.\n", dev);
234                         return -EINVAL;
235                 }
236
237                 /* get main structure */
238                 err = copy_from_user(&input, (void *) arg, sizeof(input));
239                 if (err) 
240                         return err;
241
242                 err = getdata(input.format_datalen, &input.format_data);
243                 if (err) 
244                         return err;
245
246                 if (!obddev->obd_type->typ_ops || 
247                     !obddev->obd_type->typ_ops->o_format )
248                         return -EOPNOTSUPP;
249
250                 /* do the format */
251                 CDEBUG(D_IOCTL, "Format %d, type %s\n", dev, 
252                        obddev->obd_type->typ_name);
253                 err = obddev->obd_type->typ_ops->o_format
254                         (obddev, input.format_datalen, input.format_data);
255
256                 OBD_FREE(input.format_data, input.format_datalen);
257                 return err;
258         }
259         case OBD_IOC_PARTITION: {
260                 struct ioc_part {
261                         int part_datalen;
262                         void *part_data;
263                 } input;
264
265                 /* have we attached a type to this device */
266                 if ( !obddev->obd_type ) {
267                         CDEBUG(D_IOCTL, "OBD Device %d has no type.\n", dev);
268                         return -EINVAL;
269                 }
270
271                 /* get main structure */
272                 err = copy_from_user(&input, (void *) arg, sizeof(input));
273                 if (err) 
274                         return err;
275
276                 err = getdata(input.part_datalen, &input.part_data);
277                 if (err) 
278                         return err;
279
280                 if (!obddev->obd_type->typ_ops || 
281                     !obddev->obd_type->typ_ops->o_partition )
282                         return -EOPNOTSUPP;
283
284                 /* do the partition */
285                 CDEBUG(D_IOCTL, "Partition %d, type %s\n", dev, 
286                        obddev->obd_type->typ_name);
287                 err = obddev->obd_type->typ_ops->o_partition
288                         (obddev, input.part_datalen, input.part_data);
289
290                 OBD_FREE(input.part_data, input.part_datalen);
291                 return err;
292         }
293         case OBD_IOC_SETUP_OBDDEV: {
294                 struct ioc_setup {
295                         int setup_datalen;
296                         void *setup_data;
297                 } input;
298
299                 /* have we attached a type to this device */
300                 if (!(obddev->obd_flags & OBD_ATTACHED)) {
301                         CDEBUG(D_IOCTL, "OBD Device %d has no type.\n", dev);
302                         return -EINVAL;
303                 }
304
305                 /* has this been done already? */
306                 if ( obddev->obd_flags & OBD_SET_UP ) {
307                         CDEBUG(D_IOCTL, "Device %d already setup (type %s)\n",
308                                dev, obddev->obd_type->typ_name);
309                         return -EINVAL;
310                 }
311
312                 /* get main structure */
313                 err = copy_from_user(&input, (void *) arg, sizeof(input));
314                 if (err) 
315                         return err;
316
317                 err = getdata(input.setup_datalen, &input.setup_data);
318                 if (err) 
319                         return err;
320
321                 /* do the setup */
322                 CDEBUG(D_IOCTL, "Setup %d, type %s\n", dev, 
323                        obddev->obd_type->typ_name);
324                 if ( !obddev->obd_type->typ_ops || 
325                      !obddev->obd_type->typ_ops->o_setup )
326                         return -EOPNOTSUPP;
327
328                 err = obddev->obd_type->typ_ops->o_setup
329                         (obddev, input.setup_datalen, input.setup_data);
330
331                 if ( err ) 
332                         obddev->obd_flags &= ~OBD_SET_UP;
333                 else 
334                         obddev->obd_flags |= OBD_SET_UP;
335                 return err;
336         }
337         case OBD_IOC_CLEANUP_OBDDEV: {
338                 int rc;
339
340                 /* has this minor been registered? */
341                 if (!obddev->obd_type)
342                         return -ENODEV;
343
344                 if ( !obddev->obd_type->typ_refcnt ) 
345                         printk("OBD_CLEANUP: refcount wrap!\n");
346
347                 if ( !obddev->obd_type->typ_ops->o_cleanup )
348                         goto cleanup_out;
349
350                 /* cleanup has no argument */
351                 rc = obddev->obd_type->typ_ops->o_cleanup(obddev);
352                 if ( rc )
353                         return rc;
354
355         cleanup_out: 
356                 obddev->obd_flags = 0;
357                 obddev->obd_type->typ_refcnt--;
358                 obddev->obd_type = NULL;
359                 MOD_DEC_USE_COUNT;
360                 return 0;
361         }
362         case OBD_IOC_CONNECT:
363         {
364                 struct obd_conn_info conninfo;
365
366                 if ( (!(obddev->obd_flags & OBD_SET_UP)) ||
367                      (!(obddev->obd_flags & OBD_ATTACHED))) {
368                         CDEBUG(D_IOCTL, "Device not attached or set up\n");
369                         return -EINVAL;
370                 }
371
372                 if (obddev->obd_type->typ_ops->o_connect(obddev, &conninfo))
373                         return -EINVAL;
374
375                 return copy_to_user((int *)arg, &conninfo,
376                                     sizeof(struct obd_conn_info));
377         }
378         case OBD_IOC_DISCONNECT:
379                 /* frees data structures */
380                 /* has this minor been registered? */
381                 if (!obddev->obd_type)
382                         return -ENODEV;
383
384                 get_user(cli_id, (int *) arg);
385
386                 obddev->obd_type->typ_ops->o_disconnect(cli_id);
387                 return 0;
388
389         case OBD_IOC_SYNC:
390                 /* sync doesn't need a connection ID, because it knows
391                  * what device it was called on, and can thus get the
392                  * superblock that it needs. */
393                 /* has this minor been registered? */
394                 if (!obddev->obd_type)
395                         return -ENODEV;
396
397                 if (!obddev->u.sim.sim_sb || !obddev->u.sim.sim_sb->s_dev) {
398                         CDEBUG(D_IOCTL, "fatal: device not initialized.\n");
399                         err = -EINVAL;
400                 } else {
401                         if ((err = fsync_dev(obddev->u.sim.sim_sb->s_dev)))
402                                 CDEBUG(D_IOCTL, "sync: fsync_dev failure\n");
403                         else
404                                 CDEBUG(D_IOCTL, "sync: success\n");
405                 }
406
407                 return put_user(err, (int *) arg);
408         case OBD_IOC_CREATE:
409                 /* similarly, create doesn't need a connection ID for
410                  * the same reasons. */
411
412                 /* has this minor been registered? */
413                 if (!obddev->obd_type)
414                         return -ENODEV;
415
416
417                 if (!obddev->u.sim.sim_sb) {
418                         CDEBUG(D_IOCTL, "fatal: device not initialized.\n");
419                         return put_user(-EINVAL, (int *) arg);
420                 }
421
422                 i_ino = obddev->obd_type->typ_ops->o_create(obddev, 0, &err);
423                 if (err) {
424                         CDEBUG(D_IOCTL, "create: obd_inode_new failure\n");
425                         /* 0 is the only error value */
426                         return put_user(0, (int *) arg);
427                 }
428
429                 return put_user(i_ino, (int *) arg);
430         case OBD_IOC_DESTROY:
431         {
432                 struct destroy_s {
433                         unsigned int conn_id;
434                         unsigned int ino;
435                 } destroy;
436
437                 /* has this minor been registered? */
438                 if (!obddev->obd_type)
439                         return -ENODEV;
440
441
442                 copy_from_user(&destroy, (int *)arg, sizeof(struct destroy_s));
443                 if ( !obddev->obd_type ||
444                      !obddev->obd_type->typ_ops->o_destroy)
445                         return -EINVAL;
446
447                 return obddev->obd_type->typ_ops->o_destroy(destroy.conn_id, destroy.ino);
448         }
449         case OBD_IOC_SETATTR:
450         {
451                 int err;
452                 struct tmp {
453                         unsigned int conn_id;
454                         unsigned long ino;
455                         struct iattr iattr;
456                 } foo;
457                 struct inode holder;
458                 /* has this minor been registered? */
459                 if (!obddev->obd_type)
460                         return -ENODEV;
461
462
463                 err= copy_from_user(&foo, (int *)arg, sizeof(struct tmp));
464                 if (err)
465                         return err;
466
467                 if ( !obddev->obd_type ||
468                      !obddev->obd_type->typ_ops->o_setattr)
469                         return -EINVAL;
470                 
471                 inode_setattr(&holder, &foo.iattr);
472                 return obddev->obd_type->typ_ops->o_setattr(foo.conn_id, foo.ino, &holder);
473         }
474
475         case OBD_IOC_GETATTR:
476         {
477                 int err;
478                 struct tmp {
479                         unsigned int conn_id;
480                         unsigned long ino;
481                 } foo;
482                 struct iattr iattr;
483                 struct inode holder;
484                 copy_from_user(&foo, (int *)arg, sizeof(struct tmp));
485
486                 if ( !obddev->obd_type ||
487                      !obddev->obd_type->typ_ops->o_getattr)
488                         return -EINVAL;
489
490                 if (obddev->obd_type->typ_ops->o_getattr(foo.conn_id, 
491                                                          foo.ino, &holder))
492                         return -EINVAL;
493
494                 inode_to_iattr(&holder, &iattr);
495                 err = copy_to_user((int *)arg, &iattr, sizeof(iattr));
496                 return err;
497         }
498
499         case OBD_IOC_READ2:
500         {
501                 int err;
502
503                 /* has this minor been registered? */
504                 if (!obddev->obd_type)
505                         return -ENODEV;
506
507                 err = copy_from_user(&rw_s, (int *)arg, sizeof(struct oic_rw_s));
508                 if ( err ) 
509                         return err;
510
511                 if ( !obddev->obd_type->typ_ops || 
512                      !obddev->obd_type->typ_ops->o_read ) 
513                         return -EINVAL;
514
515                 rw_s.count = obddev->obd_type->typ_ops->o_read2(rw_s.conn_id, 
516                                        rw_s.inode, 
517                                        rw_s.buf,
518                                        rw_s.count, 
519                                        rw_s.offset, 
520                                        &err);
521                 if ( err ) 
522                         return err;
523
524                 err = copy_to_user((int*)arg, &rw_s.count, 
525                                    sizeof(unsigned long));
526                 return err;
527         }
528
529
530         case OBD_IOC_READ:
531         {
532                 int err;
533                 /* has this minor been registered? */
534                 if (!obddev->obd_type)
535                         return -ENODEV;
536
537
538                 err = copy_from_user(&rw_s, (int *)arg, sizeof(struct oic_rw_s));
539                 if ( err ) 
540                         return err;
541
542                 if ( !obddev->obd_type->typ_ops || 
543                      !obddev->obd_type->typ_ops->o_read ) 
544                         return -EINVAL;
545
546                 rw_s.count = obddev->obd_type->typ_ops->o_read(rw_s.conn_id, 
547                                                                rw_s.inode, 
548                                                                rw_s.buf,
549                                                                rw_s.count, 
550                                                                rw_s.offset, 
551                                                                &err);
552                 if ( err ) 
553                         return err;
554
555                 err = copy_to_user((int*)arg, &rw_s.count, 
556                                    sizeof(unsigned long));
557                 return err;
558         }
559
560         case OBD_IOC_WRITE:
561         {
562                 int err;
563                 /* has this minor been registered? */
564                 if (!obddev->obd_type)
565                         return -ENODEV;
566
567
568                 copy_from_user(&rw_s, (int *)arg, sizeof(struct oic_rw_s));
569                 CDEBUG(D_IOCTL, "\n");
570                 if ( !obddev->obd_type->typ_ops->o_write ) 
571                         return -EINVAL;
572                 rw_s.count = 
573                         obddev->obd_type->typ_ops->o_write(rw_s.conn_id,
574                                                            rw_s.inode, 
575                                                            rw_s.buf,
576                                                            rw_s.count, 
577                                                            rw_s.offset, 
578                                                            &err);
579
580                 printk("Result rw_s.count %ld\n", rw_s.count);
581                 return (int)rw_s.count;
582                 copy_to_user((int *)arg, &rw_s.count, 
583                              sizeof(unsigned long));
584                 return err;
585         }
586         case OBD_IOC_PREALLOCATE:
587                 /* has this minor been registered? */
588                 if (!obddev->obd_type)
589                         return -ENODEV;
590
591
592                 copy_from_user(&prealloc, (int *)arg,
593                                sizeof(struct oic_prealloc_s));
594
595                 if (!obddev->u.sim.sim_sb || !obddev->u.sim.sim_sb->s_dev) {
596                         CDEBUG(D_IOCTL, "fatal: device not initialized.\n");
597                         return -EINVAL;
598                 }
599
600                 if (!obddev->obd_type || 
601                     !obddev->obd_type->typ_ops->o_preallocate)
602                         return -EINVAL;
603
604                 prealloc.alloc =
605                         obddev->obd_type->typ_ops->o_preallocate(prealloc.cli_id, prealloc.alloc,
606                                                prealloc.inodes, &err);
607                 if ( err ) 
608                         return err;
609                 return copy_to_user((int *)arg, &prealloc,
610                                     sizeof(struct oic_prealloc_s));
611         case OBD_IOC_STATFS:
612         {
613                 struct statfs *tmp;
614                 unsigned int conn_id;
615                 struct statfs buf;
616                 int rc;
617
618                 /* has this minor been registered? */
619                 if (!obddev->obd_type)
620                         return -ENODEV;
621
622                 tmp = (void *)arg + sizeof(unsigned int);
623                 get_user(conn_id, (int *) arg);
624                 if ( !obddev->obd_type ||
625                      !obddev->obd_type->typ_ops->o_statfs)
626                         return -EINVAL;
627
628                 rc = obddev->obd_type->typ_ops->o_statfs(conn_id, &buf);
629                 if ( rc ) 
630                         return rc;
631                 rc = copy_to_user(tmp, &buf, sizeof(buf));
632                 return rc;
633                 
634         }
635         default:
636                 printk("invalid ioctl: cmd = %x, arg = %lx\n", cmd, arg);
637                 return -ENOTTY;
638         }
639 }
640
641 /* Driver interface done, utility functions follow */
642
643 int obd_register_type(struct obd_ops *ops, char *nm)
644 {
645         struct obd_type *type;
646
647         if  ( obd_nm_to_type(nm) ) {
648                 CDEBUG(D_IOCTL, "Type %s already registered\n", nm);
649                 return -1;
650         }
651
652         OBD_ALLOC(type, struct obd_type * , sizeof(*type));
653         if ( !type ) 
654                 return -ENOMEM;
655         memset(type, 0, sizeof(*type));
656         INIT_LIST_HEAD(&type->typ_chain);
657
658         list_add(&type->typ_chain, obd_types.next);
659         type->typ_ops = ops;
660         type->typ_name = nm;
661         return 0;
662 }
663         
664 int obd_unregister_type(char *nm)
665 {
666         struct obd_type *type = obd_nm_to_type(nm);
667
668         if ( !type ) 
669                 return -1;
670
671         if ( type->typ_refcnt ) 
672                 return -1;
673
674         list_del(&type->typ_chain);
675         OBD_FREE(type, sizeof(*type));
676         return 0;
677 }
678
679 /* declare character device */
680 static struct file_operations obd_psdev_fops = {
681         NULL,                  /* llseek */
682         NULL,                  /* read */
683         NULL,                  /* write */
684         NULL,                  /* presto_psdev_readdir */
685         NULL,                  /* poll */
686         obd_class_ioctl,       /* ioctl */
687         NULL,                  /* presto_psdev_mmap */
688         obd_class_open,        /* open */
689         NULL,
690         obd_class_release,     /* release */
691         NULL,                  /* fsync */
692         NULL,                  /* fasync */
693         NULL,                  /* check_media_change */
694         NULL,                  /* revalidate */
695         NULL                   /* lock */
696 };
697
698
699 /* modules setup */
700
701 int init_obd(void)
702 {
703         int i;
704
705         printk(KERN_INFO "OBD class driver  v0.002, braam@stelias.com\n");
706
707         INIT_LIST_HEAD(&obd_types);
708
709         if (register_chrdev(OBD_PSDEV_MAJOR,"obd_psdev", 
710                             &obd_psdev_fops)) {
711                 printk(KERN_ERR "obd_psdev: unable to get major %d\n", 
712                        OBD_PSDEV_MAJOR);
713                 return -EIO;
714         }
715
716         for (i = 0; i < MAX_OBD_DEVICES; i++) {
717                 memset(&(obd_dev[i]), 0, sizeof(obd_dev[i]));
718                 INIT_LIST_HEAD(&obd_dev[i].u.sim.sim_clients);
719         }
720
721         obd_sysctl_init();
722
723         return 0;
724 }
725
726 EXPORT_SYMBOL(obd_register_type);
727 EXPORT_SYMBOL(obd_unregister_type);
728
729 EXPORT_SYMBOL(obd_print_entry);
730 EXPORT_SYMBOL(obd_debug_level);
731 EXPORT_SYMBOL(obd_dev);
732
733 #ifdef MODULE
734 int init_module(void)
735 {
736         return init_obd();
737 }
738
739 void cleanup_module(void)
740 {
741         int i;
742         ENTRY;
743
744         unregister_chrdev(OBD_PSDEV_MAJOR, "obd_psdev");
745         for (i = 0; i < MAX_OBD_DEVICES; i++) {
746                 struct obd_device *obddev = &obd_dev[i];
747                 if ( obddev->obd_type && 
748                      obddev->obd_type->typ_ops->o_cleanup_device )
749                         return obddev->obd_type->typ_ops->o_cleanup_device(i);
750         }
751
752         obd_sysctl_clean();
753 }
754 #endif