blob: 1f05960a916f387e3fa835f3424a8c269ca71178 [file] [log] [blame]
David Teigland597d0ca2006-07-12 16:44:04 -05001/*
2 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
3 *
4 * This copyrighted material is made available to anyone wishing to use,
5 * modify, copy, or redistribute it subject to the terms and conditions
6 * of the GNU General Public License v.2.
7 */
8
9#include <linux/miscdevice.h>
10#include <linux/init.h>
11#include <linux/wait.h>
12#include <linux/module.h>
13#include <linux/file.h>
14#include <linux/fs.h>
15#include <linux/poll.h>
16#include <linux/signal.h>
17#include <linux/spinlock.h>
18#include <linux/dlm.h>
19#include <linux/dlm_device.h>
20
21#include "dlm_internal.h"
22#include "lockspace.h"
23#include "lock.h"
24#include "lvb_table.h"
25
26static const char *name_prefix="dlm";
27static struct miscdevice ctl_device;
28static struct file_operations device_fops;
29
30#ifdef CONFIG_COMPAT
31
32struct dlm_lock_params32 {
33 __u8 mode;
34 __u8 namelen;
35 __u16 flags;
36 __u32 lkid;
37 __u32 parent;
38
39 __u32 castparam;
40 __u32 castaddr;
41 __u32 bastparam;
42 __u32 bastaddr;
43 __u32 lksb;
44
45 char lvb[DLM_USER_LVB_LEN];
46 char name[0];
47};
48
49struct dlm_write_request32 {
50 __u32 version[3];
51 __u8 cmd;
52 __u8 is64bit;
53 __u8 unused[2];
54
55 union {
56 struct dlm_lock_params32 lock;
57 struct dlm_lspace_params lspace;
58 } i;
59};
60
61struct dlm_lksb32 {
62 __u32 sb_status;
63 __u32 sb_lkid;
64 __u8 sb_flags;
65 __u32 sb_lvbptr;
66};
67
68struct dlm_lock_result32 {
69 __u32 length;
70 __u32 user_astaddr;
71 __u32 user_astparam;
72 __u32 user_lksb;
73 struct dlm_lksb32 lksb;
74 __u8 bast_mode;
75 __u8 unused[3];
76 /* Offsets may be zero if no data is present */
77 __u32 lvb_offset;
78};
79
80static void compat_input(struct dlm_write_request *kb,
81 struct dlm_write_request32 *kb32)
82{
83 kb->version[0] = kb32->version[0];
84 kb->version[1] = kb32->version[1];
85 kb->version[2] = kb32->version[2];
86
87 kb->cmd = kb32->cmd;
88 kb->is64bit = kb32->is64bit;
89 if (kb->cmd == DLM_USER_CREATE_LOCKSPACE ||
90 kb->cmd == DLM_USER_REMOVE_LOCKSPACE) {
91 kb->i.lspace.flags = kb32->i.lspace.flags;
92 kb->i.lspace.minor = kb32->i.lspace.minor;
93 strcpy(kb->i.lspace.name, kb32->i.lspace.name);
94 } else {
95 kb->i.lock.mode = kb32->i.lock.mode;
96 kb->i.lock.namelen = kb32->i.lock.namelen;
97 kb->i.lock.flags = kb32->i.lock.flags;
98 kb->i.lock.lkid = kb32->i.lock.lkid;
99 kb->i.lock.parent = kb32->i.lock.parent;
100 kb->i.lock.castparam = (void *)(long)kb32->i.lock.castparam;
101 kb->i.lock.castaddr = (void *)(long)kb32->i.lock.castaddr;
102 kb->i.lock.bastparam = (void *)(long)kb32->i.lock.bastparam;
103 kb->i.lock.bastaddr = (void *)(long)kb32->i.lock.bastaddr;
104 kb->i.lock.lksb = (void *)(long)kb32->i.lock.lksb;
105 memcpy(kb->i.lock.lvb, kb32->i.lock.lvb, DLM_USER_LVB_LEN);
106 memcpy(kb->i.lock.name, kb32->i.lock.name, kb->i.lock.namelen);
107 }
108}
109
110static void compat_output(struct dlm_lock_result *res,
111 struct dlm_lock_result32 *res32)
112{
113 res32->length = res->length - (sizeof(struct dlm_lock_result) -
114 sizeof(struct dlm_lock_result32));
115 res32->user_astaddr = (__u32)(long)res->user_astaddr;
116 res32->user_astparam = (__u32)(long)res->user_astparam;
117 res32->user_lksb = (__u32)(long)res->user_lksb;
118 res32->bast_mode = res->bast_mode;
119
120 res32->lvb_offset = res->lvb_offset;
121 res32->length = res->length;
122
123 res32->lksb.sb_status = res->lksb.sb_status;
124 res32->lksb.sb_flags = res->lksb.sb_flags;
125 res32->lksb.sb_lkid = res->lksb.sb_lkid;
126 res32->lksb.sb_lvbptr = (__u32)(long)res->lksb.sb_lvbptr;
127}
128#endif
129
130
131void dlm_user_add_ast(struct dlm_lkb *lkb, int type)
132{
133 struct dlm_ls *ls;
134 struct dlm_user_args *ua;
135 struct dlm_user_proc *proc;
136
137 /* dlm_clear_proc_locks() sets ORPHAN/DEAD flag on each
138 lkb before dealing with it. We need to check this
139 flag before taking ls_clear_proc_locks mutex because if
140 it's set, dlm_clear_proc_locks() holds the mutex. */
141
142 if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) {
143 /* log_print("user_add_ast skip1 %x", lkb->lkb_flags); */
144 return;
145 }
146
147 ls = lkb->lkb_resource->res_ls;
148 mutex_lock(&ls->ls_clear_proc_locks);
149
150 /* If ORPHAN/DEAD flag is set, it means the process is dead so an ast
151 can't be delivered. For ORPHAN's, dlm_clear_proc_locks() freed
152 lkb->ua so we can't try to use it. */
153
154 if (lkb->lkb_flags & (DLM_IFL_ORPHAN | DLM_IFL_DEAD)) {
155 /* log_print("user_add_ast skip2 %x", lkb->lkb_flags); */
156 goto out;
157 }
158
159 DLM_ASSERT(lkb->lkb_astparam, dlm_print_lkb(lkb););
160 ua = (struct dlm_user_args *)lkb->lkb_astparam;
161 proc = ua->proc;
162
163 if (type == AST_BAST && ua->bastaddr == NULL)
164 goto out;
165
166 spin_lock(&proc->asts_spin);
167 if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
168 kref_get(&lkb->lkb_ref);
169 list_add_tail(&lkb->lkb_astqueue, &proc->asts);
170 lkb->lkb_ast_type |= type;
171 wake_up_interruptible(&proc->wait);
172 }
173
174 /* We want to copy the lvb to userspace when the completion
175 ast is read if the status is 0, the lock has an lvb and
176 lvb_ops says we should. We could probably have set_lvb_lock()
177 set update_user_lvb instead and not need old_mode */
178
179 if ((lkb->lkb_ast_type & AST_COMP) &&
180 (lkb->lkb_lksb->sb_status == 0) &&
181 lkb->lkb_lksb->sb_lvbptr &&
182 dlm_lvb_operations[ua->old_mode + 1][lkb->lkb_grmode + 1])
183 ua->update_user_lvb = 1;
184 else
185 ua->update_user_lvb = 0;
186
187 spin_unlock(&proc->asts_spin);
188 out:
189 mutex_unlock(&ls->ls_clear_proc_locks);
190}
191
192static int device_user_lock(struct dlm_user_proc *proc,
193 struct dlm_lock_params *params)
194{
195 struct dlm_ls *ls;
196 struct dlm_user_args *ua;
197 int error = -ENOMEM;
198
199 ls = dlm_find_lockspace_local(proc->lockspace);
200 if (!ls)
201 return -ENOENT;
202
203 if (!params->castaddr || !params->lksb) {
204 error = -EINVAL;
205 goto out;
206 }
207
208 ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
209 if (!ua)
210 goto out;
211 ua->proc = proc;
212 ua->user_lksb = params->lksb;
213 ua->castparam = params->castparam;
214 ua->castaddr = params->castaddr;
215 ua->bastparam = params->bastparam;
216 ua->bastaddr = params->bastaddr;
217
218 if (params->flags & DLM_LKF_CONVERT)
219 error = dlm_user_convert(ls, ua,
220 params->mode, params->flags,
221 params->lkid, params->lvb);
222 else {
223 error = dlm_user_request(ls, ua,
224 params->mode, params->flags,
225 params->name, params->namelen,
226 params->parent);
227 if (!error)
228 error = ua->lksb.sb_lkid;
229 }
230 out:
231 dlm_put_lockspace(ls);
232 return error;
233}
234
235static int device_user_unlock(struct dlm_user_proc *proc,
236 struct dlm_lock_params *params)
237{
238 struct dlm_ls *ls;
239 struct dlm_user_args *ua;
240 int error = -ENOMEM;
241
242 ls = dlm_find_lockspace_local(proc->lockspace);
243 if (!ls)
244 return -ENOENT;
245
246 ua = kzalloc(sizeof(struct dlm_user_args), GFP_KERNEL);
247 if (!ua)
248 goto out;
249 ua->proc = proc;
250 ua->user_lksb = params->lksb;
251 ua->castparam = params->castparam;
252 ua->castaddr = params->castaddr;
253
254 if (params->flags & DLM_LKF_CANCEL)
255 error = dlm_user_cancel(ls, ua, params->flags, params->lkid);
256 else
257 error = dlm_user_unlock(ls, ua, params->flags, params->lkid,
258 params->lvb);
259 out:
260 dlm_put_lockspace(ls);
261 return error;
262}
263
264static int device_create_lockspace(struct dlm_lspace_params *params)
265{
266 dlm_lockspace_t *lockspace;
267 struct dlm_ls *ls;
268 int error, len;
269
270 if (!capable(CAP_SYS_ADMIN))
271 return -EPERM;
272
273 error = dlm_new_lockspace(params->name, strlen(params->name),
274 &lockspace, 0, DLM_USER_LVB_LEN);
275 if (error)
276 return error;
277
278 ls = dlm_find_lockspace_local(lockspace);
279 if (!ls)
280 return -ENOENT;
281
282 error = -ENOMEM;
283 len = strlen(params->name) + strlen(name_prefix) + 2;
284 ls->ls_device.name = kzalloc(len, GFP_KERNEL);
285 if (!ls->ls_device.name)
286 goto fail;
287 snprintf((char *)ls->ls_device.name, len, "%s_%s", name_prefix,
288 params->name);
289 ls->ls_device.fops = &device_fops;
290 ls->ls_device.minor = MISC_DYNAMIC_MINOR;
291
292 error = misc_register(&ls->ls_device);
293 if (error) {
294 kfree(ls->ls_device.name);
295 goto fail;
296 }
297
298 error = ls->ls_device.minor;
299 dlm_put_lockspace(ls);
300 return error;
301
302 fail:
303 dlm_put_lockspace(ls);
304 dlm_release_lockspace(lockspace, 0);
305 return error;
306}
307
308static int device_remove_lockspace(struct dlm_lspace_params *params)
309{
310 dlm_lockspace_t *lockspace;
311 struct dlm_ls *ls;
312 int error;
313
314 if (!capable(CAP_SYS_ADMIN))
315 return -EPERM;
316
317 ls = dlm_find_lockspace_device(params->minor);
318 if (!ls)
319 return -ENOENT;
320
321 error = misc_deregister(&ls->ls_device);
322 if (error) {
323 dlm_put_lockspace(ls);
324 goto out;
325 }
326 kfree(ls->ls_device.name);
327
328 lockspace = ls->ls_local_handle;
329
330 /* dlm_release_lockspace waits for references to go to zero,
331 so all processes will need to close their device for the ls
332 before the release will procede */
333
334 dlm_put_lockspace(ls);
335 error = dlm_release_lockspace(lockspace, 0);
336out:
337 return error;
338}
339
340/* Check the user's version matches ours */
341static int check_version(struct dlm_write_request *req)
342{
343 if (req->version[0] != DLM_DEVICE_VERSION_MAJOR ||
344 (req->version[0] == DLM_DEVICE_VERSION_MAJOR &&
345 req->version[1] > DLM_DEVICE_VERSION_MINOR)) {
346
347 printk(KERN_DEBUG "dlm: process %s (%d) version mismatch "
348 "user (%d.%d.%d) kernel (%d.%d.%d)\n",
349 current->comm,
350 current->pid,
351 req->version[0],
352 req->version[1],
353 req->version[2],
354 DLM_DEVICE_VERSION_MAJOR,
355 DLM_DEVICE_VERSION_MINOR,
356 DLM_DEVICE_VERSION_PATCH);
357 return -EINVAL;
358 }
359 return 0;
360}
361
362/*
363 * device_write
364 *
365 * device_user_lock
366 * dlm_user_request -> request_lock
367 * dlm_user_convert -> convert_lock
368 *
369 * device_user_unlock
370 * dlm_user_unlock -> unlock_lock
371 * dlm_user_cancel -> cancel_lock
372 *
373 * device_create_lockspace
374 * dlm_new_lockspace
375 *
376 * device_remove_lockspace
377 * dlm_release_lockspace
378 */
379
380/* a write to a lockspace device is a lock or unlock request, a write
381 to the control device is to create/remove a lockspace */
382
383static ssize_t device_write(struct file *file, const char __user *buf,
384 size_t count, loff_t *ppos)
385{
386 struct dlm_user_proc *proc = file->private_data;
387 struct dlm_write_request *kbuf;
388 sigset_t tmpsig, allsigs;
389 int error;
390
391#ifdef CONFIG_COMPAT
392 if (count < sizeof(struct dlm_write_request32))
393#else
394 if (count < sizeof(struct dlm_write_request))
395#endif
396 return -EINVAL;
397
398 kbuf = kmalloc(count, GFP_KERNEL);
399 if (!kbuf)
400 return -ENOMEM;
401
402 if (copy_from_user(kbuf, buf, count)) {
403 error = -EFAULT;
404 goto out_free;
405 }
406
407 if (check_version(kbuf)) {
408 error = -EBADE;
409 goto out_free;
410 }
411
412#ifdef CONFIG_COMPAT
413 if (!kbuf->is64bit) {
414 struct dlm_write_request32 *k32buf;
415 k32buf = (struct dlm_write_request32 *)kbuf;
416 kbuf = kmalloc(count + (sizeof(struct dlm_write_request) -
417 sizeof(struct dlm_write_request32)), GFP_KERNEL);
418 if (!kbuf)
419 return -ENOMEM;
420
421 if (proc)
422 set_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags);
423 compat_input(kbuf, k32buf);
424 kfree(k32buf);
425 }
426#endif
427
428 /* do we really need this? can a write happen after a close? */
429 if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
430 test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
431 return -EINVAL;
432
433 sigfillset(&allsigs);
434 sigprocmask(SIG_BLOCK, &allsigs, &tmpsig);
435
436 error = -EINVAL;
437
438 switch (kbuf->cmd)
439 {
440 case DLM_USER_LOCK:
441 if (!proc) {
442 log_print("no locking on control device");
443 goto out_sig;
444 }
445 error = device_user_lock(proc, &kbuf->i.lock);
446 break;
447
448 case DLM_USER_UNLOCK:
449 if (!proc) {
450 log_print("no locking on control device");
451 goto out_sig;
452 }
453 error = device_user_unlock(proc, &kbuf->i.lock);
454 break;
455
456 case DLM_USER_CREATE_LOCKSPACE:
457 if (proc) {
458 log_print("create/remove only on control device");
459 goto out_sig;
460 }
461 error = device_create_lockspace(&kbuf->i.lspace);
462 break;
463
464 case DLM_USER_REMOVE_LOCKSPACE:
465 if (proc) {
466 log_print("create/remove only on control device");
467 goto out_sig;
468 }
469 error = device_remove_lockspace(&kbuf->i.lspace);
470 break;
471
472 default:
473 log_print("Unknown command passed to DLM device : %d\n",
474 kbuf->cmd);
475 }
476
477 out_sig:
478 sigprocmask(SIG_SETMASK, &tmpsig, NULL);
479 recalc_sigpending();
480 out_free:
481 kfree(kbuf);
482 return error;
483}
484
485/* Every process that opens the lockspace device has its own "proc" structure
486 hanging off the open file that's used to keep track of locks owned by the
487 process and asts that need to be delivered to the process. */
488
489static int device_open(struct inode *inode, struct file *file)
490{
491 struct dlm_user_proc *proc;
492 struct dlm_ls *ls;
493
494 ls = dlm_find_lockspace_device(iminor(inode));
495 if (!ls)
496 return -ENOENT;
497
498 proc = kzalloc(sizeof(struct dlm_user_proc), GFP_KERNEL);
499 if (!proc) {
500 dlm_put_lockspace(ls);
501 return -ENOMEM;
502 }
503
504 proc->lockspace = ls->ls_local_handle;
505 INIT_LIST_HEAD(&proc->asts);
506 INIT_LIST_HEAD(&proc->locks);
507 spin_lock_init(&proc->asts_spin);
508 spin_lock_init(&proc->locks_spin);
509 init_waitqueue_head(&proc->wait);
510 file->private_data = proc;
511
512 return 0;
513}
514
515static int device_close(struct inode *inode, struct file *file)
516{
517 struct dlm_user_proc *proc = file->private_data;
518 struct dlm_ls *ls;
519 sigset_t tmpsig, allsigs;
520
521 ls = dlm_find_lockspace_local(proc->lockspace);
522 if (!ls)
523 return -ENOENT;
524
525 sigfillset(&allsigs);
526 sigprocmask(SIG_BLOCK, &allsigs, &tmpsig);
527
528 set_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags);
529
530 dlm_clear_proc_locks(ls, proc);
531
532 /* at this point no more lkb's should exist for this lockspace,
533 so there's no chance of dlm_user_add_ast() being called and
534 looking for lkb->ua->proc */
535
536 kfree(proc);
537 file->private_data = NULL;
538
539 dlm_put_lockspace(ls);
540 dlm_put_lockspace(ls); /* for the find in device_open() */
541
542 /* FIXME: AUTOFREE: if this ls is no longer used do
543 device_remove_lockspace() */
544
545 sigprocmask(SIG_SETMASK, &tmpsig, NULL);
546 recalc_sigpending();
547
548 return 0;
549}
550
551static int copy_result_to_user(struct dlm_user_args *ua, int compat, int type,
552 int bmode, char __user *buf, size_t count)
553{
554#ifdef CONFIG_COMPAT
555 struct dlm_lock_result32 result32;
556#endif
557 struct dlm_lock_result result;
558 void *resultptr;
559 int error=0;
560 int len;
561 int struct_len;
562
563 memset(&result, 0, sizeof(struct dlm_lock_result));
564 memcpy(&result.lksb, &ua->lksb, sizeof(struct dlm_lksb));
565 result.user_lksb = ua->user_lksb;
566
567 /* FIXME: dlm1 provides for the user's bastparam/addr to not be updated
568 in a conversion unless the conversion is successful. See code
569 in dlm_user_convert() for updating ua from ua_tmp. OpenVMS, though,
570 notes that a new blocking AST address and parameter are set even if
571 the conversion fails, so maybe we should just do that. */
572
573 if (type == AST_BAST) {
574 result.user_astaddr = ua->bastaddr;
575 result.user_astparam = ua->bastparam;
576 result.bast_mode = bmode;
577 } else {
578 result.user_astaddr = ua->castaddr;
579 result.user_astparam = ua->castparam;
580 }
581
582#ifdef CONFIG_COMPAT
583 if (compat)
584 len = sizeof(struct dlm_lock_result32);
585 else
586#endif
587 len = sizeof(struct dlm_lock_result);
588 struct_len = len;
589
590 /* copy lvb to userspace if there is one, it's been updated, and
591 the user buffer has space for it */
592
593 if (ua->update_user_lvb && ua->lksb.sb_lvbptr &&
594 count >= len + DLM_USER_LVB_LEN) {
595 if (copy_to_user(buf+len, ua->lksb.sb_lvbptr,
596 DLM_USER_LVB_LEN)) {
597 error = -EFAULT;
598 goto out;
599 }
600
601 result.lvb_offset = len;
602 len += DLM_USER_LVB_LEN;
603 }
604
605 result.length = len;
606 resultptr = &result;
607#ifdef CONFIG_COMPAT
608 if (compat) {
609 compat_output(&result, &result32);
610 resultptr = &result32;
611 }
612#endif
613
614 if (copy_to_user(buf, resultptr, struct_len))
615 error = -EFAULT;
616 else
617 error = len;
618 out:
619 return error;
620}
621
622/* a read returns a single ast described in a struct dlm_lock_result */
623
624static ssize_t device_read(struct file *file, char __user *buf, size_t count,
625 loff_t *ppos)
626{
627 struct dlm_user_proc *proc = file->private_data;
628 struct dlm_lkb *lkb;
629 struct dlm_user_args *ua;
630 DECLARE_WAITQUEUE(wait, current);
631 int error, type=0, bmode=0, removed = 0;
632
633#ifdef CONFIG_COMPAT
634 if (count < sizeof(struct dlm_lock_result32))
635#else
636 if (count < sizeof(struct dlm_lock_result))
637#endif
638 return -EINVAL;
639
640 /* do we really need this? can a read happen after a close? */
641 if (test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
642 return -EINVAL;
643
644 spin_lock(&proc->asts_spin);
645 if (list_empty(&proc->asts)) {
646 if (file->f_flags & O_NONBLOCK) {
647 spin_unlock(&proc->asts_spin);
648 return -EAGAIN;
649 }
650
651 add_wait_queue(&proc->wait, &wait);
652
653 repeat:
654 set_current_state(TASK_INTERRUPTIBLE);
655 if (list_empty(&proc->asts) && !signal_pending(current)) {
656 spin_unlock(&proc->asts_spin);
657 schedule();
658 spin_lock(&proc->asts_spin);
659 goto repeat;
660 }
661 set_current_state(TASK_RUNNING);
662 remove_wait_queue(&proc->wait, &wait);
663
664 if (signal_pending(current)) {
665 spin_unlock(&proc->asts_spin);
666 return -ERESTARTSYS;
667 }
668 }
669
670 if (list_empty(&proc->asts)) {
671 spin_unlock(&proc->asts_spin);
672 return -EAGAIN;
673 }
674
675 /* there may be both completion and blocking asts to return for
676 the lkb, don't remove lkb from asts list unless no asts remain */
677
678 lkb = list_entry(proc->asts.next, struct dlm_lkb, lkb_astqueue);
679
680 if (lkb->lkb_ast_type & AST_COMP) {
681 lkb->lkb_ast_type &= ~AST_COMP;
682 type = AST_COMP;
683 } else if (lkb->lkb_ast_type & AST_BAST) {
684 lkb->lkb_ast_type &= ~AST_BAST;
685 type = AST_BAST;
686 bmode = lkb->lkb_bastmode;
687 }
688
689 if (!lkb->lkb_ast_type) {
690 list_del(&lkb->lkb_astqueue);
691 removed = 1;
692 }
693 spin_unlock(&proc->asts_spin);
694
695 ua = (struct dlm_user_args *)lkb->lkb_astparam;
696 error = copy_result_to_user(ua,
697 test_bit(DLM_PROC_FLAGS_COMPAT, &proc->flags),
698 type, bmode, buf, count);
699
700 /* removes reference for the proc->asts lists added by
701 dlm_user_add_ast() and may result in the lkb being freed */
702 if (removed)
703 dlm_put_lkb(lkb);
704
705 return error;
706}
707
708static unsigned int device_poll(struct file *file, poll_table *wait)
709{
710 struct dlm_user_proc *proc = file->private_data;
711
712 poll_wait(file, &proc->wait, wait);
713
714 spin_lock(&proc->asts_spin);
715 if (!list_empty(&proc->asts)) {
716 spin_unlock(&proc->asts_spin);
717 return POLLIN | POLLRDNORM;
718 }
719 spin_unlock(&proc->asts_spin);
720 return 0;
721}
722
723static int ctl_device_open(struct inode *inode, struct file *file)
724{
725 file->private_data = NULL;
726 return 0;
727}
728
729static int ctl_device_close(struct inode *inode, struct file *file)
730{
731 return 0;
732}
733
734static struct file_operations device_fops = {
735 .open = device_open,
736 .release = device_close,
737 .read = device_read,
738 .write = device_write,
739 .poll = device_poll,
740 .owner = THIS_MODULE,
741};
742
743static struct file_operations ctl_device_fops = {
744 .open = ctl_device_open,
745 .release = ctl_device_close,
746 .write = device_write,
747 .owner = THIS_MODULE,
748};
749
750int dlm_user_init(void)
751{
752 int error;
753
754 ctl_device.name = "dlm-control";
755 ctl_device.fops = &ctl_device_fops;
756 ctl_device.minor = MISC_DYNAMIC_MINOR;
757
758 error = misc_register(&ctl_device);
759 if (error)
760 log_print("misc_register failed for control device");
761
762 return error;
763}
764
765void dlm_user_exit(void)
766{
767 misc_deregister(&ctl_device);
768}
769