diff options
Diffstat (limited to 'fs/ocfs2/stackglue.c')
-rw-r--r-- | fs/ocfs2/stackglue.c | 568 |
1 files changed, 568 insertions, 0 deletions
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c new file mode 100644 index 000000000000..119f60cea9cc --- /dev/null +++ b/fs/ocfs2/stackglue.c | |||
@@ -0,0 +1,568 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8; -*- | ||
2 | * vim: noexpandtab sw=8 ts=8 sts=0: | ||
3 | * | ||
4 | * stackglue.c | ||
5 | * | ||
6 | * Code which implements an OCFS2 specific interface to underlying | ||
7 | * cluster stacks. | ||
8 | * | ||
9 | * Copyright (C) 2007 Oracle. All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public | ||
13 | * License as published by the Free Software Foundation, version 2. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | */ | ||
20 | |||
21 | #include <linux/list.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/kmod.h> | ||
26 | #include <linux/fs.h> | ||
27 | #include <linux/kobject.h> | ||
28 | #include <linux/sysfs.h> | ||
29 | |||
30 | #include "ocfs2_fs.h" | ||
31 | |||
32 | #include "stackglue.h" | ||
33 | |||
34 | #define OCFS2_STACK_PLUGIN_O2CB "o2cb" | ||
35 | #define OCFS2_STACK_PLUGIN_USER "user" | ||
36 | |||
37 | static struct ocfs2_locking_protocol *lproto; | ||
38 | static DEFINE_SPINLOCK(ocfs2_stack_lock); | ||
39 | static LIST_HEAD(ocfs2_stack_list); | ||
40 | static char cluster_stack_name[OCFS2_STACK_LABEL_LEN + 1]; | ||
41 | |||
42 | /* | ||
43 | * The stack currently in use. If not null, active_stack->sp_count > 0, | ||
44 | * the module is pinned, and the locking protocol cannot be changed. | ||
45 | */ | ||
46 | static struct ocfs2_stack_plugin *active_stack; | ||
47 | |||
48 | static struct ocfs2_stack_plugin *ocfs2_stack_lookup(const char *name) | ||
49 | { | ||
50 | struct ocfs2_stack_plugin *p; | ||
51 | |||
52 | assert_spin_locked(&ocfs2_stack_lock); | ||
53 | |||
54 | list_for_each_entry(p, &ocfs2_stack_list, sp_list) { | ||
55 | if (!strcmp(p->sp_name, name)) | ||
56 | return p; | ||
57 | } | ||
58 | |||
59 | return NULL; | ||
60 | } | ||
61 | |||
62 | static int ocfs2_stack_driver_request(const char *stack_name, | ||
63 | const char *plugin_name) | ||
64 | { | ||
65 | int rc; | ||
66 | struct ocfs2_stack_plugin *p; | ||
67 | |||
68 | spin_lock(&ocfs2_stack_lock); | ||
69 | |||
70 | /* | ||
71 | * If the stack passed by the filesystem isn't the selected one, | ||
72 | * we can't continue. | ||
73 | */ | ||
74 | if (strcmp(stack_name, cluster_stack_name)) { | ||
75 | rc = -EBUSY; | ||
76 | goto out; | ||
77 | } | ||
78 | |||
79 | if (active_stack) { | ||
80 | /* | ||
81 | * If the active stack isn't the one we want, it cannot | ||
82 | * be selected right now. | ||
83 | */ | ||
84 | if (!strcmp(active_stack->sp_name, plugin_name)) | ||
85 | rc = 0; | ||
86 | else | ||
87 | rc = -EBUSY; | ||
88 | goto out; | ||
89 | } | ||
90 | |||
91 | p = ocfs2_stack_lookup(plugin_name); | ||
92 | if (!p || !try_module_get(p->sp_owner)) { | ||
93 | rc = -ENOENT; | ||
94 | goto out; | ||
95 | } | ||
96 | |||
97 | /* Ok, the stack is pinned */ | ||
98 | p->sp_count++; | ||
99 | active_stack = p; | ||
100 | |||
101 | rc = 0; | ||
102 | |||
103 | out: | ||
104 | spin_unlock(&ocfs2_stack_lock); | ||
105 | return rc; | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * This function looks up the appropriate stack and makes it active. If | ||
110 | * there is no stack, it tries to load it. It will fail if the stack still | ||
111 | * cannot be found. It will also fail if a different stack is in use. | ||
112 | */ | ||
113 | static int ocfs2_stack_driver_get(const char *stack_name) | ||
114 | { | ||
115 | int rc; | ||
116 | char *plugin_name = OCFS2_STACK_PLUGIN_O2CB; | ||
117 | |||
118 | /* | ||
119 | * Classic stack does not pass in a stack name. This is | ||
120 | * compatible with older tools as well. | ||
121 | */ | ||
122 | if (!stack_name || !*stack_name) | ||
123 | stack_name = OCFS2_STACK_PLUGIN_O2CB; | ||
124 | |||
125 | if (strlen(stack_name) != OCFS2_STACK_LABEL_LEN) { | ||
126 | printk(KERN_ERR | ||
127 | "ocfs2 passed an invalid cluster stack label: \"%s\"\n", | ||
128 | stack_name); | ||
129 | return -EINVAL; | ||
130 | } | ||
131 | |||
132 | /* Anything that isn't the classic stack is a user stack */ | ||
133 | if (strcmp(stack_name, OCFS2_STACK_PLUGIN_O2CB)) | ||
134 | plugin_name = OCFS2_STACK_PLUGIN_USER; | ||
135 | |||
136 | rc = ocfs2_stack_driver_request(stack_name, plugin_name); | ||
137 | if (rc == -ENOENT) { | ||
138 | request_module("ocfs2_stack_%s", plugin_name); | ||
139 | rc = ocfs2_stack_driver_request(stack_name, plugin_name); | ||
140 | } | ||
141 | |||
142 | if (rc == -ENOENT) { | ||
143 | printk(KERN_ERR | ||
144 | "ocfs2: Cluster stack driver \"%s\" cannot be found\n", | ||
145 | plugin_name); | ||
146 | } else if (rc == -EBUSY) { | ||
147 | printk(KERN_ERR | ||
148 | "ocfs2: A different cluster stack is in use\n"); | ||
149 | } | ||
150 | |||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | static void ocfs2_stack_driver_put(void) | ||
155 | { | ||
156 | spin_lock(&ocfs2_stack_lock); | ||
157 | BUG_ON(active_stack == NULL); | ||
158 | BUG_ON(active_stack->sp_count == 0); | ||
159 | |||
160 | active_stack->sp_count--; | ||
161 | if (!active_stack->sp_count) { | ||
162 | module_put(active_stack->sp_owner); | ||
163 | active_stack = NULL; | ||
164 | } | ||
165 | spin_unlock(&ocfs2_stack_lock); | ||
166 | } | ||
167 | |||
168 | int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin) | ||
169 | { | ||
170 | int rc; | ||
171 | |||
172 | spin_lock(&ocfs2_stack_lock); | ||
173 | if (!ocfs2_stack_lookup(plugin->sp_name)) { | ||
174 | plugin->sp_count = 0; | ||
175 | plugin->sp_proto = lproto; | ||
176 | list_add(&plugin->sp_list, &ocfs2_stack_list); | ||
177 | printk(KERN_INFO "ocfs2: Registered cluster interface %s\n", | ||
178 | plugin->sp_name); | ||
179 | rc = 0; | ||
180 | } else { | ||
181 | printk(KERN_ERR "ocfs2: Stack \"%s\" already registered\n", | ||
182 | plugin->sp_name); | ||
183 | rc = -EEXIST; | ||
184 | } | ||
185 | spin_unlock(&ocfs2_stack_lock); | ||
186 | |||
187 | return rc; | ||
188 | } | ||
189 | EXPORT_SYMBOL_GPL(ocfs2_stack_glue_register); | ||
190 | |||
191 | void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin) | ||
192 | { | ||
193 | struct ocfs2_stack_plugin *p; | ||
194 | |||
195 | spin_lock(&ocfs2_stack_lock); | ||
196 | p = ocfs2_stack_lookup(plugin->sp_name); | ||
197 | if (p) { | ||
198 | BUG_ON(p != plugin); | ||
199 | BUG_ON(plugin == active_stack); | ||
200 | BUG_ON(plugin->sp_count != 0); | ||
201 | list_del_init(&plugin->sp_list); | ||
202 | printk(KERN_INFO "ocfs2: Unregistered cluster interface %s\n", | ||
203 | plugin->sp_name); | ||
204 | } else { | ||
205 | printk(KERN_ERR "Stack \"%s\" is not registered\n", | ||
206 | plugin->sp_name); | ||
207 | } | ||
208 | spin_unlock(&ocfs2_stack_lock); | ||
209 | } | ||
210 | EXPORT_SYMBOL_GPL(ocfs2_stack_glue_unregister); | ||
211 | |||
212 | void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto) | ||
213 | { | ||
214 | struct ocfs2_stack_plugin *p; | ||
215 | |||
216 | BUG_ON(proto == NULL); | ||
217 | |||
218 | spin_lock(&ocfs2_stack_lock); | ||
219 | BUG_ON(active_stack != NULL); | ||
220 | |||
221 | lproto = proto; | ||
222 | list_for_each_entry(p, &ocfs2_stack_list, sp_list) { | ||
223 | p->sp_proto = lproto; | ||
224 | } | ||
225 | |||
226 | spin_unlock(&ocfs2_stack_lock); | ||
227 | } | ||
228 | EXPORT_SYMBOL_GPL(ocfs2_stack_glue_set_locking_protocol); | ||
229 | |||
230 | |||
231 | /* | ||
232 | * The ocfs2_dlm_lock() and ocfs2_dlm_unlock() functions take | ||
233 | * "struct ocfs2_lock_res *astarg" instead of "void *astarg" because the | ||
234 | * underlying stack plugins need to pilfer the lksb off of the lock_res. | ||
235 | * If some other structure needs to be passed as an astarg, the plugins | ||
236 | * will need to be given a different avenue to the lksb. | ||
237 | */ | ||
238 | int ocfs2_dlm_lock(struct ocfs2_cluster_connection *conn, | ||
239 | int mode, | ||
240 | union ocfs2_dlm_lksb *lksb, | ||
241 | u32 flags, | ||
242 | void *name, | ||
243 | unsigned int namelen, | ||
244 | struct ocfs2_lock_res *astarg) | ||
245 | { | ||
246 | BUG_ON(lproto == NULL); | ||
247 | |||
248 | return active_stack->sp_ops->dlm_lock(conn, mode, lksb, flags, | ||
249 | name, namelen, astarg); | ||
250 | } | ||
251 | EXPORT_SYMBOL_GPL(ocfs2_dlm_lock); | ||
252 | |||
253 | int ocfs2_dlm_unlock(struct ocfs2_cluster_connection *conn, | ||
254 | union ocfs2_dlm_lksb *lksb, | ||
255 | u32 flags, | ||
256 | struct ocfs2_lock_res *astarg) | ||
257 | { | ||
258 | BUG_ON(lproto == NULL); | ||
259 | |||
260 | return active_stack->sp_ops->dlm_unlock(conn, lksb, flags, astarg); | ||
261 | } | ||
262 | EXPORT_SYMBOL_GPL(ocfs2_dlm_unlock); | ||
263 | |||
264 | int ocfs2_dlm_lock_status(union ocfs2_dlm_lksb *lksb) | ||
265 | { | ||
266 | return active_stack->sp_ops->lock_status(lksb); | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(ocfs2_dlm_lock_status); | ||
269 | |||
270 | /* | ||
271 | * Why don't we cast to ocfs2_meta_lvb? The "clean" answer is that we | ||
272 | * don't cast at the glue level. The real answer is that the header | ||
273 | * ordering is nigh impossible. | ||
274 | */ | ||
275 | void *ocfs2_dlm_lvb(union ocfs2_dlm_lksb *lksb) | ||
276 | { | ||
277 | return active_stack->sp_ops->lock_lvb(lksb); | ||
278 | } | ||
279 | EXPORT_SYMBOL_GPL(ocfs2_dlm_lvb); | ||
280 | |||
281 | void ocfs2_dlm_dump_lksb(union ocfs2_dlm_lksb *lksb) | ||
282 | { | ||
283 | active_stack->sp_ops->dump_lksb(lksb); | ||
284 | } | ||
285 | EXPORT_SYMBOL_GPL(ocfs2_dlm_dump_lksb); | ||
286 | |||
287 | int ocfs2_cluster_connect(const char *stack_name, | ||
288 | const char *group, | ||
289 | int grouplen, | ||
290 | void (*recovery_handler)(int node_num, | ||
291 | void *recovery_data), | ||
292 | void *recovery_data, | ||
293 | struct ocfs2_cluster_connection **conn) | ||
294 | { | ||
295 | int rc = 0; | ||
296 | struct ocfs2_cluster_connection *new_conn; | ||
297 | |||
298 | BUG_ON(group == NULL); | ||
299 | BUG_ON(conn == NULL); | ||
300 | BUG_ON(recovery_handler == NULL); | ||
301 | |||
302 | if (grouplen > GROUP_NAME_MAX) { | ||
303 | rc = -EINVAL; | ||
304 | goto out; | ||
305 | } | ||
306 | |||
307 | new_conn = kzalloc(sizeof(struct ocfs2_cluster_connection), | ||
308 | GFP_KERNEL); | ||
309 | if (!new_conn) { | ||
310 | rc = -ENOMEM; | ||
311 | goto out; | ||
312 | } | ||
313 | |||
314 | memcpy(new_conn->cc_name, group, grouplen); | ||
315 | new_conn->cc_namelen = grouplen; | ||
316 | new_conn->cc_recovery_handler = recovery_handler; | ||
317 | new_conn->cc_recovery_data = recovery_data; | ||
318 | |||
319 | /* Start the new connection at our maximum compatibility level */ | ||
320 | new_conn->cc_version = lproto->lp_max_version; | ||
321 | |||
322 | /* This will pin the stack driver if successful */ | ||
323 | rc = ocfs2_stack_driver_get(stack_name); | ||
324 | if (rc) | ||
325 | goto out_free; | ||
326 | |||
327 | rc = active_stack->sp_ops->connect(new_conn); | ||
328 | if (rc) { | ||
329 | ocfs2_stack_driver_put(); | ||
330 | goto out_free; | ||
331 | } | ||
332 | |||
333 | *conn = new_conn; | ||
334 | |||
335 | out_free: | ||
336 | if (rc) | ||
337 | kfree(new_conn); | ||
338 | |||
339 | out: | ||
340 | return rc; | ||
341 | } | ||
342 | EXPORT_SYMBOL_GPL(ocfs2_cluster_connect); | ||
343 | |||
344 | /* If hangup_pending is 0, the stack driver will be dropped */ | ||
345 | int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn, | ||
346 | int hangup_pending) | ||
347 | { | ||
348 | int ret; | ||
349 | |||
350 | BUG_ON(conn == NULL); | ||
351 | |||
352 | ret = active_stack->sp_ops->disconnect(conn, hangup_pending); | ||
353 | |||
354 | /* XXX Should we free it anyway? */ | ||
355 | if (!ret) { | ||
356 | kfree(conn); | ||
357 | if (!hangup_pending) | ||
358 | ocfs2_stack_driver_put(); | ||
359 | } | ||
360 | |||
361 | return ret; | ||
362 | } | ||
363 | EXPORT_SYMBOL_GPL(ocfs2_cluster_disconnect); | ||
364 | |||
365 | void ocfs2_cluster_hangup(const char *group, int grouplen) | ||
366 | { | ||
367 | BUG_ON(group == NULL); | ||
368 | BUG_ON(group[grouplen] != '\0'); | ||
369 | |||
370 | if (active_stack->sp_ops->hangup) | ||
371 | active_stack->sp_ops->hangup(group, grouplen); | ||
372 | |||
373 | /* cluster_disconnect() was called with hangup_pending==1 */ | ||
374 | ocfs2_stack_driver_put(); | ||
375 | } | ||
376 | EXPORT_SYMBOL_GPL(ocfs2_cluster_hangup); | ||
377 | |||
378 | int ocfs2_cluster_this_node(unsigned int *node) | ||
379 | { | ||
380 | return active_stack->sp_ops->this_node(node); | ||
381 | } | ||
382 | EXPORT_SYMBOL_GPL(ocfs2_cluster_this_node); | ||
383 | |||
384 | |||
385 | /* | ||
386 | * Sysfs bits | ||
387 | */ | ||
388 | |||
389 | static ssize_t ocfs2_max_locking_protocol_show(struct kobject *kobj, | ||
390 | struct kobj_attribute *attr, | ||
391 | char *buf) | ||
392 | { | ||
393 | ssize_t ret = 0; | ||
394 | |||
395 | spin_lock(&ocfs2_stack_lock); | ||
396 | if (lproto) | ||
397 | ret = snprintf(buf, PAGE_SIZE, "%u.%u\n", | ||
398 | lproto->lp_max_version.pv_major, | ||
399 | lproto->lp_max_version.pv_minor); | ||
400 | spin_unlock(&ocfs2_stack_lock); | ||
401 | |||
402 | return ret; | ||
403 | } | ||
404 | |||
405 | static struct kobj_attribute ocfs2_attr_max_locking_protocol = | ||
406 | __ATTR(max_locking_protocol, S_IFREG | S_IRUGO, | ||
407 | ocfs2_max_locking_protocol_show, NULL); | ||
408 | |||
409 | static ssize_t ocfs2_loaded_cluster_plugins_show(struct kobject *kobj, | ||
410 | struct kobj_attribute *attr, | ||
411 | char *buf) | ||
412 | { | ||
413 | ssize_t ret = 0, total = 0, remain = PAGE_SIZE; | ||
414 | struct ocfs2_stack_plugin *p; | ||
415 | |||
416 | spin_lock(&ocfs2_stack_lock); | ||
417 | list_for_each_entry(p, &ocfs2_stack_list, sp_list) { | ||
418 | ret = snprintf(buf, remain, "%s\n", | ||
419 | p->sp_name); | ||
420 | if (ret < 0) { | ||
421 | total = ret; | ||
422 | break; | ||
423 | } | ||
424 | if (ret == remain) { | ||
425 | /* snprintf() didn't fit */ | ||
426 | total = -E2BIG; | ||
427 | break; | ||
428 | } | ||
429 | total += ret; | ||
430 | remain -= ret; | ||
431 | } | ||
432 | spin_unlock(&ocfs2_stack_lock); | ||
433 | |||
434 | return total; | ||
435 | } | ||
436 | |||
437 | static struct kobj_attribute ocfs2_attr_loaded_cluster_plugins = | ||
438 | __ATTR(loaded_cluster_plugins, S_IFREG | S_IRUGO, | ||
439 | ocfs2_loaded_cluster_plugins_show, NULL); | ||
440 | |||
441 | static ssize_t ocfs2_active_cluster_plugin_show(struct kobject *kobj, | ||
442 | struct kobj_attribute *attr, | ||
443 | char *buf) | ||
444 | { | ||
445 | ssize_t ret = 0; | ||
446 | |||
447 | spin_lock(&ocfs2_stack_lock); | ||
448 | if (active_stack) { | ||
449 | ret = snprintf(buf, PAGE_SIZE, "%s\n", | ||
450 | active_stack->sp_name); | ||
451 | if (ret == PAGE_SIZE) | ||
452 | ret = -E2BIG; | ||
453 | } | ||
454 | spin_unlock(&ocfs2_stack_lock); | ||
455 | |||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | static struct kobj_attribute ocfs2_attr_active_cluster_plugin = | ||
460 | __ATTR(active_cluster_plugin, S_IFREG | S_IRUGO, | ||
461 | ocfs2_active_cluster_plugin_show, NULL); | ||
462 | |||
463 | static ssize_t ocfs2_cluster_stack_show(struct kobject *kobj, | ||
464 | struct kobj_attribute *attr, | ||
465 | char *buf) | ||
466 | { | ||
467 | ssize_t ret; | ||
468 | spin_lock(&ocfs2_stack_lock); | ||
469 | ret = snprintf(buf, PAGE_SIZE, "%s\n", cluster_stack_name); | ||
470 | spin_unlock(&ocfs2_stack_lock); | ||
471 | |||
472 | return ret; | ||
473 | } | ||
474 | |||
475 | static ssize_t ocfs2_cluster_stack_store(struct kobject *kobj, | ||
476 | struct kobj_attribute *attr, | ||
477 | const char *buf, size_t count) | ||
478 | { | ||
479 | size_t len = count; | ||
480 | ssize_t ret; | ||
481 | |||
482 | if (len == 0) | ||
483 | return len; | ||
484 | |||
485 | if (buf[len - 1] == '\n') | ||
486 | len--; | ||
487 | |||
488 | if ((len != OCFS2_STACK_LABEL_LEN) || | ||
489 | (strnlen(buf, len) != len)) | ||
490 | return -EINVAL; | ||
491 | |||
492 | spin_lock(&ocfs2_stack_lock); | ||
493 | if (active_stack) { | ||
494 | if (!strncmp(buf, cluster_stack_name, len)) | ||
495 | ret = count; | ||
496 | else | ||
497 | ret = -EBUSY; | ||
498 | } else { | ||
499 | memcpy(cluster_stack_name, buf, len); | ||
500 | ret = count; | ||
501 | } | ||
502 | spin_unlock(&ocfs2_stack_lock); | ||
503 | |||
504 | return ret; | ||
505 | } | ||
506 | |||
507 | |||
508 | static struct kobj_attribute ocfs2_attr_cluster_stack = | ||
509 | __ATTR(cluster_stack, S_IFREG | S_IRUGO | S_IWUSR, | ||
510 | ocfs2_cluster_stack_show, | ||
511 | ocfs2_cluster_stack_store); | ||
512 | |||
513 | static struct attribute *ocfs2_attrs[] = { | ||
514 | &ocfs2_attr_max_locking_protocol.attr, | ||
515 | &ocfs2_attr_loaded_cluster_plugins.attr, | ||
516 | &ocfs2_attr_active_cluster_plugin.attr, | ||
517 | &ocfs2_attr_cluster_stack.attr, | ||
518 | NULL, | ||
519 | }; | ||
520 | |||
521 | static struct attribute_group ocfs2_attr_group = { | ||
522 | .attrs = ocfs2_attrs, | ||
523 | }; | ||
524 | |||
525 | static struct kset *ocfs2_kset; | ||
526 | |||
527 | static void ocfs2_sysfs_exit(void) | ||
528 | { | ||
529 | kset_unregister(ocfs2_kset); | ||
530 | } | ||
531 | |||
532 | static int ocfs2_sysfs_init(void) | ||
533 | { | ||
534 | int ret; | ||
535 | |||
536 | ocfs2_kset = kset_create_and_add("ocfs2", NULL, fs_kobj); | ||
537 | if (!ocfs2_kset) | ||
538 | return -ENOMEM; | ||
539 | |||
540 | ret = sysfs_create_group(&ocfs2_kset->kobj, &ocfs2_attr_group); | ||
541 | if (ret) | ||
542 | goto error; | ||
543 | |||
544 | return 0; | ||
545 | |||
546 | error: | ||
547 | kset_unregister(ocfs2_kset); | ||
548 | return ret; | ||
549 | } | ||
550 | |||
551 | static int __init ocfs2_stack_glue_init(void) | ||
552 | { | ||
553 | strcpy(cluster_stack_name, OCFS2_STACK_PLUGIN_O2CB); | ||
554 | |||
555 | return ocfs2_sysfs_init(); | ||
556 | } | ||
557 | |||
558 | static void __exit ocfs2_stack_glue_exit(void) | ||
559 | { | ||
560 | lproto = NULL; | ||
561 | ocfs2_sysfs_exit(); | ||
562 | } | ||
563 | |||
564 | MODULE_AUTHOR("Oracle"); | ||
565 | MODULE_DESCRIPTION("ocfs2 cluter stack glue layer"); | ||
566 | MODULE_LICENSE("GPL"); | ||
567 | module_init(ocfs2_stack_glue_init); | ||
568 | module_exit(ocfs2_stack_glue_exit); | ||