/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2006 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/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/blkdev.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
#include "gfs2.h"
#include "incore.h"
#include "bmap.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "quota.h"
#include "rgrp.h"
#include "log.h"
#include "super.h"
#include "trans.h"
#include "dir.h"
#include "util.h"
#include "trace_gfs2.h"
/* This doesn't need to be that large as max 64 bit pointers in a 4k
* block is 512, so __u16 is fine for that. It saves stack space to
* keep it small.
*/
struct metapath {
struct buffer_head *mp_bh[GFS2_MAX_META_HEIGHT];
__u16 mp_list[GFS2_MAX_META_HEIGHT];
};
struct strip_mine {
int sm_first;
unsigned int sm_height;
};
/**
* gfs2_unstuffer_page - unstuff a stuffed inode into a block cached by a page
* @ip: the inode
* @dibh: the dinode buffer
* @block: the block number that was allocated
* @page: The (optional) page. This is looked up if @page is NULL
*
* Returns: errno
*/
static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
u64 block, struct page *page)
{
struct inode *inode = &ip->i_inode;
struct buffer_head *bh;
int release = 0;
if (!page || page->index) {
page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
if (!page)
return -ENOMEM;
release = 1;
}
if (!PageUptodate(page)) {
void *kaddr = kmap(page);
u64 dsize = i_size_read(inode);
if (dsize > (dibh->b_size - sizeof(struct gfs2_dinode)))
dsize = dibh->b_size - sizeof(struct gfs2_dinode);
memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize);
memset(kaddr + dsize, 0, PAGE_CACHE_SIZE - dsize);
kunmap(page);
SetPageUptodate(page);
}
if (!page_has_buffers(page))
create_empty_buffers(page, 1 << inode->i_blkbits,
(1 << BH_Uptodate));
bh = page_buffers(page);
if (!buffer_mapped(bh))
map_bh(bh, inode->i_sb, block);
set_buffer_uptodate(bh);
if (!gfs2_is_jdata(ip))
mark_buffer_dirty(bh);
if (!gfs2_is_writeback(ip))
gfs2_trans_add_data(ip->i_gl, bh);
if (release) {
unlock_page(page);
page_cache_release(page);
}
return 0;
}
/**
* gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big
* @ip: The GFS2 inode to unstuff
* @page: The (optional) page. This is looked up if the @page is NULL
*
* This routine unstuffs a dinode and returns it to a "normal" state such
* that the height can be grown in the traditional way.
*
* Returns: errno
*/
int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page)
{
struct buffer_head *bh, *dibh;
struct gfs2_dinode *di;
u64 block = 0;
int isdir = gfs2_is_dir(ip);
int error;
down_write(&ip->i_rw_mutex);
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
goto out;
if (i_size_read(&ip->i_inode)) {
/* Get a free block, fill it with the stuffed data,
and write it out to disk */
unsigned int n = 1;
error = gfs2_alloc_blocks(ip, &block, &n, 0, NULL);
if (error)
goto out_brelse;
if (isdir) {
gfs2_trans_add_unrevoke(GFS2_SB(&ip->i_inode), block, 1);
error = gfs2_dir_get_new_buffer(ip, block, &bh);
if (error)
goto out_brelse;
gfs2_buffer_copy_tail(bh, sizeof(struct gfs2_meta_header),
dibh, sizeof(struct gfs2_dinode));
brelse(bh);
} else {
error = gfs2_unstuffer_page(ip, dibh, block, page);
if (error)
goto out_brelse;
}
}
/* Set up the pointer to the new block */
gfs2_trans_add_meta(ip->i_gl, dibh);
di = (struct gfs2_dinode *)dibh->b_data;
gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode));
if (i_size_read(&ip->i_inode)) {
*(__be64 *)(di + 1) = cpu_to_be64(block);
gfs2_add_inode_blocks(&ip->i_inode, 1);
di->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode));
}
ip->i_height = 1;
di->di_height = cpu_to_be16(1);
out_brelse:
brelse(dibh);
out:
up_write(&ip->i_rw_mutex);
return error;
}
/**
* find_metapath - Find path through the metadata tree
* @sdp: The superblock
* @mp: The metapath to return the result in
* @block: The disk block to look up
* @height: The pre-calculated height of the metadata tree
*
* This routine returns a struct metapath structure that defines a path
* through the metadata of inode "ip" to get to block "block".
*
* Example:
* Given: "ip" is a height 3 file, "offset" is 101342453, and this is a
* filesystem with a blocksize of 4096.
*
* find_metapath() would return a struct metapath structure set to:
* mp_offset = 101342453, mp_height = 3, mp_list[0] = 0, mp_list[1] = 48,
* and mp_list[2] = 165.
*
* That means that in order to get to the block containing the byte at
* offset 101342453, we would load the indirect block pointed to by pointer
* 0 in the dinode. We would then load the indirect block pointed to by
* pointer 48 in that indirect block. We would then load the data block
* pointed to by pointer 165 in that indirect block.
*
* ----------------------------------------
* | Dinode | |
* | | 4|
* | |0 1 2 3 4 5 9|
* | | 6|
* ----------------------------------------
* |
* |
* V
* ----------------------------------------
* | Indirect Block |
* | 5|
* | 4 4 4 4 4 5 5 1|
* |0 5 6 7 8 9 0 1 2|
* ----------------------------------------
* |
* |
* V
* ----------------------------------------
* | Indirect Block |
* | 1 1 1 1 1 5|
* | 6 6 6 6 6 1|
* |0 3 4 5 6 7 2|
* ----------------------------------------
* |
* |
* V
* ----------------------------------------
* | Data block containing offset |
* | 101342453 |
* | |
* | |
* ----------------------------------------
*
*/
static void find_metapath(const struct gfs2_sbd *sdp, u64 block,
struct metapath *mp, unsigned int height)
{
unsigned int i;
for (i = height; i--;)
mp->mp_list[i] = do_div(block, sdp->sd_inptrs);
}
static inline unsigned int metapath_branch_start(const struct metapath *mp)
{
if (mp->mp_list[0] == 0)
return 2;
return 1;
}
/**
* metapointer - Return pointer to start of metadata in a buffer
* @height: The metadata height (0 = dinode)
* @mp: The metapath
*
* Return a pointer to the block number of the next height of the metadata
* tree given a buffer containing the pointer to the current height of the
* metadata tree.
*/
static inline __be64 *metapointer(unsigned int height, const struct metapath *mp)
{
struct buffer_head *bh = mp->mp_bh[height];
unsigned int head_size = (height > 0) ?
sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode);
return ((__be64 *)(bh->b_data + head_size)) + mp->mp_list[height];
}
static void gfs2_metapath_ra(struct gfs2_glock *gl,
const struct buffer_head *bh, const __be64 *pos)
{
struct buffer_head *rabh;
const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size);
const __be64 *t;
for (t = pos; t < endp; t++) {
if (!*t)
continue;
rabh = gfs2_getbuf(gl, be64_to_cpu(*t), CREATE);
if (trylock_buffer(rabh)) {
if (!buffer_uptodate(rabh)) {
rabh->b_end_io = end_buffer_read_sync;
submit_bh(READA | REQ_META, rabh);
continue;
}
unlock_buffer(rabh);
}
brelse(rabh);
}
}
/**
* lookup_metapath - Walk the metadata tree to a specific point
* @ip: The inode
* @mp: The metapath
*
* Assumes that the inode's buffer has already been looked up and
* hooked onto mp->mp_bh[0] and that the metapath has been initialised
* by find_metapath().
*
* If this function encounters part of the tree which has
|