Merge rsync://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / fs / dlm / ast.c
1 /******************************************************************************
2 *******************************************************************************
3 **
4 **  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
5 **  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
6 **
7 **  This copyrighted material is made available to anyone wishing to use,
8 **  modify, copy, or redistribute it subject to the terms and conditions
9 **  of the GNU General Public License v.2.
10 **
11 *******************************************************************************
12 ******************************************************************************/
13
14 #include "dlm_internal.h"
15 #include "lock.h"
16 #include "ast.h"
17
18 #define WAKE_ASTS  0
19
20 static struct list_head         ast_queue;
21 static spinlock_t               ast_queue_lock;
22 static struct task_struct *     astd_task;
23 static unsigned long            astd_wakeflags;
24 static struct mutex             astd_running;
25
26
27 void dlm_del_ast(struct dlm_lkb *lkb)
28 {
29         spin_lock(&ast_queue_lock);
30         if (lkb->lkb_ast_type & (AST_COMP | AST_BAST))
31                 list_del(&lkb->lkb_astqueue);
32         spin_unlock(&ast_queue_lock);
33 }
34
35 void dlm_add_ast(struct dlm_lkb *lkb, int type)
36 {
37         spin_lock(&ast_queue_lock);
38         if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
39                 kref_get(&lkb->lkb_ref);
40                 list_add_tail(&lkb->lkb_astqueue, &ast_queue);
41         }
42         lkb->lkb_ast_type |= type;
43         spin_unlock(&ast_queue_lock);
44
45         set_bit(WAKE_ASTS, &astd_wakeflags);
46         wake_up_process(astd_task);
47 }
48
49 static void process_asts(void)
50 {
51         struct dlm_ls *ls = NULL;
52         struct dlm_rsb *r = NULL;
53         struct dlm_lkb *lkb;
54         void (*cast) (long param);
55         void (*bast) (long param, int mode);
56         int type = 0, found, bmode;
57
58         for (;;) {
59                 found = 0;
60                 spin_lock(&ast_queue_lock);
61                 list_for_each_entry(lkb, &ast_queue, lkb_astqueue) {
62                         r = lkb->lkb_resource;
63                         ls = r->res_ls;
64
65                         if (dlm_locking_stopped(ls))
66                                 continue;
67
68                         list_del(&lkb->lkb_astqueue);
69                         type = lkb->lkb_ast_type;
70                         lkb->lkb_ast_type = 0;
71                         found = 1;
72                         break;
73                 }
74                 spin_unlock(&ast_queue_lock);
75
76                 if (!found)
77                         break;
78
79                 cast = lkb->lkb_astaddr;
80                 bast = lkb->lkb_bastaddr;
81                 bmode = lkb->lkb_bastmode;
82
83                 if ((type & AST_COMP) && cast)
84                         cast(lkb->lkb_astparam);
85
86                 /* FIXME: Is it safe to look at lkb_grmode here
87                    without doing a lock_rsb() ?
88                    Look at other checks in v1 to avoid basts. */
89
90                 if ((type & AST_BAST) && bast)
91                         if (!dlm_modes_compat(lkb->lkb_grmode, bmode))
92                                 bast(lkb->lkb_astparam, bmode);
93
94                 /* this removes the reference added by dlm_add_ast
95                    and may result in the lkb being freed */
96                 dlm_put_lkb(lkb);
97
98                 schedule();
99         }
100 }
101
102 static inline int no_asts(void)
103 {
104         int ret;
105
106         spin_lock(&ast_queue_lock);
107         ret = list_empty(&ast_queue);
108         spin_unlock(&ast_queue_lock);
109         return ret;
110 }
111
112 static int dlm_astd(void *data)
113 {
114         while (!kthread_should_stop()) {
115                 set_current_state(TASK_INTERRUPTIBLE);
116                 if (!test_bit(WAKE_ASTS, &astd_wakeflags))
117                         schedule();
118                 set_current_state(TASK_RUNNING);
119
120                 mutex_lock(&astd_running);
121                 if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
122                         process_asts();
123                 mutex_unlock(&astd_running);
124         }
125         return 0;
126 }
127
128 void dlm_astd_wake(void)
129 {
130         if (!no_asts()) {
131                 set_bit(WAKE_ASTS, &astd_wakeflags);
132                 wake_up_process(astd_task);
133         }
134 }
135
136 int dlm_astd_start(void)
137 {
138         struct task_struct *p;
139         int error = 0;
140
141         INIT_LIST_HEAD(&ast_queue);
142         spin_lock_init(&ast_queue_lock);
143         mutex_init(&astd_running);
144
145         p = kthread_run(dlm_astd, NULL, "dlm_astd");
146         if (IS_ERR(p))
147                 error = PTR_ERR(p);
148         else
149                 astd_task = p;
150         return error;
151 }
152
153 void dlm_astd_stop(void)
154 {
155         kthread_stop(astd_task);
156 }
157
158 void dlm_astd_suspend(void)
159 {
160         mutex_lock(&astd_running);
161 }
162
163 void dlm_astd_resume(void)
164 {
165         mutex_unlock(&astd_running);
166 }
167