aboutsummaryrefslogblamecommitdiffstats
path: root/fs/gfs2/rgrp.c
blob: fd3fd9074779157bf4c754a23f3a4ddcc7f4ec18 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                                       
                                                              


                                                                        
                                               

   



                              
                     
                              
                               

                 
                   

                  





                    
                     
                 
                
                  
 
                           



                                                                       





                                                             



                                      
                           
                           
                           


                          


                                                                          








                                               
                                                                      
                                                       
                                                


























                                                      
                                                                                
                                                                 






























                                                                               

                                                                     
                                                    

                                         
                       








































                                                                  
                                                                       
                                                                       





                                             
                      














                                               










                                                                
                                    
                          

                   
                                          

















                                                                      
                            

                                      
                                         





                                                                      
                                                
                                            

                                                                          


                       
                                  
                                            

                                                                      

                       
 

 
                                                                        
 

                                        
                                              









                                                                          
                                                                





                                                                         
                                                    





























































                                                                                
                                             









                                           
                                          
                          
                                            

 








                                                                                   












                                                                              
                                                                   
                              

              


                               
                                                                             


                               
                                      



















                                                                               
                                                                     


                                           

                                                                
                                                                        
                                                                     










                                           
                                                                      
                                              
                                               









                                                                        
 







                                                                           














                                                                         
                                                                                




                                            










                                                                   
   
                                                                       

                                           

































                                                                   
                                 



                                        
                                                 





                                                                      
                                        






                                                              




                                                        

                                                     
                                      
                                          

                  
                                                             





                                       
                                                        
                                                                              



                                                         
                 
         
 


                                            
 















                                                                             
 










                                                                      


                                            
                 





















                                                                            
                                                       








                                                                 
                                                  




                                                            
                                                    




                     





















                                                                     













                                                                     
                                             



                               
                                   




                                                  
                                             





                                          
                                                                            





                                      
                                                       

                                  
                                                                              















                                                                           
                                     


                 
     





                                                     
                                     






















                                                                        
                                       





















                                                          
                                             






                                                          
                                                                      























                                                                          




                                                                        









                                                                      


                                                   










                                                     






























                                                                                







                                                                 
                                                       











                                                                          
                                           


                                 
      

                                                                            
    
                                          








































                                                                               
    
                                          


























                                                                          
    






















                                                                              
                                                                    


































                                                                          
                                                                              
 
                                   
                                                     











                                                          
                                                                       
                                                                        



                                                  

                                                                            
                                                             

                                             







                                                       
                                              







                                            
                                                                              




                                                          

                                                                            
                                                             

                                             






                                   
                                              






                                                        
                                         
                                                        

                                        
                                  

                                                          


                 
    
                                           








                                                        
                    










                                                                                
                                                     
                                             
                            
                      
                              



                                                    
          




                                                                              
                                                   
 


                             

                                                   

                                                            




                                              
















                                                        
                                                     









                                                                            

                                                    









                                                               
                                                                    

                                      
                                          


                           

                                           









                                                                         
                                                                   

























                                                                             
                                                         
                                                                         

                                      
                                    
                    




















                                                                              
                                                                            













                                                                                

                                                                    
 

                                                                   
                                                        



                                                                      
 
                                                                         











                                                                   

                                                                       


                                      
                                      




                                          
                                                                                  


                            
                                
 
                                          














                                                                               
                                                                        



                                                                 
                                                            
                                                                   












                                                            
                                          
 
                                                     

                                             

                      
 

                                                             


                                               
                                                                        

                                      
                                    




                                                      
                                                                




                                                                  
                                                                        














                                                    
                                          
 
                                                     

                                             

                      
 

                                                             


                                               
                                                                        

                                      
                                    




                                                      
                                                                




                                                                  
                                                                        















                                                 
                                                          
 
                                                      

                                              

                  





                                                               
                                    



                                                      
                                                  
                                                                





















                                                                  
                                                                
 
                                                     







                                                             
                                                                




                                                                  
                                                                                









                                                          
                                                                
 
                                                     







                                                             
                                                                




                                                                  
                                                                                


                                         




                                              
                                  








                                                                  
                                                                  













                                                             
                                                                





                                                                  


                                                                
                                                
                                                                        
                                             














                                                                       
                              











                                                  
                                                                                 










                                                                     
                                                       






























                                                                              
                                                         
























                                                              
/*
 * Copyright (C) Sistina Software, Inc.  1997-2003 All rights reserved.
 * Copyright (C) 2004-2007 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 version 2.
 */

