2019-05-28 19:57:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-01-18 12:30:29 +03:00
/******************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* * Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
* * Copyright ( C ) 2004 - 2005 Red Hat , Inc . All rights reserved .
* *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "dlm_internal.h"
# include "lockspace.h"
# include "member.h"
# include "lowcomms.h"
# include "rcom.h"
# include "config.h"
# include "memory.h"
# include "recover.h"
# include "util.h"
# include "lock.h"
# include "dir.h"
/*
* We use the upper 16 bits of the hash value to select the directory node .
* Low bits are used for distribution of rsb ' s among hash buckets on each node .
*
* To give the exact range wanted ( 0 to num_nodes - 1 ) , we apply a modulus of
* num_nodes to the hash value . This value in the desired range is used as an
* offset into the sorted list of nodeid ' s to give the particular nodeid .
*/
int dlm_hash2nodeid ( struct dlm_ls * ls , uint32_t hash )
{
2012-05-10 19:18:07 +04:00
uint32_t node ;
2006-01-18 12:30:29 +03:00
2012-05-10 19:18:07 +04:00
if ( ls - > ls_num_nodes = = 1 )
return dlm_our_nodeid ( ) ;
else {
2006-01-18 12:30:29 +03:00
node = ( hash > > 16 ) % ls - > ls_total_weight ;
2012-05-10 19:18:07 +04:00
return ls - > ls_node_array [ node ] ;
2006-01-18 12:30:29 +03:00
}
}
int dlm_dir_nodeid ( struct dlm_rsb * r )
{
2012-05-10 19:18:07 +04:00
return r - > res_dir_nodeid ;
2006-01-18 12:30:29 +03:00
}
2012-05-10 19:18:07 +04:00
void dlm_recover_dir_nodeid ( struct dlm_ls * ls )
2006-01-18 12:30:29 +03:00
{
2012-05-10 19:18:07 +04:00
struct dlm_rsb * r ;
2006-01-18 12:30:29 +03:00
2012-05-10 19:18:07 +04:00
down_read ( & ls - > ls_root_sem ) ;
list_for_each_entry ( r , & ls - > ls_root_list , res_root_list ) {
r - > res_dir_nodeid = dlm_hash2nodeid ( ls , r - > res_hash ) ;
2006-01-18 12:30:29 +03:00
}
2012-05-10 19:18:07 +04:00
up_read ( & ls - > ls_root_sem ) ;
2006-01-18 12:30:29 +03:00
}
int dlm_recover_directory ( struct dlm_ls * ls )
{
struct dlm_member * memb ;
char * b , * last_name = NULL ;
2012-05-10 19:18:07 +04:00
int error = - ENOMEM , last_len , nodeid , result ;
2006-01-18 12:30:29 +03:00
uint16_t namelen ;
2012-05-10 19:18:07 +04:00
unsigned int count = 0 , count_match = 0 , count_bad = 0 , count_add = 0 ;
2006-01-18 12:30:29 +03:00
2014-02-14 21:54:44 +04:00
log_rinfo ( ls , " dlm_recover_directory " ) ;
2006-01-18 12:30:29 +03:00
if ( dlm_no_directory ( ls ) )
goto out_status ;
2009-12-01 01:34:43 +03:00
last_name = kmalloc ( DLM_RESNAME_MAXLEN , GFP_NOFS ) ;
2006-01-18 12:30:29 +03:00
if ( ! last_name )
goto out ;
list_for_each_entry ( memb , & ls - > ls_nodes , list ) {
2012-05-10 19:18:07 +04:00
if ( memb - > nodeid = = dlm_our_nodeid ( ) )
continue ;
2006-01-18 12:30:29 +03:00
memset ( last_name , 0 , DLM_RESNAME_MAXLEN ) ;
last_len = 0 ;
for ( ; ; ) {
2008-01-25 12:08:09 +03:00
int left ;
2021-11-02 22:17:13 +03:00
if ( dlm_recovery_stopped ( ls ) ) {
2021-08-18 23:27:14 +03:00
error = - EINTR ;
2006-01-18 12:30:29 +03:00
goto out_free ;
2021-08-18 23:27:14 +03:00
}
2006-01-18 12:30:29 +03:00
error = dlm_rcom_names ( ls , memb - > nodeid ,
last_name , last_len ) ;
if ( error )
goto out_free ;
2012-05-10 19:18:07 +04:00
cond_resched ( ) ;
2006-01-18 12:30:29 +03:00
/*
* pick namelen / name pairs out of received buffer
*/
2008-01-25 11:01:51 +03:00
b = ls - > ls_recover_buf - > rc_buf ;
2022-04-04 23:06:39 +03:00
left = le16_to_cpu ( ls - > ls_recover_buf - > rc_header . h_length ) ;
2008-01-25 12:08:09 +03:00
left - = sizeof ( struct dlm_rcom ) ;
2006-01-18 12:30:29 +03:00
for ( ; ; ) {
2008-01-25 12:08:09 +03:00
__be16 v ;
error = - EINVAL ;
if ( left < sizeof ( __be16 ) )
goto out_free ;
memcpy ( & v , b , sizeof ( __be16 ) ) ;
namelen = be16_to_cpu ( v ) ;
b + = sizeof ( __be16 ) ;
left - = sizeof ( __be16 ) ;
2006-01-18 12:30:29 +03:00
/* namelen of 0xFFFFF marks end of names for
this node ; namelen of 0 marks end of the
buffer */
if ( namelen = = 0xFFFF )
goto done ;
if ( ! namelen )
break ;
2008-01-25 12:08:09 +03:00
if ( namelen > left )
goto out_free ;
if ( namelen > DLM_RESNAME_MAXLEN )
goto out_free ;
2012-05-10 19:18:07 +04:00
error = dlm_master_lookup ( ls , memb - > nodeid ,
b , namelen ,
DLM_LU_RECOVER_DIR ,
& nodeid , & result ) ;
if ( error ) {
log_error ( ls , " recover_dir lookup %d " ,
error ) ;
2006-01-18 12:30:29 +03:00
goto out_free ;
2012-05-10 19:18:07 +04:00
}
/* The name was found in rsbtbl, but the
* master nodeid is different from
* memb - > nodeid which says it is the master .
* This should not happen . */
if ( result = = DLM_LU_MATCH & &
nodeid ! = memb - > nodeid ) {
count_bad + + ;
log_error ( ls , " recover_dir lookup %d "
" nodeid %d memb %d bad %u " ,
result , nodeid , memb - > nodeid ,
count_bad ) ;
print_hex_dump_bytes ( " dlm_recover_dir " ,
DUMP_PREFIX_NONE ,
b , namelen ) ;
}
/* The name was found in rsbtbl, and the
* master nodeid matches memb - > nodeid . */
if ( result = = DLM_LU_MATCH & &
nodeid = = memb - > nodeid ) {
count_match + + ;
}
/* The name was not found in rsbtbl and was
* added with memb - > nodeid as the master . */
if ( result = = DLM_LU_ADD ) {
count_add + + ;
}
2006-01-18 12:30:29 +03:00
last_len = namelen ;
memcpy ( last_name , b , namelen ) ;
b + = namelen ;
2008-01-25 12:08:09 +03:00
left - = namelen ;
2006-01-18 12:30:29 +03:00
count + + ;
}
}
2012-05-10 19:18:07 +04:00
done :
2006-01-18 12:30:29 +03:00
;
}
out_status :
error = 0 ;
2012-05-10 19:18:07 +04:00
dlm_set_recover_status ( ls , DLM_RS_DIR ) ;
2014-02-14 21:54:44 +04:00
log_rinfo ( ls , " dlm_recover_directory %u in %u new " ,
2012-05-10 19:18:07 +04:00
count , count_add ) ;
2006-01-18 12:30:29 +03:00
out_free :
kfree ( last_name ) ;
out :
return error ;
}
2008-01-16 22:02:31 +03:00
static struct dlm_rsb * find_rsb_root ( struct dlm_ls * ls , char * name , int len )
{
struct dlm_rsb * r ;
2012-03-08 22:37:12 +04:00
uint32_t hash , bucket ;
int rv ;
hash = jhash ( name , len , 0 ) ;
bucket = hash & ( ls - > ls_rsbtbl_size - 1 ) ;
spin_lock ( & ls - > ls_rsbtbl [ bucket ] . lock ) ;
2012-05-10 19:18:07 +04:00
rv = dlm_search_rsb_tree ( & ls - > ls_rsbtbl [ bucket ] . keep , name , len , & r ) ;
2012-03-08 22:37:12 +04:00
if ( rv )
rv = dlm_search_rsb_tree ( & ls - > ls_rsbtbl [ bucket ] . toss ,
2012-05-10 19:18:07 +04:00
name , len , & r ) ;
2012-03-08 22:37:12 +04:00
spin_unlock ( & ls - > ls_rsbtbl [ bucket ] . lock ) ;
if ( ! rv )
return r ;
2008-01-16 22:02:31 +03:00
down_read ( & ls - > ls_root_sem ) ;
list_for_each_entry ( r , & ls - > ls_root_list , res_root_list ) {
if ( len = = r - > res_length & & ! memcmp ( name , r - > res_name , len ) ) {
up_read ( & ls - > ls_root_sem ) ;
2012-05-10 19:18:07 +04:00
log_debug ( ls , " find_rsb_root revert to root_list %s " ,
2012-03-08 22:37:12 +04:00
r - > res_name ) ;
2008-01-16 22:02:31 +03:00
return r ;
}
}
up_read ( & ls - > ls_root_sem ) ;
return NULL ;
}
/* Find the rsb where we left off (or start again), then send rsb names
for rsb ' s we ' re master of and whose directory node matches the requesting
node . inbuf is the rsb name last sent , inlen is the name ' s length */
2006-01-18 12:30:29 +03:00
void dlm_copy_master_names ( struct dlm_ls * ls , char * inbuf , int inlen ,
char * outbuf , int outlen , int nodeid )
{
struct list_head * list ;
2008-01-16 22:02:31 +03:00
struct dlm_rsb * r ;
int offset = 0 , dir_nodeid ;
2008-11-13 01:28:43 +03:00
__be16 be_namelen ;
2006-01-18 12:30:29 +03:00
down_read ( & ls - > ls_root_sem ) ;
2008-01-16 22:02:31 +03:00
if ( inlen > 1 ) {
r = find_rsb_root ( ls , inbuf , inlen ) ;
if ( ! r ) {
inbuf [ inlen - 1 ] = ' \0 ' ;
log_error ( ls , " copy_master_names from %d start %d %s " ,
nodeid , inlen , inbuf ) ;
goto out ;
}
list = r - > res_root_list . next ;
} else {
2006-01-18 12:30:29 +03:00
list = ls - > ls_root_list . next ;
2008-01-16 22:02:31 +03:00
}
2006-01-18 12:30:29 +03:00
for ( offset = 0 ; list ! = & ls - > ls_root_list ; list = list - > next ) {
r = list_entry ( list , struct dlm_rsb , res_root_list ) ;
if ( r - > res_nodeid )
continue ;
dir_nodeid = dlm_dir_nodeid ( r ) ;
if ( dir_nodeid ! = nodeid )
continue ;
/*
* The block ends when we can ' t fit the following in the
* remaining buffer space :
* namelen ( uint16_t ) +
* name ( r - > res_length ) +
* end - of - block record 0x0000 ( uint16_t )
*/
if ( offset + sizeof ( uint16_t ) * 2 + r - > res_length > outlen ) {
/* Write end-of-block record */
2008-11-13 01:28:43 +03:00
be_namelen = cpu_to_be16 ( 0 ) ;
memcpy ( outbuf + offset , & be_namelen , sizeof ( __be16 ) ) ;
offset + = sizeof ( __be16 ) ;
2012-05-10 19:18:07 +04:00
ls - > ls_recover_dir_sent_msg + + ;
2006-01-18 12:30:29 +03:00
goto out ;
}
be_namelen = cpu_to_be16 ( r - > res_length ) ;
2008-11-13 01:28:43 +03:00
memcpy ( outbuf + offset , & be_namelen , sizeof ( __be16 ) ) ;
offset + = sizeof ( __be16 ) ;
2006-01-18 12:30:29 +03:00
memcpy ( outbuf + offset , r - > res_name , r - > res_length ) ;
offset + = r - > res_length ;
2012-05-10 19:18:07 +04:00
ls - > ls_recover_dir_sent_res + + ;
2006-01-18 12:30:29 +03:00
}
/*
* If we ' ve reached the end of the list ( and there ' s room ) write a
* terminating record .
*/
if ( ( list = = & ls - > ls_root_list ) & &
( offset + sizeof ( uint16_t ) < = outlen ) ) {
2008-11-13 01:28:43 +03:00
be_namelen = cpu_to_be16 ( 0xFFFF ) ;
memcpy ( outbuf + offset , & be_namelen , sizeof ( __be16 ) ) ;
offset + = sizeof ( __be16 ) ;
2012-05-10 19:18:07 +04:00
ls - > ls_recover_dir_sent_msg + + ;
2006-01-18 12:30:29 +03:00
}
out :
up_read ( & ls - > ls_root_sem ) ;
}