Commit 18ec7d5c authored by Steven Whitehouse's avatar Steven Whitehouse

[GFS2] Make journaled data files identical to normal files on disk

This is a very large patch, with a few still to be resolved issues
so you might want to check out the previous head of the tree since
this is known to be unstable. Fixes for the various bugs will be
forthcoming shortly.

This patch removes the special data format which has been used
up till now for journaled data files. Directories still retain the
old format so that they will remain on disk compatible with earlier
releases. As a result you can now do the following with journaled
data files:

 1) mmap them
 2) export them over NFS
 3) convert to/from normal files whenever you want to (the zero length
    restriction is gone)

In addition the level at which GFS' locking is done has changed for all
files (since they all now use the page cache) such that the locking is
done at the page cache level rather than the level of the fs operations.
This should mean that things like loopback mounts and other things which
touch the page cache directly should now work.

Current known issues:

 1. There is a lock mode inversion problem related to the resource
    group hold function which needs to be resolved.
 2. Any significant amount of I/O causes an oops with an offset of hex 320
    (NULL pointer dereference) which appears to be related to a journaled data
    buffer appearing on a list where it shouldn't be.
 3. Direct I/O writes are disabled for the time being (will reappear later)
 4. There is probably a deadlock between the page lock and GFS' locks under
    certain combinations of mmap and fs operation I/O.
 5. Issue relating to ref counting on internally used inodes causes a hang
    on umount (discovered before this patch, and not fixed by it)
 6. One part of the directory metadata is different from GFS1 and will need
    to be resolved before next release.
