2005-12-16 01:31:24 +03:00
/* -*- mode: c; c-basic-offset: 8; -*-
* vim : noexpandtab sw = 8 ts = 8 sts = 0 :
*
* slot_map . c
*
*
*
* Copyright ( C ) 2002 , 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/types.h>
# include <linux/slab.h>
# include <linux/highmem.h>
# define MLOG_MASK_PREFIX ML_SUPER
# include <cluster/masklog.h>
# include "ocfs2.h"
# include "dlmglue.h"
# include "extent_map.h"
# include "heartbeat.h"
# include "inode.h"
# include "slot_map.h"
# include "super.h"
# include "sysfile.h"
# include "buffer_head_io.h"
2008-02-01 23:04:48 +03:00
struct ocfs2_slot {
int sl_valid ;
unsigned int sl_node_num ;
} ;
2008-02-01 23:01:05 +03:00
struct ocfs2_slot_info {
2008-02-01 23:06:54 +03:00
int si_extended ;
int si_slots_per_block ;
2008-02-01 23:01:05 +03:00
struct inode * si_inode ;
2008-02-01 22:59:07 +03:00
unsigned int si_blocks ;
struct buffer_head * * si_bh ;
2008-02-01 23:01:05 +03:00
unsigned int si_num_slots ;
2008-02-01 23:04:48 +03:00
struct ocfs2_slot * si_slots ;
2008-02-01 23:01:05 +03:00
} ;
2008-02-01 23:04:48 +03:00
static int __ocfs2_node_num_to_slot ( struct ocfs2_slot_info * si ,
unsigned int node_num ) ;
static void ocfs2_invalidate_slot ( struct ocfs2_slot_info * si ,
int slot_num )
{
BUG_ON ( ( slot_num < 0 ) | | ( slot_num > = si - > si_num_slots ) ) ;
si - > si_slots [ slot_num ] . sl_valid = 0 ;
}
static void ocfs2_set_slot ( struct ocfs2_slot_info * si ,
int slot_num , unsigned int node_num )
{
BUG_ON ( ( slot_num < 0 ) | | ( slot_num > = si - > si_num_slots ) ) ;
si - > si_slots [ slot_num ] . sl_valid = 1 ;
si - > si_slots [ slot_num ] . sl_node_num = node_num ;
}
2005-12-16 01:31:24 +03:00
2008-02-01 23:06:54 +03:00
/* This version is for the extended slot map */
static void ocfs2_update_slot_info_extended ( struct ocfs2_slot_info * si )
{
int b , i , slotno ;
struct ocfs2_slot_map_extended * se ;
slotno = 0 ;
for ( b = 0 ; b < si - > si_blocks ; b + + ) {
se = ( struct ocfs2_slot_map_extended * ) si - > si_bh [ b ] - > b_data ;
for ( i = 0 ;
( i < si - > si_slots_per_block ) & &
( slotno < si - > si_num_slots ) ;
i + + , slotno + + ) {
if ( se - > se_slots [ i ] . es_valid )
ocfs2_set_slot ( si , slotno ,
le32_to_cpu ( se - > se_slots [ i ] . es_node_num ) ) ;
else
ocfs2_invalidate_slot ( si , slotno ) ;
}
}
}
2008-02-01 23:01:05 +03:00
/*
* Post the slot information on disk into our slot_info struct .
* Must be protected by osb_lock .
*/
2008-02-01 23:06:54 +03:00
static void ocfs2_update_slot_info_old ( struct ocfs2_slot_info * si )
2005-12-16 01:31:24 +03:00
{
int i ;
2008-02-01 22:59:05 +03:00
struct ocfs2_slot_map * sm ;
2005-12-16 01:31:24 +03:00
2008-02-01 22:59:05 +03:00
sm = ( struct ocfs2_slot_map * ) si - > si_bh [ 0 ] - > b_data ;
2005-12-16 01:31:24 +03:00
2008-02-01 23:04:48 +03:00
for ( i = 0 ; i < si - > si_num_slots ; i + + ) {
2008-02-01 22:59:05 +03:00
if ( le16_to_cpu ( sm - > sm_slots [ i ] ) = = ( u16 ) OCFS2_INVALID_SLOT )
2008-02-01 23:04:48 +03:00
ocfs2_invalidate_slot ( si , i ) ;
else
2008-02-01 22:59:05 +03:00
ocfs2_set_slot ( si , i , le16_to_cpu ( sm - > sm_slots [ i ] ) ) ;
2008-02-01 23:04:48 +03:00
}
2005-12-16 01:31:24 +03:00
}
2008-02-01 23:06:54 +03:00
static void ocfs2_update_slot_info ( struct ocfs2_slot_info * si )
{
/*
* The slot data will have been refreshed when ocfs2_super_lock
* was taken .
*/
if ( si - > si_extended )
ocfs2_update_slot_info_extended ( si ) ;
else
ocfs2_update_slot_info_old ( si ) ;
}
2008-02-01 22:59:09 +03:00
int ocfs2_refresh_slot_info ( struct ocfs2_super * osb )
{
int ret ;
struct ocfs2_slot_info * si = osb - > slot_info ;
if ( si = = NULL )
return 0 ;
2008-02-01 22:59:07 +03:00
BUG_ON ( si - > si_blocks = = 0 ) ;
BUG_ON ( si - > si_bh = = NULL ) ;
mlog ( 0 , " Refreshing slot map, reading %u block(s) \n " ,
si - > si_blocks ) ;
/*
* We pass - 1 as blocknr because we expect all of si - > si_bh to
* be ! NULL . Thus , ocfs2_read_blocks ( ) will ignore blocknr . If
* this is not true , the read of - 1 ( UINT64_MAX ) will fail .
*/
2009-02-11 07:00:41 +03:00
ret = ocfs2_read_blocks ( INODE_CACHE ( si - > si_inode ) , - 1 , si - > si_blocks ,
si - > si_bh , OCFS2_BH_IGNORE_CACHE , NULL ) ;
2008-02-01 23:01:05 +03:00
if ( ret = = 0 ) {
spin_lock ( & osb - > osb_lock ) ;
2008-02-01 22:59:09 +03:00
ocfs2_update_slot_info ( si ) ;
2008-02-01 23:01:05 +03:00
spin_unlock ( & osb - > osb_lock ) ;
}
2008-02-01 22:59:09 +03:00
return ret ;
}
2005-12-16 01:31:24 +03:00
/* post the our slot info stuff into it's destination bh and write it
* out . */
2008-02-01 23:06:54 +03:00
static void ocfs2_update_disk_slot_extended ( struct ocfs2_slot_info * si ,
int slot_num ,
struct buffer_head * * bh )
{
int blkind = slot_num / si - > si_slots_per_block ;
int slotno = slot_num % si - > si_slots_per_block ;
struct ocfs2_slot_map_extended * se ;
BUG_ON ( blkind > = si - > si_blocks ) ;
se = ( struct ocfs2_slot_map_extended * ) si - > si_bh [ blkind ] - > b_data ;
se - > se_slots [ slotno ] . es_valid = si - > si_slots [ slot_num ] . sl_valid ;
if ( si - > si_slots [ slot_num ] . sl_valid )
se - > se_slots [ slotno ] . es_node_num =
cpu_to_le32 ( si - > si_slots [ slot_num ] . sl_node_num ) ;
* bh = si - > si_bh [ blkind ] ;
}
static void ocfs2_update_disk_slot_old ( struct ocfs2_slot_info * si ,
int slot_num ,
struct buffer_head * * bh )
2005-12-16 01:31:24 +03:00
{
2008-02-01 23:06:54 +03:00
int i ;
2008-02-01 22:59:05 +03:00
struct ocfs2_slot_map * sm ;
2005-12-16 01:31:24 +03:00
2008-02-01 22:59:05 +03:00
sm = ( struct ocfs2_slot_map * ) si - > si_bh [ 0 ] - > b_data ;
2008-02-01 23:04:48 +03:00
for ( i = 0 ; i < si - > si_num_slots ; i + + ) {
if ( si - > si_slots [ i ] . sl_valid )
2008-02-01 22:59:05 +03:00
sm - > sm_slots [ i ] =
2008-02-01 23:04:48 +03:00
cpu_to_le16 ( si - > si_slots [ i ] . sl_node_num ) ;
else
2008-02-01 22:59:05 +03:00
sm - > sm_slots [ i ] = cpu_to_le16 ( OCFS2_INVALID_SLOT ) ;
2008-02-01 23:04:48 +03:00
}
2008-02-01 23:06:54 +03:00
* bh = si - > si_bh [ 0 ] ;
}
static int ocfs2_update_disk_slot ( struct ocfs2_super * osb ,
struct ocfs2_slot_info * si ,
int slot_num )
{
int status ;
struct buffer_head * bh ;
spin_lock ( & osb - > osb_lock ) ;
if ( si - > si_extended )
ocfs2_update_disk_slot_extended ( si , slot_num , & bh ) ;
else
ocfs2_update_disk_slot_old ( si , slot_num , & bh ) ;
2008-02-01 23:01:05 +03:00
spin_unlock ( & osb - > osb_lock ) ;
2005-12-16 01:31:24 +03:00
2009-02-11 07:00:41 +03:00
status = ocfs2_write_block ( osb , bh , INODE_CACHE ( si - > si_inode ) ) ;
2005-12-16 01:31:24 +03:00
if ( status < 0 )
mlog_errno ( status ) ;
return status ;
}
2008-02-01 22:59:07 +03:00
/*
* Calculate how many bytes are needed by the slot map . Returns
* an error if the slot map file is too small .
*/
static int ocfs2_slot_map_physical_size ( struct ocfs2_super * osb ,
struct inode * inode ,
unsigned long long * bytes )
{
unsigned long long bytes_needed ;
2008-02-01 23:06:54 +03:00
if ( ocfs2_uses_extended_slot_map ( osb ) ) {
bytes_needed = osb - > max_slots *
sizeof ( struct ocfs2_extended_slot ) ;
} else {
bytes_needed = osb - > max_slots * sizeof ( __le16 ) ;
}
2008-02-01 22:59:07 +03:00
if ( bytes_needed > i_size_read ( inode ) ) {
mlog ( ML_ERROR ,
" Slot map file is too small! (size %llu, needed %llu) \n " ,
i_size_read ( inode ) , bytes_needed ) ;
return - ENOSPC ;
}
* bytes = bytes_needed ;
return 0 ;
}
2008-02-01 23:04:48 +03:00
/* try to find global node in the slot info. Returns -ENOENT
* if nothing is found . */
static int __ocfs2_node_num_to_slot ( struct ocfs2_slot_info * si ,
unsigned int node_num )
2005-12-16 01:31:24 +03:00
{
2008-02-01 23:04:48 +03:00
int i , ret = - ENOENT ;
2005-12-16 01:31:24 +03:00
for ( i = 0 ; i < si - > si_num_slots ; i + + ) {
2008-02-01 23:04:48 +03:00
if ( si - > si_slots [ i ] . sl_valid & &
( node_num = = si - > si_slots [ i ] . sl_node_num ) ) {
ret = i ;
2005-12-16 01:31:24 +03:00
break ;
}
}
2008-02-01 23:04:48 +03:00
2005-12-16 01:31:24 +03:00
return ret ;
}
2008-02-01 23:04:48 +03:00
static int __ocfs2_find_empty_slot ( struct ocfs2_slot_info * si ,
int preferred )
2005-12-16 01:31:24 +03:00
{
2008-02-01 23:04:48 +03:00
int i , ret = - ENOSPC ;
2005-12-16 01:31:24 +03:00
2008-02-01 23:04:48 +03:00
if ( ( preferred > = 0 ) & & ( preferred < si - > si_num_slots ) ) {
if ( ! si - > si_slots [ preferred ] . sl_valid ) {
2007-06-19 04:00:24 +04:00
ret = preferred ;
goto out ;
}
}
2005-12-16 01:31:24 +03:00
for ( i = 0 ; i < si - > si_num_slots ; i + + ) {
2008-02-01 23:04:48 +03:00
if ( ! si - > si_slots [ i ] . sl_valid ) {
ret = i ;
2005-12-16 01:31:24 +03:00
break ;
}
}
2007-06-19 04:00:24 +04:00
out :
2005-12-16 01:31:24 +03:00
return ret ;
}
2008-02-01 23:01:05 +03:00
int ocfs2_node_num_to_slot ( struct ocfs2_super * osb , unsigned int node_num )
2005-12-16 01:31:24 +03:00
{
2008-02-01 23:04:48 +03:00
int slot ;
2008-02-01 23:01:05 +03:00
struct ocfs2_slot_info * si = osb - > slot_info ;
2005-12-16 01:31:24 +03:00
2008-02-01 23:01:05 +03:00
spin_lock ( & osb - > osb_lock ) ;
slot = __ocfs2_node_num_to_slot ( si , node_num ) ;
spin_unlock ( & osb - > osb_lock ) ;
return slot ;
}
int ocfs2_slot_to_node_num_locked ( struct ocfs2_super * osb , int slot_num ,
unsigned int * node_num )
{
struct ocfs2_slot_info * si = osb - > slot_info ;
assert_spin_locked ( & osb - > osb_lock ) ;
BUG_ON ( slot_num < 0 ) ;
BUG_ON ( slot_num > osb - > max_slots ) ;
2008-02-01 23:04:48 +03:00
if ( ! si - > si_slots [ slot_num ] . sl_valid )
2008-02-01 23:01:05 +03:00
return - ENOENT ;
2008-02-01 23:04:48 +03:00
* node_num = si - > si_slots [ slot_num ] . sl_node_num ;
2008-02-01 23:01:05 +03:00
return 0 ;
2005-12-16 01:31:24 +03:00
}
2008-02-01 22:59:09 +03:00
static void __ocfs2_free_slot_info ( struct ocfs2_slot_info * si )
{
2008-02-01 22:59:07 +03:00
unsigned int i ;
2008-02-01 22:59:09 +03:00
if ( si = = NULL )
return ;
if ( si - > si_inode )
iput ( si - > si_inode ) ;
2008-02-01 22:59:07 +03:00
if ( si - > si_bh ) {
for ( i = 0 ; i < si - > si_blocks ; i + + ) {
if ( si - > si_bh [ i ] ) {
brelse ( si - > si_bh [ i ] ) ;
si - > si_bh [ i ] = NULL ;
}
}
kfree ( si - > si_bh ) ;
}
2008-02-01 22:59:09 +03:00
kfree ( si ) ;
}
2008-02-01 23:04:48 +03:00
int ocfs2_clear_slot ( struct ocfs2_super * osb , int slot_num )
2005-12-16 01:31:24 +03:00
{
2008-02-01 22:59:09 +03:00
struct ocfs2_slot_info * si = osb - > slot_info ;
if ( si = = NULL )
return 0 ;
2008-02-01 23:01:05 +03:00
spin_lock ( & osb - > osb_lock ) ;
2008-02-01 23:04:48 +03:00
ocfs2_invalidate_slot ( si , slot_num ) ;
2008-02-01 23:01:05 +03:00
spin_unlock ( & osb - > osb_lock ) ;
2008-02-01 22:59:09 +03:00
2008-02-01 23:06:54 +03:00
return ocfs2_update_disk_slot ( osb , osb - > slot_info , slot_num ) ;
2005-12-16 01:31:24 +03:00
}
2008-02-01 22:59:07 +03:00
static int ocfs2_map_slot_buffers ( struct ocfs2_super * osb ,
struct ocfs2_slot_info * si )
{
int status = 0 ;
u64 blkno ;
unsigned long long blocks , bytes ;
unsigned int i ;
struct buffer_head * bh ;
status = ocfs2_slot_map_physical_size ( osb , si - > si_inode , & bytes ) ;
if ( status )
goto bail ;
blocks = ocfs2_blocks_for_bytes ( si - > si_inode - > i_sb , bytes ) ;
BUG_ON ( blocks > UINT_MAX ) ;
si - > si_blocks = blocks ;
if ( ! si - > si_blocks )
goto bail ;
2008-02-01 23:06:54 +03:00
if ( si - > si_extended )
si - > si_slots_per_block =
( osb - > sb - > s_blocksize /
sizeof ( struct ocfs2_extended_slot ) ) ;
else
si - > si_slots_per_block = osb - > sb - > s_blocksize / sizeof ( __le16 ) ;
/* The size checks above should ensure this */
BUG_ON ( ( osb - > max_slots / si - > si_slots_per_block ) > blocks ) ;
2008-02-01 22:59:07 +03:00
mlog ( 0 , " Slot map needs %u buffers for %llu bytes \n " ,
si - > si_blocks , bytes ) ;
si - > si_bh = kzalloc ( sizeof ( struct buffer_head * ) * si - > si_blocks ,
GFP_KERNEL ) ;
if ( ! si - > si_bh ) {
status = - ENOMEM ;
mlog_errno ( status ) ;
goto bail ;
}
for ( i = 0 ; i < si - > si_blocks ; i + + ) {
status = ocfs2_extent_map_get_blocks ( si - > si_inode , i ,
& blkno , NULL , NULL ) ;
if ( status < 0 ) {
mlog_errno ( status ) ;
goto bail ;
}
mlog ( 0 , " Reading slot map block %u at %llu \n " , i ,
( unsigned long long ) blkno ) ;
bh = NULL ; /* Acquire a fresh bh */
2009-02-11 07:00:41 +03:00
status = ocfs2_read_blocks ( INODE_CACHE ( si - > si_inode ) , blkno ,
1 , & bh , OCFS2_BH_IGNORE_CACHE , NULL ) ;
2008-02-01 22:59:07 +03:00
if ( status < 0 ) {
mlog_errno ( status ) ;
goto bail ;
}
si - > si_bh [ i ] = bh ;
}
bail :
return status ;
}
2005-12-16 01:31:24 +03:00
int ocfs2_init_slot_info ( struct ocfs2_super * osb )
{
2008-02-01 23:04:48 +03:00
int status ;
2005-12-16 01:31:24 +03:00
struct inode * inode = NULL ;
struct ocfs2_slot_info * si ;
2008-02-01 23:04:48 +03:00
si = kzalloc ( sizeof ( struct ocfs2_slot_info ) +
( sizeof ( struct ocfs2_slot ) * osb - > max_slots ) ,
GFP_KERNEL ) ;
2005-12-16 01:31:24 +03:00
if ( ! si ) {
status = - ENOMEM ;
mlog_errno ( status ) ;
goto bail ;
}
2008-02-01 23:06:54 +03:00
si - > si_extended = ocfs2_uses_extended_slot_map ( osb ) ;
2005-12-16 01:31:24 +03:00
si - > si_num_slots = osb - > max_slots ;
2008-02-01 23:04:48 +03:00
si - > si_slots = ( struct ocfs2_slot * ) ( ( char * ) si +
sizeof ( struct ocfs2_slot_info ) ) ;
2005-12-16 01:31:24 +03:00
inode = ocfs2_get_system_file_inode ( osb , SLOT_MAP_SYSTEM_INODE ,
OCFS2_INVALID_SLOT ) ;
if ( ! inode ) {
status = - EINVAL ;
mlog_errno ( status ) ;
goto bail ;
}
2008-02-01 22:59:07 +03:00
si - > si_inode = inode ;
status = ocfs2_map_slot_buffers ( osb , si ) ;
2005-12-16 01:31:24 +03:00
if ( status < 0 ) {
mlog_errno ( status ) ;
goto bail ;
}
2008-02-01 23:01:05 +03:00
osb - > slot_info = ( struct ocfs2_slot_info * ) si ;
2005-12-16 01:31:24 +03:00
bail :
if ( status < 0 & & si )
2008-02-01 22:59:09 +03:00
__ocfs2_free_slot_info ( si ) ;
2005-12-16 01:31:24 +03:00
return status ;
}
2008-02-01 22:59:09 +03:00
void ocfs2_free_slot_info ( struct ocfs2_super * osb )
2005-12-16 01:31:24 +03:00
{
2008-02-01 22:59:09 +03:00
struct ocfs2_slot_info * si = osb - > slot_info ;
osb - > slot_info = NULL ;
__ocfs2_free_slot_info ( si ) ;
2005-12-16 01:31:24 +03:00
}
int ocfs2_find_slot ( struct ocfs2_super * osb )
{
int status ;
2008-02-01 23:04:48 +03:00
int slot ;
2005-12-16 01:31:24 +03:00
struct ocfs2_slot_info * si ;
mlog_entry_void ( ) ;
si = osb - > slot_info ;
2008-02-01 23:01:05 +03:00
spin_lock ( & osb - > osb_lock ) ;
2005-12-16 01:31:24 +03:00
ocfs2_update_slot_info ( si ) ;
/* search for ourselves first and take the slot if it already
* exists . Perhaps we need to mark this in a variable for our
* own journal recovery ? Possibly not , though we certainly
* need to warn to the user */
slot = __ocfs2_node_num_to_slot ( si , osb - > node_num ) ;
2008-02-01 23:04:48 +03:00
if ( slot < 0 ) {
2005-12-16 01:31:24 +03:00
/* if no slot yet, then just take 1st available
* one . */
2007-06-19 04:00:24 +04:00
slot = __ocfs2_find_empty_slot ( si , osb - > preferred_slot ) ;
2008-02-01 23:04:48 +03:00
if ( slot < 0 ) {
2008-02-01 23:01:05 +03:00
spin_unlock ( & osb - > osb_lock ) ;
2005-12-16 01:31:24 +03:00
mlog ( ML_ERROR , " no free slots available! \n " ) ;
status = - EINVAL ;
goto bail ;
}
} else
mlog ( ML_NOTICE , " slot %d is already allocated to this node! \n " ,
slot ) ;
2008-02-01 23:04:48 +03:00
ocfs2_set_slot ( si , slot , osb - > node_num ) ;
2005-12-16 01:31:24 +03:00
osb - > slot_num = slot ;
2008-02-01 23:01:05 +03:00
spin_unlock ( & osb - > osb_lock ) ;
2005-12-16 01:31:24 +03:00
2006-04-28 04:53:22 +04:00
mlog ( 0 , " taking node slot %d \n " , osb - > slot_num ) ;
2005-12-16 01:31:24 +03:00
2008-02-01 23:06:54 +03:00
status = ocfs2_update_disk_slot ( osb , si , osb - > slot_num ) ;
2005-12-16 01:31:24 +03:00
if ( status < 0 )
mlog_errno ( status ) ;
bail :
mlog_exit ( status ) ;
return status ;
}
void ocfs2_put_slot ( struct ocfs2_super * osb )
{
2008-02-01 23:06:54 +03:00
int status , slot_num ;
2005-12-16 01:31:24 +03:00
struct ocfs2_slot_info * si = osb - > slot_info ;
if ( ! si )
return ;
2008-02-01 23:01:05 +03:00
spin_lock ( & osb - > osb_lock ) ;
2005-12-16 01:31:24 +03:00
ocfs2_update_slot_info ( si ) ;
2008-02-01 23:06:54 +03:00
slot_num = osb - > slot_num ;
2008-02-01 23:04:48 +03:00
ocfs2_invalidate_slot ( si , osb - > slot_num ) ;
2005-12-16 01:31:24 +03:00
osb - > slot_num = OCFS2_INVALID_SLOT ;
2008-02-01 23:01:05 +03:00
spin_unlock ( & osb - > osb_lock ) ;
2005-12-16 01:31:24 +03:00
2008-02-01 23:06:54 +03:00
status = ocfs2_update_disk_slot ( osb , si , slot_num ) ;
2005-12-16 01:31:24 +03:00
if ( status < 0 ) {
mlog_errno ( status ) ;
goto bail ;
}
bail :
2008-02-01 22:59:09 +03:00
ocfs2_free_slot_info ( osb ) ;
2005-12-16 01:31:24 +03:00
}