#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/gfs2_ondisk.h>
#include <linux/lm_interface.h>

#include "gfs2.h"
#include "incore.h"
#include "glock.h"
#include "glops.h"
#include "lops.h"
#include "meta_io.h"
#include "quota.h"
#include "rgrp.h"
#include "super.h"
#include "trans.h"
#include "ops_file.h"
#include "util.h"
#include "log.h"
#include "inode.h"

#define BFITNOENT ((u32)~0)

/*
 * These routines are used by the resource group routines (rgrp.c)
 * to keep track of block allocation.  Each block is represented by two
 * bits.  So, each byte represents GFS2_NBBY (i.e. 4) blocks.
 *
 * 0 = Free
 * 1 = Used (not metadata)
 * 2 = Unlinked (still in use) inode
 * 3 = Used (metadata)
 */

static const char valid_change[16] = {
	        /* current */
	/* n */ 0, 1, 1, 1,
	/* e */ 1, 0, 0, 0,
	/* w */ 0, 0, 0, 1,
	        1, 0, 0, 0
};

static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
                        unsigned char old_state, unsigned char new_state);

/**
 * gfs2_setbit - Set a bit in the bitmaps
 * @buffer: the buffer that holds the bitmaps
 * @buflen: the length (in bytes) of the buffer
 * @block: the block to set
 * @new_state: the new state of the block
 *
 */

static void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
			unsigned int buflen, u32 block,
			unsigned char new_state)
{
	unsigned char *byte, *end, cur_state;
	unsigned int bit;

	byte = buffer + (block / GFS2_NBBY);
	bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
	end = buffer + buflen;

	gfs2_assert(rgd->rd_sbd, byte < end);

	cur_state = (*byte >> bit) & GFS2_BIT_MASK;

	if (valid_change[new_state * 4 + cur_state]) {
		*byte ^= cur_state << bit;
		*byte |= new_state << bit;
	} else
		gfs2_consist_rgrpd(rgd);
}

/**
 * gfs2_testbit - test a bit in the bitmaps
 * @buffer: the buffer that holds the bitmaps
 * @buflen: the length (in bytes) of the buffer
 * @block: the block to read
 *
 */

static unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
				  unsigned int buflen, u32 block)
{
	unsigned char *byte, *end, cur_state;
	unsigned int bit;

	byte = buffer + (block / GFS2_NBBY);
	bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
	end = buffer + buflen;

	gfs2_assert(rgd->rd_sbd, byte < end);

	cur_state = (*byte >> bit) & GFS2_BIT_MASK;

	return cur_state;
}

/**
 * gfs2_bitfit - Search an rgrp's bitmap buffer to find a bit-pair representing
 *       a block in a given allocation state.
 * @buffer: the buffer that holds the bitmaps
 * @buflen: the length (in bytes) of the buffer
 * @goal: start search at this block's bit-pair (within @buffer)
 * @old_state: GFS2_BLKST_XXX the state of the block we're looking for;
 *       bit 0 = alloc(1)/free(0), bit 1 = meta(1)/data(0)
 *
 * Scope of @goal and returned block number is only within this bitmap buffer,
 * not entire rgrp or filesystem.  @buffer will be offset from the actual
 * beginning of a bitmap block buffer, skipping any header structures.
 *
 * Return: the block number (bitmap buffer scope) that was found
 */

static u32 gfs2_bitfit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
			    unsigned int buflen, u32 goal,
			    unsigned char old_state)
{
	unsigned char *byte, *end, alloc;
	u32 blk = goal;
	unsigned int bit;

	byte = buffer + (goal / GFS2_NBBY);
	bit = (goal % GFS2_NBBY) * GFS2_BIT_SIZE;
	end = buffer + buflen;
	alloc = (old_state & 1) ? 0 : 0x55;

	while (byte < end) {
		if ((*byte & 0x55) == alloc) {
			blk += (8 - bit) >> 1;

			bit = 0;
			byte++;

			continue;
		}

		if (((*byte >> bit) & GFS2_BIT_MASK) == old_state)
			return blk;

		bit += GFS2_BIT_SIZE;
		if (bit >= 8) {
			bit = 0;
			byte++;
		}

		blk++;
	}

	return BFITNOENT;
}

/**
 * gfs2_bitcount - count the number of bits in a certain state
 * @buffer: the buffer that holds the bitmaps
 * @buflen: the length (in bytes) of the buffer
 * @state: the state of the block we're looking for
 *
 * Returns: The number of bits
 */

