2006-01-18 12:30:29 +03:00
/******************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2010-02-24 20:08:18 +03:00
* * Copyright ( C ) 2004 - 2010 Red Hat , Inc . All rights reserved .
2006-01-18 12:30:29 +03:00
* *
* * 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"
2006-07-13 01:44:04 +04:00
# include "user.h"
2007-04-04 19:25:29 +04:00
# include "ast.h"
2006-01-18 12:30:29 +03:00
# 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 ;
2006-01-20 11:47:07 +03:00
static struct mutex astd_running ;
2006-01-18 12:30:29 +03:00
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 ) ;
}
2010-02-24 20:08:18 +03:00
void dlm_add_ast ( struct dlm_lkb * lkb , int type , int mode )
2006-01-18 12:30:29 +03:00
{
2006-07-13 01:44:04 +04:00
if ( lkb - > lkb_flags & DLM_IFL_USER ) {
2010-02-24 20:08:18 +03:00
dlm_user_add_ast ( lkb , type , mode ) ;
2006-07-13 01:44:04 +04:00
return ;
}
2006-01-18 12:30:29 +03:00
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 ) ;
2010-02-24 20:08:18 +03:00
lkb - > lkb_ast_first = type ;
2006-01-18 12:30:29 +03:00
}
2010-02-24 20:08:18 +03:00
/* 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 ) ;
2006-01-18 12:30:29 +03:00
lkb - > lkb_ast_type | = type ;
2010-02-24 20:08:18 +03:00
if ( type = = AST_BAST )
lkb - > lkb_bastmode = mode ;
else
lkb - > lkb_castmode = mode ;
2006-01-18 12:30:29 +03:00
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 ;
2010-02-24 20:08:18 +03:00
void ( * castfn ) ( void * astparam ) ;
void ( * bastfn ) ( void * astparam , int mode ) ;
int type , first , bastmode , castmode , do_bast , do_cast , last_castmode ;
2008-12-23 19:22:56 +03:00
repeat :
spin_lock ( & ast_queue_lock ) ;
list_for_each_entry ( lkb , & ast_queue , lkb_astqueue ) {
r = lkb - > lkb_resource ;
ls = r - > res_ls ;
2006-01-18 12:30:29 +03:00
2008-12-23 19:22:56 +03:00
if ( dlm_locking_stopped ( ls ) )
continue ;
2006-01-18 12:30:29 +03:00
2008-12-23 19:22:56 +03:00
list_del ( & lkb - > lkb_astqueue ) ;
type = lkb - > lkb_ast_type ;
lkb - > lkb_ast_type = 0 ;
2010-02-24 20:08:18 +03:00
first = lkb - > lkb_ast_first ;
lkb - > lkb_ast_first = 0 ;
2008-12-23 19:22:56 +03:00
bastmode = lkb - > lkb_bastmode ;
2010-02-24 20:08:18 +03:00
castmode = lkb - > lkb_castmode ;
castfn = lkb - > lkb_astfn ;
bastfn = lkb - > lkb_bastfn ;
2008-12-23 19:22:56 +03:00
spin_unlock ( & ast_queue_lock ) ;
2006-01-18 12:30:29 +03:00
2010-02-24 20:08:18 +03:00
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 ;
2006-01-18 12:30:29 +03:00
/* this removes the reference added by dlm_add_ast
and may result in the lkb being freed */
dlm_put_lkb ( lkb ) ;
2008-12-10 18:31:02 +03:00
cond_resched ( ) ;
2008-12-23 19:22:56 +03:00
goto repeat ;
2006-01-18 12:30:29 +03:00
}
2008-12-23 19:22:56 +03:00
spin_unlock ( & ast_queue_lock ) ;
2006-01-18 12:30:29 +03:00
}
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 ) ;
2006-01-20 11:47:07 +03:00
mutex_lock ( & astd_running ) ;
2006-01-18 12:30:29 +03:00
if ( test_and_clear_bit ( WAKE_ASTS , & astd_wakeflags ) )
process_asts ( ) ;
2006-01-20 11:47:07 +03:00
mutex_unlock ( & astd_running ) ;
2006-01-18 12:30:29 +03:00
}
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 ) ;
2006-01-20 11:47:07 +03:00
mutex_init ( & astd_running ) ;
2006-01-18 12:30:29 +03:00
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 )
{
2006-01-20 11:47:07 +03:00
mutex_lock ( & astd_running ) ;
2006-01-18 12:30:29 +03:00
}
void dlm_astd_resume ( void )
{
2006-01-20 11:47:07 +03:00
mutex_unlock ( & astd_running ) ;
2006-01-18 12:30:29 +03:00
}