2006-01-18 12:30:29 +03:00
/******************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * Copyright ( C ) 2005 Red Hat , Inc . All rights reserved .
* *
* * 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 "lockspace.h"
# include "member.h"
# include "recoverd.h"
# include "recover.h"
# include "rcom.h"
# include "config.h"
/*
* Following called by dlm_recoverd thread
*/
static void add_ordered_member ( struct dlm_ls * ls , struct dlm_member * new )
{
struct dlm_member * memb = NULL ;
struct list_head * tmp ;
struct list_head * newlist = & new - > list ;
struct list_head * head = & ls - > ls_nodes ;
list_for_each ( tmp , head ) {
memb = list_entry ( tmp , struct dlm_member , list ) ;
if ( new - > nodeid < memb - > nodeid )
break ;
}
if ( ! memb )
list_add_tail ( newlist , head ) ;
else {
/* FIXME: can use list macro here */
newlist - > prev = tmp - > prev ;
newlist - > next = tmp ;
tmp - > prev - > next = newlist ;
tmp - > prev = newlist ;
}
}
static int dlm_add_member ( struct dlm_ls * ls , int nodeid )
{
struct dlm_member * memb ;
int w ;
2006-01-20 11:47:07 +03:00
memb = kzalloc ( sizeof ( struct dlm_member ) , GFP_KERNEL ) ;
2006-01-18 12:30:29 +03:00
if ( ! memb )
return - ENOMEM ;
w = dlm_node_weight ( ls - > ls_name , nodeid ) ;
if ( w < 0 )
return w ;
memb - > nodeid = nodeid ;
memb - > weight = w ;
add_ordered_member ( ls , memb ) ;
ls - > ls_num_nodes + + ;
return 0 ;
}
static void dlm_remove_member ( struct dlm_ls * ls , struct dlm_member * memb )
{
list_move ( & memb - > list , & ls - > ls_nodes_gone ) ;
ls - > ls_num_nodes - - ;
}
static int dlm_is_member ( struct dlm_ls * ls , int nodeid )
{
struct dlm_member * memb ;
list_for_each_entry ( memb , & ls - > ls_nodes , list ) {
if ( memb - > nodeid = = nodeid )
2006-01-20 11:47:07 +03:00
return 1 ;
2006-01-18 12:30:29 +03:00
}
2006-01-20 11:47:07 +03:00
return 0 ;
2006-01-18 12:30:29 +03:00
}
int dlm_is_removed ( struct dlm_ls * ls , int nodeid )
{
struct dlm_member * memb ;
list_for_each_entry ( memb , & ls - > ls_nodes_gone , list ) {
if ( memb - > nodeid = = nodeid )
2006-01-20 11:47:07 +03:00
return 1 ;
2006-01-18 12:30:29 +03:00
}
2006-01-20 11:47:07 +03:00
return 0 ;
2006-01-18 12:30:29 +03:00
}
static void clear_memb_list ( struct list_head * head )
{
struct dlm_member * memb ;
while ( ! list_empty ( head ) ) {
memb = list_entry ( head - > next , struct dlm_member , list ) ;
list_del ( & memb - > list ) ;
kfree ( memb ) ;
}
}
void dlm_clear_members ( struct dlm_ls * ls )
{
clear_memb_list ( & ls - > ls_nodes ) ;
ls - > ls_num_nodes = 0 ;
}
void dlm_clear_members_gone ( struct dlm_ls * ls )
{
clear_memb_list ( & ls - > ls_nodes_gone ) ;
}
static void make_member_array ( struct dlm_ls * ls )
{
struct dlm_member * memb ;
int i , w , x = 0 , total = 0 , all_zero = 0 , * array ;
kfree ( ls - > ls_node_array ) ;
ls - > ls_node_array = NULL ;
list_for_each_entry ( memb , & ls - > ls_nodes , list ) {
if ( memb - > weight )
total + = memb - > weight ;
}
/* all nodes revert to weight of 1 if all have weight 0 */
if ( ! total ) {
total = ls - > ls_num_nodes ;
all_zero = 1 ;
}
ls - > ls_total_weight = total ;
array = kmalloc ( sizeof ( int ) * total , GFP_KERNEL ) ;
if ( ! array )
return ;
list_for_each_entry ( memb , & ls - > ls_nodes , list ) {
if ( ! all_zero & & ! memb - > weight )
continue ;
if ( all_zero )
w = 1 ;
else
w = memb - > weight ;
DLM_ASSERT ( x < total , printk ( " total %d x %d \n " , total , x ) ; ) ;
for ( i = 0 ; i < w ; i + + )
array [ x + + ] = memb - > nodeid ;
}
ls - > ls_node_array = array ;
}
/* send a status request to all members just to establish comms connections */
2006-08-09 02:06:07 +04:00
static int ping_members ( struct dlm_ls * ls )
2006-01-18 12:30:29 +03:00
{
struct dlm_member * memb ;
2006-08-09 02:06:07 +04:00
int error = 0 ;
list_for_each_entry ( memb , & ls - > ls_nodes , list ) {
error = dlm_recovery_stopped ( ls ) ;
if ( error )
break ;
error = dlm_rcom_status ( ls , memb - > nodeid ) ;
if ( error )
break ;
}
if ( error )
2006-08-09 02:08:42 +04:00
log_debug ( ls , " ping_members aborted %d last nodeid %d " ,
error , ls - > ls_recover_nodeid ) ;
2006-08-09 02:06:07 +04:00
return error ;
2006-01-18 12:30:29 +03:00
}
int dlm_recover_members ( struct dlm_ls * ls , struct dlm_recover * rv , int * neg_out )
{
struct dlm_member * memb , * safe ;
int i , error , found , pos = 0 , neg = 0 , low = - 1 ;
2006-10-31 20:56:01 +03:00
/* previously removed members that we've not finished removing need to
count as a negative change so the " neg " recovery steps will happen */
list_for_each_entry ( memb , & ls - > ls_nodes_gone , list ) {
log_debug ( ls , " prev removed member %d " , memb - > nodeid ) ;
neg + + ;
}
2006-01-18 12:30:29 +03:00
/* move departed members from ls_nodes to ls_nodes_gone */
list_for_each_entry_safe ( memb , safe , & ls - > ls_nodes , list ) {
2006-01-20 11:47:07 +03:00
found = 0 ;
2006-01-18 12:30:29 +03:00
for ( i = 0 ; i < rv - > node_count ; i + + ) {
if ( memb - > nodeid = = rv - > nodeids [ i ] ) {
2006-01-20 11:47:07 +03:00
found = 1 ;
2006-01-18 12:30:29 +03:00
break ;
}
}
if ( ! found ) {
neg + + ;
dlm_remove_member ( ls , memb ) ;
log_debug ( ls , " remove member %d " , memb - > nodeid ) ;
}
}
/* add new members to ls_nodes */
for ( i = 0 ; i < rv - > node_count ; i + + ) {
if ( dlm_is_member ( ls , rv - > nodeids [ i ] ) )
continue ;
dlm_add_member ( ls , rv - > nodeids [ i ] ) ;
pos + + ;
log_debug ( ls , " add member %d " , rv - > nodeids [ i ] ) ;
}
list_for_each_entry ( memb , & ls - > ls_nodes , list ) {
if ( low = = - 1 | | memb - > nodeid < low )
low = memb - > nodeid ;
}
ls - > ls_low_nodeid = low ;
make_member_array ( ls ) ;
dlm_set_recover_status ( ls , DLM_RS_NODES ) ;
* neg_out = neg ;
2006-08-09 02:06:07 +04:00
error = ping_members ( ls ) ;
if ( error )
goto out ;
2006-01-18 12:30:29 +03:00
error = dlm_recover_members_wait ( ls ) ;
2006-08-09 02:06:07 +04:00
out :
log_debug ( ls , " total members %d error %d " , ls - > ls_num_nodes , error ) ;
2006-01-18 12:30:29 +03:00
return error ;
}
/*
* Following called from lockspace . c
*/
int dlm_ls_stop ( struct dlm_ls * ls )
{
int new ;
/*
* A stop cancels any recovery that ' s in progress ( see RECOVERY_STOP ,
* dlm_recovery_stopped ( ) ) and prevents any new locks from being
* processed ( see RUNNING , dlm_locking_stopped ( ) ) .
*/
spin_lock ( & ls - > ls_recover_lock ) ;
set_bit ( LSFL_RECOVERY_STOP , & ls - > ls_flags ) ;
new = test_and_clear_bit ( LSFL_RUNNING , & ls - > ls_flags ) ;
ls - > ls_recover_seq + + ;
spin_unlock ( & ls - > ls_recover_lock ) ;
/*
* This in_recovery lock does two things :
*
* 1 ) Keeps this function from returning until all threads are out
* of locking routines and locking is truely stopped .
* 2 ) Keeps any new requests from being processed until it ' s unlocked
* when recovery is complete .
*/
if ( new )
down_write ( & ls - > ls_in_recovery ) ;
/*
* The recoverd suspend / resume makes sure that dlm_recoverd ( if
* running ) has noticed the clearing of RUNNING above and quit
* processing the previous recovery . This will be true for all nodes
* before any nodes start the new recovery .
*/
dlm_recoverd_suspend ( ls ) ;
ls - > ls_recover_status = 0 ;
dlm_recoverd_resume ( ls ) ;
return 0 ;
}
int dlm_ls_start ( struct dlm_ls * ls )
{
struct dlm_recover * rv = NULL , * rv_old ;
int * ids = NULL ;
int error , count ;
2006-01-20 11:47:07 +03:00
rv = kzalloc ( sizeof ( struct dlm_recover ) , GFP_KERNEL ) ;
2006-01-18 12:30:29 +03:00
if ( ! rv )
return - ENOMEM ;
error = count = dlm_nodeid_list ( ls - > ls_name , & ids ) ;
if ( error < = 0 )
goto fail ;
spin_lock ( & ls - > ls_recover_lock ) ;
/* the lockspace needs to be stopped before it can be started */
if ( ! dlm_locking_stopped ( ls ) ) {
spin_unlock ( & ls - > ls_recover_lock ) ;
log_error ( ls , " start ignored: lockspace running " ) ;
error = - EINVAL ;
goto fail ;
}
rv - > nodeids = ids ;
rv - > node_count = count ;
rv - > seq = + + ls - > ls_recover_seq ;
rv_old = ls - > ls_recover_args ;
ls - > ls_recover_args = rv ;
spin_unlock ( & ls - > ls_recover_lock ) ;
if ( rv_old ) {
kfree ( rv_old - > nodeids ) ;
kfree ( rv_old ) ;
}
dlm_recoverd_kick ( ls ) ;
return 0 ;
fail :
kfree ( rv ) ;
kfree ( ids ) ;
return error ;
}