2005-12-16 01:31:23 +03:00
/* -*- mode: c; c-basic-offset: 8; -*-
* vim : noexpandtab sw = 8 ts = 8 sts = 0 :
*
* dlmast . c
*
* AST and BAST functionality for local and remote nodes
*
* Copyright ( C ) 2004 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*
*/
# include <linux/module.h>
# include <linux/fs.h>
# include <linux/types.h>
# include <linux/highmem.h>
# include <linux/init.h>
# include <linux/sysctl.h>
# include <linux/random.h>
# include <linux/blkdev.h>
# include <linux/socket.h>
# include <linux/inet.h>
# include <linux/spinlock.h>
# include "cluster/heartbeat.h"
# include "cluster/nodemanager.h"
# include "cluster/tcp.h"
# include "dlmapi.h"
# include "dlmcommon.h"
# define MLOG_MASK_PREFIX ML_DLM
# include "cluster/masklog.h"
static void dlm_update_lvb ( struct dlm_ctxt * dlm , struct dlm_lock_resource * res ,
struct dlm_lock * lock ) ;
static int dlm_should_cancel_bast ( struct dlm_ctxt * dlm , struct dlm_lock * lock ) ;
/* Should be called as an ast gets queued to see if the new
* lock level will obsolete a pending bast .
* For example , if dlm_thread queued a bast for an EX lock that
* was blocking another EX , but before sending the bast the
* lock owner downconverted to NL , the bast is now obsolete .
* Only the ast should be sent .
* This is needed because the lock and convert paths can queue
* asts out - of - band ( not waiting for dlm_thread ) in order to
* allow for LKM_NOQUEUE to get immediate responses . */
static int dlm_should_cancel_bast ( struct dlm_ctxt * dlm , struct dlm_lock * lock )
{
assert_spin_locked ( & dlm - > ast_lock ) ;
assert_spin_locked ( & lock - > spinlock ) ;
if ( lock - > ml . highest_blocked = = LKM_IVMODE )
return 0 ;
BUG_ON ( lock - > ml . highest_blocked = = LKM_NLMODE ) ;
if ( lock - > bast_pending & &
list_empty ( & lock - > bast_list ) )
/* old bast already sent, ok */
return 0 ;
if ( lock - > ml . type = = LKM_EXMODE )
/* EX blocks anything left, any bast still valid */
return 0 ;
else if ( lock - > ml . type = = LKM_NLMODE )
/* NL blocks nothing, no reason to send any bast, cancel it */
return 1 ;
else if ( lock - > ml . highest_blocked ! = LKM_EXMODE )
/* PR only blocks EX */
return 1 ;
return 0 ;
}
2010-05-17 16:20:44 +04:00
void __dlm_queue_ast ( struct dlm_ctxt * dlm , struct dlm_lock * lock )
2005-12-16 01:31:23 +03:00
{
mlog_entry_void ( ) ;
BUG_ON ( ! dlm ) ;
BUG_ON ( ! lock ) ;
assert_spin_locked ( & dlm - > ast_lock ) ;
if ( ! list_empty ( & lock - > ast_list ) ) {
mlog ( ML_ERROR , " ast list not empty!! pending=%d, newlevel=%d \n " ,
lock - > ast_pending , lock - > ml . type ) ;
BUG ( ) ;
}
if ( lock - > ast_pending )
mlog ( 0 , " lock has an ast getting flushed right now \n " ) ;
/* putting lock on list, add a ref */
dlm_lock_get ( lock ) ;
spin_lock ( & lock - > spinlock ) ;
/* check to see if this ast obsoletes the bast */
if ( dlm_should_cancel_bast ( dlm , lock ) ) {
struct dlm_lock_resource * res = lock - > lockres ;
mlog ( 0 , " %s: cancelling bast for %.*s \n " ,
dlm - > name , res - > lockname . len , res - > lockname . name ) ;
lock - > bast_pending = 0 ;
list_del_init ( & lock - > bast_list ) ;
lock - > ml . highest_blocked = LKM_IVMODE ;
/* removing lock from list, remove a ref. guaranteed
* this won ' t be the last ref because of the get above ,
* so res - > spinlock will not be taken here */
dlm_lock_put ( lock ) ;
/* free up the reserved bast that we are cancelling.
* guaranteed that this will not be the last reserved
2010-01-26 03:57:38 +03:00
* ast because * both * an ast and a bast were reserved
2005-12-16 01:31:23 +03:00
* to get to this point . the res - > spinlock will not be
* taken here */
dlm_lockres_release_ast ( dlm , res ) ;
}
list_add_tail ( & lock - > ast_list , & dlm - > pending_asts ) ;
lock - > ast_pending = 1 ;
spin_unlock ( & lock - > spinlock ) ;
}
void dlm_queue_ast ( struct dlm_ctxt * dlm , struct dlm_lock * lock )
{
mlog_entry_void ( ) ;
BUG_ON ( ! dlm ) ;
BUG_ON ( ! lock ) ;
spin_lock ( & dlm - > ast_lock ) ;
__dlm_queue_ast ( dlm , lock ) ;
spin_unlock ( & dlm - > ast_lock ) ;
}
2010-05-17 16:20:44 +04:00
void __dlm_queue_bast ( struct dlm_ctxt * dlm , struct dlm_lock * lock )
2005-12-16 01:31:23 +03:00
{
mlog_entry_void ( ) ;
BUG_ON ( ! dlm ) ;
BUG_ON ( ! lock ) ;
assert_spin_locked ( & dlm - > ast_lock ) ;
BUG_ON ( ! list_empty ( & lock - > bast_list ) ) ;
if ( lock - > bast_pending )
mlog ( 0 , " lock has a bast getting flushed right now \n " ) ;
/* putting lock on list, add a ref */
dlm_lock_get ( lock ) ;
spin_lock ( & lock - > spinlock ) ;
list_add_tail ( & lock - > bast_list , & dlm - > pending_basts ) ;
lock - > bast_pending = 1 ;
spin_unlock ( & lock - > spinlock ) ;
}
void dlm_queue_bast ( struct dlm_ctxt * dlm , struct dlm_lock * lock )
{
mlog_entry_void ( ) ;
BUG_ON ( ! dlm ) ;
BUG_ON ( ! lock ) ;
spin_lock ( & dlm - > ast_lock ) ;
__dlm_queue_bast ( dlm , lock ) ;
spin_unlock ( & dlm - > ast_lock ) ;
}
static void dlm_update_lvb ( struct dlm_ctxt * dlm , struct dlm_lock_resource * res ,
struct dlm_lock * lock )
{
struct dlm_lockstatus * lksb = lock - > lksb ;
BUG_ON ( ! lksb ) ;
/* only updates if this node masters the lockres */
2010-03-24 17:40:44 +03:00
spin_lock ( & res - > spinlock ) ;
2005-12-16 01:31:23 +03:00
if ( res - > owner = = dlm - > node_num ) {
/* check the lksb flags for the direction */
if ( lksb - > flags & DLM_LKSB_GET_LVB ) {
mlog ( 0 , " getting lvb from lockres for %s node \n " ,
lock - > ml . node = = dlm - > node_num ? " master " :
" remote " ) ;
memcpy ( lksb - > lvb , res - > lvb , DLM_LVB_LEN ) ;
}
2006-04-28 06:07:45 +04:00
/* Do nothing for lvb put requests - they should be done in
* place when the lock is downconverted - otherwise we risk
* racing gets and puts which could result in old lvb data
* being propagated . We leave the put flag set and clear it
* here . In the future we might want to clear it at the time
* the put is actually done .
*/
2005-12-16 01:31:23 +03:00
}
2010-03-24 17:40:44 +03:00
spin_unlock ( & res - > spinlock ) ;
2005-12-16 01:31:23 +03:00
/* reset any lvb flags on the lksb */
lksb - > flags & = ~ ( DLM_LKSB_PUT_LVB | DLM_LKSB_GET_LVB ) ;
}
void dlm_do_local_ast ( struct dlm_ctxt * dlm , struct dlm_lock_resource * res ,
struct dlm_lock * lock )
{
dlm_astlockfunc_t * fn ;
struct dlm_lockstatus * lksb ;
mlog_entry_void ( ) ;
lksb = lock - > lksb ;
fn = lock - > ast ;
BUG_ON ( lock - > ml . node ! = dlm - > node_num ) ;
dlm_update_lvb ( dlm , res , lock ) ;
( * fn ) ( lock - > astdata ) ;
}
int dlm_do_remote_ast ( struct dlm_ctxt * dlm , struct dlm_lock_resource * res ,
struct dlm_lock * lock )
{
int ret ;
struct dlm_lockstatus * lksb ;
int lksbflags ;
mlog_entry_void ( ) ;
lksb = lock - > lksb ;
BUG_ON ( lock - > ml . node = = dlm - > node_num ) ;
lksbflags = lksb - > flags ;
dlm_update_lvb ( dlm , res , lock ) ;
/* lock request came from another node
* go do the ast over there */
ret = dlm_send_proxy_ast ( dlm , res , lock , lksbflags ) ;
return ret ;
}
void dlm_do_local_bast ( struct dlm_ctxt * dlm , struct dlm_lock_resource * res ,
struct dlm_lock * lock , int blocked_type )
{
dlm_bastlockfunc_t * fn = lock - > bast ;
mlog_entry_void ( ) ;
BUG_ON ( lock - > ml . node ! = dlm - > node_num ) ;
( * fn ) ( lock - > astdata , blocked_type ) ;
}
2007-01-18 04:04:25 +03:00
int dlm_proxy_ast_handler ( struct o2net_msg * msg , u32 len , void * data ,
void * * ret_data )
2005-12-16 01:31:23 +03:00
{
int ret ;
unsigned int locklen ;
struct dlm_ctxt * dlm = data ;
struct dlm_lock_resource * res = NULL ;
struct dlm_lock * lock = NULL ;
struct dlm_proxy_ast * past = ( struct dlm_proxy_ast * ) msg - > buf ;
char * name ;
struct list_head * iter , * head = NULL ;
u64 cookie ;
u32 flags ;
2008-12-17 02:49:20 +03:00
u8 node ;
2005-12-16 01:31:23 +03:00
if ( ! dlm_grab ( dlm ) ) {
dlm_error ( DLM_REJECTED ) ;
return DLM_REJECTED ;
}
mlog_bug_on_msg ( ! dlm_domain_fully_joined ( dlm ) ,
" Domain %s not fully joined! \n " , dlm - > name ) ;
name = past - > name ;
locklen = past - > namelen ;
2008-12-17 02:49:20 +03:00
cookie = past - > cookie ;
2005-12-16 01:31:23 +03:00
flags = be32_to_cpu ( past - > flags ) ;
2008-12-17 02:49:20 +03:00
node = past - > node_idx ;
2005-12-16 01:31:23 +03:00
if ( locklen > DLM_LOCKID_NAME_MAX ) {
ret = DLM_IVBUFLEN ;
2008-12-17 02:49:20 +03:00
mlog ( ML_ERROR , " Invalid name length (%d) in proxy ast "
" handler! \n " , locklen ) ;
2005-12-16 01:31:23 +03:00
goto leave ;
}
if ( ( flags & ( LKM_PUT_LVB | LKM_GET_LVB ) ) = =
( LKM_PUT_LVB | LKM_GET_LVB ) ) {
2008-12-17 02:49:20 +03:00
mlog ( ML_ERROR , " Both PUT and GET lvb specified, (0x%x) \n " ,
flags ) ;
2005-12-16 01:31:23 +03:00
ret = DLM_BADARGS ;
goto leave ;
}
mlog ( 0 , " lvb: %s \n " , flags & LKM_PUT_LVB ? " put lvb " :
( flags & LKM_GET_LVB ? " get lvb " : " none " ) ) ;
mlog ( 0 , " type=%d, blocked_type=%d \n " , past - > type , past - > blocked_type ) ;
if ( past - > type ! = DLM_AST & &
past - > type ! = DLM_BAST ) {
2006-03-03 03:43:36 +03:00
mlog ( ML_ERROR , " Unknown ast type! %d, cookie=%u:%llu "
2008-12-17 02:49:20 +03:00
" name=%.*s, node=%u \n " , past - > type ,
dlm_get_lock_cookie_node ( be64_to_cpu ( cookie ) ) ,
dlm_get_lock_cookie_seq ( be64_to_cpu ( cookie ) ) ,
locklen , name , node ) ;
2005-12-16 01:31:23 +03:00
ret = DLM_IVLOCKID ;
goto leave ;
}
res = dlm_lookup_lockres ( dlm , name , locklen ) ;
if ( ! res ) {
2008-12-17 02:49:20 +03:00
mlog ( 0 , " Got %sast for unknown lockres! cookie=%u:%llu, "
" name=%.*s, node=%u \n " , ( past - > type = = DLM_AST ? " " : " b " ) ,
dlm_get_lock_cookie_node ( be64_to_cpu ( cookie ) ) ,
dlm_get_lock_cookie_seq ( be64_to_cpu ( cookie ) ) ,
locklen , name , node ) ;
2005-12-16 01:31:23 +03:00
ret = DLM_IVLOCKID ;
goto leave ;
}
/* cannot get a proxy ast message if this node owns it */
BUG_ON ( res - > owner = = dlm - > node_num ) ;
mlog ( 0 , " lockres %.*s \n " , res - > lockname . len , res - > lockname . name ) ;
spin_lock ( & res - > spinlock ) ;
if ( res - > state & DLM_LOCK_RES_RECOVERING ) {
2008-12-17 02:49:20 +03:00
mlog ( 0 , " Responding with DLM_RECOVERING! \n " ) ;
2005-12-16 01:31:23 +03:00
ret = DLM_RECOVERING ;
goto unlock_out ;
}
if ( res - > state & DLM_LOCK_RES_MIGRATING ) {
2008-12-17 02:49:20 +03:00
mlog ( 0 , " Responding with DLM_MIGRATING! \n " ) ;
2005-12-16 01:31:23 +03:00
ret = DLM_MIGRATING ;
goto unlock_out ;
}
/* try convert queue for both ast/bast */
head = & res - > converting ;
lock = NULL ;
list_for_each ( iter , head ) {
lock = list_entry ( iter , struct dlm_lock , list ) ;
2008-12-17 02:49:20 +03:00
if ( lock - > ml . cookie = = cookie )
2005-12-16 01:31:23 +03:00
goto do_ast ;
}
/* if not on convert, try blocked for ast, granted for bast */
if ( past - > type = = DLM_AST )
head = & res - > blocked ;
else
head = & res - > granted ;
list_for_each ( iter , head ) {
lock = list_entry ( iter , struct dlm_lock , list ) ;
2008-12-17 02:49:20 +03:00
if ( lock - > ml . cookie = = cookie )
2005-12-16 01:31:23 +03:00
goto do_ast ;
}
2008-12-17 02:49:20 +03:00
mlog ( 0 , " Got %sast for unknown lock! cookie=%u:%llu, name=%.*s, "
" node=%u \n " , past - > type = = DLM_AST ? " " : " b " ,
dlm_get_lock_cookie_node ( be64_to_cpu ( cookie ) ) ,
dlm_get_lock_cookie_seq ( be64_to_cpu ( cookie ) ) ,
locklen , name , node ) ;
2005-12-16 01:31:23 +03:00
ret = DLM_NORMAL ;
unlock_out :
spin_unlock ( & res - > spinlock ) ;
goto leave ;
do_ast :
ret = DLM_NORMAL ;
if ( past - > type = = DLM_AST ) {
/* do not alter lock refcount. switching lists. */
2006-06-26 11:24:46 +04:00
list_move_tail ( & lock - > list , & res - > granted ) ;
2008-12-17 02:49:20 +03:00
mlog ( 0 , " ast: Adding to granted list... type=%d, "
" convert_type=%d \n " , lock - > ml . type , lock - > ml . convert_type ) ;
2005-12-16 01:31:23 +03:00
if ( lock - > ml . convert_type ! = LKM_IVMODE ) {
lock - > ml . type = lock - > ml . convert_type ;
lock - > ml . convert_type = LKM_IVMODE ;
} else {
// should already be there....
}
lock - > lksb - > status = DLM_NORMAL ;
/* if we requested the lvb, fetch it into our lksb now */
if ( flags & LKM_GET_LVB ) {
BUG_ON ( ! ( lock - > lksb - > flags & DLM_LKSB_GET_LVB ) ) ;
memcpy ( lock - > lksb - > lvb , past - > lvb , DLM_LVB_LEN ) ;
}
}
spin_unlock ( & res - > spinlock ) ;
if ( past - > type = = DLM_AST )
dlm_do_local_ast ( dlm , res , lock ) ;
else
dlm_do_local_bast ( dlm , res , lock , past - > blocked_type ) ;
leave :
if ( res )
dlm_lockres_put ( res ) ;
dlm_put ( dlm ) ;
return ret ;
}
int dlm_send_proxy_ast_msg ( struct dlm_ctxt * dlm , struct dlm_lock_resource * res ,
struct dlm_lock * lock , int msg_type ,
int blocked_type , int flags )
{
int ret = 0 ;
struct dlm_proxy_ast past ;
struct kvec vec [ 2 ] ;
size_t veclen = 1 ;
int status ;
mlog_entry ( " res %.*s, to=%u, type=%d, blocked_type=%d \n " ,
res - > lockname . len , res - > lockname . name , lock - > ml . node ,
msg_type , blocked_type ) ;
memset ( & past , 0 , sizeof ( struct dlm_proxy_ast ) ) ;
past . node_idx = dlm - > node_num ;
past . type = msg_type ;
past . blocked_type = blocked_type ;
past . namelen = res - > lockname . len ;
memcpy ( past . name , res - > lockname . name , past . namelen ) ;
past . cookie = lock - > ml . cookie ;
vec [ 0 ] . iov_len = sizeof ( struct dlm_proxy_ast ) ;
vec [ 0 ] . iov_base = & past ;
if ( flags & DLM_LKSB_GET_LVB ) {
mlog ( 0 , " returning requested LVB data \n " ) ;
be32_add_cpu ( & past . flags , LKM_GET_LVB ) ;
vec [ 1 ] . iov_len = DLM_LVB_LEN ;
vec [ 1 ] . iov_base = lock - > lksb - > lvb ;
veclen + + ;
}
ret = o2net_send_message_vec ( DLM_PROXY_AST_MSG , dlm - > key , vec , veclen ,
lock - > ml . node , & status ) ;
if ( ret < 0 )
2010-03-30 08:09:22 +04:00
mlog ( ML_ERROR , " Error %d when sending message %u (key 0x%x) to "
" node %u \n " , ret , DLM_PROXY_AST_MSG , dlm - > key ,
lock - > ml . node ) ;
2005-12-16 01:31:23 +03:00
else {
if ( status = = DLM_RECOVERING ) {
mlog ( ML_ERROR , " sent AST to node %u, it thinks this "
" node is dead! \n " , lock - > ml . node ) ;
BUG ( ) ;
} else if ( status = = DLM_MIGRATING ) {
mlog ( ML_ERROR , " sent AST to node %u, it returned "
" DLM_MIGRATING! \n " , lock - > ml . node ) ;
BUG ( ) ;
2006-09-08 22:37:32 +04:00
} else if ( status ! = DLM_NORMAL & & status ! = DLM_IVLOCKID ) {
2005-12-16 01:31:23 +03:00
mlog ( ML_ERROR , " AST to node %u returned %d! \n " ,
lock - > ml . node , status ) ;
/* ignore it */
}
ret = 0 ;
}
return ret ;
}