2010-03-18 12:19:30 +03:00
/*
2013-09-12 01:46:20 +04:00
* Copyright ( C ) 2009 - 2013 Red Hat , Inc . All rights reserved .
2010-03-18 12:19:30 +03:00
*
* This file is part of LVM2 .
*
* 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 Lesser General Public License v .2 .1 .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software Foundation ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2010-03-18 12:19:30 +03:00
*/
2010-06-21 19:56:57 +04:00
# include "clvmd-common.h"
2010-03-18 12:19:30 +03:00
# include <pthread.h>
# include "locking.h"
# include "clvm.h"
# include "clvmd-comms.h"
# include "clvmd.h"
2010-06-21 19:56:57 +04:00
# include <sys/un.h>
# include <sys/socket.h>
# include <fcntl.h>
2010-07-28 18:01:40 +04:00
static const char SINGLENODE_CLVMD_SOCKNAME [ ] = DEFAULT_RUN_DIR " /clvmd_singlenode.sock " ;
2010-03-18 12:19:30 +03:00
static int listen_fd = - 1 ;
2012-04-24 16:16:40 +04:00
static struct dm_hash_table * _locks ;
static int _lockid ;
2013-09-12 01:46:20 +04:00
static pthread_mutex_t _lock_mutex = PTHREAD_MUTEX_INITIALIZER ;
/* Using one common condition for all locks for simplicity */
static pthread_cond_t _lock_cond = PTHREAD_COND_INITIALIZER ;
2012-04-24 16:16:40 +04:00
struct lock {
2013-09-12 01:46:20 +04:00
struct dm_list list ;
2012-04-24 16:16:40 +04:00
int lockid ;
int mode ;
} ;
2010-03-18 12:19:30 +03:00
2010-08-03 17:06:35 +04:00
static void close_comms ( void )
2010-07-28 18:01:40 +04:00
{
if ( listen_fd ! = - 1 & & close ( listen_fd ) )
stack ;
( void ) unlink ( SINGLENODE_CLVMD_SOCKNAME ) ;
listen_fd = - 1 ;
}
2010-08-03 17:06:35 +04:00
static int init_comms ( void )
2010-03-18 12:19:30 +03:00
{
2010-07-28 18:01:40 +04:00
mode_t old_mask ;
2012-06-21 23:19:28 +04:00
struct sockaddr_un addr = { . sun_family = AF_UNIX } ;
if ( ! dm_strncpy ( addr . sun_path , SINGLENODE_CLVMD_SOCKNAME ,
sizeof ( addr . sun_path ) ) ) {
DEBUGLOG ( " %s: singlenode socket name too long. " ,
SINGLENODE_CLVMD_SOCKNAME ) ;
return - 1 ;
}
2010-07-28 18:01:40 +04:00
close_comms ( ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( SINGLENODE_CLVMD_SOCKNAME , S_IFSOCK ) ;
2010-07-28 18:01:40 +04:00
old_mask = umask ( 0077 ) ;
2010-03-18 12:19:30 +03:00
2010-03-26 18:45:36 +03:00
listen_fd = socket ( PF_UNIX , SOCK_STREAM , 0 ) ;
if ( listen_fd < 0 ) {
DEBUGLOG ( " Can't create local socket: %s \n " , strerror ( errno ) ) ;
2010-07-28 18:01:40 +04:00
goto error ;
2010-03-26 18:45:36 +03:00
}
2010-03-18 12:19:30 +03:00
/* Set Close-on-exec */
2011-08-04 16:13:50 +04:00
if ( fcntl ( listen_fd , F_SETFD , 1 ) ) {
2011-09-29 12:57:21 +04:00
DEBUGLOG ( " Setting CLOEXEC on client fd failed: %s \n " , strerror ( errno ) ) ;
2011-08-04 16:13:50 +04:00
goto error ;
}
2010-03-18 12:19:30 +03:00
2010-03-26 18:45:36 +03:00
if ( bind ( listen_fd , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
DEBUGLOG ( " Can't bind local socket: %s \n " , strerror ( errno ) ) ;
2010-07-28 18:01:40 +04:00
goto error ;
2010-03-26 18:45:36 +03:00
}
if ( listen ( listen_fd , 10 ) < 0 ) {
DEBUGLOG ( " Can't listen local socket: %s \n " , strerror ( errno ) ) ;
2010-07-28 18:01:40 +04:00
goto error ;
2010-03-26 18:45:36 +03:00
}
2010-07-28 18:01:40 +04:00
umask ( old_mask ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2010-03-18 12:19:30 +03:00
return 0 ;
2010-07-28 18:01:40 +04:00
error :
umask ( old_mask ) ;
2010-12-13 13:43:56 +03:00
( void ) dm_prepare_selinux_context ( NULL , 0 ) ;
2010-07-28 18:01:40 +04:00
close_comms ( ) ;
return - 1 ;
2010-03-18 12:19:30 +03:00
}
static int _init_cluster ( void )
{
int r ;
2012-04-24 16:16:40 +04:00
if ( ! ( _locks = dm_hash_create ( 128 ) ) ) {
DEBUGLOG ( " Failed to allocate single-node hash table. \n " ) ;
return 1 ;
}
2010-03-18 12:19:30 +03:00
r = init_comms ( ) ;
2012-04-24 16:16:40 +04:00
if ( r ) {
dm_hash_destroy ( _locks ) ;
2014-03-20 02:34:11 +04:00
_locks = NULL ;
2010-03-18 12:19:30 +03:00
return r ;
2012-04-24 16:16:40 +04:00
}
2010-03-18 12:19:30 +03:00
DEBUGLOG ( " Single-node cluster initialised. \n " ) ;
return 0 ;
}
static void _cluster_closedown ( void )
{
2010-07-28 18:01:40 +04:00
close_comms ( ) ;
2010-03-18 12:19:30 +03:00
2013-09-12 01:46:20 +04:00
/* If there is any awaited resource, kill it softly */
pthread_mutex_lock ( & _lock_mutex ) ;
2012-04-24 16:16:40 +04:00
dm_hash_destroy ( _locks ) ;
2011-03-24 13:45:00 +03:00
_locks = NULL ;
2012-04-24 16:16:40 +04:00
_lockid = 0 ;
2013-09-12 01:46:20 +04:00
pthread_cond_broadcast ( & _lock_cond ) ; /* wakeup waiters */
pthread_mutex_unlock ( & _lock_mutex ) ;
2010-03-18 12:19:30 +03:00
}
static void _get_our_csid ( char * csid )
{
int nodeid = 1 ;
memcpy ( csid , & nodeid , sizeof ( int ) ) ;
}
static int _csid_from_name ( char * csid , const char * name )
{
return 1 ;
}
static int _name_from_csid ( const char * csid , char * name )
{
2012-12-14 17:01:19 +04:00
strcpy ( name , " SINGLENODE " ) ;
2010-03-18 12:19:30 +03:00
return 0 ;
}
2010-08-03 17:06:35 +04:00
static int _get_num_nodes ( void )
2010-03-18 12:19:30 +03:00
{
return 1 ;
}
/* Node is now known to be running a clvmd */
static void _add_up_node ( const char * csid )
{
}
/* Call a callback for each node, so the caller knows whether it's up or down */
static int _cluster_do_node_callback ( struct local_client * master_client ,
void ( * callback ) ( struct local_client * ,
const char * csid , int node_up ) )
{
return 0 ;
}
int _lock_file ( const char * file , uint32_t flags ) ;
2013-09-12 01:46:20 +04:00
static const char * _get_mode ( int mode )
{
switch ( mode ) {
case LCK_NULL : return " NULL " ;
case LCK_READ : return " READ " ;
case LCK_PREAD : return " PREAD " ;
case LCK_WRITE : return " WRITE " ;
case LCK_EXCL : return " EXCLUSIVE " ;
case LCK_UNLOCK : return " UNLOCK " ;
default : return " ???? " ;
}
}
2010-03-18 12:19:30 +03:00
/* Real locking */
static int _lock_resource ( const char * resource , int mode , int flags , int * lockid )
{
2013-09-12 01:46:20 +04:00
/* DLM table of allowed transition states */
static const int _dlm_table [ 6 ] [ 6 ] = {
/* Mode NL CR CW PR PW EX */
/* NL */ { 1 , 1 , 1 , 1 , 1 , 1 } ,
/* CR */ { 1 , 1 , 1 , 1 , 1 , 0 } ,
/* CW */ { 1 , 1 , 1 , 0 , 0 , 0 } ,
/* PR */ { 1 , 1 , 0 , 1 , 0 , 0 } ,
/* PW */ { 1 , 1 , 0 , 0 , 0 , 0 } ,
/* EX */ { 1 , 0 , 0 , 0 , 0 , 0 }
} ;
2013-09-12 13:29:18 +04:00
struct lock * lck = NULL , * lckt ;
2013-09-12 01:46:20 +04:00
struct dm_list * head ;
DEBUGLOG ( " Locking resource %s, flags=0x%02x (%s%s%s), mode=%s (%d) \n " ,
resource , flags ,
( flags & LCKF_NOQUEUE ) ? " NOQUEUE " : " " ,
( ( flags & ( LCKF_NOQUEUE | LCKF_CONVERT ) ) = =
( LCKF_NOQUEUE | LCKF_CONVERT ) ) ? " | " : " " ,
( flags & LCKF_CONVERT ) ? " CONVERT " : " " ,
_get_mode ( mode ) , mode ) ;
2010-03-18 12:19:30 +03:00
2012-04-24 16:16:40 +04:00
mode & = LCK_TYPE_MASK ;
2010-03-18 12:19:30 +03:00
pthread_mutex_lock ( & _lock_mutex ) ;
2012-04-24 16:16:40 +04:00
2013-09-12 01:46:20 +04:00
retry :
if ( ! ( head = dm_hash_lookup ( _locks , resource ) ) ) {
2013-09-12 13:29:18 +04:00
if ( flags & LCKF_CONVERT ) {
/* In real DLM, lock is identified only by lockid, resource is not used */
DEBUGLOG ( " Unlocked resource %s cannot be converted \n " , resource ) ;
goto_bad ;
}
2013-09-12 01:46:20 +04:00
/* Add new locked resource */
if ( ! ( head = dm_malloc ( sizeof ( struct dm_list ) ) ) | |
! dm_hash_insert ( _locks , resource , head ) ) {
dm_free ( head ) ;
goto_bad ;
}
dm_list_init ( head ) ;
} else /* Update/convert locked resource */
dm_list_iterate_items ( lck , head ) {
2013-09-12 13:29:18 +04:00
/* Check is all locks are compatible with requested lock */
2013-09-12 01:46:20 +04:00
if ( flags & LCKF_CONVERT ) {
if ( lck - > lockid ! = * lockid )
continue ;
DEBUGLOG ( " Converting resource %s lockid=%d mode:%s -> %s... \n " ,
resource , lck - > lockid , _get_mode ( lck - > mode ) , _get_mode ( mode ) ) ;
dm_list_iterate_items ( lckt , head ) {
if ( ( lckt - > lockid ! = * lockid ) & &
! _dlm_table [ mode ] [ lckt - > mode ] ) {
if ( ! ( flags & LCKF_NOQUEUE ) & &
/* TODO: Real dlm uses here conversion queues */
! pthread_cond_wait ( & _lock_cond , & _lock_mutex ) & &
_locks ) /* End of the game? */
goto retry ;
goto bad ;
}
}
lck - > mode = mode ; /* Lock is now converted */
goto out ;
} else if ( ! _dlm_table [ mode ] [ lck - > mode ] ) {
DEBUGLOG ( " Resource %s already locked lockid=%d, mode:%s \n " ,
resource , lck - > lockid , _get_mode ( lck - > mode ) ) ;
if ( ! ( flags & LCKF_NOQUEUE ) & &
! pthread_cond_wait ( & _lock_cond , & _lock_mutex ) & &
_locks ) { /* End of the game? */
DEBUGLOG ( " Resource %s retrying lock in mode:%s... \n " ,
resource , _get_mode ( mode ) ) ;
goto retry ;
}
goto bad ;
}
}
if ( ! ( flags & LCKF_CONVERT ) ) {
if ( ! ( lck = dm_malloc ( sizeof ( struct lock ) ) ) )
goto_bad ;
* lockid = lck - > lockid = + + _lockid ;
lck - > mode = mode ;
dm_list_add ( head , & lck - > list ) ;
2010-03-18 12:19:30 +03:00
}
2012-04-24 16:16:40 +04:00
out :
2015-04-09 00:12:29 +03:00
pthread_cond_broadcast ( & _lock_cond ) ; /* to wakeup waiters */
2010-03-18 12:19:30 +03:00
pthread_mutex_unlock ( & _lock_mutex ) ;
2013-09-12 01:46:20 +04:00
DEBUGLOG ( " Locked resource %s, lockid=%d, mode=%s \n " ,
resource , lck - > lockid , _get_mode ( lck - > mode ) ) ;
2011-10-11 13:05:20 +04:00
2010-03-18 12:19:30 +03:00
return 0 ;
2011-10-11 13:05:20 +04:00
bad :
2015-04-09 00:12:29 +03:00
pthread_cond_broadcast ( & _lock_cond ) ; /* to wakeup waiters */
2011-10-11 13:05:20 +04:00
pthread_mutex_unlock ( & _lock_mutex ) ;
2013-04-18 20:01:52 +04:00
DEBUGLOG ( " Failed to lock resource %s \n " , resource ) ;
2010-03-18 12:19:30 +03:00
return 1 ; /* fail */
}
static int _unlock_resource ( const char * resource , int lockid )
{
2012-04-24 16:16:40 +04:00
struct lock * lck ;
2013-09-12 01:46:20 +04:00
struct dm_list * head ;
int r = 1 ;
2012-04-24 16:16:40 +04:00
2011-11-07 21:11:23 +04:00
if ( lockid < 0 ) {
DEBUGLOG ( " Not tracking unlock of lockid -1: %s, lockid=%d \n " ,
resource , lockid ) ;
2013-09-12 01:46:20 +04:00
return 1 ;
2011-11-07 21:11:23 +04:00
}
2011-10-11 13:05:20 +04:00
DEBUGLOG ( " Unlocking resource %s, lockid=%d \n " , resource , lockid ) ;
pthread_mutex_lock ( & _lock_mutex ) ;
2013-09-12 01:46:20 +04:00
pthread_cond_broadcast ( & _lock_cond ) ; /* wakeup waiters */
2011-10-11 13:05:20 +04:00
2013-09-12 01:46:20 +04:00
if ( ! ( head = dm_hash_lookup ( _locks , resource ) ) ) {
2011-10-11 13:05:20 +04:00
pthread_mutex_unlock ( & _lock_mutex ) ;
2013-09-12 13:29:18 +04:00
DEBUGLOG ( " Resource %s is not locked. \n " , resource ) ;
2010-03-18 12:19:30 +03:00
return 1 ;
}
2011-10-11 13:05:20 +04:00
2013-09-12 01:46:20 +04:00
dm_list_iterate_items ( lck , head )
if ( lck - > lockid = = lockid ) {
dm_list_del ( & lck - > list ) ;
dm_free ( lck ) ;
r = 0 ;
goto out ;
}
DEBUGLOG ( " Resource %s has wrong lockid %d. \n " , resource , lockid ) ;
out :
if ( dm_list_empty ( head ) ) {
//DEBUGLOG("Resource %s is no longer hashed (lockid=%d).\n", resource, lockid);
dm_hash_remove ( _locks , resource ) ;
dm_free ( head ) ;
2010-03-18 12:19:30 +03:00
}
2011-10-11 13:05:20 +04:00
pthread_mutex_unlock ( & _lock_mutex ) ;
2013-09-12 01:46:20 +04:00
return r ;
2010-03-18 12:19:30 +03:00
}
2010-08-03 17:06:35 +04:00
static int _is_quorate ( void )
2010-03-18 12:19:30 +03:00
{
return 1 ;
}
static int _get_main_cluster_fd ( void )
{
return listen_fd ;
}
static int _cluster_fd_callback ( struct local_client * fd , char * buf , int len ,
const char * csid ,
struct local_client * * new_client )
{
return 1 ;
}
static int _cluster_send_message ( const void * buf , int msglen ,
const char * csid ,
const char * errtext )
{
return 0 ;
}
static int _get_cluster_name ( char * buf , int buflen )
{
2012-12-14 17:01:19 +04:00
return dm_strncpy ( buf , " localcluster " , buflen ) ? 0 : 1 ;
2010-03-18 12:19:30 +03:00
}
static struct cluster_ops _cluster_singlenode_ops = {
2011-09-25 23:37:00 +04:00
. name = " singlenode " ,
2010-03-18 12:19:30 +03:00
. cluster_init_completed = NULL ,
. cluster_send_message = _cluster_send_message ,
. name_from_csid = _name_from_csid ,
. csid_from_name = _csid_from_name ,
. get_num_nodes = _get_num_nodes ,
. cluster_fd_callback = _cluster_fd_callback ,
. get_main_cluster_fd = _get_main_cluster_fd ,
. cluster_do_node_callback = _cluster_do_node_callback ,
. is_quorate = _is_quorate ,
. get_our_csid = _get_our_csid ,
. add_up_node = _add_up_node ,
. reread_config = NULL ,
. cluster_closedown = _cluster_closedown ,
. get_cluster_name = _get_cluster_name ,
. sync_lock = _lock_resource ,
. sync_unlock = _unlock_resource ,
} ;
struct cluster_ops * init_singlenode_cluster ( void )
{
if ( ! _init_cluster ( ) )
return & _cluster_singlenode_ops ;
2013-04-18 20:01:52 +04:00
return NULL ;
2010-03-18 12:19:30 +03:00
}