Signed-off-by: Steven Whitehouse's avatarSteven Whitehouse <swhiteho@redhat.com>
parent 257f9b4e
......@@ -10,7 +10,6 @@ gfs2-y := \
glock.o \
glops.o \
inode.o \
jdata.o \
lm.o \
log.o \
lops.o \
......
......@@ -18,12 +18,12 @@
#include "bmap.h"
#include "glock.h"
#include "inode.h"
#include "jdata.h"
#include "meta_io.h"
#include "page.h"
#include "quota.h"
#include "rgrp.h"
#include "trans.h"
#include "dir.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
......@@ -90,7 +90,7 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer,
{
struct buffer_head *bh, *dibh;
uint64_t block = 0;
int journaled = gfs2_is_jdata(ip);
int isdir = gfs2_is_dir(ip);
int error;
down_write(&ip->i_rw_mutex);
......@@ -103,10 +103,10 @@ int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer,
/* Get a free block, fill it with the stuffed data,
and write it out to disk */
if (journaled) {
if (isdir) {
block = gfs2_alloc_meta(ip);
error = gfs2_jdata_get_buffer(ip, block, 1, &bh);
error = gfs2_dir_get_buffer(ip, block, 1, &bh);
if (error)
goto out_brelse;
gfs2_buffer_copy_tail(bh,
......@@ -168,7 +168,7 @@ static unsigned int calc_tree_height(struct gfs2_inode *ip, uint64_t size)
if (ip->i_di.di_size > size)
size = ip->i_di.di_size;
if (gfs2_is_jdata(ip)) {
if (gfs2_is_dir(ip)) {
arr = sdp->sd_jheightsize;
max = sdp->sd_max_jheight;
} else {
......@@ -377,7 +377,7 @@ static void lookup_block(struct gfs2_inode *ip, struct buffer_head *bh,
return;
if (height == ip->i_di.di_height - 1 &&
!gfs2_is_jdata(ip))
!gfs2_is_dir(ip))
*block = gfs2_alloc_data(ip);
else
*block = gfs2_alloc_meta(ip);
......@@ -430,7 +430,7 @@ int gfs2_block_map(struct gfs2_inode *ip, uint64_t lblock, int *new,
if (gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip)))
goto out;
bsize = (gfs2_is_jdata(ip)) ? sdp->sd_jbsize : sdp->sd_sb.sb_bsize;
bsize = (gfs2_is_dir(ip)) ? sdp->sd_jbsize : sdp->sd_sb.sb_bsize;
height = calc_tree_height(ip, (lblock + 1) * bsize);
if (ip->i_di.di_height < height) {
......@@ -618,7 +618,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,
sm->sm_first = 0;
}
metadata = (height != ip->i_di.di_height - 1) || gfs2_is_jdata(ip);
metadata = (height != ip->i_di.di_height - 1);
if (metadata)
revokes = (height) ? sdp->sd_inptrs : sdp->sd_diptrs;
......@@ -814,33 +814,6 @@ static int do_grow(struct gfs2_inode *ip, uint64_t size)
return error;
}
static int truncator_journaled(struct gfs2_inode *ip, uint64_t size)
{
uint64_t lbn, dbn;
uint32_t off;
struct buffer_head *bh;
int new = 0;
int error;
lbn = size;
off = do_div(lbn, ip->i_sbd->sd_jbsize);
error = gfs2_block_map(ip, lbn, &new, &dbn, NULL);
if (error || !dbn)
return error;
error = gfs2_jdata_get_buffer(ip, dbn, 0, &bh);
if (error)
return error;
gfs2_trans_add_bh(ip->i_gl, bh, 1);
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header) + off);
brelse(bh);
return 0;
}
static int trunc_start(struct gfs2_inode *ip, uint64_t size)
{
struct gfs2_sbd *sdp = ip->i_sbd;
......@@ -866,12 +839,7 @@ static int trunc_start(struct gfs2_inode *ip, uint64_t size)
error = 1;
} else {
if (journaled) {
uint64_t junk = size;
/* we're just interested in the modulus */
if (do_div(junk, sdp->sd_jbsize))
error = truncator_journaled(ip, size);
} else if (size & (uint64_t)(sdp->sd_sb.sb_bsize - 1))
if (size & (uint64_t)(sdp->sd_sb.sb_bsize - 1))
error = gfs2_block_truncate_page(ip->i_vnode->i_mapping);
if (!error) {
......@@ -900,10 +868,7 @@ static int trunc_dealloc(struct gfs2_inode *ip, uint64_t size)
if (!size)
lblock = 0;
else if (gfs2_is_jdata(ip)) {
lblock = size - 1;
do_div(lblock, ip->i_sbd->sd_jbsize);
} else
else
lblock = (size - 1) >> ip->i_sbd->sd_sb.sb_bsize_shift;
find_metapath(ip, lblock, &mp);
......@@ -1051,7 +1016,7 @@ void gfs2_write_calc_reserv(struct gfs2_inode *ip, unsigned int len,
struct gfs2_sbd *sdp = ip->i_sbd;
unsigned int tmp;
if (gfs2_is_jdata(ip)) {
if (gfs2_is_dir(ip)) {
*data_blocks = DIV_RU(len, sdp->sd_jbsize) + 2;
*ind_blocks = 3 * (sdp->sd_max_jheight - 1);
} else {
......@@ -1096,7 +1061,7 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, uint64_t offset,
return 0;
}
if (gfs2_is_jdata(ip)) {
if (gfs2_is_dir(ip)) {
unsigned int bsize = sdp->sd_jbsize;
lblock = offset;
do_div(lblock, bsize);
......
......@@ -86,8 +86,8 @@ typedef int (*leaf_call_t) (struct gfs2_inode *dip,
uint32_t index, uint32_t len, uint64_t leaf_no,
void *data);
static int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp)
int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp)
{
struct buffer_head *bh;
int error = 0;
......
......@@ -45,5 +45,7 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip);
int gfs2_diradd_alloc_required(struct gfs2_inode *dip, struct qstr *filename,
int *alloc_required);
int gfs2_dir_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp);
#endif /* __DIR_DOT_H__ */
......@@ -20,6 +20,11 @@ static inline int gfs2_is_jdata(struct gfs2_inode *ip)
return ip->i_di.di_flags & GFS2_DIF_JDATA;
}
static inline int gfs2_is_dir(struct gfs2_inode *ip)
{
return S_ISDIR(ip->i_di.di_mode);
}
void gfs2_inode_attr_in(struct gfs2_inode *ip);
void gfs2_inode_attr_out(struct gfs2_inode *ip);
struct inode *gfs2_ip2v_lookup(struct gfs2_inode *ip);
......@@ -72,9 +77,9 @@ static inline int gfs2_lookup_simple(struct inode *dip, char *name,
err = gfs2_lookupi(get_v2ip(dip), &qstr, 1, &ip);
if (err == 0) {
*ipp = gfs2_ip2v(ip);
gfs2_inode_put(ip);
if (*ipp == NULL)
err = -ENOMEM;
gfs2_inode_put(ip);
}
return err;
}
......
/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2005 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 v.2.
*/
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include "gfs2.h"
#include "bmap.h"
#include "inode.h"
#include "jdata.h"
#include "meta_io.h"
#include "trans.h"
int gfs2_internal_read(struct gfs2_inode *ip,
struct file_ra_state *ra_state,
char *buf, loff_t *pos, unsigned size)
{
return gfs2_jdata_read_mem(ip, buf, *pos, size);
}
int gfs2_jdata_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp)
{
struct buffer_head *bh;
int error = 0;
if (new) {
bh = gfs2_meta_new(ip->i_gl, block);
gfs2_trans_add_bh(ip->i_gl, bh, 1);
gfs2_metatype_set(bh, GFS2_METATYPE_JD, GFS2_FORMAT_JD);
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
} else {
error = gfs2_meta_read(ip->i_gl, block,
DIO_START | DIO_WAIT, &bh);
if (error)
return error;
if (gfs2_metatype_check(ip->i_sbd, bh, GFS2_METATYPE_JD)) {
brelse(bh);
return -EIO;
}
}
*bhp = bh;
return 0;
}
/**
* gfs2_copy2mem - Trivial copy function for gfs2_jdata_read()
* @bh: The buffer to copy from, or NULL meaning zero the buffer
* @buf: The buffer to copy/zero
* @offset: The offset in the buffer to copy from
* @size: The amount of data to copy/zero
*
* Returns: errno
*/
int gfs2_copy2mem(struct buffer_head *bh, char **buf, unsigned int offset,
unsigned int size)
{
if (bh)
memcpy(*buf, bh->b_data + offset, size);
else
memset(*buf, 0, size);
*buf += size;
return 0;
}
/**
* gfs2_copy2user - Copy bytes to user space for gfs2_jdata_read()
* @bh: The buffer
* @buf: The destination of the data
* @offset: The offset into the buffer
* @size: The amount of data to copy
*
* Returns: errno
*/
int gfs2_copy2user(struct buffer_head *bh, char **buf, unsigned int offset,
unsigned int size)
{
int error;
if (bh)
error = copy_to_user(*buf, bh->b_data + offset, size);
else
error = clear_user(*buf, size);
if (error)
error = -EFAULT;
else
*buf += size;
return error;
}
static int jdata_read_stuffed(struct gfs2_inode *ip, char *buf,
unsigned int offset, unsigned int size,
read_copy_fn_t copy_fn)
{
struct buffer_head *dibh;
int error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (!error) {
error = copy_fn(dibh, &buf,
offset + sizeof(struct gfs2_dinode), size);
brelse(dibh);
}
return (error) ? error : size;
}
/**
* gfs2_jdata_read - Read a jdata file
* @ip: The GFS2 Inode
* @buf: The buffer to place result into
* @offset: File offset to begin jdata_readng from
* @size: Amount of data to transfer
* @copy_fn: Function to actually perform the copy
*
* The @copy_fn only copies a maximum of a single block at once so
* we are safe calling it with int arguments. It is done so that
* we don't needlessly put 64bit arguments on the stack and it
* also makes the code in the @copy_fn nicer too.
*
* Returns: The amount of data actually copied or the error
*/
int gfs2_jdata_read(struct gfs2_inode *ip, char __user *buf, uint64_t offset,
unsigned int size, read_copy_fn_t copy_fn)
{
struct gfs2_sbd *sdp = ip->i_sbd;
uint64_t lblock, dblock;
uint32_t extlen = 0;
unsigned int o;
int copied = 0;
int error = 0;
if (offset >= ip->i_di.di_size)
return 0;
if ((offset + size) > ip->i_di.di_size)
size = ip->i_di.di_size - offset;
if (!size)
return 0;
if (gfs2_is_stuffed(ip))
return jdata_read_stuffed(ip, buf, (unsigned int)offset, size,
copy_fn);
if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip)))
return -EINVAL;
lblock = offset;
o = do_div(lblock, sdp->sd_jbsize) +
sizeof(struct gfs2_meta_header);
while (copied < size) {
unsigned int amount;
struct buffer_head *bh;
int new;
amount = size - copied;
if (amount > sdp->sd_sb.sb_bsize - o)
amount = sdp->sd_sb.sb_bsize - o;
if (!extlen) {
new = 0;
error = gfs2_block_map(ip, lblock, &new,
&dblock, &extlen);
if (error)
goto fail;
}
if (extlen > 1)
gfs2_meta_ra(ip->i_gl, dblock, extlen);
if (dblock) {
error = gfs2_jdata_get_buffer(ip, dblock, new, &bh);
if (error)
goto fail;
dblock++;
extlen--;
} else
bh = NULL;
error = copy_fn(bh, &buf, o, amount);
brelse(bh);
if (error)
goto fail;
copied += amount;
lblock++;
o = sizeof(struct gfs2_meta_header);
}
return copied;
fail:
return (copied) ? copied : error;
}
/**
* gfs2_copy_from_mem - Trivial copy function for gfs2_jdata_write()
* @bh: The buffer to copy to or clear
* @buf: The buffer to copy from
* @offset: The offset in the buffer to write to
* @size: The amount of data to write
*
* Returns: errno
*/
int gfs2_copy_from_mem(struct gfs2_inode *ip, struct buffer_head *bh,
const char **buf, unsigned int offset, unsigned int size)
{
gfs2_trans_add_bh(ip->i_gl, bh, 1);
memcpy(bh->b_data + offset, *buf, size);
*buf += size;
return 0;
}
/**
* gfs2_copy_from_user - Copy bytes from user space for gfs2_jdata_write()
* @bh: The buffer to copy to or clear
* @buf: The buffer to copy from
* @offset: The offset in the buffer to write to
* @size: The amount of data to write
*
* Returns: errno
*/
int gfs2_copy_from_user(struct gfs2_inode *ip, struct buffer_head *bh,
const char __user **buf, unsigned int offset, unsigned int size)
{
int error = 0;
gfs2_trans_add_bh(ip->i_gl, bh, 1);
if (copy_from_user(bh->b_data + offset, *buf, size))
error = -EFAULT;
else
*buf += size;
return error;
}
static int jdata_write_stuffed(struct gfs2_inode *ip, char *buf,
unsigned int offset, unsigned int size,
write_copy_fn_t copy_fn)
{
struct buffer_head *dibh;
int error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
error = copy_fn(ip,
dibh, &buf,
offset + sizeof(struct gfs2_dinode), size);
if (!error) {
if (ip->i_di.di_size < offset + size)
ip->i_di.di_size = offset + size;
ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
gfs2_dinode_out(&ip->i_di, dibh->b_data);
}
brelse(dibh);
return (error) ? error : size;
}
/**
* gfs2_jdata_write - Write bytes to a file
* @ip: The GFS2 inode
* @buf: The buffer containing information to be written
* @offset: The file offset to start writing at
* @size: The amount of data to write
* @copy_fn: Function to do the actual copying
*
* Returns: The number of bytes correctly written or error code
*/
int gfs2_jdata_write(struct gfs2_inode *ip, const char __user *buf, uint64_t offset,
unsigned int size, write_copy_fn_t copy_fn)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct buffer_head *dibh;
uint64_t lblock, dblock;
uint32_t extlen = 0;
unsigned int o;
int copied = 0;
int error = 0;
if (!size)
return 0;
if (gfs2_is_stuffed(ip) &&
offset + size <= sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode))
return jdata_write_stuffed(ip, buf, (unsigned int)offset, size,
copy_fn);
if (gfs2_assert_warn(sdp, gfs2_is_jdata(ip)))
return -EINVAL;
if (gfs2_is_stuffed(ip)) {
error = gfs2_unstuff_dinode(ip, NULL, NULL);
if (error)
return error;
}
lblock = offset;
o = do_div(lblock, sdp->sd_jbsize) + sizeof(struct gfs2_meta_header);
while (copied < size) {
unsigned int amount;
struct buffer_head *bh;
int new;
amount = size - copied;
if (amount > sdp->sd_sb.sb_bsize - o)
amount = sdp->sd_sb.sb_bsize - o;
if (!extlen) {
new = 1;
error = gfs2_block_map(ip, lblock, &new,
&dblock, &extlen);
if (error)
goto fail;
error = -EIO;
if (gfs2_assert_withdraw(sdp, dblock))
goto fail;
}
error = gfs2_jdata_get_buffer(ip, dblock,
(amount == sdp->sd_jbsize) ? 1 : new,
&bh);
if (error)
goto fail;
error = copy_fn(ip, bh, &buf, o, amount);
brelse(bh);
if (error)
goto fail;
copied += amount;
lblock++;
dblock++;
extlen--;
o = sizeof(struct gfs2_meta_header);
}
out:
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
if (ip->i_di.di_size < offset + copied)
ip->i_di.di_size = offset + copied;
ip->i_di.di_mtime = ip->i_di.di_ctime = get_seconds();
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(&ip->i_di, dibh->b_data);
brelse(dibh);
return copied;
fail:
if (copied)
goto out;
return error;
}
/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2005 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 v.2.
*/
#ifndef __FILE_DOT_H__
#define __FILE_DOT_H__
int gfs2_jdata_get_buffer(struct gfs2_inode *ip, uint64_t block, int new,
struct buffer_head **bhp);
typedef int (*read_copy_fn_t) (struct buffer_head *bh, char **buf,
unsigned int offset, unsigned int size);
typedef int (*write_copy_fn_t) (struct gfs2_inode *ip,
struct buffer_head *bh, const char **buf,
unsigned int offset, unsigned int size);
int gfs2_copy2mem(struct buffer_head *bh, char **buf,
unsigned int offset, unsigned int size);
int gfs2_copy2user(struct buffer_head *bh, char __user **buf,
unsigned int offset, unsigned int size);
int gfs2_jdata_read(struct gfs2_inode *ip, char __user *buf,
uint64_t offset, unsigned int size,
read_copy_fn_t copy_fn);
int gfs2_copy_from_mem(struct gfs2_inode *ip,
struct buffer_head *bh, const char **buf,
unsigned int offset, unsigned int size);
int gfs2_copy_from_user(struct gfs2_inode *ip,
struct buffer_head *bh, const char __user **buf,
unsigned int offset, unsigned int size);
int gfs2_jdata_write(struct gfs2_inode *ip, const char __user *buf,
uint64_t offset, unsigned int size,
write_copy_fn_t copy_fn);
static inline int gfs2_jdata_read_mem(struct gfs2_inode *ip, char *buf,
uint64_t offset, unsigned int size)
{
return gfs2_jdata_read(ip, (__force char __user *)buf, offset, size, gfs2_copy2mem);
}
static inline int gfs2_jdata_write_mem(struct gfs2_inode *ip, const char *buf,
uint64_t offset, unsigned int size)
{
return gfs2_jdata_write(ip, (__force const char __user *)buf, offset, size, gfs2_copy_from_mem);
}
#endif /* __FILE_DOT_H__ */
......@@ -387,8 +387,7 @@ struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
bh = lb->lb_bh = alloc_buffer_head(GFP_NOFS | __GFP_NOFAIL);
atomic_set(&bh->b_count, 1);
bh->b_state = (1 << BH_Mapped) | (1 << BH_Uptodate);
set_bh_page(bh, virt_to_page(real->b_data),
((unsigned long)real->b_data) & (PAGE_SIZE - 1));
set_bh_page(bh, real->b_page, bh_offset(real));
bh->b_blocknr = blkno;
bh->b_size = sdp->sd_sb.sb_bsize;
bh->b_bdev = sdp->sd_vfs->s_bdev;
......@@ -634,6 +633,7 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
gfs2_assert_withdraw(sdp, !sdp->sd_log_blks_reserved);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_gl);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_buf);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_jdata);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf);
......
......@@ -428,49 +428,188 @@ static void rg_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
gfs2_assert_warn(sdp, !sdp->sd_log_num_rg);
}
/**
* databuf_lo_add - Add a databuf to the transaction.
*
* This is used in two distinct cases:
* i) In ordered write mode
* We put the data buffer on a list so that we can ensure that its
* synced to disk at the right time
* ii) In journaled data mode
* We need to journal the data block in the same way as metadata in
* the functions above. The difference is that here we have a tag
* which is two __be64's being the block number (as per meta data)
* and a flag which says whether the data block needs escaping or
* not. This means we need a new log entry for each 251 or so data
* blocks, which isn't an enormous overhead but twice as much as
* for normal metadata blocks.
*/
static void databuf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
get_transaction->tr_touched = 1;
struct gfs2_bufdata *bd = container_of(le, struct gfs2_bufdata, bd_le);
struct gfs2_trans *tr = get_transaction;
struct address_space *mapping = bd->bd_bh->b_page->mapping;
struct gfs2_inode *ip = get_v2ip(mapping->host);
tr->tr_touched = 1;
if (!list_empty(&bd->bd_list_tr) &&
(ip->i_di.di_flags & GFS2_DIF_JDATA)) {
tr->tr_num_buf++;
gfs2_trans_add_gl(bd->bd_gl);
list_add(&bd->bd_list_tr, &tr->tr_list_buf);
gfs2_pin(sdp, bd->bd_bh);
} else {
clear_buffer_pinned(bd->bd_bh);
}
gfs2_log_lock(sdp);
if (ip->i_di.di_flags & GFS2_DIF_JDATA)
sdp->sd_log_num_jdata++;
sdp->sd_log_num_databuf++;
list_add(&le->le_list, &sdp->sd_log_le_databuf);
gfs2_log_unlock(sdp);
}
static int gfs2_check_magic(struct buffer_head *bh)
{
struct page *page = bh->b_page;
void *kaddr;
__be32 *ptr;
int rv = 0;
kaddr = kmap_atomic(page, KM_USER0);
ptr = kaddr + bh_offset(bh);
if (*ptr == cpu_to_be32(GFS2_MAGIC))
rv = 1;
kunmap_atomic(page, KM_USER0);
return rv;
}
/**
* databuf_lo_before_commit - Scan the data buffers, writing as we go
*
* Here we scan through the lists of buffers and make the assumption
* that any buffer thats been pinned is being journaled, and that
* any unpinned buffer is an ordered write data buffer and therefore
* will be written back rather than journaled.
*/
static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
{
struct list_head *head = &sdp->sd_log_le_databuf;
LIST_HEAD(started);
struct gfs2_bufdata *bd;
struct buffer_head *bh;
struct gfs2_bufdata *bd1 = NULL, *bd2, *bdt;
struct buffer_head *bh = NULL;
unsigned int offset = sizeof(struct gfs2_log_descriptor);
struct gfs2_log_descriptor *ld;
unsigned int limit;