/*
* 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"
#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
};
/**
* 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;
}
/**