1/******************************************************************************
2*******************************************************************************
3**
4**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
5**  Copyright (C) 2004-2010 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#include "ast.h"
18
19#define WAKE_ASTS  0
20
21static struct list_head		ast_queue;
22static spinlock_t		ast_queue_lock;
23static struct task_struct *	astd_task;
24static unsigned long		astd_wakeflags;
25static struct mutex		astd_running;
26
27
28void dlm_del_ast(struct dlm_lkb *lkb)
29{
30	spin_lock(&ast_queue_lock);
31	if (lkb->lkb_ast_type & (AST_COMP | AST_BAST))
32		list_del(&lkb->lkb_astqueue);
33	spin_unlock(&ast_queue_lock);
34}
35
36void dlm_add_ast(struct dlm_lkb *lkb, int type, int mode)
37{
38	if (lkb->lkb_flags & DLM_IFL_USER) {
39		dlm_user_add_ast(lkb, type, mode);
40		return;
41	}
42
43	spin_lock(&ast_queue_lock);
44	if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
45		kref_get(&lkb->lkb_ref);
46		list_add_tail(&lkb->lkb_astqueue, &ast_queue);
47		lkb->lkb_ast_first = type;
48	}
49
50	/* sanity check, this should not happen */
51
52	if ((type == AST_COMP) && (lkb->lkb_ast_type & AST_COMP))
53		log_print("repeat cast %d castmode %d lock %x %s",
54			  mode, lkb->lkb_castmode,
55			  lkb->lkb_id, lkb->lkb_resource->res_name);
56
57	lkb->lkb_ast_type |= type;
58	if (type == AST_BAST)
59		lkb->lkb_bastmode = mode;
60	else
61		lkb->lkb_castmode = mode;
62	spin_unlock(&ast_queue_lock);
63
64	set_bit(WAKE_ASTS, &astd_wakeflags);
65	wake_up_process(astd_task);
66}
67
68static void process_asts(void)
69{
70	struct dlm_ls *ls = NULL;
71	struct dlm_rsb *r = NULL;
72	struct dlm_lkb *lkb;
73	void (*castfn) (void *astparam);
74	void (*bastfn) (void *astparam, int mode);
75	int type, first, bastmode, castmode, do_bast, do_cast, last_castmode;
76
77repeat:
78	spin_lock(&ast_queue_lock);
79	list_for_each_entry(lkb, &ast_queue, lkb_astqueue) {
80		r = lkb->lkb_resource;
81		ls = r->res_ls;
82
83		if (dlm_locking_stopped(ls))
84			continue;
85
86		list_del(&lkb->lkb_astqueue);
87		type = lkb->lkb_ast_type;
88		lkb->lkb_ast_type = 0;
89		first = lkb->lkb_ast_first;
90		lkb->lkb_ast_first = 0;
91		bastmode = lkb->lkb_bastmode;
92		castmode = lkb->lkb_castmode;
93		castfn = lkb->lkb_astfn;
94		bastfn = lkb->lkb_bastfn;
95		spin_unlock(&ast_queue_lock);
96
97		do_cast = (type & AST_COMP) && castfn;
98		do_bast = (type & AST_BAST) && bastfn;
99
100		/* Skip a bast if its blocking mode is compatible with the
101		   granted mode of the preceding cast. */
102
103		if (do_bast) {
104			if (first == AST_COMP)
105				last_castmode = castmode;
106			else
107				last_castmode = lkb->lkb_castmode_done;
108			if (dlm_modes_compat(bastmode, last_castmode))
109				do_bast = 0;
110		}
111
112		if (first == AST_COMP) {
113			if (do_cast)
114				castfn(lkb->lkb_astparam);
115			if (do_bast)
116				bastfn(lkb->lkb_astparam, bastmode);
117		} else if (first == AST_BAST) {
118			if (do_bast)
119				bastfn(lkb->lkb_astparam, bastmode);
120			if (do_cast)
121				castfn(lkb->lkb_astparam);
122		} else {
123			log_error(ls, "bad ast_first %d ast_type %d",
124				  first, type);
125		}
126
127		if (do_cast)
128			lkb->lkb_castmode_done = castmode;
129		if (do_bast)
130			lkb->lkb_bastmode_done = bastmode;
131
132		/* this removes the reference added by dlm_add_ast
133		   and may result in the lkb being freed */
134		dlm_put_lkb(lkb);
135
136		cond_resched();
137		goto repeat;
138	}
139	spin_unlock(&ast_queue_lock);
140}
141
142static inline int no_asts(void)
143{
144	int ret;
145
146	spin_lock(&ast_queue_lock);
147	ret = list_empty(&ast_queue);
148	spin_unlock(&ast_queue_lock);
149	return ret;
150}
151
152static int dlm_astd(void *data)
153{
154	while (!kthread_should_stop()) {
155		set_current_state(TASK_INTERRUPTIBLE);
156		if (!test_bit(WAKE_ASTS, &astd_wakeflags))
157			schedule();
158		set_current_state(TASK_RUNNING);
159
160		mutex_lock(&astd_running);
161		if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
162			process_asts();
163		mutex_unlock(&astd_running);
164	}
165	return 0;
166}
167
168void dlm_astd_wake(void)
169{
170	if (!no_asts()) {
171		set_bit(WAKE_ASTS, &astd_wakeflags);
172		wake_up_process(astd_task);
173	}
174}
175
176int dlm_astd_start(void)
177{
178	struct task_struct *p;
179	int error = 0;
180
181	INIT_LIST_HEAD(&ast_queue);
182	spin_lock_init(&ast_queue_lock);
183	mutex_init(&astd_running);
184
185	p = kthread_run(dlm_astd, NULL, "dlm_astd");
186	if (IS_ERR(p))
187		error = PTR_ERR(p);
188	else
189		astd_task = p;
190	return error;
191}
192
193void dlm_astd_stop(void)
194{
195	kthread_stop(astd_task);
196}
197
198void dlm_astd_suspend(void)
199{
200	mutex_lock(&astd_running);
201}
202
203void dlm_astd_resume(void)
204{
205	mutex_unlock(&astd_running);
206}
207