ast.c 4.7 KB
Newer Older
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 8 9 10 11 12 13 14 15
**
**  This copyrighted material is made available to anyone wishing to use,
**  modify, copy, or redistribute it subject to the terms and conditions
**  of the GNU General Public License v.2.
**
*******************************************************************************
******************************************************************************/

#include "dlm_internal.h"
#include "lock.h"
D
David Teigland 已提交
16
#include "user.h"
17
#include "ast.h"
18 19 20 21 22 23 24

#define WAKE_ASTS  0

static struct list_head		ast_queue;
static spinlock_t		ast_queue_lock;
static struct task_struct *	astd_task;
static unsigned long		astd_wakeflags;
25
static struct mutex		astd_running;
26 27 28 29 30 31 32 33 34 35


void dlm_del_ast(struct dlm_lkb *lkb)
{
	spin_lock(&ast_queue_lock);
	if (lkb->lkb_ast_type & (AST_COMP | AST_BAST))
		list_del(&lkb->lkb_astqueue);
	spin_unlock(&ast_queue_lock);
}

36
void dlm_add_ast(struct dlm_lkb *lkb, int type, int mode)
37
{
D
David Teigland 已提交
38
	if (lkb->lkb_flags & DLM_IFL_USER) {
39
		dlm_user_add_ast(lkb, type, mode);
D
David Teigland 已提交
40 41 42
		return;
	}

43 44 45 46
	spin_lock(&ast_queue_lock);
	if (!(lkb->lkb_ast_type & (AST_COMP | AST_BAST))) {
		kref_get(&lkb->lkb_ref);
		list_add_tail(&lkb->lkb_astqueue, &ast_queue);
47
		lkb->lkb_ast_first = type;
48
	}
49 50 51 52 53 54 55 56

	/* sanity check, this should not happen */

	if ((type == AST_COMP) && (lkb->lkb_ast_type & AST_COMP))
		log_print("repeat cast %d castmode %d lock %x %s",
			  mode, lkb->lkb_castmode,
			  lkb->lkb_id, lkb->lkb_resource->res_name);

57
	lkb->lkb_ast_type |= type;
58 59 60 61
	if (type == AST_BAST)
		lkb->lkb_bastmode = mode;
	else
		lkb->lkb_castmode = mode;
62 63 64 65 66 67 68 69 70 71 72
	spin_unlock(&ast_queue_lock);

	set_bit(WAKE_ASTS, &astd_wakeflags);
	wake_up_process(astd_task);
}

static void process_asts(void)
{
	struct dlm_ls *ls = NULL;
	struct dlm_rsb *r = NULL;
	struct dlm_lkb *lkb;
73 74 75
	void (*castfn) (void *astparam);
	void (*bastfn) (void *astparam, int mode);
	int type, first, bastmode, castmode, do_bast, do_cast, last_castmode;
A
Andrew Morton 已提交
76 77 78 79 80 81

repeat:
	spin_lock(&ast_queue_lock);
	list_for_each_entry(lkb, &ast_queue, lkb_astqueue) {
		r = lkb->lkb_resource;
		ls = r->res_ls;
82

A
Andrew Morton 已提交
83 84
		if (dlm_locking_stopped(ls))
			continue;
85

A
Andrew Morton 已提交
86 87 88
		list_del(&lkb->lkb_astqueue);
		type = lkb->lkb_ast_type;
		lkb->lkb_ast_type = 0;
89 90
		first = lkb->lkb_ast_first;
		lkb->lkb_ast_first = 0;
A
Andrew Morton 已提交
91
		bastmode = lkb->lkb_bastmode;
92 93 94
		castmode = lkb->lkb_castmode;
		castfn = lkb->lkb_astfn;
		bastfn = lkb->lkb_bastfn;
A
Andrew Morton 已提交
95
		spin_unlock(&ast_queue_lock);
96

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
		do_cast = (type & AST_COMP) && castfn;
		do_bast = (type & AST_BAST) && bastfn;

		/* Skip a bast if its blocking mode is compatible with the
		   granted mode of the preceding cast. */

		if (do_bast) {
			if (first == AST_COMP)
				last_castmode = castmode;
			else
				last_castmode = lkb->lkb_castmode_done;
			if (dlm_modes_compat(bastmode, last_castmode))
				do_bast = 0;
		}

		if (first == AST_COMP) {
			if (do_cast)
				castfn(lkb->lkb_astparam);
			if (do_bast)
				bastfn(lkb->lkb_astparam, bastmode);
		} else if (first == AST_BAST) {
			if (do_bast)
				bastfn(lkb->lkb_astparam, bastmode);
			if (do_cast)
				castfn(lkb->lkb_astparam);
		} else {
			log_error(ls, "bad ast_first %d ast_type %d",
				  first, type);
		}

		if (do_cast)
			lkb->lkb_castmode_done = castmode;
		if (do_bast)
			lkb->lkb_bastmode_done = bastmode;
131 132 133 134 135

		/* this removes the reference added by dlm_add_ast
		   and may result in the lkb being freed */
		dlm_put_lkb(lkb);

136
		cond_resched();
A
Andrew Morton 已提交
137
		goto repeat;
138
	}
A
Andrew Morton 已提交
139
	spin_unlock(&ast_queue_lock);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
}

static inline int no_asts(void)
{
	int ret;

	spin_lock(&ast_queue_lock);
	ret = list_empty(&ast_queue);
	spin_unlock(&ast_queue_lock);
	return ret;
}

static int dlm_astd(void *data)
{
	while (!kthread_should_stop()) {
		set_current_state(TASK_INTERRUPTIBLE);
		if (!test_bit(WAKE_ASTS, &astd_wakeflags))
			schedule();
		set_current_state(TASK_RUNNING);

160
		mutex_lock(&astd_running);
161 162
		if (test_and_clear_bit(WAKE_ASTS, &astd_wakeflags))
			process_asts();
163
		mutex_unlock(&astd_running);
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
	}
	return 0;
}

void dlm_astd_wake(void)
{
	if (!no_asts()) {
		set_bit(WAKE_ASTS, &astd_wakeflags);
		wake_up_process(astd_task);
	}
}

int dlm_astd_start(void)
{
	struct task_struct *p;
	int error = 0;

	INIT_LIST_HEAD(&ast_queue);
	spin_lock_init(&ast_queue_lock);
183
	mutex_init(&astd_running);
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199

	p = kthread_run(dlm_astd, NULL, "dlm_astd");
	if (IS_ERR(p))
		error = PTR_ERR(p);
	else
		astd_task = p;
	return error;
}

void dlm_astd_stop(void)
{
	kthread_stop(astd_task);
}

void dlm_astd_suspend(void)
{
200
	mutex_lock(&astd_running);
201 202 203 204
}

void dlm_astd_resume(void)
{
205
	mutex_unlock(&astd_running);
206 207
}