static u32 gfs2_bitcount(struct gfs2_rgrpd *rgd, unsigned char *buffer,
			      unsigned int buflen, unsigned char state)
{
	unsigned char *byte = buffer;
	unsigned char *end = buffer + buflen;
	unsigned char state1 = state << 2;
	unsigned char state2 = state << 4;
	unsigned char state3 = state << 6;
	u32 count = 0;

	for (; byte < end; byte++) {
		if (((*byte) & 0x03) == state)
			count++;
		if (((*byte) & 0x0C) == state1)
			count++;
		if (((*byte) & 0x30) == state2)
			count++;
		if (((*byte) & 0xC0) == state3)
			count++;
	}

	return count;
}

/**
 * gfs2_rgrp_verify - Verify that a resource group is consistent
 * @sdp: the filesystem
 * @rgd: the rgrp
 *
 */

void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd)
{
	struct gfs2_sbd *sdp = rgd->rd_sbd;
	struct gfs2_bitmap *bi = NULL;
	u32 length = rgd->rd_length;
	u32 count[4], tmp;
	int buf, x;

	memset(count, 0, 4 * sizeof(u32));

	/* Count # blocks in each of 4 possible allocation states */
	for (buf = 0; buf < length; buf++) {
		bi = rgd->rd_bits + buf;
		for (x = 0; x < 4; x++)
			count[x] += gfs2_bitcount(rgd,
						  bi->bi_bh->b_data +
						  bi->bi_offset,
						  bi->bi_len, x);
	}

	if (count[0] != rgd->rd_rg.rg_free) {
		if (gfs2_consist_rgrpd(rgd))
			fs_err(sdp, "free data mismatch:  %u != %u\n",
			       count[0], rgd->rd_rg.rg_free);
		return;
	}

	tmp = rgd->rd_data -
		rgd->rd_rg.rg_free -
		rgd->rd_rg.rg_dinodes;
	if (count[1] + count[2] != tmp) {
		if (gfs2_consist_rgrpd(rgd))
			fs_err(sdp, "used data mismatch:  %u != %u\n",
			       count[1], tmp);
		return;
	}

	if (count[3] != rgd->rd_rg.rg_dinodes) {
		if (gfs2_consist_rgrpd(rgd))
			fs_err(sdp, "used metadata mismatch:  %u != %u\n",
			       count[3], rgd->rd_rg.rg_dinodes);
		return;
	}

	if (count[2] > count[3]) {
		if (gfs2_consist_rgrpd(rgd))
			fs_err(sdp, "unlinked inodes > inodes:  %u\n",
			       count[2]);
		return;
	}

}

static inline int rgrp_contains_block(struct gfs2_rgrpd *rgd, u64 block)
{
	u64 first = rgd->rd_data0;
	u64 last = first + rgd->rd_data;
	return first <= block && block < last;
}

/**
 * gfs2_blk2rgrpd - Find resource group for a given data/meta block number
 * @sdp: The GFS2 superblock
 * @n: The data block number
 *
 * Returns: The resource group, or NULL if not found
 */

struct gfs2_rgrpd *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, u64 blk)
{
	struct gfs2_rgrpd *rgd;

	spin_lock(&sdp->sd_rindex_spin);

	list_for_each_entry(rgd, &sdp->sd_rindex_mru_list, rd_list_mru) {
		if (rgrp_contains_block(rgd, blk)) {
			list_move(&rgd->rd_list_mru, &sdp->sd_rindex_mru_list);
			spin_unlock(&sdp->sd_rindex_spin);
			return rgd;
		}
	}

	spin_unlock(&sdp->sd_rindex_spin);

	return NULL;
}

/**
 * gfs2_rgrpd_get_first - get the first Resource Group in the filesystem
 * @sdp: The GFS2 superblock
 *
 * Returns: The first rgrp in the filesystem
 */

struct gfs2_rgrpd *gfs2_rgrpd_get_first(struct gfs2_sbd *sdp)
{
	gfs2_assert(sdp, !list_empty(&sdp->sd_rindex_list));
	return list_entry(sdp->sd_rindex_list.next, struct gfs2_rgrpd, rd_list);
}

/**
 * gfs2_rgrpd_get_next - get the next RG
 * @rgd: A RG
 *
 * Returns: The next rgrp
 */

struct gfs2_rgrpd *gfs2_rgrpd_get_next(struct gfs2_rgrpd *rgd)
{
	if (rgd->rd_list.next == &rgd->rd_sbd->sd_rindex_list)
		return NULL;
	return list_entry(rgd->rd_list.next, struct gfs2_rgrpd, rd_list);
}

