Merge branch 'master'
[linux-2.6] / 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 "user.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         if (lkb->lkb_flags & DLM_IFL_USER) {
38                 dlm_user_add_ast(lkb, type);
39                 return;
40         }
41
42         spin_lock(&ast_queue_lock);
43         if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
44                 kref_get(&lkb->lkb_ref);
45                 list_add_tail(&lkb->lkb_astqueue, &ast_queue);
46         }
47         lkb->lkb_ast_type |= type;
48         spin_unlock(&ast_queue_lock);
49
50         set_bit(WAKE_ASTS, &astd_wakeflags);
51         wake_up_process(astd_task);
52 }
53
54 static void process_asts(void)
55 {
56         struct dlm_ls *ls = NULL;
57         struct dlm_rsb *r = NULL;
58         struct dlm_lkb *lkb;
59         void (*cast) (long param);
60         void (*bast) (long param, int mode);
61         int type = 0, found, bmode;
62
63         for (;;) {
64                 found = 0;
65                 spin_lock(&ast_queue_lock);
66                 list_for_each_entry(lkb, &ast_queue, lkb_astqueue) {
67                         r = lkb->lkb_resource;
68                         ls = r->res_ls;
69
70                         if (dlm_locking_stopped(ls))
71                                 continue;
72
73                         list_del(&lkb->lkb_astqueue);
74                         type = lkb->lkb_ast_type;
75                         lkb->lkb_ast_type = 0;
76                         found = 1;
77                         break;
78                 }
79                 spin_unlock(&ast_queue_lock);
80
81                 if (!found)
82                         break;
83
84                 cast = lkb->lkb_astaddr;
85                 bast = lkb->lkb_bastaddr;
86                 bmode = lkb->lkb_bastmode;
87
88                 if ((type & AST_COMP) && cast)
89                         cast(lkb->lkb_astparam);
90
91                 /* FIXME: Is it safe to look at lkb_grmode here
92                    without doing a lock_rsb() ?
93                    Look at other checks in v1 to avoid basts. */
94
95                 if ((type & AST_BAST) && bast)
96                         if (!dlm_modes_compat(lkb->lkb_grmode, bmode))
97                                 bast(lkb->lkb_astparam, bmode);
98
99                 /* this removes the reference added by dlm_add_ast
100                    and may result in the lkb being freed */
101                 dlm_put_lkb(lkb);
102
103                 schedule();
104         }
105 }
106
107 static inline int no_asts(void)
108 {
109         int ret;
110
111         spin_lock(&ast_queue_lock);
112         ret = list_empty(&ast_queue);
113         spin_unlock(&ast_queue_lock);
114         return ret;
115 }
116
117 static int dlm_astd(void *data)
118 {
119         while (!kthread_should_stop()) {
120                 set_current_state(TASK_INTERRUPTIBLE);
121                 if (!test_bit(WAKE_ASTS, &astd_wakeflags))
122                         schedule();
123                 set_current_state(TASK_RUNNING);
124
125                 mutex_lock(&astd_running);
126                 if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
127                         process_asts();
128                 mutex_unlock(&astd_running);
129         }
130         return 0;
131 }
132
133 void dlm_astd_wake(void)
134 {
135         if (!no_asts()) {
136                 set_bit(WAKE_ASTS, &astd_wakeflags);
137                 wake_up_process(astd_task);
138         }
139 }
140
141 int dlm_astd_start(void)
142 {
143         struct task_struct *p;
144         int error = 0;
145
146         INIT_LIST_HEAD(&ast_queue);
147         spin_lock_init(&ast_queue_lock);
148         mutex_init(&astd_running);
149
150         p = kthread_run(dlm_astd, NULL, "dlm_astd");
151         if (IS_ERR(p))
152                 error = PTR_ERR(p);
153         else
154                 astd_task = p;
155         return error;
156 }
157
158 void dlm_astd_stop(void)
159 {
160         kthread_stop(astd_task);
161 }
162
163 void dlm_astd_suspend(void)
164 {
165         mutex_lock(&astd_running);
166 }
167
168 void dlm_astd_resume(void)
169 {
170         mutex_unlock(&astd_running);
171 }
172