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"
2006-01-18 12:30:29 +03:00
2012-08-02 20:00:05 +04:00
static uint64_t dlm_cb_seq ;
static DEFINE_SPINLOCK ( dlm_cb_seq_spin ) ;
2006-01-18 12:30:29 +03:00
2011-02-21 23:58:21 +03:00
static void dlm_dump_lkb_callbacks ( struct dlm_lkb * lkb )
{
int i ;
log_print ( " last_bast %x %llu flags %x mode %d sb %d %x " ,
lkb - > lkb_id ,
( unsigned long long ) lkb - > lkb_last_bast . seq ,
lkb - > lkb_last_bast . flags ,
lkb - > lkb_last_bast . mode ,
lkb - > lkb_last_bast . sb_status ,
lkb - > lkb_last_bast . sb_flags ) ;
log_print ( " last_cast %x %llu flags %x mode %d sb %d %x " ,
lkb - > lkb_id ,
( unsigned long long ) lkb - > lkb_last_cast . seq ,
lkb - > lkb_last_cast . flags ,
lkb - > lkb_last_cast . mode ,
lkb - > lkb_last_cast . sb_status ,
lkb - > lkb_last_cast . sb_flags ) ;
for ( i = 0 ; i < DLM_CALLBACKS_SIZE ; i + + ) {
log_print ( " cb %x %llu flags %x mode %d sb %d %x " ,
lkb - > lkb_id ,
( unsigned long long ) lkb - > lkb_callbacks [ i ] . seq ,
lkb - > lkb_callbacks [ i ] . flags ,
lkb - > lkb_callbacks [ i ] . mode ,
lkb - > lkb_callbacks [ i ] . sb_status ,
lkb - > lkb_callbacks [ i ] . sb_flags ) ;
}
}
int dlm_add_lkb_callback ( struct dlm_lkb * lkb , uint32_t flags , int mode ,
int status , uint32_t sbflags , uint64_t seq )
{
struct dlm_ls * ls = lkb - > lkb_resource - > res_ls ;
uint64_t prev_seq ;
int prev_mode ;
2011-04-05 22:16:24 +04:00
int i , rv ;
2011-02-21 23:58:21 +03:00
for ( i = 0 ; i < DLM_CALLBACKS_SIZE ; i + + ) {
if ( lkb - > lkb_callbacks [ i ] . seq )
continue ;
/*
* Suppress some redundant basts here , do more on removal .
* Don ' t even add a bast if the callback just before it
* is a bast for the same mode or a more restrictive mode .
* ( the addional > PR check is needed for PR / CW inversion )
*/
if ( ( i > 0 ) & & ( flags & DLM_CB_BAST ) & &
( lkb - > lkb_callbacks [ i - 1 ] . flags & DLM_CB_BAST ) ) {
prev_seq = lkb - > lkb_callbacks [ i - 1 ] . seq ;
prev_mode = lkb - > lkb_callbacks [ i - 1 ] . mode ;
if ( ( prev_mode = = mode ) | |
( prev_mode > mode & & prev_mode > DLM_LOCK_PR ) ) {
log_debug ( ls , " skip %x add bast %llu mode %d "
" for bast %llu mode %d " ,
lkb - > lkb_id ,
( unsigned long long ) seq ,
mode ,
( unsigned long long ) prev_seq ,
prev_mode ) ;
2011-04-05 22:16:24 +04:00
rv = 0 ;
goto out ;
2011-02-21 23:58:21 +03:00
}
}
lkb - > lkb_callbacks [ i ] . seq = seq ;
lkb - > lkb_callbacks [ i ] . flags = flags ;
lkb - > lkb_callbacks [ i ] . mode = mode ;
lkb - > lkb_callbacks [ i ] . sb_status = status ;
lkb - > lkb_callbacks [ i ] . sb_flags = ( sbflags & 0x000000FF ) ;
2011-04-05 22:16:24 +04:00
rv = 0 ;
2011-02-21 23:58:21 +03:00
break ;
}
if ( i = = DLM_CALLBACKS_SIZE ) {
log_error ( ls , " no callbacks %x %llu flags %x mode %d sb %d %x " ,
lkb - > lkb_id , ( unsigned long long ) seq ,
flags , mode , status , sbflags ) ;
dlm_dump_lkb_callbacks ( lkb ) ;
2011-04-05 22:16:24 +04:00
rv = - 1 ;
goto out ;
2011-02-21 23:58:21 +03:00
}
2011-04-05 22:16:24 +04:00
out :
return rv ;
2011-02-21 23:58:21 +03:00
}
int dlm_rem_lkb_callback ( struct dlm_ls * ls , struct dlm_lkb * lkb ,
struct dlm_callback * cb , int * resid )
2006-01-18 12:30:29 +03:00
{
2011-04-05 22:16:24 +04:00
int i , rv ;
2011-02-21 23:58:21 +03:00
* resid = 0 ;
2011-04-05 22:16:24 +04:00
if ( ! lkb - > lkb_callbacks [ 0 ] . seq ) {
rv = - ENOENT ;
goto out ;
}
2011-02-21 23:58:21 +03:00
/* oldest undelivered cb is callbacks[0] */
memcpy ( cb , & lkb - > lkb_callbacks [ 0 ] , sizeof ( struct dlm_callback ) ) ;
memset ( & lkb - > lkb_callbacks [ 0 ] , 0 , sizeof ( struct dlm_callback ) ) ;
/* shift others down */
for ( i = 1 ; i < DLM_CALLBACKS_SIZE ; i + + ) {
if ( ! lkb - > lkb_callbacks [ i ] . seq )
break ;
memcpy ( & lkb - > lkb_callbacks [ i - 1 ] , & lkb - > lkb_callbacks [ i ] ,
sizeof ( struct dlm_callback ) ) ;
memset ( & lkb - > lkb_callbacks [ i ] , 0 , sizeof ( struct dlm_callback ) ) ;
( * resid ) + + ;
}
/* if cb is a bast, it should be skipped if the blocking mode is
compatible with the last granted mode */
if ( ( cb - > flags & DLM_CB_BAST ) & & lkb - > lkb_last_cast . seq ) {
if ( dlm_modes_compat ( cb - > mode , lkb - > lkb_last_cast . mode ) ) {
cb - > flags | = DLM_CB_SKIP ;
log_debug ( ls , " skip %x bast %llu mode %d "
" for cast %llu mode %d " ,
lkb - > lkb_id ,
( unsigned long long ) cb - > seq ,
cb - > mode ,
( unsigned long long ) lkb - > lkb_last_cast . seq ,
lkb - > lkb_last_cast . mode ) ;
2011-04-05 22:16:24 +04:00
rv = 0 ;
goto out ;
2011-02-21 23:58:21 +03:00
}
}
if ( cb - > flags & DLM_CB_CAST ) {
memcpy ( & lkb - > lkb_last_cast , cb , sizeof ( struct dlm_callback ) ) ;
lkb - > lkb_last_cast_time = ktime_get ( ) ;
}
if ( cb - > flags & DLM_CB_BAST ) {
memcpy ( & lkb - > lkb_last_bast , cb , sizeof ( struct dlm_callback ) ) ;
lkb - > lkb_last_bast_time = ktime_get ( ) ;
}
2011-04-05 22:16:24 +04:00
rv = 0 ;
out :
return rv ;
2011-02-21 23:58:21 +03:00
}
2011-04-05 22:16:24 +04:00
void dlm_add_cb ( struct dlm_lkb * lkb , uint32_t flags , int mode , int status ,
uint32_t sbflags )
2011-02-21 23:58:21 +03:00
{
2011-04-05 22:16:24 +04:00
struct dlm_ls * ls = lkb - > lkb_resource - > res_ls ;
uint64_t new_seq , prev_seq ;
2011-02-21 23:58:21 +03:00
int rv ;
2011-04-05 22:16:24 +04:00
spin_lock ( & dlm_cb_seq_spin ) ;
new_seq = + + dlm_cb_seq ;
spin_unlock ( & dlm_cb_seq_spin ) ;
2011-02-21 23:58:21 +03:00
2006-07-13 01:44:04 +04:00
if ( lkb - > lkb_flags & DLM_IFL_USER ) {
2011-04-05 22:16:24 +04:00
dlm_user_add_ast ( lkb , flags , mode , status , sbflags , new_seq ) ;
2006-07-13 01:44:04 +04:00
return ;
}
2011-04-05 22:16:24 +04:00
mutex_lock ( & lkb - > lkb_cb_mutex ) ;
prev_seq = lkb - > lkb_callbacks [ 0 ] . seq ;
2011-02-21 23:58:21 +03:00
2011-04-05 22:16:24 +04:00
rv = dlm_add_lkb_callback ( lkb , flags , mode , status , sbflags , new_seq ) ;
if ( rv < 0 )
goto out ;
if ( ! prev_seq ) {
2006-01-18 12:30:29 +03:00
kref_get ( & lkb - > lkb_ref ) ;
2011-04-05 22:16:24 +04:00
if ( test_bit ( LSFL_CB_DELAY , & ls - > ls_flags ) ) {
mutex_lock ( & ls - > ls_cb_mutex ) ;
list_add ( & lkb - > lkb_cb_list , & ls - > ls_cb_delay ) ;
mutex_unlock ( & ls - > ls_cb_mutex ) ;
} else {
queue_work ( ls - > ls_callback_wq , & lkb - > lkb_cb_work ) ;
}
}
out :
mutex_unlock ( & lkb - > lkb_cb_mutex ) ;
2006-01-18 12:30:29 +03:00
}
2011-04-05 22:16:24 +04:00
void dlm_callback_work ( struct work_struct * work )
2006-01-18 12:30:29 +03:00
{
2011-04-05 22:16:24 +04:00
struct dlm_lkb * lkb = container_of ( work , struct dlm_lkb , lkb_cb_work ) ;
struct dlm_ls * ls = lkb - > lkb_resource - > res_ls ;
2010-02-24 20:08:18 +03:00
void ( * castfn ) ( void * astparam ) ;
void ( * bastfn ) ( void * astparam , int mode ) ;
2011-02-21 23:58:21 +03:00
struct dlm_callback callbacks [ DLM_CALLBACKS_SIZE ] ;
int i , rv , resid ;
2008-12-23 19:22:56 +03:00
2011-04-05 22:16:24 +04:00
memset ( & callbacks , 0 , sizeof ( callbacks ) ) ;
2006-01-18 12:30:29 +03:00
2011-04-05 22:16:24 +04:00
mutex_lock ( & lkb - > lkb_cb_mutex ) ;
if ( ! lkb - > lkb_callbacks [ 0 ] . seq ) {
/* no callback work exists, shouldn't happen */
log_error ( ls , " dlm_callback_work %x no work " , lkb - > lkb_id ) ;
dlm_print_lkb ( lkb ) ;
dlm_dump_lkb_callbacks ( lkb ) ;
}
2006-01-18 12:30:29 +03:00
2011-04-05 22:16:24 +04:00
for ( i = 0 ; i < DLM_CALLBACKS_SIZE ; i + + ) {
rv = dlm_rem_lkb_callback ( ls , lkb , & callbacks [ i ] , & resid ) ;
if ( rv < 0 )
break ;
}
2010-02-24 20:08:18 +03:00
2011-04-05 22:16:24 +04:00
if ( resid ) {
/* cbs remain, loop should have removed all, shouldn't happen */
log_error ( ls , " dlm_callback_work %x resid %d " , lkb - > lkb_id ,
resid ) ;
dlm_print_lkb ( lkb ) ;
dlm_dump_lkb_callbacks ( lkb ) ;
}
mutex_unlock ( & lkb - > lkb_cb_mutex ) ;
2010-02-24 20:08:18 +03:00
2011-04-05 22:16:24 +04:00
castfn = lkb - > lkb_astfn ;
bastfn = lkb - > lkb_bastfn ;
2010-02-24 20:08:18 +03:00
2011-04-05 22:16:24 +04:00
for ( i = 0 ; i < DLM_CALLBACKS_SIZE ; i + + ) {
if ( ! callbacks [ i ] . seq )
break ;
if ( callbacks [ i ] . flags & DLM_CB_SKIP ) {
continue ;
} else if ( callbacks [ i ] . flags & DLM_CB_BAST ) {
bastfn ( lkb - > lkb_astparam , callbacks [ i ] . mode ) ;
} else if ( callbacks [ i ] . flags & DLM_CB_CAST ) {
lkb - > lkb_lksb - > sb_status = callbacks [ i ] . sb_status ;
lkb - > lkb_lksb - > sb_flags = callbacks [ i ] . sb_flags ;
castfn ( lkb - > lkb_astparam ) ;
2010-02-24 20:08:18 +03:00
}
2006-01-18 12:30:29 +03:00
}
2011-04-05 22:16:24 +04:00
/* undo kref_get from dlm_add_callback, may cause lkb to be freed */
dlm_put_lkb ( lkb ) ;
2006-01-18 12:30:29 +03:00
}
2011-04-05 22:16:24 +04:00
int dlm_callback_start ( struct dlm_ls * ls )
2006-01-18 12:30:29 +03:00
{
2011-04-05 22:16:24 +04:00
ls - > ls_callback_wq = alloc_workqueue ( " dlm_callback " ,
2013-07-30 16:40:24 +04:00
WQ_UNBOUND | WQ_MEM_RECLAIM , 0 ) ;
2011-04-05 22:16:24 +04:00
if ( ! ls - > ls_callback_wq ) {
log_print ( " can't start dlm_callback workqueue " ) ;
return - ENOMEM ;
2006-01-18 12:30:29 +03:00
}
return 0 ;
}
2011-04-05 22:16:24 +04:00
void dlm_callback_stop ( struct dlm_ls * ls )
2006-01-18 12:30:29 +03:00
{
2011-04-05 22:16:24 +04:00
if ( ls - > ls_callback_wq )
destroy_workqueue ( ls - > ls_callback_wq ) ;
2006-01-18 12:30:29 +03:00
}
2011-04-05 22:16:24 +04:00
void dlm_callback_suspend ( struct dlm_ls * ls )
2006-01-18 12:30:29 +03:00
{
2011-04-05 22:16:24 +04:00
set_bit ( LSFL_CB_DELAY , & ls - > ls_flags ) ;
2006-01-18 12:30:29 +03:00
2011-04-05 22:16:24 +04:00
if ( ls - > ls_callback_wq )
flush_workqueue ( ls - > ls_callback_wq ) ;
2006-01-18 12:30:29 +03:00
}
2011-04-05 22:16:24 +04:00
void dlm_callback_resume ( struct dlm_ls * ls )
2006-01-18 12:30:29 +03:00
{
2011-04-05 22:16:24 +04:00
struct dlm_lkb * lkb , * safe ;
int count = 0 ;
2006-01-18 12:30:29 +03:00
2011-04-05 22:16:24 +04:00
clear_bit ( LSFL_CB_DELAY , & ls - > ls_flags ) ;
if ( ! ls - > ls_callback_wq )
return ;
mutex_lock ( & ls - > ls_cb_mutex ) ;
list_for_each_entry_safe ( lkb , safe , & ls - > ls_cb_delay , lkb_cb_list ) {
list_del_init ( & lkb - > lkb_cb_list ) ;
queue_work ( ls - > ls_callback_wq , & lkb - > lkb_cb_work ) ;
count + + ;
}
mutex_unlock ( & ls - > ls_cb_mutex ) ;
dlm: fixes for nodir mode
The "nodir" mode (statically assign master nodes instead
of using the resource directory) has always been highly
experimental, and never seriously used. This commit
fixes a number of problems, making nodir much more usable.
- Major change to recovery: recover all locks and restart
all in-progress operations after recovery. In some
cases it's not possible to know which in-progess locks
to recover, so recover all. (Most require recovery
in nodir mode anyway since rehashing changes most
master nodes.)
- Change the way nodir mode is enabled, from a command
line mount arg passed through gfs2, into a sysfs
file managed by dlm_controld, consistent with the
other config settings.
- Allow recovering MSTCPY locks on an rsb that has not
yet been turned into a master copy.
- Ignore RCOM_LOCK and RCOM_LOCK_REPLY recovery messages
from a previous, aborted recovery cycle. Base this
on the local recovery status not being in the state
where any nodes should be sending LOCK messages for the
current recovery cycle.
- Hold rsb lock around dlm_purge_mstcpy_locks() because it
may run concurrently with dlm_recover_master_copy().
- Maintain highbast on process-copy lkb's (in addition to
the master as is usual), because the lkb can switch
back and forth between being a master and being a
process copy as the master node changes in recovery.
- When recovering MSTCPY locks, flag rsb's that have
non-empty convert or waiting queues for granting
at the end of recovery. (Rename flag from LOCKS_PURGED
to RECOVER_GRANT and similar for the recovery function,
because it's not only resources with purged locks
that need grant a grant attempt.)
- Replace a couple of unnecessary assertion panics with
error messages.
Signed-off-by: David Teigland <teigland@redhat.com>
2012-04-27 00:54:29 +04:00
if ( count )
log_debug ( ls , " dlm_callback_resume %d " , count ) ;
2006-01-18 12:30:29 +03:00
}