static void clear_rgrpdi(struct gfs2_sbd *sdp)
{
	struct list_head *head;
	struct gfs2_rgrpd *rgd;
	struct gfs2_glock *gl;

	spin_lock(&sdp->sd_rindex_spin);
	sdp->sd_rindex_forward = NULL;
	head = &sdp->sd_rindex_recent_list;
	while (!list_empty(head)) {
		rgd = list_entry(head->next, struct gfs2_rgrpd, rd_recent);
		list_del(&rgd->rd_recent);
	}
	spin_unlock(&sdp->sd_rindex_spin);

	head = &sdp->sd_rindex_list;
	while (!list_empty(head)) {
		rgd = list_entry(head->next, struct gfs2_rgrpd, rd_list);
		gl = rgd->rd_gl;

		list_del(&rgd->rd_list);
		list_del(&rgd->rd_list_mru);

		if (gl) {
			gl->gl_object = NULL;
			gfs2_glock_put(gl);
		}

		kfree(rgd->rd_bits);
		kfree(rgd);
	}
}

void gfs2_clear_rgrpd(struct gfs2_sbd *sdp)
{
	mutex_lock(&sdp->sd_rindex_mutex);
	clear_rgrpdi(sdp);
	mutex_unlock(&sdp->sd_rindex_mutex);
}

static void gfs2_rindex_print(const struct gfs2_rgrpd *rgd)
{
	printk(KERN_INFO "  ri_addr = %llu\n", (unsigned long long)rgd->rd_addr);
	printk(KERN_INFO "  ri_length = %u\n", rgd->rd_length);
	printk(KERN_INFO "  ri_data0 = %llu\n", (unsigned long long)rgd->rd_data0);
	printk(KERN_INFO "  ri_data = %u\n", rgd->rd_data);
	printk(KERN_INFO "  ri_bitbytes = %u\n", rgd->rd_bitbytes);
}

/**
 * gfs2_compute_bitstructs - Compute the bitmap sizes
 * @rgd: The resource group descriptor
 *
 * Calculates bitmap descriptors, one for each block that contains bitmap data
 *
 * Returns: errno
 */

static int compute_bitstructs(struct gfs2_rgrpd *rgd)
{
	struct gfs2_sbd *sdp = rgd->rd_sbd;
	struct gfs2_bitmap *bi;
	u32 length = rgd->rd_length; /* # blocks in hdr & bitmap */
	u32 bytes_left, bytes;
	int x;

	if (!length)
		return -EINVAL;

	rgd->rd_bits = kcalloc(length, sizeof(struct gfs2_bitmap), GFP_NOFS);
	if (!rgd->rd_bits)
		return -ENOMEM;

	bytes_left = rgd->rd_bitbytes;

	for (x = 0; x < length; x++) {
		bi = rgd->rd_bits + x;

		/* small rgrp; bitmap stored completely in header block */
		if (length == 1) {
			bytes = bytes_left;
			bi->bi_offset = sizeof(struct gfs2_rgrp);
			bi->bi_start = 0;
			bi->bi_len = bytes;
		/* header block */
		} else if (x == 0) {
			bytes = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_rgrp);
			bi->bi_offset = sizeof(struct gfs2_rgrp);
			bi->bi_start = 0;
			bi->bi_len = bytes;
		/* last block */
		} else if (x + 1 == length) {
			bytes = bytes_left;
			bi->bi_offset = sizeof(struct gfs2_meta_header);
			bi->bi_start = rgd->rd_bitbytes - bytes_left;
			bi->bi_len = bytes;
		/* other blocks */
		} else {
			bytes = sdp->sd_sb.sb_bsize -
				sizeof(struct gfs2_meta_header);
			bi->bi_offset = sizeof(struct gfs2_meta_header);
			bi->bi_start = rgd->rd_bitbytes - bytes_left;
			bi->bi_len = bytes;
		}

		bytes_left -= bytes;
	}

	if (bytes_left) {
		gfs2_consist_rgrpd(rgd);
		return -EIO;
	}
	bi = rgd->rd_bits + (length - 1);
	if ((bi->bi_start + bi->bi_len) * GFS2_NBBY != rgd->rd_data) {
		if (gfs2_consist_rgrpd(rgd)) {
			gfs2_rindex_print(rgd);
			fs_err(sdp, "start=%u len=%u offset=%u\n",
			       bi->bi_start, bi->bi_len, bi->bi_offset);
		}
		return -EIO;
	}

	return 0;
}

/**