[GFS2] The core of GFS2

This patch contains all the core files for GFS2.

Signed-off-by: David Teigland <teigland@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
This commit is contained in:
David Teigland 2006-01-16 16:50:04 +00:00 committed by Steven Whitehouse
parent f7825dcf8c
commit b3b94faa5f
81 changed files with 29258 additions and 0 deletions

46
fs/gfs2/Kconfig Normal file
View File

@ -0,0 +1,46 @@
config GFS2_FS
tristate "GFS2 file system support"
default m
depends on EXPERIMENTAL
select FS_POSIX_ACL
select SYSFS
help
A cluster filesystem.
Allows a cluster of computers to simultaneously use a block device
that is shared between them (with FC, iSCSI, NBD, etc...). GFS reads
and writes to the block device like a local filesystem, but also uses
a lock module to allow the computers coordinate their I/O so
filesystem consistency is maintained. One of the nifty features of
GFS is perfect consistency -- changes made to the filesystem on one
machine show up immediately on all other machines in the cluster.
To use the GFS2 filesystem, you will need to enable one or more of
the below locking modules. Documentation and utilities for GFS2 can
be found here: http://sources.redhat.com/cluster/gfs/
config GFS2_FS_LOCKING_NOLOCK
tristate "GFS2 \"nolock\" locking module"
depends on GFS2_FS
help
Single node locking module for GFS2.
Use this module if you want to use GFS2 on a single node without
its clustering features. You can still take advantage of the
large file support, and upgrade to running a full cluster later on
if required.
If you will only be using GFS2 in cluster mode, you do not need this
module.
config GFS2_FS_LOCKING_DLM
tristate "GFS2 DLM locking module"
depends on GFS2_FS
select DLM
help
Multiple node locking module for GFS2
Most users of GFS2 will require this module. It provides the locking
interface between GFS2 and the DLM, which is required to use GFS2
in a cluster environment.

44
fs/gfs2/Makefile Normal file
View File

@ -0,0 +1,44 @@
obj-$(CONFIG_GFS2_FS) += gfs2.o
gfs2-y := \
acl.o \
bits.o \
bmap.o \
daemon.o \
dir.o \
eaops.o \
eattr.o \
glock.o \
glops.o \
inode.o \
jdata.o \
lm.o \
log.o \
lops.o \
locking.o \
lvb.o \
main.o \
meta_io.o \
mount.o \
ondisk.o \
ops_address.o \
ops_dentry.o \
ops_export.o \
ops_file.o \
ops_fstype.o \
ops_inode.o \
ops_super.o \
ops_vm.o \
page.o \
quota.o \
resize.o \
recovery.o \
rgrp.o \
super.o \
sys.o \
trans.o \
unlinked.o \
util.o
obj-$(CONFIG_GFS2_LOCKING_NOLOCK) += locking/nolock/
obj-$(CONFIG_GFS2_LOCKING_DLM) += locking/dlm/

312
fs/gfs2/acl.c Normal file
View File

@ -0,0 +1,312 @@
/*
* 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 <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "acl.h"
#include "eaops.h"
#include "eattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "trans.h"
#define ACL_ACCESS 1
#define ACL_DEFAULT 0
int gfs2_acl_validate_set(struct gfs2_inode *ip, int access,
struct gfs2_ea_request *er,
int *remove, mode_t *mode)
{
struct posix_acl *acl;
int error;
error = gfs2_acl_validate_remove(ip, access);
if (error)
return error;
if (!er->er_data)
return -EINVAL;
acl = posix_acl_from_xattr(er->er_data, er->er_data_len);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (!acl) {
*remove = 1;
return 0;
}
error = posix_acl_valid(acl);
if (error)
goto out;
if (access) {
error = posix_acl_equiv_mode(acl, mode);
if (!error)
*remove = 1;
else if (error > 0)
error = 0;
}
out:
posix_acl_release(acl);
return error;
}
int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access)
{
if (!ip->i_sbd->sd_args.ar_posix_acl)
return -EOPNOTSUPP;
if (current->fsuid != ip->i_di.di_uid && !capable(CAP_FOWNER))
return -EPERM;
if (S_ISLNK(ip->i_di.di_mode))
return -EOPNOTSUPP;
if (!access && !S_ISDIR(ip->i_di.di_mode))
return -EACCES;
return 0;
}
static int acl_get(struct gfs2_inode *ip, int access, struct posix_acl **acl,
struct gfs2_ea_location *el, char **data, unsigned int *len)
{
struct gfs2_ea_request er;
struct gfs2_ea_location el_this;
int error;
if (!ip->i_di.di_eattr)
return 0;
memset(&er, 0, sizeof(struct gfs2_ea_request));
if (access) {
er.er_name = GFS2_POSIX_ACL_ACCESS;
er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN;
} else {
er.er_name = GFS2_POSIX_ACL_DEFAULT;
er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN;
}
er.er_type = GFS2_EATYPE_SYS;
if (!el)
el = &el_this;
error = gfs2_ea_find(ip, &er, el);
if (error)
return error;
if (!el->el_ea)
return 0;
if (!GFS2_EA_DATA_LEN(el->el_ea))
goto out;
er.er_data_len = GFS2_EA_DATA_LEN(el->el_ea);
er.er_data = kmalloc(er.er_data_len, GFP_KERNEL);
error = -ENOMEM;
if (!er.er_data)
goto out;
error = gfs2_ea_get_copy(ip, el, er.er_data);
if (error)
goto out_kfree;
if (acl) {
*acl = posix_acl_from_xattr(er.er_data, er.er_data_len);
if (IS_ERR(*acl))
error = PTR_ERR(*acl);
}
out_kfree:
if (error || !data)
kfree(er.er_data);
else {
*data = er.er_data;
*len = er.er_data_len;
}
out:
if (error || el == &el_this)
brelse(el->el_bh);
return error;
}
/**
* gfs2_check_acl_locked - Check an ACL to see if we're allowed to do something
* @inode: the file we want to do something to
* @mask: what we want to do
*
* Returns: errno
*/
int gfs2_check_acl_locked(struct inode *inode, int mask)
{
struct posix_acl *acl = NULL;
int error;
error = acl_get(get_v2ip(inode), ACL_ACCESS, &acl, NULL, NULL, NULL);
if (error)
return error;
if (acl) {
error = posix_acl_permission(inode, acl, mask);
posix_acl_release(acl);
return error;
}
return -EAGAIN;
}
int gfs2_check_acl(struct inode *inode, int mask)
{
struct gfs2_inode *ip = get_v2ip(inode);
struct gfs2_holder i_gh;
int error;
error = gfs2_glock_nq_init(ip->i_gl,
LM_ST_SHARED, LM_FLAG_ANY,
&i_gh);
if (!error) {
error = gfs2_check_acl_locked(inode, mask);
gfs2_glock_dq_uninit(&i_gh);
}
return error;
}
static int munge_mode(struct gfs2_inode *ip, mode_t mode)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct buffer_head *dibh;
int error;
error = gfs2_trans_begin(sdp, RES_DINODE, 0);
if (error)
return error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (!error) {
gfs2_assert_withdraw(sdp,
(ip->i_di.di_mode & S_IFMT) == (mode & S_IFMT));
ip->i_di.di_mode = mode;
gfs2_trans_add_bh(ip->i_gl, dibh);
gfs2_dinode_out(&ip->i_di, dibh->b_data);
brelse(dibh);
}
gfs2_trans_end(sdp);
return 0;
}
int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
{
struct gfs2_sbd *sdp = dip->i_sbd;
struct posix_acl *acl = NULL, *clone;
struct gfs2_ea_request er;
mode_t mode = ip->i_di.di_mode;
int error;
if (!sdp->sd_args.ar_posix_acl)
return 0;
if (S_ISLNK(ip->i_di.di_mode))
return 0;
memset(&er, 0, sizeof(struct gfs2_ea_request));
er.er_type = GFS2_EATYPE_SYS;
error = acl_get(dip, ACL_DEFAULT, &acl, NULL,
&er.er_data, &er.er_data_len);
if (error)
return error;
if (!acl) {
mode &= ~current->fs->umask;
if (mode != ip->i_di.di_mode)
error = munge_mode(ip, mode);
return error;
}
clone = posix_acl_clone(acl, GFP_KERNEL);
error = -ENOMEM;
if (!clone)
goto out;
posix_acl_release(acl);
acl = clone;
if (S_ISDIR(ip->i_di.di_mode)) {
er.er_name = GFS2_POSIX_ACL_DEFAULT;
er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN;
error = gfs2_system_eaops.eo_set(ip, &er);
if (error)
goto out;
}
error = posix_acl_create_masq(acl, &mode);
if (error < 0)
goto out;
if (error > 0) {
er.er_name = GFS2_POSIX_ACL_ACCESS;
er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN;
posix_acl_to_xattr(acl, er.er_data, er.er_data_len);
er.er_mode = mode;
er.er_flags = GFS2_ERF_MODE;
error = gfs2_system_eaops.eo_set(ip, &er);
if (error)
goto out;
} else
munge_mode(ip, mode);
out:
posix_acl_release(acl);
kfree(er.er_data);
return error;
}
int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
{
struct posix_acl *acl = NULL, *clone;
struct gfs2_ea_location el;
char *data;
unsigned int len;
int error;
error = acl_get(ip, ACL_ACCESS, &acl, &el, &data, &len);
if (error)
return error;
if (!acl)
return gfs2_setattr_simple(ip, attr);
clone = posix_acl_clone(acl, GFP_KERNEL);
error = -ENOMEM;
if (!clone)
goto out;
posix_acl_release(acl);
acl = clone;
error = posix_acl_chmod_masq(acl, attr->ia_mode);
if (!error) {
posix_acl_to_xattr(acl, data, len);
error = gfs2_ea_acl_chmod(ip, &el, attr, data);
}
out:
posix_acl_release(acl);
brelse(el.el_bh);
kfree(data);
return error;
}

37
fs/gfs2/acl.h Normal file
View File

@ -0,0 +1,37 @@
/*
* 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 __ACL_DOT_H__
#define __ACL_DOT_H__
#define GFS2_POSIX_ACL_ACCESS "posix_acl_access"
#define GFS2_POSIX_ACL_ACCESS_LEN 16
#define GFS2_POSIX_ACL_DEFAULT "posix_acl_default"
#define GFS2_POSIX_ACL_DEFAULT_LEN 17
#define GFS2_ACL_IS_ACCESS(name, len) \
((len) == GFS2_POSIX_ACL_ACCESS_LEN && \
!memcmp(GFS2_POSIX_ACL_ACCESS, (name), (len)))
#define GFS2_ACL_IS_DEFAULT(name, len) \
((len) == GFS2_POSIX_ACL_DEFAULT_LEN && \
!memcmp(GFS2_POSIX_ACL_DEFAULT, (name), (len)))
struct gfs2_ea_request;
int gfs2_acl_validate_set(struct gfs2_inode *ip, int access,
struct gfs2_ea_request *er,
int *remove, mode_t *mode);
int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access);
int gfs2_check_acl_locked(struct inode *inode, int mask);
int gfs2_check_acl(struct inode *inode, int mask);
int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip);
int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr);
#endif /* __ACL_DOT_H__ */

178
fs/gfs2/bits.c Normal file
View File

@ -0,0 +1,178 @@
/*
* 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.
*/
/*
* These routines are used by the resource group routines (rgrp.c)
* to keep track of block allocation. Each block is represented by two
* bits. One bit indicates whether or not the block is used. (1=used,
* 0=free) The other bit indicates whether or not the block contains a
* dinode or not. (1=dinode, 0=not-dinode) So, each byte represents
* GFS2_NBBY (i.e. 4) blocks.
*/
#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 "gfs2.h"
#include "bits.h"
static const char valid_change[16] = {
/* current */
/* n */ 0, 1, 0, 1,
/* e */ 1, 0, 0, 0,
/* w */ 0, 0, 0, 0,
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
*
*/
void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
unsigned int buflen, uint32_t 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
*
*/
unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
unsigned int buflen, uint32_t 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
*/
uint32_t gfs2_bitfit(struct gfs2_rgrpd *rgd, unsigned char *buffer,
unsigned int buflen, uint32_t goal,
unsigned char old_state)
{
unsigned char *byte, *end, alloc;
uint32_t 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
*/
uint32_t 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;
uint32_t 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;
}

28
fs/gfs2/bits.h Normal file
View File

@ -0,0 +1,28 @@
/*
* 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 __BITS_DOT_H__
#define __BITS_DOT_H__
#define BFITNOENT 0xFFFFFFFF
void gfs2_setbit(struct gfs2_rgrpd *rgd,
unsigned char *buffer, unsigned int buflen,
uint32_t block, unsigned char new_state);
unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd,
unsigned char *buffer, unsigned int buflen,
uint32_t block);
uint32_t gfs2_bitfit(struct gfs2_rgrpd *rgd,
unsigned char *buffer, unsigned int buflen,
uint32_t goal, unsigned char old_state);
uint32_t gfs2_bitcount(struct gfs2_rgrpd *rgd,
unsigned char *buffer, unsigned int buflen,
unsigned char state);
#endif /* __BITS_DOT_H__ */

1206
fs/gfs2/bmap.c Normal file

File diff suppressed because it is too large Load Diff

39
fs/gfs2/bmap.h Normal file
View File

@ -0,0 +1,39 @@
/*
* 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 __BMAP_DOT_H__
#define __BMAP_DOT_H__
typedef int (*gfs2_unstuffer_t) (struct gfs2_inode * ip,
struct buffer_head * dibh, uint64_t block,
void *private);
int gfs2_unstuffer_sync(struct gfs2_inode *ip, struct buffer_head *dibh,
uint64_t block, void *private);
int gfs2_unstuff_dinode(struct gfs2_inode *ip, gfs2_unstuffer_t unstuffer,
void *private);
int gfs2_block_map(struct gfs2_inode *ip,
uint64_t lblock, int *new,
uint64_t *dblock, uint32_t *extlen);
typedef int (*gfs2_truncator_t) (struct gfs2_inode * ip, uint64_t size);
int gfs2_truncatei(struct gfs2_inode *ip, uint64_t size,
gfs2_truncator_t truncator);
int gfs2_truncatei_resume(struct gfs2_inode *ip);
int gfs2_file_dealloc(struct gfs2_inode *ip);
void gfs2_write_calc_reserv(struct gfs2_inode *ip, unsigned int len,
unsigned int *data_blocks,
unsigned int *ind_blocks);
int gfs2_write_alloc_required(struct gfs2_inode *ip, uint64_t offset,
unsigned int len, int *alloc_required);
int gfs2_get_file_meta(struct gfs2_inode *ip, struct gfs2_user_buffer *ub);
#endif /* __BMAP_DOT_H__ */

225
fs/gfs2/daemon.c Normal file
View File

@ -0,0 +1,225 @@
/*
* 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 <linux/kthread.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "daemon.h"
#include "glock.h"
#include "log.h"
#include "quota.h"
#include "recovery.h"
#include "super.h"
#include "unlinked.h"
/* This uses schedule_timeout() instead of msleep() because it's good for
the daemons to wake up more often than the timeout when unmounting so
the user's unmount doesn't sit there forever.
The kthread functions used to start these daemons block and flush signals. */
/**
* gfs2_scand - Look for cached glocks and inodes to toss from memory
* @sdp: Pointer to GFS2 superblock
*
* One of these daemons runs, finding candidates to add to sd_reclaim_list.
* See gfs2_glockd()
*/
int gfs2_scand(void *data)
{
struct gfs2_sbd *sdp = (struct gfs2_sbd *)data;
unsigned long t;
while (!kthread_should_stop()) {
gfs2_scand_internal(sdp);
t = gfs2_tune_get(sdp, gt_scand_secs) * HZ;
schedule_timeout_interruptible(t);
}
return 0;
}
/**
* gfs2_glockd - Reclaim unused glock structures
* @sdp: Pointer to GFS2 superblock
*
* One or more of these daemons run, reclaiming glocks on sd_reclaim_list.
* Number of daemons can be set by user, with num_glockd mount option.
*/
int gfs2_glockd(void *data)
{
struct gfs2_sbd *sdp = (struct gfs2_sbd *)data;
DECLARE_WAITQUEUE(wait_chan, current);
while (!kthread_should_stop()) {
while (atomic_read(&sdp->sd_reclaim_count))
gfs2_reclaim_glock(sdp);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&sdp->sd_reclaim_wq, &wait_chan);
if (!atomic_read(&sdp->sd_reclaim_count) &&
!kthread_should_stop())
schedule();
remove_wait_queue(&sdp->sd_reclaim_wq, &wait_chan);
set_current_state(TASK_RUNNING);
}
return 0;
}
/**
* gfs2_recoverd - Recover dead machine's journals
* @sdp: Pointer to GFS2 superblock
*
*/
int gfs2_recoverd(void *data)
{
struct gfs2_sbd *sdp = (struct gfs2_sbd *)data;
unsigned long t;
while (!kthread_should_stop()) {
gfs2_check_journals(sdp);
t = gfs2_tune_get(sdp, gt_recoverd_secs) * HZ;
schedule_timeout_interruptible(t);
}
return 0;
}
/**
* gfs2_logd - Update log tail as Active Items get flushed to in-place blocks
* @sdp: Pointer to GFS2 superblock
*
* Also, periodically check to make sure that we're using the most recent
* journal index.
*/
int gfs2_logd(void *data)
{
struct gfs2_sbd *sdp = (struct gfs2_sbd *)data;
struct gfs2_holder ji_gh;
unsigned long t;
while (!kthread_should_stop()) {
/* Advance the log tail */
t = sdp->sd_log_flush_time +
gfs2_tune_get(sdp, gt_log_flush_secs) * HZ;
gfs2_ail1_empty(sdp, DIO_ALL);
if (time_after_eq(jiffies, t)) {
gfs2_log_flush(sdp);
sdp->sd_log_flush_time = jiffies;
}
/* Check for latest journal index */
t = sdp->sd_jindex_refresh_time +
gfs2_tune_get(sdp, gt_jindex_refresh_secs) * HZ;
if (time_after_eq(jiffies, t)) {
if (!gfs2_jindex_hold(sdp, &ji_gh))
gfs2_glock_dq_uninit(&ji_gh);
sdp->sd_jindex_refresh_time = jiffies;
}
t = gfs2_tune_get(sdp, gt_logd_secs) * HZ;
schedule_timeout_interruptible(t);
}
return 0;
}
/**
* gfs2_quotad - Write cached quota changes into the quota file
* @sdp: Pointer to GFS2 superblock
*
*/
int gfs2_quotad(void *data)
{
struct gfs2_sbd *sdp = (struct gfs2_sbd *)data;
unsigned long t;
int error;
while (!kthread_should_stop()) {
/* Update the master statfs file */
t = sdp->sd_statfs_sync_time +
gfs2_tune_get(sdp, gt_statfs_quantum) * HZ;
if (time_after_eq(jiffies, t)) {
error = gfs2_statfs_sync(sdp);
if (error &&
error != -EROFS &&
!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
fs_err(sdp, "quotad: (1) error=%d\n", error);
sdp->sd_statfs_sync_time = jiffies;
}
/* Update quota file */
t = sdp->sd_quota_sync_time +
gfs2_tune_get(sdp, gt_quota_quantum) * HZ;
if (time_after_eq(jiffies, t)) {
error = gfs2_quota_sync(sdp);
if (error &&
error != -EROFS &&
!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
fs_err(sdp, "quotad: (2) error=%d\n", error);
sdp->sd_quota_sync_time = jiffies;
}
gfs2_quota_scan(sdp);
t = gfs2_tune_get(sdp, gt_quotad_secs) * HZ;
schedule_timeout_interruptible(t);
}
return 0;
}
/**
* gfs2_inoded - Deallocate unlinked inodes
* @sdp: Pointer to GFS2 superblock
*
*/
int gfs2_inoded(void *data)
{
struct gfs2_sbd *sdp = (struct gfs2_sbd *)data;
unsigned long t;
int error;
while (!kthread_should_stop()) {
error = gfs2_unlinked_dealloc(sdp);
if (error &&
error != -EROFS &&
!test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
fs_err(sdp, "inoded: error = %d\n", error);
t = gfs2_tune_get(sdp, gt_inoded_secs) * HZ;
schedule_timeout_interruptible(t);
}
return 0;
}

20
fs/gfs2/daemon.h Normal file
View File

@ -0,0 +1,20 @@
/*
* 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 __DAEMON_DOT_H__
#define __DAEMON_DOT_H__
int gfs2_scand(void *data);
int gfs2_glockd(void *data);
int gfs2_recoverd(void *data);
int gfs2_logd(void *data);
int gfs2_quotad(void *data);
int gfs2_inoded(void *data);
#endif /* __DAEMON_DOT_H__ */

2157
fs/gfs2/dir.c Normal file

File diff suppressed because it is too large Load Diff

51
fs/gfs2/dir.h Normal file
View File

@ -0,0 +1,51 @@
/*
* 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 __DIR_DOT_H__
#define __DIR_DOT_H__
/**
* gfs2_filldir_t - Report a directory entry to the caller of gfs2_dir_read()
* @opaque: opaque data used by the function
* @name: the name of the directory entry
* @length: the length of the name
* @offset: the entry's offset in the directory
* @inum: the inode number the entry points to
* @type: the type of inode the entry points to
*
* Returns: 0 on success, 1 if buffer full
*/
typedef int (*gfs2_filldir_t) (void *opaque,
const char *name, unsigned int length,
uint64_t offset,
struct gfs2_inum *inum, unsigned int type);
int gfs2_filecmp(struct qstr *file1, char *file2, int len_of_file2);
int gfs2_dirent_alloc(struct gfs2_inode *dip, struct buffer_head *bh,
int name_len, struct gfs2_dirent **dent_out);
int gfs2_dir_search(struct gfs2_inode *dip, struct qstr *filename,
struct gfs2_inum *inum, unsigned int *type);
int gfs2_dir_add(struct gfs2_inode *dip, struct qstr *filename,
struct gfs2_inum *inum, unsigned int type);
int gfs2_dir_del(struct gfs2_inode *dip, struct qstr *filename);
int gfs2_dir_read(struct gfs2_inode *dip, uint64_t * offset, void *opaque,
gfs2_filldir_t filldir);
int gfs2_dir_mvino(struct gfs2_inode *dip, struct qstr *filename,
struct gfs2_inum *new_inum, unsigned int new_type);
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_get_dir_meta(struct gfs2_inode *ip, struct gfs2_user_buffer *ub);
#endif /* __DIR_DOT_H__ */

185
fs/gfs2/eaops.c Normal file
View File

@ -0,0 +1,185 @@
/*
* 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 <linux/xattr.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include "gfs2.h"
#include "acl.h"
#include "eaops.h"
#include "eattr.h"
/**
* gfs2_ea_name2type - get the type of the ea, and truncate type from the name
* @namep: ea name, possibly with type appended
*
* Returns: GFS2_EATYPE_XXX
*/
unsigned int gfs2_ea_name2type(const char *name, char **truncated_name)
{
unsigned int type;
if (strncmp(name, "system.", 7) == 0) {
type = GFS2_EATYPE_SYS;
if (truncated_name)
*truncated_name = strchr(name, '.') + 1;
} else if (strncmp(name, "user.", 5) == 0) {
type = GFS2_EATYPE_USR;
if (truncated_name)
*truncated_name = strchr(name, '.') + 1;
} else {
type = GFS2_EATYPE_UNUSED;
if (truncated_name)
*truncated_name = NULL;
}
return type;
}
static int user_eo_get(struct gfs2_inode *ip, struct gfs2_ea_request *er)
{
struct inode *inode = ip->i_vnode;
int error = permission(inode, MAY_READ, NULL);
if (error)
return error;
return gfs2_ea_get_i(ip, er);
}
static int user_eo_set(struct gfs2_inode *ip, struct gfs2_ea_request *er)
{
struct inode *inode = ip->i_vnode;
if (S_ISREG(inode->i_mode) ||
(S_ISDIR(inode->i_mode) && !(inode->i_mode & S_ISVTX))) {
int error = permission(inode, MAY_WRITE, NULL);
if (error)
return error;
} else
return -EPERM;
return gfs2_ea_set_i(ip, er);
}
static int user_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er)
{
struct inode *inode = ip->i_vnode;
if (S_ISREG(inode->i_mode) ||
(S_ISDIR(inode->i_mode) && !(inode->i_mode & S_ISVTX))) {
int error = permission(inode, MAY_WRITE, NULL);
if (error)
return error;
} else
return -EPERM;
return gfs2_ea_remove_i(ip, er);
}
static int system_eo_get(struct gfs2_inode *ip, struct gfs2_ea_request *er)
{
if (!GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len) &&
!GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
if (ip->i_sbd->sd_args.ar_posix_acl == 0 &&
(GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len) ||
GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)))
return -EOPNOTSUPP;
return gfs2_ea_get_i(ip, er);
}
static int system_eo_set(struct gfs2_inode *ip, struct gfs2_ea_request *er)
{
int remove = 0;
int error;
if (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len)) {
if (!(er->er_flags & GFS2_ERF_MODE)) {
er->er_mode = ip->i_di.di_mode;
er->er_flags |= GFS2_ERF_MODE;
}
error = gfs2_acl_validate_set(ip, 1, er,
&remove, &er->er_mode);
if (error)
return error;
error = gfs2_ea_set_i(ip, er);
if (error)
return error;
if (remove)
gfs2_ea_remove_i(ip, er);
return 0;
} else if (GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) {
error = gfs2_acl_validate_set(ip, 0, er,
&remove, NULL);
if (error)
return error;
if (!remove)
error = gfs2_ea_set_i(ip, er);
else {
error = gfs2_ea_remove_i(ip, er);
if (error == -ENODATA)
error = 0;
}
return error;
}
return -EPERM;
}
static int system_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er)
{
if (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len)) {
int error = gfs2_acl_validate_remove(ip, 1);
if (error)
return error;
} else if (GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) {
int error = gfs2_acl_validate_remove(ip, 0);
if (error)
return error;
} else
return -EPERM;
return gfs2_ea_remove_i(ip, er);
}
struct gfs2_eattr_operations gfs2_user_eaops = {
.eo_get = user_eo_get,
.eo_set = user_eo_set,
.eo_remove = user_eo_remove,
.eo_name = "user",
};
struct gfs2_eattr_operations gfs2_system_eaops = {
.eo_get = system_eo_get,
.eo_set = system_eo_set,
.eo_remove = system_eo_remove,
.eo_name = "system",
};
struct gfs2_eattr_operations *gfs2_ea_ops[] = {
NULL,
&gfs2_user_eaops,
&gfs2_system_eaops,
};

30
fs/gfs2/eaops.h Normal file
View File

@ -0,0 +1,30 @@
/*
* 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 __EAOPS_DOT_H__
#define __EAOPS_DOT_H__
struct gfs2_ea_request;
struct gfs2_eattr_operations {
int (*eo_get) (struct gfs2_inode *ip, struct gfs2_ea_request *er);
int (*eo_set) (struct gfs2_inode *ip, struct gfs2_ea_request *er);
int (*eo_remove) (struct gfs2_inode *ip, struct gfs2_ea_request *er);
char *eo_name;
};
unsigned int gfs2_ea_name2type(const char *name, char **truncated_name);
extern struct gfs2_eattr_operations gfs2_user_eaops;
extern struct gfs2_eattr_operations gfs2_system_eaops;
extern struct gfs2_eattr_operations *gfs2_ea_ops[];
#endif /* __EAOPS_DOT_H__ */

1620
fs/gfs2/eattr.c Normal file

File diff suppressed because it is too large Load Diff

90
fs/gfs2/eattr.h Normal file
View File

@ -0,0 +1,90 @@
/*
* 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 __EATTR_DOT_H__
#define __EATTR_DOT_H__
#define GFS2_EA_REC_LEN(ea) be32_to_cpu((ea)->ea_rec_len)
#define GFS2_EA_DATA_LEN(ea) be32_to_cpu((ea)->ea_data_len)
#define GFS2_EA_SIZE(ea) \
ALIGN(sizeof(struct gfs2_ea_header) + (ea)->ea_name_len + \
((GFS2_EA_IS_STUFFED(ea)) ? GFS2_EA_DATA_LEN(ea) : \
(sizeof(uint64_t) * (ea)->ea_num_ptrs)), 8)
#define GFS2_EA_STRLEN(ea) \
((((ea)->ea_type == GFS2_EATYPE_USR) ? 5 : 7) + (ea)->ea_name_len + 1)
#define GFS2_EA_IS_STUFFED(ea) (!(ea)->ea_num_ptrs)
#define GFS2_EA_IS_LAST(ea) ((ea)->ea_flags & GFS2_EAFLAG_LAST)
#define GFS2_EAREQ_SIZE_STUFFED(er) \
ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + (er)->er_data_len, 8)
#define GFS2_EAREQ_SIZE_UNSTUFFED(sdp, er) \
ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + \
sizeof(uint64_t) * DIV_RU((er)->er_data_len, (sdp)->sd_jbsize), 8)
#define GFS2_EA2NAME(ea) ((char *)((struct gfs2_ea_header *)(ea) + 1))
#define GFS2_EA2DATA(ea) (GFS2_EA2NAME(ea) + (ea)->ea_name_len)
#define GFS2_EA2DATAPTRS(ea) \
((uint64_t *)(GFS2_EA2NAME(ea) + ALIGN((ea)->ea_name_len, 8)))
#define GFS2_EA2NEXT(ea) \
((struct gfs2_ea_header *)((char *)(ea) + GFS2_EA_REC_LEN(ea)))
#define GFS2_EA_BH2FIRST(bh) \
((struct gfs2_ea_header *)((bh)->b_data + sizeof(struct gfs2_meta_header)))
#define GFS2_ERF_MODE 0x80000000
struct gfs2_ea_request {
char *er_name;
char *er_data;
unsigned int er_name_len;
unsigned int er_data_len;
unsigned int er_type; /* GFS2_EATYPE_... */
int er_flags;
mode_t er_mode;
};
struct gfs2_ea_location {
struct buffer_head *el_bh;
struct gfs2_ea_header *el_ea;
struct gfs2_ea_header *el_prev;
};
int gfs2_ea_repack(struct gfs2_inode *ip);
int gfs2_ea_get_i(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_list(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_get(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_set(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er);
int gfs2_ea_dealloc(struct gfs2_inode *ip);
int gfs2_get_eattr_meta(struct gfs2_inode *ip, struct gfs2_user_buffer *ub);
/* Exported to acl.c */
int gfs2_ea_find(struct gfs2_inode *ip,
struct gfs2_ea_request *er,
struct gfs2_ea_location *el);
int gfs2_ea_get_copy(struct gfs2_inode *ip,
struct gfs2_ea_location *el,
char *data);
int gfs2_ea_acl_chmod(struct gfs2_inode *ip, struct gfs2_ea_location *el,
struct iattr *attr, char *data);
#endif /* __EATTR_DOT_H__ */

21
fs/gfs2/format.h Normal file
View File

@ -0,0 +1,21 @@
/*
* 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 __FORMAT_DOT_H__
#define __FORMAT_DOT_H__
static const uint32_t gfs2_old_fs_formats[] = {
0
};
static const uint32_t gfs2_old_multihost_formats[] = {
0
};
#endif /* __FORMAT_DOT_H__ */

62
fs/gfs2/gfs2.h Normal file
View File

@ -0,0 +1,62 @@
/*
* 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 __GFS2_DOT_H__
#define __GFS2_DOT_H__
#include <linux/gfs2_ondisk.h>
#include "lm_interface.h"
#include "lvb.h"
#include "incore.h"
#include "util.h"
enum {
NO_CREATE = 0,
CREATE = 1,
};
enum {
NO_WAIT = 0,
WAIT = 1,
};
enum {
NO_FORCE = 0,
FORCE = 1,
};
/* Divide num by den. Round up if there is a remainder. */
#define DIV_RU(num, den) (((num) + (den) - 1) / (den))
#define GFS2_FAST_NAME_SIZE 8
#define get_v2sdp(sb) ((struct gfs2_sbd *)(sb)->s_fs_info)
#define set_v2sdp(sb, sdp) (sb)->s_fs_info = (sdp)
#define get_v2ip(inode) ((struct gfs2_inode *)(inode)->u.generic_ip)
#define set_v2ip(inode, ip) (inode)->u.generic_ip = (ip)
#define get_v2fp(file) ((struct gfs2_file *)(file)->private_data)
#define set_v2fp(file, fp) (file)->private_data = (fp)
#define get_v2bd(bh) ((struct gfs2_bufdata *)(bh)->b_private)
#define set_v2bd(bh, bd) (bh)->b_private = (bd)
#define get_v2db(bh) ((struct gfs2_databuf *)(bh)->b_private)
#define set_v2db(bh, db) (bh)->b_private = (db)
#define get_transaction ((struct gfs2_trans *)(current->journal_info))
#define set_transaction(tr) (current->journal_info) = (tr)
#define get_gl2ip(gl) ((struct gfs2_inode *)(gl)->gl_object)
#define set_gl2ip(gl, ip) (gl)->gl_object = (ip)
#define get_gl2rgd(gl) ((struct gfs2_rgrpd *)(gl)->gl_object)
#define set_gl2rgd(gl, rgd) (gl)->gl_object = (rgd)
#define get_gl2gl(gl) ((struct gfs2_glock *)(gl)->gl_object)
#define set_gl2gl(gl, gl2) (gl)->gl_object = (gl2)
#endif /* __GFS2_DOT_H__ */

2513
fs/gfs2/glock.c Normal file

File diff suppressed because it is too large Load Diff

143
fs/gfs2/glock.h Normal file
View File

@ -0,0 +1,143 @@
/*
* 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 __GLOCK_DOT_H__
#define __GLOCK_DOT_H__
/* Flags for lock requests; used in gfs2_holder gh_flag field.
From lm_interface.h:
#define LM_FLAG_TRY 0x00000001
#define LM_FLAG_TRY_1CB 0x00000002
#define LM_FLAG_NOEXP 0x00000004
#define LM_FLAG_ANY 0x00000008
#define LM_FLAG_PRIORITY 0x00000010 */
#define GL_LOCAL_EXCL 0x00000020
#define GL_ASYNC 0x00000040
#define GL_EXACT 0x00000080
#define GL_SKIP 0x00000100
#define GL_ATIME 0x00000200
#define GL_NOCACHE 0x00000400
#define GL_SYNC 0x00000800
#define GL_NOCANCEL 0x00001000
#define GL_NEVER_RECURSE 0x00002000
#define GLR_TRYFAILED 13
#define GLR_CANCELED 14
static inline int gfs2_glock_is_locked_by_me(struct gfs2_glock *gl)
{
struct gfs2_holder *gh;
int locked = 0;
/* Look in glock's list of holders for one with current task as owner */
spin_lock(&gl->gl_spin);
list_for_each_entry(gh, &gl->gl_holders, gh_list) {
if (gh->gh_owner == current) {
locked = 1;
break;
}
}
spin_unlock(&gl->gl_spin);
return locked;
}
static inline int gfs2_glock_is_held_excl(struct gfs2_glock *gl)
{
return (gl->gl_state == LM_ST_EXCLUSIVE);
}
static inline int gfs2_glock_is_held_dfrd(struct gfs2_glock *gl)
{
return (gl->gl_state == LM_ST_DEFERRED);
}
static inline int gfs2_glock_is_held_shrd(struct gfs2_glock *gl)
{
return (gl->gl_state == LM_ST_SHARED);
}
static inline int gfs2_glock_is_blocking(struct gfs2_glock *gl)
{
int ret;
spin_lock(&gl->gl_spin);
ret = !list_empty(&gl->gl_waiters2) || !list_empty(&gl->gl_waiters3);
spin_unlock(&gl->gl_spin);
return ret;
}
struct gfs2_glock *gfs2_glock_find(struct gfs2_sbd *sdp,
struct lm_lockname *name);
int gfs2_glock_get(struct gfs2_sbd *sdp,
uint64_t number, struct gfs2_glock_operations *glops,
int create, struct gfs2_glock **glp);
void gfs2_glock_hold(struct gfs2_glock *gl);
int gfs2_glock_put(struct gfs2_glock *gl);
void gfs2_holder_init(struct gfs2_glock *gl, unsigned int state, int flags,
struct gfs2_holder *gh);
void gfs2_holder_reinit(unsigned int state, int flags, struct gfs2_holder *gh);
void gfs2_holder_uninit(struct gfs2_holder *gh);
struct gfs2_holder *gfs2_holder_get(struct gfs2_glock *gl, unsigned int state,
int flags, gfp_t gfp_flags);
void gfs2_holder_put(struct gfs2_holder *gh);
void gfs2_glock_xmote_th(struct gfs2_glock *gl, unsigned int state, int flags);
void gfs2_glock_drop_th(struct gfs2_glock *gl);
void gfs2_glmutex_lock(struct gfs2_glock *gl);
int gfs2_glmutex_trylock(struct gfs2_glock *gl);
void gfs2_glmutex_unlock(struct gfs2_glock *gl);
int gfs2_glock_nq(struct gfs2_holder *gh);
int gfs2_glock_poll(struct gfs2_holder *gh);
int gfs2_glock_wait(struct gfs2_holder *gh);
void gfs2_glock_dq(struct gfs2_holder *gh);
void gfs2_glock_prefetch(struct gfs2_glock *gl, unsigned int state, int flags);
void gfs2_glock_force_drop(struct gfs2_glock *gl);
int gfs2_glock_be_greedy(struct gfs2_glock *gl, unsigned int time);
int gfs2_glock_nq_init(struct gfs2_glock *gl, unsigned int state, int flags,
struct gfs2_holder *gh);
void gfs2_glock_dq_uninit(struct gfs2_holder *gh);
int gfs2_glock_nq_num(struct gfs2_sbd *sdp,
uint64_t number, struct gfs2_glock_operations *glops,
unsigned int state, int flags, struct gfs2_holder *gh);
int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs);
void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs);
void gfs2_glock_dq_uninit_m(unsigned int num_gh, struct gfs2_holder *ghs);
void gfs2_glock_prefetch_num(struct gfs2_sbd *sdp, uint64_t number,
struct gfs2_glock_operations *glops,
unsigned int state, int flags);
/* Lock Value Block functions */
int gfs2_lvb_hold(struct gfs2_glock *gl);
void gfs2_lvb_unhold(struct gfs2_glock *gl);
void gfs2_lvb_sync(struct gfs2_glock *gl);
void gfs2_glock_cb(lm_fsdata_t *fsdata, unsigned int type, void *data);
void gfs2_try_toss_inode(struct gfs2_sbd *sdp, struct gfs2_inum *inum);
void gfs2_iopen_go_callback(struct gfs2_glock *gl, unsigned int state);
void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl);
void gfs2_reclaim_glock(struct gfs2_sbd *sdp);
void gfs2_scand_internal(struct gfs2_sbd *sdp);
void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait);
int gfs2_dump_lockstate(struct gfs2_sbd *sdp);
#endif /* __GLOCK_DOT_H__ */

487
fs/gfs2/glops.c Normal file
View File

@ -0,0 +1,487 @@
/*
* 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 "gfs2.h"
#include "bmap.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
#include "log.h"
#include "meta_io.h"
#include "page.h"
#include "recovery.h"
#include "rgrp.h"
/**
* meta_go_sync - sync out the metadata for this glock
* @gl: the glock
* @flags: DIO_*
*
* Called when demoting or unlocking an EX glock. We must flush
* to disk all dirty buffers/pages relating to this glock, and must not
* not return to caller to demote/unlock the glock until I/O is complete.
*/
static void meta_go_sync(struct gfs2_glock *gl, int flags)
{
if (!(flags & DIO_METADATA))
return;
if (test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) {
gfs2_log_flush_glock(gl);
gfs2_meta_sync(gl, flags | DIO_START | DIO_WAIT);
if (flags & DIO_RELEASE)
gfs2_ail_empty_gl(gl);
}
clear_bit(GLF_SYNC, &gl->gl_flags);
}
/**
* meta_go_inval - invalidate the metadata for this glock
* @gl: the glock
* @flags:
*
*/
static void meta_go_inval(struct gfs2_glock *gl, int flags)
{
if (!(flags & DIO_METADATA))
return;
gfs2_meta_inval(gl);
gl->gl_vn++;
}
/**
* meta_go_demote_ok - Check to see if it's ok to unlock a glock
* @gl: the glock
*
* Returns: 1 if we have no cached data; ok to demote meta glock
*/
static int meta_go_demote_ok(struct gfs2_glock *gl)
{
return !gl->gl_aspace->i_mapping->nrpages;
}
/**
* inode_go_xmote_th - promote/demote a glock
* @gl: the glock
* @state: the requested state
* @flags:
*
*/
static void inode_go_xmote_th(struct gfs2_glock *gl, unsigned int state,
int flags)
{
if (gl->gl_state != LM_ST_UNLOCKED)
gfs2_pte_inval(gl);
gfs2_glock_xmote_th(gl, state, flags);
}
/**
* inode_go_xmote_bh - After promoting/demoting a glock
* @gl: the glock
*
*/
static void inode_go_xmote_bh(struct gfs2_glock *gl)
{
struct gfs2_holder *gh = gl->gl_req_gh;
struct buffer_head *bh;
int error;
if (gl->gl_state != LM_ST_UNLOCKED &&
(!gh || !(gh->gh_flags & GL_SKIP))) {
error = gfs2_meta_read(gl, gl->gl_name.ln_number, DIO_START,
&bh);
if (!error)
brelse(bh);
}
}
/**
* inode_go_drop_th - unlock a glock
* @gl: the glock
*
* Invoked from rq_demote().
* Another node needs the lock in EXCLUSIVE mode, or lock (unused for too long)
* is being purged from our node's glock cache; we're dropping lock.
*/
static void inode_go_drop_th(struct gfs2_glock *gl)
{
gfs2_pte_inval(gl);
gfs2_glock_drop_th(gl);
}
/**
* inode_go_sync - Sync the dirty data and/or metadata for an inode glock
* @gl: the glock protecting the inode
* @flags:
*
*/
static void inode_go_sync(struct gfs2_glock *gl, int flags)
{
int meta = (flags & DIO_METADATA);
int data = (flags & DIO_DATA);
if (test_bit(GLF_DIRTY, &gl->gl_flags)) {
if (meta && data) {
gfs2_page_sync(gl, flags | DIO_START);
gfs2_log_flush_glock(gl);
gfs2_meta_sync(gl, flags | DIO_START | DIO_WAIT);
gfs2_page_sync(gl, flags | DIO_WAIT);
clear_bit(GLF_DIRTY, &gl->gl_flags);
} else if (meta) {
gfs2_log_flush_glock(gl);
gfs2_meta_sync(gl, flags | DIO_START | DIO_WAIT);
} else if (data)
gfs2_page_sync(gl, flags | DIO_START | DIO_WAIT);
if (flags & DIO_RELEASE)
gfs2_ail_empty_gl(gl);
}
clear_bit(GLF_SYNC, &gl->gl_flags);
}
/**
* inode_go_inval - prepare a inode glock to be released
* @gl: the glock
* @flags:
*
*/
static void inode_go_inval(struct gfs2_glock *gl, int flags)
{
int meta = (flags & DIO_METADATA);
int data = (flags & DIO_DATA);
if (meta) {
gfs2_meta_inval(gl);
gl->gl_vn++;
}
if (data)
gfs2_page_inval(gl);
}
/**
* inode_go_demote_ok - Check to see if it's ok to unlock an inode glock
* @gl: the glock
*
* Returns: 1 if it's ok
*/
static int inode_go_demote_ok(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
int demote = 0;
if (!get_gl2ip(gl) && !gl->gl_aspace->i_mapping->nrpages)
demote = 1;
else if (!sdp->sd_args.ar_localcaching &&
time_after_eq(jiffies, gl->gl_stamp +
gfs2_tune_get(sdp, gt_demote_secs) * HZ))
demote = 1;
return demote;
}
/**
* inode_go_lock - operation done after an inode lock is locked by a process
* @gl: the glock
* @flags:
*
* Returns: errno
*/
static int inode_go_lock(struct gfs2_holder *gh)
{
struct gfs2_glock *gl = gh->gh_gl;
struct gfs2_inode *ip = get_gl2ip(gl);
int error = 0;
if (!ip)
return 0;
if (ip->i_vn != gl->gl_vn) {
error = gfs2_inode_refresh(ip);
if (error)
return error;
gfs2_inode_attr_in(ip);
}
if ((ip->i_di.di_flags & GFS2_DIF_TRUNC_IN_PROG) &&
(gl->gl_state == LM_ST_EXCLUSIVE) &&
(gh->gh_flags & GL_LOCAL_EXCL))
error = gfs2_truncatei_resume(ip);
return error;
}
/**
* inode_go_unlock - operation done before an inode lock is unlocked by a
* process
* @gl: the glock
* @flags:
*
*/
static void inode_go_unlock(struct gfs2_holder *gh)
{
struct gfs2_glock *gl = gh->gh_gl;
struct gfs2_inode *ip = get_gl2ip(gl);
if (ip && test_bit(GLF_DIRTY, &gl->gl_flags))
gfs2_inode_attr_in(ip);
if (ip)
gfs2_meta_cache_flush(ip);
}
/**
* inode_greedy -
* @gl: the glock
*
*/
static void inode_greedy(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct gfs2_inode *ip = get_gl2ip(gl);
unsigned int quantum = gfs2_tune_get(sdp, gt_greedy_quantum);
unsigned int max = gfs2_tune_get(sdp, gt_greedy_max);
unsigned int new_time;
spin_lock(&ip->i_spin);
if (time_after(ip->i_last_pfault + quantum, jiffies)) {
new_time = ip->i_greedy + quantum;
if (new_time > max)
new_time = max;
} else {
new_time = ip->i_greedy - quantum;
if (!new_time || new_time > max)
new_time = 1;
}
ip->i_greedy = new_time;
spin_unlock(&ip->i_spin);
gfs2_inode_put(ip);
}
/**
* rgrp_go_demote_ok - Check to see if it's ok to unlock a RG's glock
* @gl: the glock
*
* Returns: 1 if it's ok
*/
static int rgrp_go_demote_ok(struct gfs2_glock *gl)
{
return !gl->gl_aspace->i_mapping->nrpages;
}
/**
* rgrp_go_lock - operation done after an rgrp lock is locked by
* a first holder on this node.
* @gl: the glock
* @flags:
*
* Returns: errno
*/
static int rgrp_go_lock(struct gfs2_holder *gh)
{
return gfs2_rgrp_bh_get(get_gl2rgd(gh->gh_gl));
}
/**
* rgrp_go_unlock - operation done before an rgrp lock is unlocked by
* a last holder on this node.
* @gl: the glock
* @flags:
*
*/
static void rgrp_go_unlock(struct gfs2_holder *gh)
{
gfs2_rgrp_bh_put(get_gl2rgd(gh->gh_gl));
}
/**
* trans_go_xmote_th - promote/demote the transaction glock
* @gl: the glock
* @state: the requested state
* @flags:
*
*/
static void trans_go_xmote_th(struct gfs2_glock *gl, unsigned int state,
int flags)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
if (gl->gl_state != LM_ST_UNLOCKED &&
test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
gfs2_meta_syncfs(sdp);
gfs2_log_shutdown(sdp);
}
gfs2_glock_xmote_th(gl, state, flags);
}
/**
* trans_go_xmote_bh - After promoting/demoting the transaction glock
* @gl: the glock
*
*/
static void trans_go_xmote_bh(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct gfs2_glock *j_gl = sdp->sd_jdesc->jd_inode->i_gl;
struct gfs2_log_header head;
int error;
if (gl->gl_state != LM_ST_UNLOCKED &&
test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
gfs2_meta_cache_flush(sdp->sd_jdesc->jd_inode);
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA | DIO_DATA);
error = gfs2_find_jhead(sdp->sd_jdesc, &head);
if (error)
gfs2_consist(sdp);
if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT))
gfs2_consist(sdp);
/* Initialize some head of the log stuff */
if (!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)) {
sdp->sd_log_sequence = head.lh_sequence + 1;
gfs2_log_pointers_init(sdp, head.lh_blkno);
}
}
}
/**
* trans_go_drop_th - unlock the transaction glock
* @gl: the glock
*
* We want to sync the device even with localcaching. Remember
* that localcaching journal replay only marks buffers dirty.
*/
static void trans_go_drop_th(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
gfs2_meta_syncfs(sdp);
gfs2_log_shutdown(sdp);
}
gfs2_glock_drop_th(gl);
}
/**
* quota_go_demote_ok - Check to see if it's ok to unlock a quota glock
* @gl: the glock
*
* Returns: 1 if it's ok
*/
static int quota_go_demote_ok(struct gfs2_glock *gl)
{
return !atomic_read(&gl->gl_lvb_count);
}
struct gfs2_glock_operations gfs2_meta_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_sync = meta_go_sync,
.go_inval = meta_go_inval,
.go_demote_ok = meta_go_demote_ok,
.go_type = LM_TYPE_META
};
struct gfs2_glock_operations gfs2_inode_glops = {
.go_xmote_th = inode_go_xmote_th,
.go_xmote_bh = inode_go_xmote_bh,
.go_drop_th = inode_go_drop_th,
.go_sync = inode_go_sync,
.go_inval = inode_go_inval,
.go_demote_ok = inode_go_demote_ok,
.go_lock = inode_go_lock,
.go_unlock = inode_go_unlock,
.go_greedy = inode_greedy,
.go_type = LM_TYPE_INODE
};
struct gfs2_glock_operations gfs2_rgrp_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_sync = meta_go_sync,
.go_inval = meta_go_inval,
.go_demote_ok = rgrp_go_demote_ok,
.go_lock = rgrp_go_lock,
.go_unlock = rgrp_go_unlock,
.go_type = LM_TYPE_RGRP
};
struct gfs2_glock_operations gfs2_trans_glops = {
.go_xmote_th = trans_go_xmote_th,
.go_xmote_bh = trans_go_xmote_bh,
.go_drop_th = trans_go_drop_th,
.go_type = LM_TYPE_NONDISK
};
struct gfs2_glock_operations gfs2_iopen_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_callback = gfs2_iopen_go_callback,
.go_type = LM_TYPE_IOPEN
};
struct gfs2_glock_operations gfs2_flock_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_type = LM_TYPE_FLOCK
};
struct gfs2_glock_operations gfs2_nondisk_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_type = LM_TYPE_NONDISK
};
struct gfs2_glock_operations gfs2_quota_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_demote_ok = quota_go_demote_ok,
.go_type = LM_TYPE_QUOTA
};
struct gfs2_glock_operations gfs2_journal_glops = {
.go_xmote_th = gfs2_glock_xmote_th,
.go_drop_th = gfs2_glock_drop_th,
.go_type = LM_TYPE_JOURNAL
};

23
fs/gfs2/glops.h Normal file
View File

@ -0,0 +1,23 @@
/*
* 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 __GLOPS_DOT_H__
#define __GLOPS_DOT_H__
extern struct gfs2_glock_operations gfs2_meta_glops;
extern struct gfs2_glock_operations gfs2_inode_glops;
extern struct gfs2_glock_operations gfs2_rgrp_glops;
extern struct gfs2_glock_operations gfs2_trans_glops;
extern struct gfs2_glock_operations gfs2_iopen_glops;
extern struct gfs2_glock_operations gfs2_flock_glops;
extern struct gfs2_glock_operations gfs2_nondisk_glops;
extern struct gfs2_glock_operations gfs2_quota_glops;
extern struct gfs2_glock_operations gfs2_journal_glops;
#endif /* __GLOPS_DOT_H__ */

703
fs/gfs2/incore.h Normal file
View File

@ -0,0 +1,703 @@
/*
* 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 __INCORE_DOT_H__
#define __INCORE_DOT_H__
#define DIO_FORCE 0x00000001
#define DIO_CLEAN 0x00000002
#define DIO_DIRTY 0x00000004
#define DIO_START 0x00000008
#define DIO_WAIT 0x00000010
#define DIO_METADATA 0x00000020
#define DIO_DATA 0x00000040
#define DIO_RELEASE 0x00000080
#define DIO_ALL 0x00000100
struct gfs2_log_operations;
struct gfs2_log_element;
struct gfs2_bitmap;
struct gfs2_rgrpd;
struct gfs2_bufdata;
struct gfs2_databuf;
struct gfs2_glock_operations;
struct gfs2_holder;
struct gfs2_glock;
struct gfs2_alloc;
struct gfs2_inode;
struct gfs2_file;
struct gfs2_revoke;
struct gfs2_revoke_replay;
struct gfs2_unlinked;
struct gfs2_quota_data;
struct gfs2_log_buf;
struct gfs2_trans;
struct gfs2_ail;
struct gfs2_jdesc;
struct gfs2_args;
struct gfs2_tune;
struct gfs2_gl_hash_bucket;
struct gfs2_sbd;
typedef void (*gfs2_glop_bh_t) (struct gfs2_glock *gl, unsigned int ret);
/*
* Structure of operations that are associated with each
* type of element in the log.
*/
struct gfs2_log_operations {
void (*lo_add) (struct gfs2_sbd *sdp, struct gfs2_log_element *le);
void (*lo_incore_commit) (struct gfs2_sbd *sdp, struct gfs2_trans *tr);
void (*lo_before_commit) (struct gfs2_sbd *sdp);
void (*lo_after_commit) (struct gfs2_sbd *sdp, struct gfs2_ail *ai);
void (*lo_before_scan) (struct gfs2_jdesc *jd,
struct gfs2_log_header *head, int pass);
int (*lo_scan_elements) (struct gfs2_jdesc *jd, unsigned int start,
struct gfs2_log_descriptor *ld, __be64 *ptr,
int pass);
void (*lo_after_scan) (struct gfs2_jdesc *jd, int error, int pass);
char *lo_name;
};
struct gfs2_log_element {
struct list_head le_list;
struct gfs2_log_operations *le_ops;
};
struct gfs2_bitmap {
struct buffer_head *bi_bh;
char *bi_clone;
uint32_t bi_offset;
uint32_t bi_start;
uint32_t bi_len;
};
struct gfs2_rgrpd {
struct list_head rd_list; /* Link with superblock */
struct list_head rd_list_mru;
struct list_head rd_recent; /* Recently used rgrps */
struct gfs2_glock *rd_gl; /* Glock for this rgrp */
struct gfs2_rindex rd_ri;
struct gfs2_rgrp rd_rg;
uint64_t rd_rg_vn;
struct gfs2_bitmap *rd_bits;
unsigned int rd_bh_count;
struct semaphore rd_mutex;
uint32_t rd_free_clone;
struct gfs2_log_element rd_le;
uint32_t rd_last_alloc_data;
uint32_t rd_last_alloc_meta;
struct gfs2_sbd *rd_sbd;
};
enum gfs2_state_bits {
BH_Pinned = BH_PrivateStart,
};
BUFFER_FNS(Pinned, pinned)
TAS_BUFFER_FNS(Pinned, pinned)
struct gfs2_bufdata {
struct buffer_head *bd_bh;
struct gfs2_glock *bd_gl;
struct list_head bd_list_tr;
struct gfs2_log_element bd_le;
struct gfs2_ail *bd_ail;
struct list_head bd_ail_st_list;
struct list_head bd_ail_gl_list;
};
struct gfs2_databuf {
struct gfs2_log_element db_le;
struct buffer_head *db_bh;
};
struct gfs2_glock_operations {
void (*go_xmote_th) (struct gfs2_glock * gl, unsigned int state,
int flags);
void (*go_xmote_bh) (struct gfs2_glock * gl);
void (*go_drop_th) (struct gfs2_glock * gl);
void (*go_drop_bh) (struct gfs2_glock * gl);
void (*go_sync) (struct gfs2_glock * gl, int flags);
void (*go_inval) (struct gfs2_glock * gl, int flags);
int (*go_demote_ok) (struct gfs2_glock * gl);
int (*go_lock) (struct gfs2_holder * gh);
void (*go_unlock) (struct gfs2_holder * gh);
void (*go_callback) (struct gfs2_glock * gl, unsigned int state);
void (*go_greedy) (struct gfs2_glock * gl);
int go_type;
};
enum {
/* Actions */
HIF_MUTEX = 0,
HIF_PROMOTE = 1,
HIF_DEMOTE = 2,
HIF_GREEDY = 3,
/* States */
HIF_ALLOCED = 4,
HIF_DEALLOC = 5,
HIF_HOLDER = 6,
HIF_FIRST = 7,
HIF_RECURSE = 8,
HIF_ABORTED = 9,
};
struct gfs2_holder {
struct list_head gh_list;
struct gfs2_glock *gh_gl;
struct task_struct *gh_owner;
unsigned int gh_state;
int gh_flags;
int gh_error;
unsigned long gh_iflags;
struct completion gh_wait;
};
enum {
GLF_PLUG = 0,
GLF_LOCK = 1,
GLF_STICKY = 2,
GLF_PREFETCH = 3,
GLF_SYNC = 4,
GLF_DIRTY = 5,
GLF_SKIP_WAITERS2 = 6,
GLF_GREEDY = 7,
};
struct gfs2_glock {
struct list_head gl_list;
unsigned long gl_flags; /* GLF_... */
struct lm_lockname gl_name;
struct kref gl_ref;
spinlock_t gl_spin;
unsigned int gl_state;
struct list_head gl_holders;
struct list_head gl_waiters1; /* HIF_MUTEX */
struct list_head gl_waiters2; /* HIF_DEMOTE, HIF_GREEDY */
struct list_head gl_waiters3; /* HIF_PROMOTE */
struct gfs2_glock_operations *gl_ops;
struct gfs2_holder *gl_req_gh;
gfs2_glop_bh_t gl_req_bh;
lm_lock_t *gl_lock;
char *gl_lvb;
atomic_t gl_lvb_count;
uint64_t gl_vn;
unsigned long gl_stamp;
void *gl_object;
struct gfs2_gl_hash_bucket *gl_bucket;
struct list_head gl_reclaim;
struct gfs2_sbd *gl_sbd;
struct inode *gl_aspace;
struct gfs2_log_element gl_le;
struct list_head gl_ail_list;
atomic_t gl_ail_count;
};
struct gfs2_alloc {
/* Quota stuff */
unsigned int al_qd_num;
struct gfs2_quota_data *al_qd[4];
struct gfs2_holder al_qd_ghs[4];
/* Filled in by the caller to gfs2_inplace_reserve() */
uint32_t al_requested;
/* Filled in by gfs2_inplace_reserve() */
char *al_file;
unsigned int al_line;
struct gfs2_holder al_ri_gh;
struct gfs2_holder al_rgd_gh;
struct gfs2_rgrpd *al_rgd;
/* Filled in by gfs2_alloc_*() */
uint32_t al_alloced;
};
enum {
GIF_MIN_INIT = 0,
GIF_QD_LOCKED = 1,
GIF_PAGED = 2,
GIF_SW_PAGED = 3,
};
struct gfs2_inode {
struct gfs2_inum i_num;
atomic_t i_count;
unsigned long i_flags; /* GIF_... */
uint64_t i_vn;
struct gfs2_dinode i_di;
struct gfs2_glock *i_gl;
struct gfs2_sbd *i_sbd;
struct inode *i_vnode;
struct gfs2_holder i_iopen_gh;
struct gfs2_alloc i_alloc;
uint64_t i_last_rg_alloc;
spinlock_t i_spin;
struct rw_semaphore i_rw_mutex;
unsigned int i_greedy;
unsigned long i_last_pfault;
struct buffer_head *i_cache[GFS2_MAX_META_HEIGHT];
};
enum {
GFF_DID_DIRECT_ALLOC = 0,
};
struct gfs2_file {
unsigned long f_flags; /* GFF_... */
struct semaphore f_fl_mutex;
struct gfs2_holder f_fl_gh;
struct gfs2_inode *f_inode;
struct file *f_vfile;
};
struct gfs2_revoke {
struct gfs2_log_element rv_le;
uint64_t rv_blkno;
};
struct gfs2_revoke_replay {
struct list_head rr_list;
uint64_t rr_blkno;
unsigned int rr_where;
};
enum {
ULF_LOCKED = 0,
};
struct gfs2_unlinked {
struct list_head ul_list;
unsigned int ul_count;
struct gfs2_unlinked_tag ul_ut;
unsigned long ul_flags; /* ULF_... */
unsigned int ul_slot;
};
enum {
QDF_USER = 0,
QDF_CHANGE = 1,
QDF_LOCKED = 2,
};
struct gfs2_quota_data {
struct list_head qd_list;
unsigned int qd_count;
uint32_t qd_id;
unsigned long qd_flags; /* QDF_... */
int64_t qd_change;
int64_t qd_change_sync;
unsigned int qd_slot;
unsigned int qd_slot_count;
struct buffer_head *qd_bh;
struct gfs2_quota_change *qd_bh_qc;
unsigned int qd_bh_count;
struct gfs2_glock *qd_gl;
struct gfs2_quota_lvb qd_qb;
uint64_t qd_sync_gen;
unsigned long qd_last_warn;
unsigned long qd_last_touched;
};
struct gfs2_log_buf {
struct list_head lb_list;
struct buffer_head *lb_bh;
struct buffer_head *lb_real;
};
struct gfs2_trans {
char *tr_file;
unsigned int tr_line;
unsigned int tr_blocks;
unsigned int tr_revokes;
unsigned int tr_reserved;
struct gfs2_holder *tr_t_gh;
int tr_touched;
unsigned int tr_num_buf;
unsigned int tr_num_buf_new;
unsigned int tr_num_buf_rm;
struct list_head tr_list_buf;
unsigned int tr_num_revoke;
unsigned int tr_num_revoke_rm;
};
struct gfs2_ail {
struct list_head ai_list;
unsigned int ai_first;
struct list_head ai_ail1_list;
struct list_head ai_ail2_list;
uint64_t ai_sync_gen;
};
struct gfs2_jdesc {
struct list_head jd_list;
struct gfs2_inode *jd_inode;
unsigned int jd_jid;
int jd_dirty;
unsigned int jd_blocks;
};
#define GFS2_GLOCKD_DEFAULT 1
#define GFS2_GLOCKD_MAX 16
#define GFS2_QUOTA_DEFAULT GFS2_QUOTA_OFF
#define GFS2_QUOTA_OFF 0
#define GFS2_QUOTA_ACCOUNT 1
#define GFS2_QUOTA_ON 2
#define GFS2_DATA_DEFAULT GFS2_DATA_ORDERED
#define GFS2_DATA_WRITEBACK 1
#define GFS2_DATA_ORDERED 2
struct gfs2_args {
char ar_lockproto[GFS2_LOCKNAME_LEN]; /* Name of the Lock Protocol */
char ar_locktable[GFS2_LOCKNAME_LEN]; /* Name of the Lock Table */
char ar_hostdata[GFS2_LOCKNAME_LEN]; /* Host specific data */
int ar_spectator; /* Don't get a journal because we're always RO */
int ar_ignore_local_fs; /* Don't optimize even if local_fs is 1 */
int ar_localflocks; /* Let the VFS do flock|fcntl locks for us */
int ar_localcaching; /* Local-style caching (dangerous on multihost) */
int ar_debug; /* Oops on errors instead of trying to be graceful */
int ar_upgrade; /* Upgrade ondisk/multihost format */
unsigned int ar_num_glockd; /* Number of glockd threads */
int ar_posix_acl; /* Enable posix acls */
int ar_quota; /* off/account/on */
int ar_suiddir; /* suiddir support */
int ar_data; /* ordered/writeback */
};
struct gfs2_tune {
spinlock_t gt_spin;
unsigned int gt_ilimit;
unsigned int gt_ilimit_tries;
unsigned int gt_ilimit_min;
unsigned int gt_demote_secs; /* Cache retention for unheld glock */
unsigned int gt_incore_log_blocks;
unsigned int gt_log_flush_secs;
unsigned int gt_jindex_refresh_secs; /* Check for new journal index */
unsigned int gt_scand_secs;
unsigned int gt_recoverd_secs;
unsigned int gt_logd_secs;
unsigned int gt_quotad_secs;
unsigned int gt_inoded_secs;
unsigned int gt_quota_simul_sync; /* Max quotavals to sync at once */
unsigned int gt_quota_warn_period; /* Secs between quota warn msgs */
unsigned int gt_quota_scale_num; /* Numerator */
unsigned int gt_quota_scale_den; /* Denominator */
unsigned int gt_quota_cache_secs;
unsigned int gt_quota_quantum; /* Secs between syncs to quota file */
unsigned int gt_atime_quantum; /* Min secs between atime updates */
unsigned int gt_new_files_jdata;
unsigned int gt_new_files_directio;
unsigned int gt_max_atomic_write; /* Split big writes into this size */
unsigned int gt_max_readahead; /* Max bytes to read-ahead from disk */
unsigned int gt_lockdump_size;
unsigned int gt_stall_secs; /* Detects trouble! */
unsigned int gt_complain_secs;
unsigned int gt_reclaim_limit; /* Max num of glocks in reclaim list */
unsigned int gt_entries_per_readdir;
unsigned int gt_prefetch_secs; /* Usage window for prefetched glocks */
unsigned int gt_greedy_default;
unsigned int gt_greedy_quantum;
unsigned int gt_greedy_max;
unsigned int gt_statfs_quantum;
unsigned int gt_statfs_slow;
};
struct gfs2_gl_hash_bucket {
rwlock_t hb_lock;
struct list_head hb_list;
};
enum {
SDF_JOURNAL_CHECKED = 0,
SDF_JOURNAL_LIVE = 1,
SDF_SHUTDOWN = 2,
SDF_NOATIME = 3,
};
#define GFS2_GL_HASH_SHIFT 13
#define GFS2_GL_HASH_SIZE (1 << GFS2_GL_HASH_SHIFT)
#define GFS2_GL_HASH_MASK (GFS2_GL_HASH_SIZE - 1)
#define GFS2_FSNAME_LEN 256
struct gfs2_sbd {
struct super_block *sd_vfs;
struct kobject sd_kobj;
unsigned long sd_flags; /* SDF_... */
struct gfs2_sb sd_sb;
/* Constants computed on mount */
uint32_t sd_fsb2bb;
uint32_t sd_fsb2bb_shift;
uint32_t sd_diptrs; /* Number of pointers in a dinode */
uint32_t sd_inptrs; /* Number of pointers in a indirect block */
uint32_t sd_jbsize; /* Size of a journaled data block */
uint32_t sd_hash_bsize; /* sizeof(exhash block) */
uint32_t sd_hash_bsize_shift;
uint32_t sd_hash_ptrs; /* Number of pointers in a hash block */
uint32_t sd_ut_per_block;
uint32_t sd_qc_per_block;
uint32_t sd_max_dirres; /* Max blocks needed to add a directory entry */
uint32_t sd_max_height; /* Max height of a file's metadata tree */
uint64_t sd_heightsize[GFS2_MAX_META_HEIGHT];
uint32_t sd_max_jheight; /* Max height of journaled file's meta tree */
uint64_t sd_jheightsize[GFS2_MAX_META_HEIGHT];
struct gfs2_args sd_args; /* Mount arguments */
struct gfs2_tune sd_tune; /* Filesystem tuning structure */
/* Lock Stuff */
struct lm_lockstruct sd_lockstruct;
struct gfs2_gl_hash_bucket sd_gl_hash[GFS2_GL_HASH_SIZE];
struct list_head sd_reclaim_list;
spinlock_t sd_reclaim_lock;
wait_queue_head_t sd_reclaim_wq;
atomic_t sd_reclaim_count;
struct gfs2_holder sd_live_gh;
struct gfs2_glock *sd_rename_gl;
struct gfs2_glock *sd_trans_gl;
struct semaphore sd_invalidate_inodes_mutex;
/* Inode Stuff */
struct gfs2_inode *sd_master_dir;
struct gfs2_inode *sd_jindex;
struct gfs2_inode *sd_inum_inode;
struct gfs2_inode *sd_statfs_inode;
struct gfs2_inode *sd_ir_inode;
struct gfs2_inode *sd_sc_inode;
struct gfs2_inode *sd_ut_inode;
struct gfs2_inode *sd_qc_inode;
struct gfs2_inode *sd_rindex;
struct gfs2_inode *sd_quota_inode;
struct gfs2_inode *sd_root_dir;
/* Inum stuff */
struct semaphore sd_inum_mutex;
/* StatFS stuff */
spinlock_t sd_statfs_spin;
struct semaphore sd_statfs_mutex;
struct gfs2_statfs_change sd_statfs_master;
struct gfs2_statfs_change sd_statfs_local;
unsigned long sd_statfs_sync_time;
/* Resource group stuff */
uint64_t sd_rindex_vn;
spinlock_t sd_rindex_spin;
struct semaphore sd_rindex_mutex;
struct list_head sd_rindex_list;
struct list_head sd_rindex_mru_list;
struct list_head sd_rindex_recent_list;
struct gfs2_rgrpd *sd_rindex_forward;
unsigned int sd_rgrps;
/* Journal index stuff */
struct list_head sd_jindex_list;
spinlock_t sd_jindex_spin;
struct semaphore sd_jindex_mutex;
unsigned int sd_journals;
unsigned long sd_jindex_refresh_time;
struct gfs2_jdesc *sd_jdesc;
struct gfs2_holder sd_journal_gh;
struct gfs2_holder sd_jinode_gh;
struct gfs2_holder sd_ir_gh;
struct gfs2_holder sd_sc_gh;
struct gfs2_holder sd_ut_gh;
struct gfs2_holder sd_qc_gh;
/* Daemon stuff */
struct task_struct *sd_scand_process;
struct task_struct *sd_recoverd_process;
struct task_struct *sd_logd_process;
struct task_struct *sd_quotad_process;
struct task_struct *sd_inoded_process;
struct task_struct *sd_glockd_process[GFS2_GLOCKD_MAX];
unsigned int sd_glockd_num;
/* Unlinked inode stuff */
struct list_head sd_unlinked_list;
atomic_t sd_unlinked_count;
spinlock_t sd_unlinked_spin;
struct semaphore sd_unlinked_mutex;
unsigned int sd_unlinked_slots;
unsigned int sd_unlinked_chunks;
unsigned char **sd_unlinked_bitmap;
/* Quota stuff */
struct list_head sd_quota_list;
atomic_t sd_quota_count;
spinlock_t sd_quota_spin;
struct semaphore sd_quota_mutex;
unsigned int sd_quota_slots;
unsigned int sd_quota_chunks;
unsigned char **sd_quota_bitmap;
uint64_t sd_quota_sync_gen;
unsigned long sd_quota_sync_time;
/* Log stuff */
spinlock_t sd_log_lock;
atomic_t sd_log_trans_count;
wait_queue_head_t sd_log_trans_wq;
atomic_t sd_log_flush_count;
wait_queue_head_t sd_log_flush_wq;
unsigned int sd_log_blks_reserved;
unsigned int sd_log_commited_buf;
unsigned int sd_log_commited_revoke;
unsigned int sd_log_num_gl;
unsigned int sd_log_num_buf;
unsigned int sd_log_num_revoke;
unsigned int sd_log_num_rg;
unsigned int sd_log_num_databuf;
struct list_head sd_log_le_gl;
struct list_head sd_log_le_buf;
struct list_head sd_log_le_revoke;
struct list_head sd_log_le_rg;
struct list_head sd_log_le_databuf;
unsigned int sd_log_blks_free;
struct list_head sd_log_blks_list;
wait_queue_head_t sd_log_blks_wait;
uint64_t sd_log_sequence;
unsigned int sd_log_head;
unsigned int sd_log_tail;
uint64_t sd_log_wraps;
int sd_log_idle;
unsigned long sd_log_flush_time;
struct semaphore sd_log_flush_lock;
struct list_head sd_log_flush_list;
unsigned int sd_log_flush_head;
uint64_t sd_log_flush_wrapped;
struct list_head sd_ail1_list;
struct list_head sd_ail2_list;
uint64_t sd_ail_sync_gen;
/* Replay stuff */
struct list_head sd_revoke_list;
unsigned int sd_replay_tail;
unsigned int sd_found_blocks;
unsigned int sd_found_revokes;
unsigned int sd_replayed_blocks;
/* For quiescing the filesystem */
struct gfs2_holder sd_freeze_gh;
struct semaphore sd_freeze_lock;
unsigned int sd_freeze_count;
/* Counters */
atomic_t sd_glock_count;
atomic_t sd_glock_held_count;
atomic_t sd_inode_count;
atomic_t sd_bufdata_count;
atomic_t sd_fh2dentry_misses;
atomic_t sd_reclaimed;
atomic_t sd_log_flush_incore;
atomic_t sd_log_flush_ondisk;
atomic_t sd_glock_nq_calls;
atomic_t sd_glock_dq_calls;
atomic_t sd_glock_prefetch_calls;
atomic_t sd_lm_lock_calls;
atomic_t sd_lm_unlock_calls;
atomic_t sd_lm_callbacks;
atomic_t sd_ops_address;
atomic_t sd_ops_dentry;
atomic_t sd_ops_export;
atomic_t sd_ops_file;
atomic_t sd_ops_inode;
atomic_t sd_ops_super;
atomic_t sd_ops_vm;
char sd_fsname[GFS2_FSNAME_LEN];
char sd_table_name[GFS2_FSNAME_LEN];
char sd_proto_name[GFS2_FSNAME_LEN];
/* Debugging crud */
unsigned long sd_last_warning;
};
#endif /* __INCORE_DOT_H__ */

1805
fs/gfs2/inode.c Normal file

File diff suppressed because it is too large Load Diff

74
fs/gfs2/inode.h Normal file
View File

@ -0,0 +1,74 @@
/*
* 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 __INODE_DOT_H__
#define __INODE_DOT_H__
static inline int gfs2_is_stuffed(struct gfs2_inode *ip)
{
return !ip->i_di.di_height;
}
static inline int gfs2_is_jdata(struct gfs2_inode *ip)
{
return ip->i_di.di_flags & GFS2_DIF_JDATA;
}
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);
struct inode *gfs2_ip2v(struct gfs2_inode *ip);
struct inode *gfs2_iget(struct super_block *sb, struct gfs2_inum *inum);
void gfs2_inode_min_init(struct gfs2_inode *ip, unsigned int type);
int gfs2_inode_refresh(struct gfs2_inode *ip);
int gfs2_inode_get(struct gfs2_glock *i_gl,
struct gfs2_inum *inum, int create,
struct gfs2_inode **ipp);
void gfs2_inode_hold(struct gfs2_inode *ip);
void gfs2_inode_put(struct gfs2_inode *ip);
void gfs2_inode_destroy(struct gfs2_inode *ip);
int gfs2_inode_dealloc(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul);
int gfs2_change_nlink(struct gfs2_inode *ip, int diff);
int gfs2_lookupi(struct gfs2_inode *dip, struct qstr *name, int is_root,
struct gfs2_inode **ipp);
int gfs2_createi(struct gfs2_holder *ghs, struct qstr *name, unsigned int mode);
int gfs2_unlinki(struct gfs2_inode *dip, struct qstr *name,
struct gfs2_inode *ip, struct gfs2_unlinked *ul);
int gfs2_rmdiri(struct gfs2_inode *dip, struct qstr *name,
struct gfs2_inode *ip, struct gfs2_unlinked *ul);
int gfs2_unlink_ok(struct gfs2_inode *dip, struct qstr *name,
struct gfs2_inode *ip);
int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to);
int gfs2_readlinki(struct gfs2_inode *ip, char **buf, unsigned int *len);
int gfs2_glock_nq_atime(struct gfs2_holder *gh);
int gfs2_glock_nq_m_atime(unsigned int num_gh, struct gfs2_holder *ghs);
void gfs2_try_toss_vnode(struct gfs2_inode *ip);
int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
int gfs2_repermission(struct inode *inode, int mask, struct nameidata *nd);
static inline int gfs2_lookup_simple(struct gfs2_inode *dip, char *name,
struct gfs2_inode **ipp)
{
struct qstr qstr;
memset(&qstr, 0, sizeof(struct qstr));
qstr.name = name;
qstr.len = strlen(name);
return gfs2_lookupi(dip, &qstr, 1, ipp);
}
#endif /* __INODE_DOT_H__ */

382
fs/gfs2/jdata.c Normal file
View File

@ -0,0 +1,382 @@
/*
* 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_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);
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);
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);
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);
gfs2_dinode_out(&ip->i_di, dibh->b_data);
brelse(dibh);
return copied;
fail:
if (copied)
goto out;
return error;
}

52
fs/gfs2/jdata.h Normal file
View File

@ -0,0 +1,52 @@
/*
* 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__ */

235
fs/gfs2/lm.c Normal file
View File

@ -0,0 +1,235 @@
/*
* 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 <linux/delay.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "glock.h"
#include "lm.h"
#include "super.h"
/**
* gfs2_lm_mount - mount a locking protocol
* @sdp: the filesystem
* @args: mount arguements
* @silent: if 1, don't complain if the FS isn't a GFS2 fs
*
* Returns: errno
*/
int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent)
{
char *proto = sdp->sd_proto_name;
char *table = sdp->sd_table_name;
int flags = 0;
int error;
if (sdp->sd_args.ar_spectator)
flags |= LM_MFLAG_SPECTATOR;
fs_info(sdp, "Trying to join cluster \"%s\", \"%s\"\n", proto, table);
error = gfs2_mount_lockproto(proto, table, sdp->sd_args.ar_hostdata,
gfs2_glock_cb, sdp,
GFS2_MIN_LVB_SIZE, flags,
&sdp->sd_lockstruct, &sdp->sd_kobj);
if (error) {
fs_info(sdp, "can't mount proto=%s, table=%s, hostdata=%s\n",
proto, table, sdp->sd_args.ar_hostdata);
goto out;
}
if (gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_lockspace) ||
gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_ops) ||
gfs2_assert_warn(sdp, sdp->sd_lockstruct.ls_lvb_size >=
GFS2_MIN_LVB_SIZE)) {
gfs2_unmount_lockproto(&sdp->sd_lockstruct);
goto out;
}
if (sdp->sd_args.ar_spectator)
snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s.s", table);
else
snprintf(sdp->sd_fsname, GFS2_FSNAME_LEN, "%s.%u", table,
sdp->sd_lockstruct.ls_jid);
fs_info(sdp, "Joined cluster. Now mounting FS...\n");
if ((sdp->sd_lockstruct.ls_flags & LM_LSFLAG_LOCAL) &&
!sdp->sd_args.ar_ignore_local_fs) {
sdp->sd_args.ar_localflocks = 1;
sdp->sd_args.ar_localcaching = 1;
}
out:
return error;
}
void gfs2_lm_others_may_mount(struct gfs2_sbd *sdp)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
sdp->sd_lockstruct.ls_ops->lm_others_may_mount(sdp->sd_lockstruct.ls_lockspace);
}
void gfs2_lm_unmount(struct gfs2_sbd *sdp)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
gfs2_unmount_lockproto(&sdp->sd_lockstruct);
}
int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...)
{
va_list args;
if (test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
return 0;
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
fs_err(sdp, "about to withdraw from the cluster\n");
if (sdp->sd_args.ar_debug)
BUG();
fs_err(sdp, "waiting for outstanding I/O\n");
/* FIXME: suspend dm device so oustanding bio's complete
and all further io requests fail */
fs_err(sdp, "telling LM to withdraw\n");
gfs2_withdraw_lockproto(&sdp->sd_lockstruct);
fs_err(sdp, "withdrawn\n");
dump_stack();
return -1;
}
int gfs2_lm_get_lock(struct gfs2_sbd *sdp, struct lm_lockname *name,
lm_lock_t **lockp)
{
int error;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
error = -EIO;
else
error = sdp->sd_lockstruct.ls_ops->lm_get_lock(sdp->sd_lockstruct.ls_lockspace, name, lockp);
return error;
}
void gfs2_lm_put_lock(struct gfs2_sbd *sdp, lm_lock_t *lock)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
sdp->sd_lockstruct.ls_ops->lm_put_lock(lock);
}
unsigned int gfs2_lm_lock(struct gfs2_sbd *sdp, lm_lock_t *lock,
unsigned int cur_state, unsigned int req_state,
unsigned int flags)
{
int ret;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
ret = 0;
else
ret = sdp->sd_lockstruct.ls_ops->lm_lock(lock,
cur_state,
req_state, flags);
return ret;
}
unsigned int gfs2_lm_unlock(struct gfs2_sbd *sdp, lm_lock_t *lock,
unsigned int cur_state)
{
int ret;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
ret = 0;
else
ret = sdp->sd_lockstruct.ls_ops->lm_unlock(lock, cur_state);
return ret;
}
void gfs2_lm_cancel(struct gfs2_sbd *sdp, lm_lock_t *lock)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
sdp->sd_lockstruct.ls_ops->lm_cancel(lock);
}
int gfs2_lm_hold_lvb(struct gfs2_sbd *sdp, lm_lock_t *lock, char **lvbp)
{
int error;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
error = -EIO;
else
error = sdp->sd_lockstruct.ls_ops->lm_hold_lvb(lock, lvbp);
return error;
}
void gfs2_lm_unhold_lvb(struct gfs2_sbd *sdp, lm_lock_t *lock, char *lvb)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
sdp->sd_lockstruct.ls_ops->lm_unhold_lvb(lock, lvb);
}
void gfs2_lm_sync_lvb(struct gfs2_sbd *sdp, lm_lock_t *lock, char *lvb)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
sdp->sd_lockstruct.ls_ops->lm_sync_lvb(lock, lvb);
}
int gfs2_lm_plock_get(struct gfs2_sbd *sdp, struct lm_lockname *name,
struct file *file, struct file_lock *fl)
{
int error;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
error = -EIO;
else
error = sdp->sd_lockstruct.ls_ops->lm_plock_get(
sdp->sd_lockstruct.ls_lockspace,
name, file, fl);
return error;
}
int gfs2_lm_plock(struct gfs2_sbd *sdp, struct lm_lockname *name,
struct file *file, int cmd, struct file_lock *fl)
{
int error;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
error = -EIO;
else
error = sdp->sd_lockstruct.ls_ops->lm_plock(
sdp->sd_lockstruct.ls_lockspace,
name, file, cmd, fl);
return error;
}
int gfs2_lm_punlock(struct gfs2_sbd *sdp, struct lm_lockname *name,
struct file *file, struct file_lock *fl)
{
int error;
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
error = -EIO;
else
error = sdp->sd_lockstruct.ls_ops->lm_punlock(
sdp->sd_lockstruct.ls_lockspace,
name, file, fl);
return error;
}
void gfs2_lm_recovery_done(struct gfs2_sbd *sdp, unsigned int jid,
unsigned int message)
{
if (likely(!test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
sdp->sd_lockstruct.ls_ops->lm_recovery_done(sdp->sd_lockstruct.ls_lockspace, jid, message);
}

42
fs/gfs2/lm.h Normal file
View File

@ -0,0 +1,42 @@
/*
* 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 __LM_DOT_H__
#define __LM_DOT_H__
int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent);
void gfs2_lm_others_may_mount(struct gfs2_sbd *sdp);
void gfs2_lm_unmount(struct gfs2_sbd *sdp);
int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
int gfs2_lm_get_lock(struct gfs2_sbd *sdp,
struct lm_lockname *name, lm_lock_t **lockp);
void gfs2_lm_put_lock(struct gfs2_sbd *sdp, lm_lock_t *lock);
unsigned int gfs2_lm_lock(struct gfs2_sbd *sdp, lm_lock_t *lock,
unsigned int cur_state, unsigned int req_state,
unsigned int flags);
unsigned int gfs2_lm_unlock(struct gfs2_sbd *sdp, lm_lock_t *lock,
unsigned int cur_state);
void gfs2_lm_cancel(struct gfs2_sbd *sdp, lm_lock_t *lock);
int gfs2_lm_hold_lvb(struct gfs2_sbd *sdp, lm_lock_t *lock, char **lvbp);
void gfs2_lm_unhold_lvb(struct gfs2_sbd *sdp, lm_lock_t *lock, char *lvb);
void gfs2_lm_sync_lvb(struct gfs2_sbd *sdp, lm_lock_t *lock, char *lvb);
int gfs2_lm_plock_get(struct gfs2_sbd *sdp,
struct lm_lockname *name,
struct file *file, struct file_lock *fl);
int gfs2_lm_plock(struct gfs2_sbd *sdp,
struct lm_lockname *name,
struct file *file, int cmd, struct file_lock *fl);
int gfs2_lm_punlock(struct gfs2_sbd *sdp,
struct lm_lockname *name,
struct file *file, struct file_lock *fl);
void gfs2_lm_recovery_done(struct gfs2_sbd *sdp,
unsigned int jid, unsigned int message);
#endif /* __LM_DOT_H__ */

295
fs/gfs2/lm_interface.h Normal file
View File

@ -0,0 +1,295 @@
/*
* 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 __LM_INTERFACE_DOT_H__
#define __LM_INTERFACE_DOT_H__
/*
* Opaque handles represent the lock module's lockspace structure, the lock
* module's lock structures, and GFS's file system (superblock) structure.
*/
typedef void lm_lockspace_t;
typedef void lm_lock_t;
typedef void lm_fsdata_t;
typedef void (*lm_callback_t) (lm_fsdata_t *fsdata, unsigned int type,
void *data);
/*
* lm_mount() flags
*
* LM_MFLAG_SPECTATOR
* GFS is asking to join the filesystem's lockspace, but it doesn't want to
* modify the filesystem. The lock module shouldn't assign a journal to the FS
* mount. It shouldn't send recovery callbacks to the FS mount. If the node
* dies or withdraws, all locks can be wiped immediately.
*/
#define LM_MFLAG_SPECTATOR 0x00000001
/*
* lm_lockstruct flags
*
* LM_LSFLAG_LOCAL
* The lock_nolock module returns LM_LSFLAG_LOCAL to GFS, indicating that GFS
* can make single-node optimizations.
*/
#define LM_LSFLAG_LOCAL 0x00000001
/*
* lm_lockname types
*/
#define LM_TYPE_RESERVED 0x00
#define LM_TYPE_NONDISK 0x01
#define LM_TYPE_INODE 0x02
#define LM_TYPE_RGRP 0x03
#define LM_TYPE_META 0x04
#define LM_TYPE_IOPEN 0x05
#define LM_TYPE_FLOCK 0x06
#define LM_TYPE_PLOCK 0x07
#define LM_TYPE_QUOTA 0x08
#define LM_TYPE_JOURNAL 0x09
/*
* lm_lock() states
*
* SHARED is compatible with SHARED, not with DEFERRED or EX.
* DEFERRED is compatible with DEFERRED, not with SHARED or EX.
*/
#define LM_ST_UNLOCKED 0
#define LM_ST_EXCLUSIVE 1
#define LM_ST_DEFERRED 2
#define LM_ST_SHARED 3
/*
* lm_lock() flags
*
* LM_FLAG_TRY
* Don't wait to acquire the lock if it can't be granted immediately.
*
* LM_FLAG_TRY_1CB
* Send one blocking callback if TRY is set and the lock is not granted.
*
* LM_FLAG_NOEXP
* GFS sets this flag on lock requests it makes while doing journal recovery.
* These special requests should not be blocked due to the recovery like
* ordinary locks would be.
*
* LM_FLAG_ANY
* A SHARED request may also be granted in DEFERRED, or a DEFERRED request may
* also be granted in SHARED. The preferred state is whichever is compatible
* with other granted locks, or the specified state if no other locks exist.
*
* LM_FLAG_PRIORITY
* Override fairness considerations. Suppose a lock is held in a shared state
* and there is a pending request for the deferred state. A shared lock
* request with the priority flag would be allowed to bypass the deferred
* request and directly join the other shared lock. A shared lock request
* without the priority flag might be forced to wait until the deferred
* requested had acquired and released the lock.
*/
#define LM_FLAG_TRY 0x00000001
#define LM_FLAG_TRY_1CB 0x00000002
#define LM_FLAG_NOEXP 0x00000004
#define LM_FLAG_ANY 0x00000008
#define LM_FLAG_PRIORITY 0x00000010
/*
* lm_lock() and lm_async_cb return flags
*
* LM_OUT_ST_MASK
* Masks the lower two bits of lock state in the returned value.
*
* LM_OUT_CACHEABLE
* The lock hasn't been released so GFS can continue to cache data for it.
*
* LM_OUT_CANCELED
* The lock request was canceled.
*
* LM_OUT_ASYNC
* The result of the request will be returned in an LM_CB_ASYNC callback.
*/
#define LM_OUT_ST_MASK 0x00000003
#define LM_OUT_CACHEABLE 0x00000004
#define LM_OUT_CANCELED 0x00000008
#define LM_OUT_ASYNC 0x00000080
#define LM_OUT_ERROR 0x00000100
/*
* lm_callback_t types
*
* LM_CB_NEED_E LM_CB_NEED_D LM_CB_NEED_S
* Blocking callback, a remote node is requesting the given lock in
* EXCLUSIVE, DEFERRED, or SHARED.
*
* LM_CB_NEED_RECOVERY
* The given journal needs to be recovered.
*
* LM_CB_DROPLOCKS
* Reduce the number of cached locks.
*
* LM_CB_ASYNC
* The given lock has been granted.
*/
#define LM_CB_NEED_E 257
#define LM_CB_NEED_D 258
#define LM_CB_NEED_S 259
#define LM_CB_NEED_RECOVERY 260
#define LM_CB_DROPLOCKS 261
#define LM_CB_ASYNC 262
/*
* lm_recovery_done() messages
*/
#define LM_RD_GAVEUP 308
#define LM_RD_SUCCESS 309
struct lm_lockname {
uint64_t ln_number;
unsigned int ln_type;
};
#define lm_name_equal(name1, name2) \
(((name1)->ln_number == (name2)->ln_number) && \
((name1)->ln_type == (name2)->ln_type)) \
struct lm_async_cb {
struct lm_lockname lc_name;
int lc_ret;
};
struct lm_lockstruct;
struct lm_lockops {
char lm_proto_name[256];
/*
* Mount/Unmount
*/
int (*lm_mount) (char *table_name, char *host_data,
lm_callback_t cb, lm_fsdata_t *fsdata,
unsigned int min_lvb_size, int flags,
struct lm_lockstruct *lockstruct,
struct kobject *fskobj);
void (*lm_others_may_mount) (lm_lockspace_t *lockspace);
void (*lm_unmount) (lm_lockspace_t *lockspace);
void (*lm_withdraw) (lm_lockspace_t *lockspace);
/*
* Lock oriented operations
*/
int (*lm_get_lock) (lm_lockspace_t *lockspace,
struct lm_lockname *name, lm_lock_t **lockp);
void (*lm_put_lock) (lm_lock_t *lock);
unsigned int (*lm_lock) (lm_lock_t *lock, unsigned int cur_state,
unsigned int req_state, unsigned int flags);
unsigned int (*lm_unlock) (lm_lock_t *lock, unsigned int cur_state);
void (*lm_cancel) (lm_lock_t *lock);
int (*lm_hold_lvb) (lm_lock_t *lock, char **lvbp);
void (*lm_unhold_lvb) (lm_lock_t *lock, char *lvb);
void (*lm_sync_lvb) (lm_lock_t *lock, char *lvb);
/*
* Posix Lock oriented operations
*/
int (*lm_plock_get) (lm_lockspace_t *lockspace,
struct lm_lockname *name,
struct file *file, struct file_lock *fl);
int (*lm_plock) (lm_lockspace_t *lockspace,
struct lm_lockname *name,
struct file *file, int cmd, struct file_lock *fl);
int (*lm_punlock) (lm_lockspace_t *lockspace,
struct lm_lockname *name,
struct file *file, struct file_lock *fl);
/*
* Client oriented operations
*/
void (*lm_recovery_done) (lm_lockspace_t *lockspace, unsigned int jid,
unsigned int message);
struct module *lm_owner;
};
/*
* lm_mount() return values
*
* ls_jid - the journal ID this node should use
* ls_first - this node is the first to mount the file system
* ls_lvb_size - size in bytes of lock value blocks
* ls_lockspace - lock module's context for this file system
* ls_ops - lock module's functions
* ls_flags - lock module features
*/
struct lm_lockstruct {
unsigned int ls_jid;
unsigned int ls_first;
unsigned int ls_lvb_size;
lm_lockspace_t *ls_lockspace;
struct lm_lockops *ls_ops;
int ls_flags;
};
void __init gfs2_init_lmh(void);
/*
* Lock module bottom interface. A lock module makes itself available to GFS
* with these functions.
*
* For the time being, we copy the gfs1 lock module bottom interface so the
* same lock modules can be used with both gfs1 and gfs2 (it won't be possible
* to load both gfs1 and gfs2 at once.) Eventually the lock modules will fork
* for gfs1/gfs2 and this API can change to the gfs2_ prefix.
*/
int gfs_register_lockproto(struct lm_lockops *proto);
void gfs_unregister_lockproto(struct lm_lockops *proto);
/*
* Lock module top interface. GFS calls these functions when mounting or
* unmounting a file system.
*/
int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data,
lm_callback_t cb, lm_fsdata_t *fsdata,
unsigned int min_lvb_size, int flags,
struct lm_lockstruct *lockstruct,
struct kobject *fskobj);
void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct);
void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct);
#endif /* __LM_INTERFACE_DOT_H__ */

192
fs/gfs2/locking.c Normal file
View File

@ -0,0 +1,192 @@
/*
* 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/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/kmod.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include "lm_interface.h"
struct lmh_wrapper {
struct list_head lw_list;
struct lm_lockops *lw_ops;
};
/* List of registered low-level locking protocols. A file system selects one
of them by name at mount time, e.g. lock_nolock, lock_dlm. */
static struct list_head lmh_list;
static struct semaphore lmh_lock;
/**
* gfs_register_lockproto - Register a low-level locking protocol
* @proto: the protocol definition
*
* Returns: 0 on success, -EXXX on failure
*/
int gfs_register_lockproto(struct lm_lockops *proto)
{
struct lmh_wrapper *lw;
down(&lmh_lock);
list_for_each_entry(lw, &lmh_list, lw_list) {
if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) {
up(&lmh_lock);
printk("GFS2: protocol %s already exists\n",
proto->lm_proto_name);
return -EEXIST;
}
}
lw = kmalloc(sizeof(struct lmh_wrapper), GFP_KERNEL);
if (!lw) {
up(&lmh_lock);
return -ENOMEM;
}
memset(lw, 0, sizeof(struct lmh_wrapper));
lw->lw_ops = proto;
list_add(&lw->lw_list, &lmh_list);
up(&lmh_lock);
return 0;
}
/**
* gfs_unregister_lockproto - Unregister a low-level locking protocol
* @proto: the protocol definition
*
*/
void gfs_unregister_lockproto(struct lm_lockops *proto)
{
struct lmh_wrapper *lw;
down(&lmh_lock);
list_for_each_entry(lw, &lmh_list, lw_list) {
if (!strcmp(lw->lw_ops->lm_proto_name, proto->lm_proto_name)) {
list_del(&lw->lw_list);
up(&lmh_lock);
kfree(lw);
return;
}
}
up(&lmh_lock);
printk("GFS2: can't unregister lock protocol %s\n",
proto->lm_proto_name);
}
/**
* gfs2_mount_lockproto - Mount a lock protocol
* @proto_name - the name of the protocol
* @table_name - the name of the lock space
* @host_data - data specific to this host
* @cb - the callback to the code using the lock module
* @fsdata - data to pass back with the callback
* @min_lvb_size - the mininum LVB size that the caller can deal with
* @flags - LM_MFLAG_*
* @lockstruct - a structure returned describing the mount
*
* Returns: 0 on success, -EXXX on failure
*/
int gfs2_mount_lockproto(char *proto_name, char *table_name, char *host_data,
lm_callback_t cb, lm_fsdata_t *fsdata,
unsigned int min_lvb_size, int flags,
struct lm_lockstruct *lockstruct,
struct kobject *fskobj)
{
struct lmh_wrapper *lw = NULL;
int try = 0;
int error, found;
retry:
down(&lmh_lock);
found = 0;
list_for_each_entry(lw, &lmh_list, lw_list) {
if (!strcmp(lw->lw_ops->lm_proto_name, proto_name)) {
found = 1;
break;
}
}
if (!found) {
if (!try && capable(CAP_SYS_MODULE)) {
try = 1;
up(&lmh_lock);
request_module(proto_name);
goto retry;
}
printk("GFS2: can't find protocol %s\n", proto_name);
error = -ENOENT;
goto out;
}
if (!try_module_get(lw->lw_ops->lm_owner)) {
try = 0;
up(&lmh_lock);
msleep(1000);
goto retry;
}
error = lw->lw_ops->lm_mount(table_name, host_data, cb, fsdata,
min_lvb_size, flags, lockstruct, fskobj);
if (error)
module_put(lw->lw_ops->lm_owner);
out:
up(&lmh_lock);
return error;
}
void gfs2_unmount_lockproto(struct lm_lockstruct *lockstruct)
{
down(&lmh_lock);
lockstruct->ls_ops->lm_unmount(lockstruct->ls_lockspace);
if (lockstruct->ls_ops->lm_owner)
module_put(lockstruct->ls_ops->lm_owner);
up(&lmh_lock);
}
/**
* gfs2_withdraw_lockproto - abnormally unmount a lock module
* @lockstruct: the lockstruct passed into mount
*
*/
void gfs2_withdraw_lockproto(struct lm_lockstruct *lockstruct)
{
down(&lmh_lock);
lockstruct->ls_ops->lm_withdraw(lockstruct->ls_lockspace);
if (lockstruct->ls_ops->lm_owner)
module_put(lockstruct->ls_ops->lm_owner);
up(&lmh_lock);
}
void __init gfs2_init_lmh(void)
{
init_MUTEX(&lmh_lock);
INIT_LIST_HEAD(&lmh_list);
}
EXPORT_SYMBOL_GPL(gfs_register_lockproto);
EXPORT_SYMBOL_GPL(gfs_unregister_lockproto);

659
fs/gfs2/log.c Normal file
View File

@ -0,0 +1,659 @@
/*
* 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 "gfs2.h"
#include "bmap.h"
#include "glock.h"
#include "log.h"
#include "lops.h"
#include "meta_io.h"
#define PULL 1
static inline int is_done(struct gfs2_sbd *sdp, atomic_t *a)
{
int done;
gfs2_log_lock(sdp);
done = atomic_read(a) ? 0 : 1;
gfs2_log_unlock(sdp);
return done;
}
static void do_lock_wait(struct gfs2_sbd *sdp, wait_queue_head_t *wq,
atomic_t *a)
{
gfs2_log_unlock(sdp);
wait_event(*wq, is_done(sdp, a));
gfs2_log_lock(sdp);
}
static void lock_for_trans(struct gfs2_sbd *sdp)
{
gfs2_log_lock(sdp);
do_lock_wait(sdp, &sdp->sd_log_trans_wq, &sdp->sd_log_flush_count);
atomic_inc(&sdp->sd_log_trans_count);
gfs2_log_unlock(sdp);
}
static void unlock_from_trans(struct gfs2_sbd *sdp)
{
gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_trans_count));
if (atomic_dec_and_test(&sdp->sd_log_trans_count))
wake_up(&sdp->sd_log_flush_wq);
}
void gfs2_lock_for_flush(struct gfs2_sbd *sdp)
{
gfs2_log_lock(sdp);
atomic_inc(&sdp->sd_log_flush_count);
do_lock_wait(sdp, &sdp->sd_log_flush_wq, &sdp->sd_log_trans_count);
gfs2_log_unlock(sdp);
}
void gfs2_unlock_from_flush(struct gfs2_sbd *sdp)
{
gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_flush_count));
if (atomic_dec_and_test(&sdp->sd_log_flush_count))
wake_up(&sdp->sd_log_trans_wq);
}
/**
* gfs2_struct2blk - compute stuff
* @sdp: the filesystem
* @nstruct: the number of structures
* @ssize: the size of the structures
*
* Compute the number of log descriptor blocks needed to hold a certain number
* of structures of a certain size.
*
* Returns: the number of blocks needed (minimum is always 1)
*/
unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct,
unsigned int ssize)
{
unsigned int blks;
unsigned int first, second;
blks = 1;
first = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / ssize;
if (nstruct > first) {
second = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / ssize;
blks += DIV_RU(nstruct - first, second);
}
return blks;
}
void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags)
{
struct list_head *head = &sdp->sd_ail1_list;
uint64_t sync_gen;
struct list_head *first, *tmp;
struct gfs2_ail *first_ai, *ai;
gfs2_log_lock(sdp);
if (list_empty(head)) {
gfs2_log_unlock(sdp);
return;
}
sync_gen = sdp->sd_ail_sync_gen++;
first = head->prev;
first_ai = list_entry(first, struct gfs2_ail, ai_list);
first_ai->ai_sync_gen = sync_gen;
gfs2_ail1_start_one(sdp, first_ai);
if (flags & DIO_ALL)
first = NULL;
for (;;) {
if (first &&
(head->prev != first ||
gfs2_ail1_empty_one(sdp, first_ai, 0)))
break;
for (tmp = head->prev; tmp != head; tmp = tmp->prev) {
ai = list_entry(tmp, struct gfs2_ail, ai_list);
if (ai->ai_sync_gen >= sync_gen)
continue;
ai->ai_sync_gen = sync_gen;
gfs2_ail1_start_one(sdp, ai);
break;
}
if (tmp == head)
break;
}
gfs2_log_unlock(sdp);
}
int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags)
{
struct gfs2_ail *ai, *s;
int ret;
gfs2_log_lock(sdp);
list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) {
if (gfs2_ail1_empty_one(sdp, ai, flags))
list_move(&ai->ai_list, &sdp->sd_ail2_list);
else if (!(flags & DIO_ALL))
break;
}
ret = list_empty(&sdp->sd_ail1_list);
gfs2_log_unlock(sdp);
return ret;
}
static void ail2_empty(struct gfs2_sbd *sdp, unsigned int new_tail)
{
struct gfs2_ail *ai, *safe;
unsigned int old_tail = sdp->sd_log_tail;
int wrap = (new_tail < old_tail);
int a, b, rm;
gfs2_log_lock(sdp);
list_for_each_entry_safe(ai, safe, &sdp->sd_ail2_list, ai_list) {
a = (old_tail <= ai->ai_first);
b = (ai->ai_first < new_tail);
rm = (wrap) ? (a || b) : (a && b);
if (!rm)
continue;
gfs2_ail2_empty_one(sdp, ai);
list_del(&ai->ai_list);
gfs2_assert_warn(sdp, list_empty(&ai->ai_ail1_list));
gfs2_assert_warn(sdp, list_empty(&ai->ai_ail2_list));
kfree(ai);
}
gfs2_log_unlock(sdp);
}
/**
* gfs2_log_reserve - Make a log reservation
* @sdp: The GFS2 superblock
* @blks: The number of blocks to reserve
*
* Returns: errno
*/
int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks)
{
LIST_HEAD(list);
unsigned int try = 0;
if (gfs2_assert_warn(sdp, blks) ||
gfs2_assert_warn(sdp, blks <= sdp->sd_jdesc->jd_blocks))
return -EINVAL;
for (;;) {
gfs2_log_lock(sdp);
if (list_empty(&list)) {
list_add_tail(&list, &sdp->sd_log_blks_list);
while (sdp->sd_log_blks_list.next != &list) {
DECLARE_WAITQUEUE(__wait_chan, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&sdp->sd_log_blks_wait,
&__wait_chan);
gfs2_log_unlock(sdp);
schedule();
gfs2_log_lock(sdp);
remove_wait_queue(&sdp->sd_log_blks_wait,
&__wait_chan);
set_current_state(TASK_RUNNING);
}
}
/* Never give away the last block so we can
always pull the tail if we need to. */
if (sdp->sd_log_blks_free > blks) {
sdp->sd_log_blks_free -= blks;
list_del(&list);
gfs2_log_unlock(sdp);
wake_up(&sdp->sd_log_blks_wait);
break;
}
gfs2_log_unlock(sdp);
gfs2_ail1_empty(sdp, 0);
gfs2_log_flush(sdp);
if (try++)
gfs2_ail1_start(sdp, 0);
}
lock_for_trans(sdp);
return 0;
}
/**
* gfs2_log_release - Release a given number of log blocks
* @sdp: The GFS2 superblock
* @blks: The number of blocks
*
*/
void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks)
{
unlock_from_trans(sdp);
gfs2_log_lock(sdp);
sdp->sd_log_blks_free += blks;
gfs2_assert_withdraw(sdp,
sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks);
gfs2_log_unlock(sdp);
}
static uint64_t log_bmap(struct gfs2_sbd *sdp, unsigned int lbn)
{
int new = 0;
uint64_t dbn;
int error;
error = gfs2_block_map(sdp->sd_jdesc->jd_inode, lbn, &new, &dbn, NULL);
gfs2_assert_withdraw(sdp, !error && dbn);
return dbn;
}
/**
* log_distance - Compute distance between two journal blocks
* @sdp: The GFS2 superblock
* @newer: The most recent journal block of the pair
* @older: The older journal block of the pair
*
* Compute the distance (in the journal direction) between two
* blocks in the journal
*
* Returns: the distance in blocks
*/
static inline unsigned int log_distance(struct gfs2_sbd *sdp,
unsigned int newer,
unsigned int older)
{
int dist;
dist = newer - older;
if (dist < 0)
dist += sdp->sd_jdesc->jd_blocks;
return dist;
}
static unsigned int current_tail(struct gfs2_sbd *sdp)
{
struct gfs2_ail *ai;
unsigned int tail;
gfs2_log_lock(sdp);
if (list_empty(&sdp->sd_ail1_list))
tail = sdp->sd_log_head;
else {
ai = list_entry(sdp->sd_ail1_list.prev,
struct gfs2_ail, ai_list);
tail = ai->ai_first;
}
gfs2_log_unlock(sdp);
return tail;
}
static inline void log_incr_head(struct gfs2_sbd *sdp)
{
if (sdp->sd_log_flush_head == sdp->sd_log_tail)
gfs2_assert_withdraw(sdp,
sdp->sd_log_flush_head == sdp->sd_log_head);
if (++sdp->sd_log_flush_head == sdp->sd_jdesc->jd_blocks) {
sdp->sd_log_flush_head = 0;
sdp->sd_log_flush_wrapped = 1;
}
}
/**
* gfs2_log_get_buf - Get and initialize a buffer to use for log control data
* @sdp: The GFS2 superblock
*
* Returns: the buffer_head
*/
struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp)
{
uint64_t blkno = log_bmap(sdp, sdp->sd_log_flush_head);
struct gfs2_log_buf *lb;
struct buffer_head *bh;
lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_KERNEL | __GFP_NOFAIL);
list_add(&lb->lb_list, &sdp->sd_log_flush_list);
bh = lb->lb_bh = sb_getblk(sdp->sd_vfs, blkno);
lock_buffer(bh);
memset(bh->b_data, 0, bh->b_size);
set_buffer_uptodate(bh);
clear_buffer_dirty(bh);
unlock_buffer(bh);
log_incr_head(sdp);
return bh;
}
/**
* gfs2_log_fake_buf - Build a fake buffer head to write metadata buffer to log
* @sdp: the filesystem
* @data: the data the buffer_head should point to
*
* Returns: the log buffer descriptor
*/
struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
struct buffer_head *real)
{
uint64_t blkno = log_bmap(sdp, sdp->sd_log_flush_head);
struct gfs2_log_buf *lb;
struct buffer_head *bh;
lb = kzalloc(sizeof(struct gfs2_log_buf), GFP_KERNEL | __GFP_NOFAIL);
list_add(&lb->lb_list, &sdp->sd_log_flush_list);
lb->lb_real = real;
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));
bh->b_blocknr = blkno;
bh->b_size = sdp->sd_sb.sb_bsize;
bh->b_bdev = sdp->sd_vfs->s_bdev;
log_incr_head(sdp);
return bh;
}
static void log_pull_tail(struct gfs2_sbd *sdp, unsigned int new_tail, int pull)
{
unsigned int dist = log_distance(sdp, new_tail, sdp->sd_log_tail);
ail2_empty(sdp, new_tail);
gfs2_log_lock(sdp);
sdp->sd_log_blks_free += dist - ((pull) ? 1 : 0);
gfs2_assert_withdraw(sdp,
sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks);
gfs2_log_unlock(sdp);
sdp->sd_log_tail = new_tail;
}
/**
* log_write_header - Get and initialize a journal header buffer
* @sdp: The GFS2 superblock
*
* Returns: the initialized log buffer descriptor
*/
static void log_write_header(struct gfs2_sbd *sdp, uint32_t flags, int pull)
{
uint64_t blkno = log_bmap(sdp, sdp->sd_log_flush_head);
struct buffer_head *bh;
struct gfs2_log_header *lh;
unsigned int tail;
uint32_t hash;
atomic_inc(&sdp->sd_log_flush_ondisk);
bh = sb_getblk(sdp->sd_vfs, blkno);
lock_buffer(bh);
memset(bh->b_data, 0, bh->b_size);
set_buffer_uptodate(bh);
clear_buffer_dirty(bh);
unlock_buffer(bh);
gfs2_ail1_empty(sdp, 0);
tail = current_tail(sdp);
lh = (struct gfs2_log_header *)bh->b_data;
memset(lh, 0, sizeof(struct gfs2_log_header));
lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
lh->lh_header.mh_type = cpu_to_be16(GFS2_METATYPE_LH);
lh->lh_header.mh_format = cpu_to_be16(GFS2_FORMAT_LH);
lh->lh_sequence = be64_to_cpu(sdp->sd_log_sequence++);
lh->lh_flags = be32_to_cpu(flags);
lh->lh_tail = be32_to_cpu(tail);
lh->lh_blkno = be32_to_cpu(sdp->sd_log_flush_head);
hash = gfs2_disk_hash(bh->b_data, sizeof(struct gfs2_log_header));
lh->lh_hash = cpu_to_be32(hash);
set_buffer_dirty(bh);
if (sync_dirty_buffer(bh))
gfs2_io_error_bh(sdp, bh);
brelse(bh);
if (sdp->sd_log_tail != tail)
log_pull_tail(sdp, tail, pull);
else
gfs2_assert_withdraw(sdp, !pull);
sdp->sd_log_idle = (tail == sdp->sd_log_flush_head);
log_incr_head(sdp);
}
static void log_flush_commit(struct gfs2_sbd *sdp)
{
struct list_head *head = &sdp->sd_log_flush_list;
struct gfs2_log_buf *lb;
struct buffer_head *bh;
unsigned int d;
d = log_distance(sdp, sdp->sd_log_flush_head, sdp->sd_log_head);
gfs2_assert_withdraw(sdp, d + 1 == sdp->sd_log_blks_reserved);
while (!list_empty(head)) {
lb = list_entry(head->next, struct gfs2_log_buf, lb_list);
list_del(&lb->lb_list);
bh = lb->lb_bh;
wait_on_buffer(bh);
if (!buffer_uptodate(bh))
gfs2_io_error_bh(sdp, bh);
if (lb->lb_real) {
while (atomic_read(&bh->b_count) != 1) /* Grrrr... */
schedule();
free_buffer_head(bh);
} else
brelse(bh);
kfree(lb);
}
log_write_header(sdp, 0, 0);
}
/**
* gfs2_log_flush_i - flush incore transaction(s)
* @sdp: the filesystem
* @gl: The glock structure to flush. If NULL, flush the whole incore log
*
*/
void gfs2_log_flush_i(struct gfs2_sbd *sdp, struct gfs2_glock *gl)
{
struct gfs2_ail *ai;
atomic_inc(&sdp->sd_log_flush_incore);
ai = kzalloc(sizeof(struct gfs2_ail), GFP_KERNEL | __GFP_NOFAIL);
INIT_LIST_HEAD(&ai->ai_ail1_list);
INIT_LIST_HEAD(&ai->ai_ail2_list);
gfs2_lock_for_flush(sdp);
down(&sdp->sd_log_flush_lock);
gfs2_assert_withdraw(sdp,
sdp->sd_log_num_buf == sdp->sd_log_commited_buf);
gfs2_assert_withdraw(sdp,
sdp->sd_log_num_revoke == sdp->sd_log_commited_revoke);
if (gl && list_empty(&gl->gl_le.le_list)) {
up(&sdp->sd_log_flush_lock);
gfs2_unlock_from_flush(sdp);
kfree(ai);
return;
}
sdp->sd_log_flush_head = sdp->sd_log_head;
sdp->sd_log_flush_wrapped = 0;
ai->ai_first = sdp->sd_log_flush_head;
lops_before_commit(sdp);
if (!list_empty(&sdp->sd_log_flush_list))
log_flush_commit(sdp);
else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle)
log_write_header(sdp, 0, PULL);
lops_after_commit(sdp, ai);
sdp->sd_log_head = sdp->sd_log_flush_head;
if (sdp->sd_log_flush_wrapped)
sdp->sd_log_wraps++;
sdp->sd_log_blks_reserved =
sdp->sd_log_commited_buf =
sdp->sd_log_commited_revoke = 0;
gfs2_log_lock(sdp);
if (!list_empty(&ai->ai_ail1_list)) {
list_add(&ai->ai_list, &sdp->sd_ail1_list);
ai = NULL;
}
gfs2_log_unlock(sdp);
up(&sdp->sd_log_flush_lock);
sdp->sd_vfs->s_dirt = 0;
gfs2_unlock_from_flush(sdp);
kfree(ai);
}
static void log_refund(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
unsigned int reserved = 1;
unsigned int old;
gfs2_log_lock(sdp);
sdp->sd_log_commited_buf += tr->tr_num_buf_new - tr->tr_num_buf_rm;
gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_buf) >= 0);
sdp->sd_log_commited_revoke += tr->tr_num_revoke - tr->tr_num_revoke_rm;
gfs2_assert_withdraw(sdp, ((int)sdp->sd_log_commited_revoke) >= 0);
if (sdp->sd_log_commited_buf)
reserved += 1 + sdp->sd_log_commited_buf + sdp->sd_log_commited_buf/503;
if (sdp->sd_log_commited_revoke)
reserved += gfs2_struct2blk(sdp, sdp->sd_log_commited_revoke,
sizeof(uint64_t));
old = sdp->sd_log_blks_free;
sdp->sd_log_blks_free += tr->tr_reserved -
(reserved - sdp->sd_log_blks_reserved);
gfs2_assert_withdraw(sdp,
sdp->sd_log_blks_free >= old);
gfs2_assert_withdraw(sdp,
sdp->sd_log_blks_free <= sdp->sd_jdesc->jd_blocks);
sdp->sd_log_blks_reserved = reserved;
gfs2_log_unlock(sdp);
}
/**
* gfs2_log_commit - Commit a transaction to the log
* @sdp: the filesystem
* @tr: the transaction
*
* Returns: errno
*/
void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
log_refund(sdp, tr);
lops_incore_commit(sdp, tr);
sdp->sd_vfs->s_dirt = 1;
unlock_from_trans(sdp);
kfree(tr);
gfs2_log_lock(sdp);
if (sdp->sd_log_num_buf > gfs2_tune_get(sdp, gt_incore_log_blocks)) {
gfs2_log_unlock(sdp);
gfs2_log_flush(sdp);
} else
gfs2_log_unlock(sdp);
}
/**
* gfs2_log_shutdown - write a shutdown header into a journal
* @sdp: the filesystem
*
*/
void gfs2_log_shutdown(struct gfs2_sbd *sdp)
{
down(&sdp->sd_log_flush_lock);
gfs2_assert_withdraw(sdp, !atomic_read(&sdp->sd_log_trans_count));
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_revoke);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_rg);
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_databuf);
gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail1_list));
sdp->sd_log_flush_head = sdp->sd_log_head;
sdp->sd_log_flush_wrapped = 0;
log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT, 0);
gfs2_assert_withdraw(sdp, sdp->sd_log_blks_free ==
sdp->sd_jdesc->jd_blocks);
gfs2_assert_withdraw(sdp, sdp->sd_log_head == sdp->sd_log_tail);
gfs2_assert_withdraw(sdp, list_empty(&sdp->sd_ail2_list));
sdp->sd_log_head = sdp->sd_log_flush_head;
if (sdp->sd_log_flush_wrapped)
sdp->sd_log_wraps++;
sdp->sd_log_tail = sdp->sd_log_head;
up(&sdp->sd_log_flush_lock);
}

68
fs/gfs2/log.h Normal file
View File

@ -0,0 +1,68 @@
/*
* 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 __LOG_DOT_H__
#define __LOG_DOT_H__
/**
* gfs2_log_lock - acquire the right to mess with the log manager
* @sdp: the filesystem
*
*/
static inline void gfs2_log_lock(struct gfs2_sbd *sdp)
{
spin_lock(&sdp->sd_log_lock);
}
/**
* gfs2_log_unlock - release the right to mess with the log manager
* @sdp: the filesystem
*
*/
static inline void gfs2_log_unlock(struct gfs2_sbd *sdp)
{
spin_unlock(&sdp->sd_log_lock);
}
static inline void gfs2_log_pointers_init(struct gfs2_sbd *sdp,
unsigned int value)
{
if (++value == sdp->sd_jdesc->jd_blocks) {
value = 0;
sdp->sd_log_wraps++;
}
sdp->sd_log_head = sdp->sd_log_tail = value;
}
void gfs2_lock_for_flush(struct gfs2_sbd *sdp);
void gfs2_unlock_from_flush(struct gfs2_sbd *sdp);
unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct,
unsigned int ssize);
void gfs2_ail1_start(struct gfs2_sbd *sdp, int flags);
int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags);
int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks);
void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks);
struct buffer_head *gfs2_log_get_buf(struct gfs2_sbd *sdp);
struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
struct buffer_head *real);
#define gfs2_log_flush(sdp) gfs2_log_flush_i((sdp), NULL)
#define gfs2_log_flush_glock(gl) gfs2_log_flush_i((gl)->gl_sbd, (gl))
void gfs2_log_flush_i(struct gfs2_sbd *sdp, struct gfs2_glock *gl);
void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
void gfs2_log_shutdown(struct gfs2_sbd *sdp);
#endif /* __LOG_DOT_H__ */

534
fs/gfs2/lops.c Normal file
View File

@ -0,0 +1,534 @@
/*
* 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 "gfs2.h"
#include "glock.h"
#include "log.h"
#include "lops.h"
#include "meta_io.h"
#include "recovery.h"
#include "rgrp.h"
#include "trans.h"
static void glock_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
struct gfs2_glock *gl;
get_transaction->tr_touched = 1;
if (!list_empty(&le->le_list))
return;
gl = container_of(le, struct gfs2_glock, gl_le);
if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(gl)))
return;
gfs2_glock_hold(gl);
set_bit(GLF_DIRTY, &gl->gl_flags);
gfs2_log_lock(sdp);
sdp->sd_log_num_gl++;
list_add(&le->le_list, &sdp->sd_log_le_gl);
gfs2_log_unlock(sdp);
}
static void glock_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
struct list_head *head = &sdp->sd_log_le_gl;
struct gfs2_glock *gl;
while (!list_empty(head)) {
gl = list_entry(head->next, struct gfs2_glock, gl_le.le_list);
list_del_init(&gl->gl_le.le_list);
sdp->sd_log_num_gl--;
gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(gl));
gfs2_glock_put(gl);
}
gfs2_assert_warn(sdp, !sdp->sd_log_num_gl);
}
static void buf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
struct gfs2_bufdata *bd = container_of(le, struct gfs2_bufdata, bd_le);
struct gfs2_trans *tr;
if (!list_empty(&bd->bd_list_tr))
return;
tr = get_transaction;
tr->tr_touched = 1;
tr->tr_num_buf++;
list_add(&bd->bd_list_tr, &tr->tr_list_buf);
if (!list_empty(&le->le_list))
return;
gfs2_trans_add_gl(bd->bd_gl);
gfs2_meta_check(sdp, bd->bd_bh);
gfs2_meta_pin(sdp, bd->bd_bh);
gfs2_log_lock(sdp);
sdp->sd_log_num_buf++;
list_add(&le->le_list, &sdp->sd_log_le_buf);
gfs2_log_unlock(sdp);
tr->tr_num_buf_new++;
}
static void buf_lo_incore_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
struct list_head *head = &tr->tr_list_buf;
struct gfs2_bufdata *bd;
while (!list_empty(head)) {
bd = list_entry(head->next, struct gfs2_bufdata, bd_list_tr);
list_del_init(&bd->bd_list_tr);
tr->tr_num_buf--;
}
gfs2_assert_warn(sdp, !tr->tr_num_buf);
}
static void buf_lo_before_commit(struct gfs2_sbd *sdp)
{
struct buffer_head *bh;
struct gfs2_log_descriptor *ld;
struct gfs2_bufdata *bd1 = NULL, *bd2;
unsigned int total = sdp->sd_log_num_buf;
unsigned int offset = sizeof(struct gfs2_log_descriptor);
unsigned int limit;
unsigned int num;
unsigned n;
__be64 *ptr;
offset += (sizeof(__be64) - 1);
offset &= ~(sizeof(__be64) - 1);
limit = (sdp->sd_sb.sb_bsize - offset)/sizeof(__be64);
/* for 4k blocks, limit = 503 */
bd1 = bd2 = list_prepare_entry(bd1, &sdp->sd_log_le_buf, bd_le.le_list);
while(total) {
num = total;
if (total > limit)
num = limit;
bh = gfs2_log_get_buf(sdp);
ld = (struct gfs2_log_descriptor *)bh->b_data;
ptr = (__be64 *)(bh->b_data + offset);
ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
ld->ld_header.mh_type = cpu_to_be16(GFS2_METATYPE_LD);
ld->ld_header.mh_format = cpu_to_be16(GFS2_FORMAT_LD);
ld->ld_type = cpu_to_be32(GFS2_LOG_DESC_METADATA);
ld->ld_length = cpu_to_be32(num + 1);
ld->ld_data1 = cpu_to_be32(num);
ld->ld_data2 = cpu_to_be32(0);
memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved));
n = 0;
list_for_each_entry_continue(bd1, &sdp->sd_log_le_buf, bd_le.le_list) {
*ptr++ = cpu_to_be64(bd1->bd_bh->b_blocknr);
if (++n >= num)
break;
}
set_buffer_dirty(bh);
ll_rw_block(WRITE, 1, &bh);
n = 0;
list_for_each_entry_continue(bd2, &sdp->sd_log_le_buf, bd_le.le_list) {
bh = gfs2_log_fake_buf(sdp, bd2->bd_bh);
set_buffer_dirty(bh);
ll_rw_block(WRITE, 1, &bh);
if (++n >= num)
break;
}
total -= num;
}
}
static void buf_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
struct list_head *head = &sdp->sd_log_le_buf;
struct gfs2_bufdata *bd;
while (!list_empty(head)) {
bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list);
list_del_init(&bd->bd_le.le_list);
sdp->sd_log_num_buf--;
gfs2_meta_unpin(sdp, bd->bd_bh, ai);
}
gfs2_assert_warn(sdp, !sdp->sd_log_num_buf);
}
static void buf_lo_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header *head, int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
if (pass != 0)
return;
sdp->sd_found_blocks = 0;
sdp->sd_replayed_blocks = 0;
}
static int buf_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
struct gfs2_log_descriptor *ld, __be64 *ptr,
int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
struct gfs2_glock *gl = jd->jd_inode->i_gl;
unsigned int blks = be32_to_cpu(ld->ld_data1);
struct buffer_head *bh_log, *bh_ip;
uint64_t blkno;
int error = 0;
if (pass != 1 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_METADATA)
return 0;
gfs2_replay_incr_blk(sdp, &start);
for (; blks; gfs2_replay_incr_blk(sdp, &start), blks--) {
blkno = be64_to_cpu(*ptr++);
sdp->sd_found_blocks++;
if (gfs2_revoke_check(sdp, blkno, start))
continue;
error = gfs2_replay_read_block(jd, start, &bh_log);
if (error)
return error;
bh_ip = gfs2_meta_new(gl, blkno);
memcpy(bh_ip->b_data, bh_log->b_data, bh_log->b_size);
if (gfs2_meta_check(sdp, bh_ip))
error = -EIO;
else
mark_buffer_dirty(bh_ip);
brelse(bh_log);
brelse(bh_ip);
if (error)
break;
sdp->sd_replayed_blocks++;
}
return error;
}
static void buf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
if (error) {
gfs2_meta_sync(jd->jd_inode->i_gl, DIO_START | DIO_WAIT);
return;
}
if (pass != 1)
return;
gfs2_meta_sync(jd->jd_inode->i_gl, DIO_START | DIO_WAIT);
fs_info(sdp, "jid=%u: Replayed %u of %u blocks\n",
jd->jd_jid, sdp->sd_replayed_blocks, sdp->sd_found_blocks);
}
static void revoke_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
struct gfs2_trans *tr;
tr = get_transaction;
tr->tr_touched = 1;
tr->tr_num_revoke++;
gfs2_log_lock(sdp);
sdp->sd_log_num_revoke++;
list_add(&le->le_list, &sdp->sd_log_le_revoke);
gfs2_log_unlock(sdp);
}
static void revoke_lo_before_commit(struct gfs2_sbd *sdp)
{
struct gfs2_log_descriptor *ld;
struct gfs2_meta_header *mh;
struct buffer_head *bh;
unsigned int offset;
struct list_head *head = &sdp->sd_log_le_revoke;
struct gfs2_revoke *rv;
if (!sdp->sd_log_num_revoke)
return;
bh = gfs2_log_get_buf(sdp);
ld = (struct gfs2_log_descriptor *)bh->b_data;
ld->ld_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
ld->ld_header.mh_type = cpu_to_be16(GFS2_METATYPE_LD);
ld->ld_header.mh_format = cpu_to_be16(GFS2_FORMAT_LD);
ld->ld_type = cpu_to_be32(GFS2_LOG_DESC_REVOKE);
ld->ld_length = cpu_to_be32(gfs2_struct2blk(sdp, sdp->sd_log_num_revoke, sizeof(uint64_t)));
ld->ld_data1 = cpu_to_be32(sdp->sd_log_num_revoke);
ld->ld_data2 = cpu_to_be32(0);
memset(ld->ld_reserved, 0, sizeof(ld->ld_reserved));
offset = sizeof(struct gfs2_log_descriptor);
while (!list_empty(head)) {
rv = list_entry(head->next, struct gfs2_revoke, rv_le.le_list);
list_del(&rv->rv_le.le_list);
sdp->sd_log_num_revoke--;
if (offset + sizeof(uint64_t) > sdp->sd_sb.sb_bsize) {
set_buffer_dirty(bh);
ll_rw_block(WRITE, 1, &bh);
bh = gfs2_log_get_buf(sdp);
mh = (struct gfs2_meta_header *)bh->b_data;
mh->mh_magic = cpu_to_be32(GFS2_MAGIC);
mh->mh_type = cpu_to_be16(GFS2_METATYPE_LB);
mh->mh_format = cpu_to_be16(GFS2_FORMAT_LB);
offset = sizeof(struct gfs2_meta_header);
}
*(__be64 *)(bh->b_data + offset) = cpu_to_be64(rv->rv_blkno);
kfree(rv);
offset += sizeof(uint64_t);
}
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
set_buffer_dirty(bh);
ll_rw_block(WRITE, 1, &bh);
}
static void revoke_lo_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header *head, int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
if (pass != 0)
return;
sdp->sd_found_revokes = 0;
sdp->sd_replay_tail = head->lh_tail;
}
static int revoke_lo_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
struct gfs2_log_descriptor *ld, __be64 *ptr,
int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
unsigned int blks = be32_to_cpu(ld->ld_length);
unsigned int revokes = be32_to_cpu(ld->ld_data1);
struct buffer_head *bh;
unsigned int offset;
uint64_t blkno;
int first = 1;
int error;
if (pass != 0 || be32_to_cpu(ld->ld_type) != GFS2_LOG_DESC_REVOKE)
return 0;
offset = sizeof(struct gfs2_log_descriptor);
for (; blks; gfs2_replay_incr_blk(sdp, &start), blks--) {
error = gfs2_replay_read_block(jd, start, &bh);
if (error)
return error;
if (!first)
gfs2_metatype_check(sdp, bh, GFS2_METATYPE_LB);
while (offset + sizeof(uint64_t) <= sdp->sd_sb.sb_bsize) {
blkno = be64_to_cpu(*(__be64 *)(bh->b_data + offset));
error = gfs2_revoke_add(sdp, blkno, start);
if (error < 0)
return error;
else if (error)
sdp->sd_found_revokes++;
if (!--revokes)
break;
offset += sizeof(uint64_t);
}
brelse(bh);
offset = sizeof(struct gfs2_meta_header);
first = 0;
}
return 0;
}
static void revoke_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
if (error) {
gfs2_revoke_clean(sdp);
return;
}
if (pass != 1)
return;
fs_info(sdp, "jid=%u: Found %u revoke tags\n",
jd->jd_jid, sdp->sd_found_revokes);
gfs2_revoke_clean(sdp);
}
static void rg_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
struct gfs2_rgrpd *rgd;
get_transaction->tr_touched = 1;
if (!list_empty(&le->le_list))
return;
rgd = container_of(le, struct gfs2_rgrpd, rd_le);
gfs2_rgrp_bh_hold(rgd);
gfs2_log_lock(sdp);
sdp->sd_log_num_rg++;
list_add(&le->le_list, &sdp->sd_log_le_rg);
gfs2_log_unlock(sdp);
}
static void rg_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
struct list_head *head = &sdp->sd_log_le_rg;
struct gfs2_rgrpd *rgd;
while (!list_empty(head)) {
rgd = list_entry(head->next, struct gfs2_rgrpd, rd_le.le_list);
list_del_init(&rgd->rd_le.le_list);
sdp->sd_log_num_rg--;
gfs2_rgrp_repolish_clones(rgd);
gfs2_rgrp_bh_put(rgd);
}
gfs2_assert_warn(sdp, !sdp->sd_log_num_rg);
}
static void databuf_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
get_transaction->tr_touched = 1;
gfs2_log_lock(sdp);
sdp->sd_log_num_databuf++;
list_add(&le->le_list, &sdp->sd_log_le_databuf);
gfs2_log_unlock(sdp);
}
static void databuf_lo_before_commit(struct gfs2_sbd *sdp)
{
struct list_head *head = &sdp->sd_log_le_databuf;
LIST_HEAD(started);
struct gfs2_databuf *db;
struct buffer_head *bh;
while (!list_empty(head)) {
db = list_entry(head->prev, struct gfs2_databuf, db_le.le_list);
list_move(&db->db_le.le_list, &started);
gfs2_log_lock(sdp);
bh = db->db_bh;
if (bh) {
get_bh(bh);
gfs2_log_unlock(sdp);
if (buffer_dirty(bh)) {
wait_on_buffer(bh);
ll_rw_block(WRITE, 1, &bh);
}
brelse(bh);
} else
gfs2_log_unlock(sdp);
}
while (!list_empty(&started)) {
db = list_entry(started.next, struct gfs2_databuf,
db_le.le_list);
list_del(&db->db_le.le_list);
sdp->sd_log_num_databuf--;
gfs2_log_lock(sdp);
bh = db->db_bh;
if (bh) {
set_v2db(bh, NULL);
gfs2_log_unlock(sdp);
wait_on_buffer(bh);
brelse(bh);
} else
gfs2_log_unlock(sdp);
kfree(db);
}
gfs2_assert_warn(sdp, !sdp->sd_log_num_databuf);
}
struct gfs2_log_operations gfs2_glock_lops = {
.lo_add = glock_lo_add,
.lo_after_commit = glock_lo_after_commit,
.lo_name = "glock"
};
struct gfs2_log_operations gfs2_buf_lops = {
.lo_add = buf_lo_add,
.lo_incore_commit = buf_lo_incore_commit,
.lo_before_commit = buf_lo_before_commit,
.lo_after_commit = buf_lo_after_commit,
.lo_before_scan = buf_lo_before_scan,
.lo_scan_elements = buf_lo_scan_elements,
.lo_after_scan = buf_lo_after_scan,
.lo_name = "buf"
};
struct gfs2_log_operations gfs2_revoke_lops = {
.lo_add = revoke_lo_add,
.lo_before_commit = revoke_lo_before_commit,
.lo_before_scan = revoke_lo_before_scan,
.lo_scan_elements = revoke_lo_scan_elements,
.lo_after_scan = revoke_lo_after_scan,
.lo_name = "revoke"
};
struct gfs2_log_operations gfs2_rg_lops = {
.lo_add = rg_lo_add,
.lo_after_commit = rg_lo_after_commit,
.lo_name = "rg"
};
struct gfs2_log_operations gfs2_databuf_lops = {
.lo_add = databuf_lo_add,
.lo_before_commit = databuf_lo_before_commit,
.lo_name = "databuf"
};
struct gfs2_log_operations *gfs2_log_ops[] = {
&gfs2_glock_lops,
&gfs2_buf_lops,
&gfs2_revoke_lops,
&gfs2_rg_lops,
&gfs2_databuf_lops,
NULL
};

96
fs/gfs2/lops.h Normal file
View File

@ -0,0 +1,96 @@
/*
* 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 __LOPS_DOT_H__
#define __LOPS_DOT_H__
extern struct gfs2_log_operations gfs2_glock_lops;
extern struct gfs2_log_operations gfs2_buf_lops;
extern struct gfs2_log_operations gfs2_revoke_lops;
extern struct gfs2_log_operations gfs2_rg_lops;
extern struct gfs2_log_operations gfs2_databuf_lops;
extern struct gfs2_log_operations *gfs2_log_ops[];
static inline void lops_init_le(struct gfs2_log_element *le,
struct gfs2_log_operations *lops)
{
INIT_LIST_HEAD(&le->le_list);
le->le_ops = lops;
}
static inline void lops_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le)
{
if (le->le_ops->lo_add)
le->le_ops->lo_add(sdp, le);
}
static inline void lops_incore_commit(struct gfs2_sbd *sdp,
struct gfs2_trans *tr)
{
int x;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_incore_commit)
gfs2_log_ops[x]->lo_incore_commit(sdp, tr);
}
static inline void lops_before_commit(struct gfs2_sbd *sdp)
{
int x;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_before_commit)
gfs2_log_ops[x]->lo_before_commit(sdp);
}
static inline void lops_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
int x;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_after_commit)
gfs2_log_ops[x]->lo_after_commit(sdp, ai);
}
static inline void lops_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header *head,
unsigned int pass)
{
int x;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_before_scan)
gfs2_log_ops[x]->lo_before_scan(jd, head, pass);
}
static inline int lops_scan_elements(struct gfs2_jdesc *jd, unsigned int start,
struct gfs2_log_descriptor *ld,
__be64 *ptr,
unsigned int pass)
{
int x, error;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_scan_elements) {
error = gfs2_log_ops[x]->lo_scan_elements(jd, start,
ld, ptr, pass);
if (error)
return error;
}
return 0;
}
static inline void lops_after_scan(struct gfs2_jdesc *jd, int error,
unsigned int pass)
{
int x;
for (x = 0; gfs2_log_ops[x]; x++)
if (gfs2_log_ops[x]->lo_before_scan)
gfs2_log_ops[x]->lo_after_scan(jd, error, pass);
}
#endif /* __LOPS_DOT_H__ */

48
fs/gfs2/lvb.c Normal file
View File

@ -0,0 +1,48 @@
/*
* 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 "gfs2.h"
#define pv(struct, member, fmt) printk(" "#member" = "fmt"\n", struct->member);
void gfs2_quota_lvb_in(struct gfs2_quota_lvb *qb, char *lvb)
{
struct gfs2_quota_lvb *str = (struct gfs2_quota_lvb *)lvb;
qb->qb_magic = be32_to_cpu(str->qb_magic);
qb->qb_limit = be64_to_cpu(str->qb_limit);
qb->qb_warn = be64_to_cpu(str->qb_warn);
qb->qb_value = be64_to_cpu(str->qb_value);
}
void gfs2_quota_lvb_out(struct gfs2_quota_lvb *qb, char *lvb)
{
struct gfs2_quota_lvb *str = (struct gfs2_quota_lvb *)lvb;
str->qb_magic = cpu_to_be32(qb->qb_magic);
str->qb_limit = cpu_to_be64(qb->qb_limit);
str->qb_warn = cpu_to_be64(qb->qb_warn);
str->qb_value = cpu_to_be64(qb->qb_value);
}
void gfs2_quota_lvb_print(struct gfs2_quota_lvb *qb)
{
pv(qb, qb_magic, "%u");
pv(qb, qb_limit, "%llu");
pv(qb, qb_warn, "%llu");
pv(qb, qb_value, "%lld");
}

28
fs/gfs2/lvb.h Normal file
View File

@ -0,0 +1,28 @@
/*
* 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 __LVB_DOT_H__
#define __LVB_DOT_H__
#define GFS2_MIN_LVB_SIZE 32
struct gfs2_quota_lvb {
uint32_t qb_magic;
uint32_t __pad;
uint64_t qb_limit; /* Hard limit of # blocks to alloc */
uint64_t qb_warn; /* Warn user when alloc is above this # */
int64_t qb_value; /* Current # blocks allocated */
};
void gfs2_quota_lvb_in(struct gfs2_quota_lvb *qb, char *lvb);
void gfs2_quota_lvb_out(struct gfs2_quota_lvb *qb, char *lvb);
void gfs2_quota_lvb_print(struct gfs2_quota_lvb *qb);
#endif /* __LVB_DOT_H__ */

103
fs/gfs2/main.c Normal file
View File

@ -0,0 +1,103 @@
/*
* 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 <linux/module.h>
#include <linux/init.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "ops_fstype.h"
#include "sys.h"
/**
* init_gfs2_fs - Register GFS2 as a filesystem
*
* Returns: 0 on success, error code on failure
*/
static int __init init_gfs2_fs(void)
{
int error;
gfs2_init_lmh();
error = gfs2_sys_init();
if (error)
return error;
error = -ENOMEM;
gfs2_glock_cachep = kmem_cache_create("gfs2_glock",
sizeof(struct gfs2_glock),
0, 0, NULL, NULL);
if (!gfs2_glock_cachep)
goto fail;
gfs2_inode_cachep = kmem_cache_create("gfs2_inode",
sizeof(struct gfs2_inode),
0, 0, NULL, NULL);
if (!gfs2_inode_cachep)
goto fail;
gfs2_bufdata_cachep = kmem_cache_create("gfs2_bufdata",
sizeof(struct gfs2_bufdata),
0, 0, NULL, NULL);
if (!gfs2_bufdata_cachep)
goto fail;
error = register_filesystem(&gfs2_fs_type);
if (error)
goto fail;
printk("GFS2 (built %s %s) installed\n", __DATE__, __TIME__);
return 0;
fail:
if (gfs2_bufdata_cachep)
kmem_cache_destroy(gfs2_bufdata_cachep);
if (gfs2_inode_cachep)
kmem_cache_destroy(gfs2_inode_cachep);
if (gfs2_glock_cachep)
kmem_cache_destroy(gfs2_glock_cachep);
gfs2_sys_uninit();
return error;
}
/**
* exit_gfs2_fs - Unregister the file system
*
*/
static void __exit exit_gfs2_fs(void)
{
unregister_filesystem(&gfs2_fs_type);
kmem_cache_destroy(gfs2_bufdata_cachep);
kmem_cache_destroy(gfs2_inode_cachep);
kmem_cache_destroy(gfs2_glock_cachep);
gfs2_sys_uninit();
}
MODULE_DESCRIPTION("Global File System");
MODULE_AUTHOR("Red Hat, Inc.");
MODULE_LICENSE("GPL");
module_init(init_gfs2_fs);
module_exit(exit_gfs2_fs);

876
fs/gfs2/meta_io.c Normal file
View File

@ -0,0 +1,876 @@
/*
* 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 <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/writeback.h>
#include <linux/swap.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
#include "log.h"
#include "lops.h"
#include "meta_io.h"
#include "rgrp.h"
#include "trans.h"
#define buffer_busy(bh) \
((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock) | (1ul << BH_Pinned)))
#define buffer_in_io(bh) \
((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock)))
static int aspace_get_block(struct inode *inode, sector_t lblock,
struct buffer_head *bh_result, int create)
{
gfs2_assert_warn(get_v2sdp(inode->i_sb), 0);
return -EOPNOTSUPP;
}
static int gfs2_aspace_writepage(struct page *page,
struct writeback_control *wbc)
{
return block_write_full_page(page, aspace_get_block, wbc);
}
/**
* stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out.
* @bh: the buffer we're stuck on
*
*/
static void stuck_releasepage(struct buffer_head *bh)
{
struct gfs2_sbd *sdp = get_v2sdp(bh->b_page->mapping->host->i_sb);
struct gfs2_bufdata *bd = get_v2bd(bh);
struct gfs2_glock *gl;
fs_warn(sdp, "stuck in gfs2_releasepage()\n");
fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n",
(uint64_t)bh->b_blocknr, atomic_read(&bh->b_count));
fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh));
fs_warn(sdp, "get_v2bd(bh) = %s\n", (bd) ? "!NULL" : "NULL");
if (!bd)
return;
gl = bd->bd_gl;
fs_warn(sdp, "gl = (%u, %llu)\n",
gl->gl_name.ln_type, gl->gl_name.ln_number);
fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n",
(list_empty(&bd->bd_list_tr)) ? "no" : "yes",
(list_empty(&bd->bd_le.le_list)) ? "no" : "yes");
if (gl->gl_ops == &gfs2_inode_glops) {
struct gfs2_inode *ip = get_gl2ip(gl);
unsigned int x;
if (!ip)
return;
fs_warn(sdp, "ip = %llu %llu\n",
ip->i_num.no_formal_ino, ip->i_num.no_addr);
fs_warn(sdp, "ip->i_count = %d, ip->i_vnode = %s\n",
atomic_read(&ip->i_count),
(ip->i_vnode) ? "!NULL" : "NULL");
for (x = 0; x < GFS2_MAX_META_HEIGHT; x++)
fs_warn(sdp, "ip->i_cache[%u] = %s\n",
x, (ip->i_cache[x]) ? "!NULL" : "NULL");
}
}
/**
* gfs2_aspace_releasepage - free the metadata associated with a page
* @page: the page that's being released
* @gfp_mask: passed from Linux VFS, ignored by us
*
* Call try_to_free_buffers() if the buffers in this page can be
* released.
*
* Returns: 0
*/
static int gfs2_aspace_releasepage(struct page *page, gfp_t gfp_mask)
{
struct inode *aspace = page->mapping->host;
struct gfs2_sbd *sdp = get_v2sdp(aspace->i_sb);
struct buffer_head *bh, *head;
struct gfs2_bufdata *bd;
unsigned long t;
if (!page_has_buffers(page))
goto out;
head = bh = page_buffers(page);
do {
t = jiffies;
while (atomic_read(&bh->b_count)) {
if (atomic_read(&aspace->i_writecount)) {
if (time_after_eq(jiffies, t +
gfs2_tune_get(sdp, gt_stall_secs) * HZ)) {
stuck_releasepage(bh);
t = jiffies;
}
yield();
continue;
}
return 0;
}
gfs2_assert_warn(sdp, !buffer_pinned(bh));
bd = get_v2bd(bh);
if (bd) {
gfs2_assert_warn(sdp, bd->bd_bh == bh);
gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr));
gfs2_assert_warn(sdp, list_empty(&bd->bd_le.le_list));
gfs2_assert_warn(sdp, !bd->bd_ail);
kmem_cache_free(gfs2_bufdata_cachep, bd);
atomic_dec(&sdp->sd_bufdata_count);
set_v2bd(bh, NULL);
}
bh = bh->b_this_page;
}
while (bh != head);
out:
return try_to_free_buffers(page);
}
static struct address_space_operations aspace_aops = {
.writepage = gfs2_aspace_writepage,
.releasepage = gfs2_aspace_releasepage,
};
/**
* gfs2_aspace_get - Create and initialize a struct inode structure
* @sdp: the filesystem the aspace is in
*
* Right now a struct inode is just a struct inode. Maybe Linux
* will supply a more lightweight address space construct (that works)
* in the future.
*
* Make sure pages/buffers in this aspace aren't in high memory.
*
* Returns: the aspace
*/
struct inode *gfs2_aspace_get(struct gfs2_sbd *sdp)
{
struct inode *aspace;
aspace = new_inode(sdp->sd_vfs);
if (aspace) {
mapping_set_gfp_mask(aspace->i_mapping, GFP_KERNEL);
aspace->i_mapping->a_ops = &aspace_aops;
aspace->i_size = ~0ULL;
set_v2ip(aspace, NULL);
insert_inode_hash(aspace);
}
return aspace;
}
void gfs2_aspace_put(struct inode *aspace)
{
remove_inode_hash(aspace);
iput(aspace);
}
/**
* gfs2_ail1_start_one - Start I/O on a part of the AIL
* @sdp: the filesystem
* @tr: the part of the AIL
*
*/
void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
struct gfs2_bufdata *bd, *s;
struct buffer_head *bh;
int retry;
do {
retry = 0;
list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,
bd_ail_st_list) {
bh = bd->bd_bh;
gfs2_assert(sdp, bd->bd_ail == ai);
if (!buffer_busy(bh)) {
if (!buffer_uptodate(bh))
gfs2_io_error_bh(sdp, bh);
list_move(&bd->bd_ail_st_list,
&ai->ai_ail2_list);
continue;
}
if (!buffer_dirty(bh))
continue;
list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
gfs2_log_unlock(sdp);
wait_on_buffer(bh);
ll_rw_block(WRITE, 1, &bh);
gfs2_log_lock(sdp);
retry = 1;
break;
}
} while (retry);
}
/**
* gfs2_ail1_empty_one - Check whether or not a trans in the AIL has been synced
* @sdp: the filesystem
* @ai: the AIL entry
*
*/
int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags)
{
struct gfs2_bufdata *bd, *s;
struct buffer_head *bh;
list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,
bd_ail_st_list) {
bh = bd->bd_bh;
gfs2_assert(sdp, bd->bd_ail == ai);
if (buffer_busy(bh)) {
if (flags & DIO_ALL)
continue;
else
break;
}
if (!buffer_uptodate(bh))
gfs2_io_error_bh(sdp, bh);
list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
}
return list_empty(&ai->ai_ail1_list);
}
/**
* gfs2_ail2_empty_one - Check whether or not a trans in the AIL has been synced
* @sdp: the filesystem
* @ai: the AIL entry
*
*/
void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
struct list_head *head = &ai->ai_ail2_list;
struct gfs2_bufdata *bd;
while (!list_empty(head)) {
bd = list_entry(head->prev, struct gfs2_bufdata,
bd_ail_st_list);
gfs2_assert(sdp, bd->bd_ail == ai);
bd->bd_ail = NULL;
list_del(&bd->bd_ail_st_list);
list_del(&bd->bd_ail_gl_list);
atomic_dec(&bd->bd_gl->gl_ail_count);
brelse(bd->bd_bh);
}
}
/**
* ail_empty_gl - remove all buffers for a given lock from the AIL
* @gl: the glock
*
* None of the buffers should be dirty, locked, or pinned.
*/
void gfs2_ail_empty_gl(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
unsigned int blocks;
struct list_head *head = &gl->gl_ail_list;
struct gfs2_bufdata *bd;
struct buffer_head *bh;
uint64_t blkno;
int error;
blocks = atomic_read(&gl->gl_ail_count);
if (!blocks)
return;
error = gfs2_trans_begin(sdp, 0, blocks);
if (gfs2_assert_withdraw(sdp, !error))
return;
gfs2_log_lock(sdp);
while (!list_empty(head)) {
bd = list_entry(head->next, struct gfs2_bufdata,
bd_ail_gl_list);
bh = bd->bd_bh;
blkno = bh->b_blocknr;
gfs2_assert_withdraw(sdp, !buffer_busy(bh));
bd->bd_ail = NULL;
list_del(&bd->bd_ail_st_list);
list_del(&bd->bd_ail_gl_list);
atomic_dec(&gl->gl_ail_count);
brelse(bh);
gfs2_log_unlock(sdp);
gfs2_trans_add_revoke(sdp, blkno);
gfs2_log_lock(sdp);
}
gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
gfs2_log_unlock(sdp);
gfs2_trans_end(sdp);
gfs2_log_flush(sdp);
}
/**
* gfs2_meta_inval - Invalidate all buffers associated with a glock
* @gl: the glock
*
*/
void gfs2_meta_inval(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct inode *aspace = gl->gl_aspace;
struct address_space *mapping = gl->gl_aspace->i_mapping;
gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
atomic_inc(&aspace->i_writecount);
truncate_inode_pages(mapping, 0);
atomic_dec(&aspace->i_writecount);
gfs2_assert_withdraw(sdp, !mapping->nrpages);
}
/**
* gfs2_meta_sync - Sync all buffers associated with a glock
* @gl: The glock
* @flags: DIO_START | DIO_WAIT
*
*/
void gfs2_meta_sync(struct gfs2_glock *gl, int flags)
{
struct address_space *mapping = gl->gl_aspace->i_mapping;
int error = 0;
if (flags & DIO_START)
filemap_fdatawrite(mapping);
if (!error && (flags & DIO_WAIT))
error = filemap_fdatawait(mapping);
if (error)
gfs2_io_error(gl->gl_sbd);
}
/**
* getbuf - Get a buffer with a given address space
* @sdp: the filesystem
* @aspace: the address space
* @blkno: the block number (filesystem scope)
* @create: 1 if the buffer should be created
*
* Returns: the buffer
*/
static struct buffer_head *getbuf(struct gfs2_sbd *sdp, struct inode *aspace,
uint64_t blkno, int create)
{
struct page *page;
struct buffer_head *bh;
unsigned int shift;
unsigned long index;
unsigned int bufnum;
shift = PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift;
index = blkno >> shift; /* convert block to page */
bufnum = blkno - (index << shift); /* block buf index within page */
if (create) {
for (;;) {
page = grab_cache_page(aspace->i_mapping, index);
if (page)
break;
yield();
}
} else {
page = find_lock_page(aspace->i_mapping, index);
if (!page)
return NULL;
}
if (!page_has_buffers(page))
create_empty_buffers(page, sdp->sd_sb.sb_bsize, 0);
/* Locate header for our buffer within our page */
for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
/* Do nothing */;
get_bh(bh);
if (!buffer_mapped(bh))
map_bh(bh, sdp->sd_vfs, blkno);
unlock_page(page);
mark_page_accessed(page);
page_cache_release(page);
return bh;
}
static void meta_prep_new(struct buffer_head *bh)
{
struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data;
lock_buffer(bh);
clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
unlock_buffer(bh);
mh->mh_magic = cpu_to_be32(GFS2_MAGIC);
}
/**
* gfs2_meta_new - Get a block
* @gl: The glock associated with this block
* @blkno: The block number
*
* Returns: The buffer
*/
struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, uint64_t blkno)
{
struct buffer_head *bh;
bh = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE);
meta_prep_new(bh);
return bh;
}
/**
* gfs2_meta_read - Read a block from disk
* @gl: The glock covering the block
* @blkno: The block number
* @flags: flags to gfs2_dreread()
* @bhp: the place where the buffer is returned (NULL on failure)
*
* Returns: errno
*/
int gfs2_meta_read(struct gfs2_glock *gl, uint64_t blkno, int flags,
struct buffer_head **bhp)
{
int error;
*bhp = getbuf(gl->gl_sbd, gl->gl_aspace, blkno, CREATE);
error = gfs2_meta_reread(gl->gl_sbd, *bhp, flags);
if (error)
brelse(*bhp);
return error;
}
/**
* gfs2_meta_reread - Reread a block from disk
* @sdp: the filesystem
* @bh: The block to read
* @flags: Flags that control the read
*
* Returns: errno
*/
int gfs2_meta_reread(struct gfs2_sbd *sdp, struct buffer_head *bh, int flags)
{
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
return -EIO;
if (flags & DIO_FORCE)
clear_buffer_uptodate(bh);
if ((flags & DIO_START) && !buffer_uptodate(bh))
ll_rw_block(READ, 1, &bh);
if (flags & DIO_WAIT) {
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
struct gfs2_trans *tr = get_transaction;
if (tr && tr->tr_touched)
gfs2_io_error_bh(sdp, bh);
return -EIO;
}
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
return -EIO;
}
return 0;
}
/**
* gfs2_meta_attach_bufdata - attach a struct gfs2_bufdata structure to a buffer
* @gl: the glock the buffer belongs to
* @bh: The buffer to be attached to
*
*/
void gfs2_meta_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh)
{
struct gfs2_bufdata *bd;
lock_page(bh->b_page);
if (get_v2bd(bh)) {
unlock_page(bh->b_page);
return;
}
bd = kmem_cache_alloc(gfs2_bufdata_cachep, GFP_KERNEL | __GFP_NOFAIL),
atomic_inc(&gl->gl_sbd->sd_bufdata_count);
memset(bd, 0, sizeof(struct gfs2_bufdata));
bd->bd_bh = bh;
bd->bd_gl = gl;
INIT_LIST_HEAD(&bd->bd_list_tr);
lops_init_le(&bd->bd_le, &gfs2_buf_lops);
set_v2bd(bh, bd);
unlock_page(bh->b_page);
}
/**
* gfs2_meta_pin - Pin a metadata buffer in memory
* @sdp: the filesystem the buffer belongs to
* @bh: The buffer to be pinned
*
*/
void gfs2_meta_pin(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
struct gfs2_bufdata *bd = get_v2bd(bh);
gfs2_assert_withdraw(sdp, test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags));
if (test_set_buffer_pinned(bh))
gfs2_assert_withdraw(sdp, 0);
wait_on_buffer(bh);
/* If this buffer is in the AIL and it has already been written
to in-place disk block, remove it from the AIL. */
gfs2_log_lock(sdp);
if (bd->bd_ail && !buffer_in_io(bh))
list_move(&bd->bd_ail_st_list, &bd->bd_ail->ai_ail2_list);
gfs2_log_unlock(sdp);
clear_buffer_dirty(bh);
wait_on_buffer(bh);
if (!buffer_uptodate(bh))
gfs2_io_error_bh(sdp, bh);
get_bh(bh);
}
/**
* gfs2_meta_unpin - Unpin a buffer
* @sdp: the filesystem the buffer belongs to
* @bh: The buffer to unpin
* @ai:
*
*/
void gfs2_meta_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh,
struct gfs2_ail *ai)
{
struct gfs2_bufdata *bd = get_v2bd(bh);
gfs2_assert_withdraw(sdp, buffer_uptodate(bh));
if (!buffer_pinned(bh))
gfs2_assert_withdraw(sdp, 0);
mark_buffer_dirty(bh);
clear_buffer_pinned(bh);
gfs2_log_lock(sdp);
if (bd->bd_ail) {
list_del(&bd->bd_ail_st_list);
brelse(bh);
} else {
struct gfs2_glock *gl = bd->bd_gl;
list_add(&bd->bd_ail_gl_list, &gl->gl_ail_list);
atomic_inc(&gl->gl_ail_count);
}
bd->bd_ail = ai;
list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list);
gfs2_log_unlock(sdp);
}
/**
* gfs2_meta_wipe - make inode's buffers so they aren't dirty/pinned anymore
* @ip: the inode who owns the buffers
* @bstart: the first buffer in the run
* @blen: the number of buffers in the run
*
*/
void gfs2_meta_wipe(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct inode *aspace = ip->i_gl->gl_aspace;
struct buffer_head *bh;
while (blen) {
bh = getbuf(sdp, aspace, bstart, NO_CREATE);
if (bh) {
struct gfs2_bufdata *bd = get_v2bd(bh);
if (test_clear_buffer_pinned(bh)) {
gfs2_log_lock(sdp);
list_del_init(&bd->bd_le.le_list);
gfs2_assert_warn(sdp, sdp->sd_log_num_buf);
sdp->sd_log_num_buf--;
gfs2_log_unlock(sdp);
get_transaction->tr_num_buf_rm++;
brelse(bh);
}
if (bd) {
gfs2_log_lock(sdp);
if (bd->bd_ail) {
uint64_t blkno = bh->b_blocknr;
bd->bd_ail = NULL;
list_del(&bd->bd_ail_st_list);
list_del(&bd->bd_ail_gl_list);
atomic_dec(&bd->bd_gl->gl_ail_count);
brelse(bh);
gfs2_log_unlock(sdp);
gfs2_trans_add_revoke(sdp, blkno);
} else
gfs2_log_unlock(sdp);
}
lock_buffer(bh);
clear_buffer_dirty(bh);
clear_buffer_uptodate(bh);
unlock_buffer(bh);
brelse(bh);
}
bstart++;
blen--;
}
}
/**
* gfs2_meta_cache_flush - get rid of any references on buffers for this inode
* @ip: The GFS2 inode
*
* This releases buffers that are in the most-recently-used array of
* blocks used for indirect block addressing for this inode.
*/
void gfs2_meta_cache_flush(struct gfs2_inode *ip)
{
struct buffer_head **bh_slot;
unsigned int x;
spin_lock(&ip->i_spin);
for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) {
bh_slot = &ip->i_cache[x];
if (!*bh_slot)
break;
brelse(*bh_slot);
*bh_slot = NULL;
}
spin_unlock(&ip->i_spin);
}
/**
* gfs2_meta_indirect_buffer - Get a metadata buffer
* @ip: The GFS2 inode
* @height: The level of this buf in the metadata (indir addr) tree (if any)
* @num: The block number (device relative) of the buffer
* @new: Non-zero if we may create a new buffer
* @bhp: the buffer is returned here
*
* Try to use the gfs2_inode's MRU metadata tree cache.
*
* Returns: errno
*/
int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, uint64_t num,
int new, struct buffer_head **bhp)
{
struct buffer_head *bh, **bh_slot = ip->i_cache + height;
int error;
spin_lock(&ip->i_spin);
bh = *bh_slot;
if (bh) {
if (bh->b_blocknr == num)
get_bh(bh);
else
bh = NULL;
}
spin_unlock(&ip->i_spin);
if (bh) {
if (new)
meta_prep_new(bh);
else {
error = gfs2_meta_reread(ip->i_sbd, bh,
DIO_START | DIO_WAIT);
if (error) {
brelse(bh);
return error;
}
}
} else {
if (new)
bh = gfs2_meta_new(ip->i_gl, num);
else {
error = gfs2_meta_read(ip->i_gl, num,
DIO_START | DIO_WAIT, &bh);
if (error)
return error;
}
spin_lock(&ip->i_spin);
if (*bh_slot != bh) {
brelse(*bh_slot);
*bh_slot = bh;
get_bh(bh);
}
spin_unlock(&ip->i_spin);
}
if (new) {
if (gfs2_assert_warn(ip->i_sbd, height)) {
brelse(bh);
return -EIO;
}
gfs2_trans_add_bh(ip->i_gl, bh);
gfs2_metatype_set(bh, GFS2_METATYPE_IN, GFS2_FORMAT_IN);
gfs2_buffer_clear_tail(bh, sizeof(struct gfs2_meta_header));
} else if (gfs2_metatype_check(ip->i_sbd, bh,
(height) ? GFS2_METATYPE_IN : GFS2_METATYPE_DI)) {
brelse(bh);
return -EIO;
}
*bhp = bh;
return 0;
}
/**
* gfs2_meta_ra - start readahead on an extent of a file
* @gl: the glock the blocks belong to
* @dblock: the starting disk block
* @extlen: the number of blocks in the extent
*
*/
void gfs2_meta_ra(struct gfs2_glock *gl, uint64_t dblock, uint32_t extlen)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct inode *aspace = gl->gl_aspace;
struct buffer_head *first_bh, *bh;
uint32_t max_ra = gfs2_tune_get(sdp, gt_max_readahead) >> sdp->sd_sb.sb_bsize_shift;
int error;
if (!extlen || !max_ra)
return;
if (extlen > max_ra)
extlen = max_ra;
first_bh = getbuf(sdp, aspace, dblock, CREATE);
if (buffer_uptodate(first_bh))
goto out;
if (!buffer_locked(first_bh)) {
error = gfs2_meta_reread(sdp, first_bh, DIO_START);
if (error)
goto out;
}
dblock++;
extlen--;
while (extlen) {
bh = getbuf(sdp, aspace, dblock, CREATE);
if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
error = gfs2_meta_reread(sdp, bh, DIO_START);
brelse(bh);
if (error)
goto out;
} else
brelse(bh);
dblock++;
extlen--;
if (buffer_uptodate(first_bh))
break;
}
out:
brelse(first_bh);
}
/**
* gfs2_meta_syncfs - sync all the buffers in a filesystem
* @sdp: the filesystem
*
*/
void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
{
gfs2_log_flush(sdp);
for (;;) {
gfs2_ail1_start(sdp, DIO_ALL);
if (gfs2_ail1_empty(sdp, DIO_ALL))
break;
msleep(100);
}
}

88
fs/gfs2/meta_io.h Normal file
View File

@ -0,0 +1,88 @@
/*
* 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 __DIO_DOT_H__
#define __DIO_DOT_H__
static inline void gfs2_buffer_clear(struct buffer_head *bh)
{
memset(bh->b_data, 0, bh->b_size);
}
static inline void gfs2_buffer_clear_tail(struct buffer_head *bh, int head)
{
memset(bh->b_data + head, 0, bh->b_size - head);
}
static inline void gfs2_buffer_clear_ends(struct buffer_head *bh, int offset,
int amount, int journaled)
{
int z_off1 = (journaled) ? sizeof(struct gfs2_meta_header) : 0;
int z_len1 = offset - z_off1;
int z_off2 = offset + amount;
int z_len2 = (bh)->b_size - z_off2;
if (z_len1)
memset(bh->b_data + z_off1, 0, z_len1);
if (z_len2)
memset(bh->b_data + z_off2, 0, z_len2);
}
static inline void gfs2_buffer_copy_tail(struct buffer_head *to_bh,
int to_head,
struct buffer_head *from_bh,
int from_head)
{
memcpy(to_bh->b_data + to_head,
from_bh->b_data + from_head,
from_bh->b_size - from_head);
memset(to_bh->b_data + to_bh->b_size + to_head - from_head,
0,
from_head - to_head);
}
struct inode *gfs2_aspace_get(struct gfs2_sbd *sdp);
void gfs2_aspace_put(struct inode *aspace);
void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai);
int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags);
void gfs2_ail2_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai);
void gfs2_ail_empty_gl(struct gfs2_glock *gl);
void gfs2_meta_inval(struct gfs2_glock *gl);
void gfs2_meta_sync(struct gfs2_glock *gl, int flags);
struct buffer_head *gfs2_meta_new(struct gfs2_glock *gl, uint64_t blkno);
int gfs2_meta_read(struct gfs2_glock *gl, uint64_t blkno,
int flags, struct buffer_head **bhp);
int gfs2_meta_reread(struct gfs2_sbd *sdp, struct buffer_head *bh, int flags);
void gfs2_meta_attach_bufdata(struct gfs2_glock *gl, struct buffer_head *bh);
void gfs2_meta_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
void gfs2_meta_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh,
struct gfs2_ail *ai);
void gfs2_meta_wipe(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen);
void gfs2_meta_cache_flush(struct gfs2_inode *ip);
int gfs2_meta_indirect_buffer(struct gfs2_inode *ip, int height, uint64_t num,
int new, struct buffer_head **bhp);
static inline int gfs2_meta_inode_buffer(struct gfs2_inode *ip,
struct buffer_head **bhp)
{
return gfs2_meta_indirect_buffer(ip, 0, ip->i_num.no_addr, 0, bhp);
}
void gfs2_meta_ra(struct gfs2_glock *gl, uint64_t dblock, uint32_t extlen);
void gfs2_meta_syncfs(struct gfs2_sbd *sdp);
#endif /* __DIO_DOT_H__ */

211
fs/gfs2/mount.c Normal file
View File

@ -0,0 +1,211 @@
/*
* 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 "gfs2.h"
#include "mount.h"
#include "sys.h"
/**
* gfs2_mount_args - Parse mount options
* @sdp:
* @data:
*
* Return: errno
*/
int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount)
{
struct gfs2_args *args = &sdp->sd_args;
char *data = data_arg;
char *options, *o, *v;
int error = 0;
if (!remount) {
/* If someone preloaded options, use those instead */
spin_lock(&gfs2_sys_margs_lock);
if (gfs2_sys_margs) {
data = gfs2_sys_margs;
gfs2_sys_margs = NULL;
}
spin_unlock(&gfs2_sys_margs_lock);
/* Set some defaults */
args->ar_num_glockd = GFS2_GLOCKD_DEFAULT;
args->ar_quota = GFS2_QUOTA_DEFAULT;
args->ar_data = GFS2_DATA_DEFAULT;
}
/* Split the options into tokens with the "," character and
process them */
for (options = data; (o = strsep(&options, ",")); ) {
if (!*o)
continue;
v = strchr(o, '=');
if (v)
*v++ = 0;
if (!strcmp(o, "lockproto")) {
if (!v)
goto need_value;
if (remount && strcmp(v, args->ar_lockproto))
goto cant_remount;
strncpy(args->ar_lockproto, v, GFS2_LOCKNAME_LEN);
args->ar_lockproto[GFS2_LOCKNAME_LEN - 1] = 0;
}
else if (!strcmp(o, "locktable")) {
if (!v)
goto need_value;
if (remount && strcmp(v, args->ar_locktable))
goto cant_remount;
strncpy(args->ar_locktable, v, GFS2_LOCKNAME_LEN);
args->ar_locktable[GFS2_LOCKNAME_LEN - 1] = 0;
}
else if (!strcmp(o, "hostdata")) {
if (!v)
goto need_value;
if (remount && strcmp(v, args->ar_hostdata))
goto cant_remount;
strncpy(args->ar_hostdata, v, GFS2_LOCKNAME_LEN);
args->ar_hostdata[GFS2_LOCKNAME_LEN - 1] = 0;
}
else if (!strcmp(o, "spectator")) {
if (remount && !args->ar_spectator)
goto cant_remount;
args->ar_spectator = 1;
sdp->sd_vfs->s_flags |= MS_RDONLY;
}
else if (!strcmp(o, "ignore_local_fs")) {
if (remount && !args->ar_ignore_local_fs)
goto cant_remount;
args->ar_ignore_local_fs = 1;
}
else if (!strcmp(o, "localflocks")) {
if (remount && !args->ar_localflocks)
goto cant_remount;
args->ar_localflocks = 1;
}
else if (!strcmp(o, "localcaching")) {
if (remount && !args->ar_localcaching)
goto cant_remount;
args->ar_localcaching = 1;
}
else if (!strcmp(o, "debug"))
args->ar_debug = 1;
else if (!strcmp(o, "nodebug"))
args->ar_debug = 0;
else if (!strcmp(o, "upgrade")) {
if (remount && !args->ar_upgrade)
goto cant_remount;
args->ar_upgrade = 1;
}
else if (!strcmp(o, "num_glockd")) {
unsigned int x;
if (!v)
goto need_value;
sscanf(v, "%u", &x);
if (remount && x != args->ar_num_glockd)
goto cant_remount;
if (!x || x > GFS2_GLOCKD_MAX) {
fs_info(sdp, "0 < num_glockd <= %u (not %u)\n",
GFS2_GLOCKD_MAX, x);
error = -EINVAL;
break;
}
args->ar_num_glockd = x;
}
else if (!strcmp(o, "acl")) {
args->ar_posix_acl = 1;
sdp->sd_vfs->s_flags |= MS_POSIXACL;
}
else if (!strcmp(o, "noacl")) {
args->ar_posix_acl = 0;
sdp->sd_vfs->s_flags &= ~MS_POSIXACL;
}
else if (!strcmp(o, "quota")) {
if (!v)
goto need_value;
if (!strcmp(v, "off"))
args->ar_quota = GFS2_QUOTA_OFF;
else if (!strcmp(v, "account"))
args->ar_quota = GFS2_QUOTA_ACCOUNT;
else if (!strcmp(v, "on"))
args->ar_quota = GFS2_QUOTA_ON;
else {
fs_info(sdp, "invalid value for quota\n");
error = -EINVAL;
break;
}
}
else if (!strcmp(o, "suiddir"))
args->ar_suiddir = 1;
else if (!strcmp(o, "nosuiddir"))
args->ar_suiddir = 0;
else if (!strcmp(o, "data")) {
if (!v)
goto need_value;
if (!strcmp(v, "writeback"))
args->ar_data = GFS2_DATA_WRITEBACK;
else if (!strcmp(v, "ordered"))
args->ar_data = GFS2_DATA_ORDERED;
else {
fs_info(sdp, "invalid value for data\n");
error = -EINVAL;
break;
}
}
else {
fs_info(sdp, "unknown option: %s\n", o);
error = -EINVAL;
break;
}
}
if (error)
fs_info(sdp, "invalid mount option(s)\n");
if (data != data_arg)
kfree(data);
return error;
need_value:
fs_info(sdp, "need value for option %s\n", o);
return -EINVAL;
cant_remount:
fs_info(sdp, "can't remount with option %s\n", o);
return -EINVAL;
}

15
fs/gfs2/mount.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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 __MOUNT_DOT_H__
#define __MOUNT_DOT_H__
int gfs2_mount_args(struct gfs2_sbd *sdp, char *data_arg, int remount);
#endif /* __MOUNT_DOT_H__ */

590
fs/gfs2/ondisk.c Normal file
View File

@ -0,0 +1,590 @@
/*
* 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 "gfs2.h"
#include <linux/gfs2_ondisk.h>
#define pv(struct, member, fmt) printk(" "#member" = "fmt"\n", struct->member);
#define pa(struct, member, count) print_array(#member, struct->member, count);
/**
* print_array - Print out an array of bytes
* @title: what to print before the array
* @buf: the array
* @count: the number of bytes
*
*/
static void print_array(char *title, char *buf, int count)
{
int x;
printk(" %s =\n", title);
for (x = 0; x < count; x++) {
printk("%.2X ", (unsigned char)buf[x]);
if (x % 16 == 15)
printk("\n");
}
if (x % 16)
printk("\n");
}
/*
* gfs2_xxx_in - read in an xxx struct
* first arg: the cpu-order structure
* buf: the disk-order buffer
*
* gfs2_xxx_out - write out an xxx struct
* first arg: the cpu-order structure
* buf: the disk-order buffer
*
* gfs2_xxx_print - print out an xxx struct
* first arg: the cpu-order structure
*/
void gfs2_inum_in(struct gfs2_inum *no, char *buf)
{
struct gfs2_inum *str = (struct gfs2_inum *)buf;
no->no_formal_ino = be64_to_cpu(str->no_formal_ino);
no->no_addr = be64_to_cpu(str->no_addr);
}
void gfs2_inum_out(struct gfs2_inum *no, char *buf)
{
struct gfs2_inum *str = (struct gfs2_inum *)buf;
str->no_formal_ino = cpu_to_be64(no->no_formal_ino);
str->no_addr = cpu_to_be64(no->no_addr);
}
void gfs2_inum_print(struct gfs2_inum *no)
{
pv(no, no_formal_ino, "%llu");
pv(no, no_addr, "%llu");
}
void gfs2_meta_header_in(struct gfs2_meta_header *mh, char *buf)
{
struct gfs2_meta_header *str = (struct gfs2_meta_header *)buf;
mh->mh_magic = be32_to_cpu(str->mh_magic);
mh->mh_type = be16_to_cpu(str->mh_type);
mh->mh_format = be16_to_cpu(str->mh_format);
}
void gfs2_meta_header_out(struct gfs2_meta_header *mh, char *buf)
{
struct gfs2_meta_header *str = (struct gfs2_meta_header *)buf;
str->mh_magic = cpu_to_be32(mh->mh_magic);
str->mh_type = cpu_to_be16(mh->mh_type);
str->mh_format = cpu_to_be16(mh->mh_format);
}
void gfs2_meta_header_print(struct gfs2_meta_header *mh)
{
pv(mh, mh_magic, "0x%.8X");
pv(mh, mh_type, "%u");
pv(mh, mh_format, "%u");
}
void gfs2_sb_in(struct gfs2_sb *sb, char *buf)
{
struct gfs2_sb *str = (struct gfs2_sb *)buf;
gfs2_meta_header_in(&sb->sb_header, buf);
sb->sb_fs_format = be32_to_cpu(str->sb_fs_format);
sb->sb_multihost_format = be32_to_cpu(str->sb_multihost_format);
sb->sb_bsize = be32_to_cpu(str->sb_bsize);
sb->sb_bsize_shift = be32_to_cpu(str->sb_bsize_shift);
gfs2_inum_in(&sb->sb_master_dir, (char *)&str->sb_master_dir);
gfs2_inum_in(&sb->sb_root_dir, (char *)&str->sb_root_dir);
memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN);
memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN);
}
void gfs2_sb_out(struct gfs2_sb *sb, char *buf)
{
struct gfs2_sb *str = (struct gfs2_sb *)buf;
gfs2_meta_header_out(&sb->sb_header, buf);
str->sb_fs_format = cpu_to_be32(sb->sb_fs_format);
str->sb_multihost_format = cpu_to_be32(sb->sb_multihost_format);
str->sb_bsize = cpu_to_be32(sb->sb_bsize);
str->sb_bsize_shift = cpu_to_be32(sb->sb_bsize_shift);
gfs2_inum_out(&sb->sb_master_dir, (char *)&str->sb_master_dir);
gfs2_inum_out(&sb->sb_root_dir, (char *)&str->sb_root_dir);
memcpy(str->sb_lockproto, sb->sb_lockproto, GFS2_LOCKNAME_LEN);
memcpy(str->sb_locktable, sb->sb_locktable, GFS2_LOCKNAME_LEN);
}
void gfs2_sb_print(struct gfs2_sb *sb)
{
gfs2_meta_header_print(&sb->sb_header);
pv(sb, sb_fs_format, "%u");
pv(sb, sb_multihost_format, "%u");
pv(sb, sb_bsize, "%u");
pv(sb, sb_bsize_shift, "%u");
gfs2_inum_print(&sb->sb_master_dir);
pv(sb, sb_lockproto, "%s");
pv(sb, sb_locktable, "%s");
}
void gfs2_rindex_in(struct gfs2_rindex *ri, char *buf)
{
struct gfs2_rindex *str = (struct gfs2_rindex *)buf;
ri->ri_addr = be64_to_cpu(str->ri_addr);
ri->ri_length = be32_to_cpu(str->ri_length);
ri->ri_data0 = be64_to_cpu(str->ri_data0);
ri->ri_data = be32_to_cpu(str->ri_data);
ri->ri_bitbytes = be32_to_cpu(str->ri_bitbytes);
}
void gfs2_rindex_out(struct gfs2_rindex *ri, char *buf)
{
struct gfs2_rindex *str = (struct gfs2_rindex *)buf;
str->ri_addr = cpu_to_be64(ri->ri_addr);
str->ri_length = cpu_to_be32(ri->ri_length);
str->__pad = 0;
str->ri_data0 = cpu_to_be64(ri->ri_data0);
str->ri_data = cpu_to_be32(ri->ri_data);
str->ri_bitbytes = cpu_to_be32(ri->ri_bitbytes);
memset(str->ri_reserved, 0, sizeof(str->ri_reserved));
}
void gfs2_rindex_print(struct gfs2_rindex *ri)
{
pv(ri, ri_addr, "%llu");
pv(ri, ri_length, "%u");
pv(ri, ri_data0, "%llu");
pv(ri, ri_data, "%u");
pv(ri, ri_bitbytes, "%u");
}
void gfs2_rgrp_in(struct gfs2_rgrp *rg, char *buf)
{
struct gfs2_rgrp *str = (struct gfs2_rgrp *)buf;
gfs2_meta_header_in(&rg->rg_header, buf);
rg->rg_flags = be32_to_cpu(str->rg_flags);
rg->rg_free = be32_to_cpu(str->rg_free);
rg->rg_dinodes = be32_to_cpu(str->rg_dinodes);
}
void gfs2_rgrp_out(struct gfs2_rgrp *rg, char *buf)
{
struct gfs2_rgrp *str = (struct gfs2_rgrp *)buf;
gfs2_meta_header_out(&rg->rg_header, buf);
str->rg_flags = cpu_to_be32(rg->rg_flags);
str->rg_free = cpu_to_be32(rg->rg_free);
str->rg_dinodes = cpu_to_be32(rg->rg_dinodes);
memset(&str->rg_reserved, 0, sizeof(str->rg_reserved));
}
void gfs2_rgrp_print(struct gfs2_rgrp *rg)
{
gfs2_meta_header_print(&rg->rg_header);
pv(rg, rg_flags, "%u");
pv(rg, rg_free, "%u");
pv(rg, rg_dinodes, "%u");
pa(rg, rg_reserved, 36);
}
void gfs2_quota_in(struct gfs2_quota *qu, char *buf)
{
struct gfs2_quota *str = (struct gfs2_quota *)buf;
qu->qu_limit = be64_to_cpu(str->qu_limit);
qu->qu_warn = be64_to_cpu(str->qu_warn);
qu->qu_value = be64_to_cpu(str->qu_value);
}
void gfs2_quota_out(struct gfs2_quota *qu, char *buf)
{
struct gfs2_quota *str = (struct gfs2_quota *)buf;
str->qu_limit = cpu_to_be64(qu->qu_limit);
str->qu_warn = cpu_to_be64(qu->qu_warn);
str->qu_value = cpu_to_be64(qu->qu_value);
}
void gfs2_quota_print(struct gfs2_quota *qu)
{
pv(qu, qu_limit, "%llu");
pv(qu, qu_warn, "%llu");
pv(qu, qu_value, "%lld");
}
void gfs2_dinode_in(struct gfs2_dinode *di, char *buf)
{
struct gfs2_dinode *str = (struct gfs2_dinode *)buf;
gfs2_meta_header_in(&di->di_header, buf);
gfs2_inum_in(&di->di_num, (char *)&str->di_num);
di->di_mode = be32_to_cpu(str->di_mode);
di->di_uid = be32_to_cpu(str->di_uid);
di->di_gid = be32_to_cpu(str->di_gid);
di->di_nlink = be32_to_cpu(str->di_nlink);
di->di_size = be64_to_cpu(str->di_size);
di->di_blocks = be64_to_cpu(str->di_blocks);
di->di_atime = be64_to_cpu(str->di_atime);
di->di_mtime = be64_to_cpu(str->di_mtime);
di->di_ctime = be64_to_cpu(str->di_ctime);
di->di_major = be32_to_cpu(str->di_major);
di->di_minor = be32_to_cpu(str->di_minor);
di->di_goal_meta = be64_to_cpu(str->di_goal_meta);
di->di_goal_data = be64_to_cpu(str->di_goal_data);
di->di_flags = be32_to_cpu(str->di_flags);
di->di_payload_format = be32_to_cpu(str->di_payload_format);
di->di_height = be16_to_cpu(str->di_height);
di->di_depth = be16_to_cpu(str->di_depth);
di->di_entries = be32_to_cpu(str->di_entries);
di->di_eattr = be64_to_cpu(str->di_eattr);
}
void gfs2_dinode_out(struct gfs2_dinode *di, char *buf)
{
struct gfs2_dinode *str = (struct gfs2_dinode *)buf;
gfs2_meta_header_out(&di->di_header, buf);
gfs2_inum_out(&di->di_num, (char *)&str->di_num);
str->di_mode = cpu_to_be32(di->di_mode);
str->di_uid = cpu_to_be32(di->di_uid);
str->di_gid = cpu_to_be32(di->di_gid);
str->di_nlink = cpu_to_be32(di->di_nlink);
str->di_size = cpu_to_be64(di->di_size);
str->di_blocks = cpu_to_be64(di->di_blocks);
str->di_atime = cpu_to_be64(di->di_atime);
str->di_mtime = cpu_to_be64(di->di_mtime);
str->di_ctime = cpu_to_be64(di->di_ctime);
str->di_major = cpu_to_be32(di->di_major);
str->di_minor = cpu_to_be32(di->di_minor);
str->di_goal_meta = cpu_to_be64(di->di_goal_meta);
str->di_goal_data = cpu_to_be64(di->di_goal_data);
str->di_flags = cpu_to_be32(di->di_flags);
str->di_payload_format = cpu_to_be32(di->di_payload_format);
str->di_height = cpu_to_be16(di->di_height);
str->di_depth = cpu_to_be16(di->di_depth);
str->di_entries = cpu_to_be32(di->di_entries);
str->di_eattr = cpu_to_be64(di->di_eattr);
}
void gfs2_dinode_print(struct gfs2_dinode *di)
{
gfs2_meta_header_print(&di->di_header);
gfs2_inum_print(&di->di_num);
pv(di, di_mode, "0%o");
pv(di, di_uid, "%u");
pv(di, di_gid, "%u");
pv(di, di_nlink, "%u");
pv(di, di_size, "%llu");
pv(di, di_blocks, "%llu");
pv(di, di_atime, "%lld");
pv(di, di_mtime, "%lld");
pv(di, di_ctime, "%lld");
pv(di, di_major, "%u");
pv(di, di_minor, "%u");
pv(di, di_goal_meta, "%llu");
pv(di, di_goal_data, "%llu");
pv(di, di_flags, "0x%.8X");
pv(di, di_payload_format, "%u");
pv(di, di_height, "%u");
pv(di, di_depth, "%u");
pv(di, di_entries, "%u");
pv(di, di_eattr, "%llu");
}
void gfs2_dirent_in(struct gfs2_dirent *de, char *buf)
{
struct gfs2_dirent *str = (struct gfs2_dirent *)buf;
gfs2_inum_in(&de->de_inum, buf);
de->de_hash = be32_to_cpu(str->de_hash);
de->de_rec_len = be32_to_cpu(str->de_rec_len);
de->de_name_len = str->de_name_len;
de->de_type = str->de_type;
}
void gfs2_dirent_out(struct gfs2_dirent *de, char *buf)
{
struct gfs2_dirent *str = (struct gfs2_dirent *)buf;
gfs2_inum_out(&de->de_inum, buf);
str->de_hash = cpu_to_be32(de->de_hash);
str->de_rec_len = cpu_to_be32(de->de_rec_len);
str->de_name_len = de->de_name_len;
str->de_type = de->de_type;
str->__pad1 = 0;
str->__pad2 = 0;
}
void gfs2_dirent_print(struct gfs2_dirent *de, char *name)
{
char buf[GFS2_FNAMESIZE + 1];
gfs2_inum_print(&de->de_inum);
pv(de, de_hash, "0x%.8X");
pv(de, de_rec_len, "%u");
pv(de, de_name_len, "%u");
pv(de, de_type, "%u");
memset(buf, 0, GFS2_FNAMESIZE + 1);
memcpy(buf, name, de->de_name_len);
printk(" name = %s\n", buf);
}
void gfs2_leaf_in(struct gfs2_leaf *lf, char *buf)
{
struct gfs2_leaf *str = (struct gfs2_leaf *)buf;
gfs2_meta_header_in(&lf->lf_header, buf);
lf->lf_depth = be16_to_cpu(str->lf_depth);
lf->lf_entries = be16_to_cpu(str->lf_entries);
lf->lf_dirent_format = be32_to_cpu(str->lf_dirent_format);
lf->lf_next = be64_to_cpu(str->lf_next);
}
void gfs2_leaf_out(struct gfs2_leaf *lf, char *buf)
{
struct gfs2_leaf *str = (struct gfs2_leaf *)buf;
gfs2_meta_header_out(&lf->lf_header, buf);
str->lf_depth = cpu_to_be16(lf->lf_depth);
str->lf_entries = cpu_to_be16(lf->lf_entries);
str->lf_dirent_format = cpu_to_be32(lf->lf_dirent_format);
str->lf_next = cpu_to_be64(lf->lf_next);
memset(&str->lf_reserved, 0, sizeof(str->lf_reserved));
}
void gfs2_leaf_print(struct gfs2_leaf *lf)
{
gfs2_meta_header_print(&lf->lf_header);
pv(lf, lf_depth, "%u");
pv(lf, lf_entries, "%u");
pv(lf, lf_dirent_format, "%u");
pv(lf, lf_next, "%llu");
pa(lf, lf_reserved, 32);
}
void gfs2_ea_header_in(struct gfs2_ea_header *ea, char *buf)
{
struct gfs2_ea_header *str = (struct gfs2_ea_header *)buf;
ea->ea_rec_len = be32_to_cpu(str->ea_rec_len);
ea->ea_data_len = be32_to_cpu(str->ea_data_len);
ea->ea_name_len = str->ea_name_len;
ea->ea_type = str->ea_type;
ea->ea_flags = str->ea_flags;
ea->ea_num_ptrs = str->ea_num_ptrs;
}
void gfs2_ea_header_out(struct gfs2_ea_header *ea, char *buf)
{
struct gfs2_ea_header *str = (struct gfs2_ea_header *)buf;
str->ea_rec_len = cpu_to_be32(ea->ea_rec_len);
str->ea_data_len = cpu_to_be32(ea->ea_data_len);
str->ea_name_len = ea->ea_name_len;
str->ea_type = ea->ea_type;
str->ea_flags = ea->ea_flags;
str->ea_num_ptrs = ea->ea_num_ptrs;
str->__pad = 0;
}
void gfs2_ea_header_print(struct gfs2_ea_header *ea, char *name)
{
char buf[GFS2_EA_MAX_NAME_LEN + 1];
pv(ea, ea_rec_len, "%u");
pv(ea, ea_data_len, "%u");
pv(ea, ea_name_len, "%u");
pv(ea, ea_type, "%u");
pv(ea, ea_flags, "%u");
pv(ea, ea_num_ptrs, "%u");
memset(buf, 0, GFS2_EA_MAX_NAME_LEN + 1);
memcpy(buf, name, ea->ea_name_len);
printk(" name = %s\n", buf);
}
void gfs2_log_header_in(struct gfs2_log_header *lh, char *buf)
{
struct gfs2_log_header *str = (struct gfs2_log_header *)buf;
gfs2_meta_header_in(&lh->lh_header, buf);
lh->lh_sequence = be64_to_cpu(str->lh_sequence);
lh->lh_flags = be32_to_cpu(str->lh_flags);
lh->lh_tail = be32_to_cpu(str->lh_tail);
lh->lh_blkno = be32_to_cpu(str->lh_blkno);
lh->lh_hash = be32_to_cpu(str->lh_hash);
}
void gfs2_log_header_print(struct gfs2_log_header *lh)
{
gfs2_meta_header_print(&lh->lh_header);
pv(lh, lh_sequence, "%llu");
pv(lh, lh_flags, "0x%.8X");
pv(lh, lh_tail, "%u");
pv(lh, lh_blkno, "%u");
pv(lh, lh_hash, "0x%.8X");
}
void gfs2_log_descriptor_print(struct gfs2_log_descriptor *ld)
{
gfs2_meta_header_print(&ld->ld_header);
pv(ld, ld_type, "%u");
pv(ld, ld_length, "%u");
pv(ld, ld_data1, "%u");
pv(ld, ld_data2, "%u");
pa(ld, ld_reserved, 32);
}
void gfs2_inum_range_in(struct gfs2_inum_range *ir, char *buf)
{
struct gfs2_inum_range *str = (struct gfs2_inum_range *)buf;
ir->ir_start = be64_to_cpu(str->ir_start);
ir->ir_length = be64_to_cpu(str->ir_length);
}
void gfs2_inum_range_out(struct gfs2_inum_range *ir, char *buf)
{
struct gfs2_inum_range *str = (struct gfs2_inum_range *)buf;
str->ir_start = cpu_to_be64(ir->ir_start);
str->ir_length = cpu_to_be64(ir->ir_length);
}
void gfs2_inum_range_print(struct gfs2_inum_range *ir)
{
pv(ir, ir_start, "%llu");
pv(ir, ir_length, "%llu");
}
void gfs2_statfs_change_in(struct gfs2_statfs_change *sc, char *buf)
{
struct gfs2_statfs_change *str = (struct gfs2_statfs_change *)buf;
sc->sc_total = be64_to_cpu(str->sc_total);
sc->sc_free = be64_to_cpu(str->sc_free);
sc->sc_dinodes = be64_to_cpu(str->sc_dinodes);
}
void gfs2_statfs_change_out(struct gfs2_statfs_change *sc, char *buf)
{
struct gfs2_statfs_change *str = (struct gfs2_statfs_change *)buf;
str->sc_total = cpu_to_be64(sc->sc_total);
str->sc_free = cpu_to_be64(sc->sc_free);
str->sc_dinodes = cpu_to_be64(sc->sc_dinodes);
}
void gfs2_statfs_change_print(struct gfs2_statfs_change *sc)
{
pv(sc, sc_total, "%lld");
pv(sc, sc_free, "%lld");
pv(sc, sc_dinodes, "%lld");
}
void gfs2_unlinked_tag_in(struct gfs2_unlinked_tag *ut, char *buf)
{
struct gfs2_unlinked_tag *str = (struct gfs2_unlinked_tag *)buf;
gfs2_inum_in(&ut->ut_inum, buf);
ut->ut_flags = be32_to_cpu(str->ut_flags);
}
void gfs2_unlinked_tag_out(struct gfs2_unlinked_tag *ut, char *buf)
{
struct gfs2_unlinked_tag *str = (struct gfs2_unlinked_tag *)buf;
gfs2_inum_out(&ut->ut_inum, buf);
str->ut_flags = cpu_to_be32(ut->ut_flags);
str->__pad = 0;
}
void gfs2_unlinked_tag_print(struct gfs2_unlinked_tag *ut)
{
gfs2_inum_print(&ut->ut_inum);
pv(ut, ut_flags, "%u");
}
void gfs2_quota_change_in(struct gfs2_quota_change *qc, char *buf)
{
struct gfs2_quota_change *str = (struct gfs2_quota_change *)buf;
qc->qc_change = be64_to_cpu(str->qc_change);
qc->qc_flags = be32_to_cpu(str->qc_flags);
qc->qc_id = be32_to_cpu(str->qc_id);
}
void gfs2_quota_change_out(struct gfs2_quota_change *qc, char *buf)
{
struct gfs2_quota_change *str = (struct gfs2_quota_change *)buf;
str->qc_change = cpu_to_be64(qc->qc_change);
str->qc_flags = cpu_to_be32(qc->qc_flags);
str->qc_id = cpu_to_be32(qc->qc_id);
}
void gfs2_quota_change_print(struct gfs2_quota_change *qc)
{
pv(qc, qc_change, "%lld");
pv(qc, qc_flags, "0x%.8X");
pv(qc, qc_id, "%u");
}

515
fs/gfs2/ops_address.c Normal file
View File

@ -0,0 +1,515 @@
/*
* 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 <linux/pagemap.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "bmap.h"
#include "glock.h"
#include "inode.h"
#include "jdata.h"
#include "log.h"
#include "meta_io.h"
#include "ops_address.h"
#include "page.h"
#include "quota.h"
#include "trans.h"
/**
* get_block - Fills in a buffer head with details about a block
* @inode: The inode
* @lblock: The block number to look up
* @bh_result: The buffer head to return the result in
* @create: Non-zero if we may add block to the file
*
* Returns: errno
*/
static int get_block(struct inode *inode, sector_t lblock,
struct buffer_head *bh_result, int create)
{
struct gfs2_inode *ip = get_v2ip(inode);
int new = create;
uint64_t dblock;
int error;
error = gfs2_block_map(ip, lblock, &new, &dblock, NULL);
if (error)
return error;
if (!dblock)
return 0;
map_bh(bh_result, inode->i_sb, dblock);
if (new)
set_buffer_new(bh_result);
return 0;
}
/**
* get_block_noalloc - Fills in a buffer head with details about a block
* @inode: The inode
* @lblock: The block number to look up
* @bh_result: The buffer head to return the result in
* @create: Non-zero if we may add block to the file
*
* Returns: errno
*/
static int get_block_noalloc(struct inode *inode, sector_t lblock,
struct buffer_head *bh_result, int create)
{
struct gfs2_inode *ip = get_v2ip(inode);
int new = 0;
uint64_t dblock;
int error;
error = gfs2_block_map(ip, lblock, &new, &dblock, NULL);
if (error)
return error;
if (dblock)
map_bh(bh_result, inode->i_sb, dblock);
else if (gfs2_assert_withdraw(ip->i_sbd, !create))
error = -EIO;
return error;
}
static int get_blocks(struct inode *inode, sector_t lblock,
unsigned long max_blocks, struct buffer_head *bh_result,
int create)
{
struct gfs2_inode *ip = get_v2ip(inode);
int new = create;
uint64_t dblock;
uint32_t extlen;
int error;
error = gfs2_block_map(ip, lblock, &new, &dblock, &extlen);
if (error)
return error;
if (!dblock)
return 0;
map_bh(bh_result, inode->i_sb, dblock);
if (new)
set_buffer_new(bh_result);
if (extlen > max_blocks)
extlen = max_blocks;
bh_result->b_size = extlen << inode->i_blkbits;
return 0;
}
static int get_blocks_noalloc(struct inode *inode, sector_t lblock,
unsigned long max_blocks,
struct buffer_head *bh_result, int create)
{
struct gfs2_inode *ip = get_v2ip(inode);
int new = 0;
uint64_t dblock;
uint32_t extlen;
int error;
error = gfs2_block_map(ip, lblock, &new, &dblock, &extlen);
if (error)
return error;
if (dblock) {
map_bh(bh_result, inode->i_sb, dblock);
if (extlen > max_blocks)
extlen = max_blocks;
bh_result->b_size = extlen << inode->i_blkbits;
} else if (gfs2_assert_withdraw(ip->i_sbd, !create))
error = -EIO;
return error;
}
/**
* gfs2_writepage - Write complete page
* @page: Page to write
*
* Returns: errno
*
* Use Linux VFS block_write_full_page() to write one page,
* using GFS2's get_block_noalloc to find which blocks to write.
*/
static int gfs2_writepage(struct page *page, struct writeback_control *wbc)
{
struct gfs2_inode *ip = get_v2ip(page->mapping->host);
struct gfs2_sbd *sdp = ip->i_sbd;
int error;
atomic_inc(&sdp->sd_ops_address);
if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl))) {
unlock_page(page);
return -EIO;
}
if (get_transaction) {
redirty_page_for_writepage(wbc, page);
unlock_page(page);
return 0;
}
error = block_write_full_page(page, get_block_noalloc, wbc);
gfs2_meta_cache_flush(ip);
return error;
}
/**
* stuffed_readpage - Fill in a Linux page with stuffed file data
* @ip: the inode
* @page: the page
*
* Returns: errno
*/
static int stuffed_readpage(struct gfs2_inode *ip, struct page *page)
{
struct buffer_head *dibh;
void *kaddr;
int error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
kaddr = kmap(page);
memcpy((char *)kaddr,
dibh->b_data + sizeof(struct gfs2_dinode),
ip->i_di.di_size);
memset((char *)kaddr + ip->i_di.di_size,
0,
PAGE_CACHE_SIZE - ip->i_di.di_size);
kunmap(page);
brelse(dibh);
SetPageUptodate(page);
return 0;
}
static int zero_readpage(struct page *page)
{
void *kaddr;
kaddr = kmap(page);
memset(kaddr, 0, PAGE_CACHE_SIZE);
kunmap(page);
SetPageUptodate(page);
unlock_page(page);
return 0;
}
/**
* jdata_readpage - readpage that goes through gfs2_jdata_read_mem()
* @ip:
* @page: The page to read
*
* Returns: errno
*/
static int jdata_readpage(struct gfs2_inode *ip, struct page *page)
{
void *kaddr;
int ret;
kaddr = kmap(page);
ret = gfs2_jdata_read_mem(ip, kaddr,
(uint64_t)page->index << PAGE_CACHE_SHIFT,
PAGE_CACHE_SIZE);
if (ret >= 0) {
if (ret < PAGE_CACHE_SIZE)
memset(kaddr + ret, 0, PAGE_CACHE_SIZE - ret);
SetPageUptodate(page);
ret = 0;
}
kunmap(page);
unlock_page(page);
return ret;
}
/**
* gfs2_readpage - readpage with locking
* @file: The file to read a page for
* @page: The page to read
*
* Returns: errno
*/
static int gfs2_readpage(struct file *file, struct page *page)
{
struct gfs2_inode *ip = get_v2ip(page->mapping->host);
struct gfs2_sbd *sdp = ip->i_sbd;
int error;
atomic_inc(&sdp->sd_ops_address);
if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl))) {
unlock_page(page);
return -EOPNOTSUPP;
}
if (!gfs2_is_jdata(ip)) {
if (gfs2_is_stuffed(ip)) {
if (!page->index) {
error = stuffed_readpage(ip, page);
unlock_page(page);
} else
error = zero_readpage(page);
} else
error = block_read_full_page(page, get_block);
} else
error = jdata_readpage(ip, page);
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
error = -EIO;
return error;
}
/**
* gfs2_prepare_write - Prepare to write a page to a file
* @file: The file to write to
* @page: The page which is to be prepared for writing
* @from: From (byte range within page)
* @to: To (byte range within page)
*
* Returns: errno
*/
static int gfs2_prepare_write(struct file *file, struct page *page,
unsigned from, unsigned to)
{
struct gfs2_inode *ip = get_v2ip(page->mapping->host);
struct gfs2_sbd *sdp = ip->i_sbd;
int error = 0;
atomic_inc(&sdp->sd_ops_address);
if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl)))
return -EOPNOTSUPP;
if (gfs2_is_stuffed(ip)) {
uint64_t file_size;
file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to;
if (file_size > sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_dinode)) {
error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page,
page);
if (!error)
error = block_prepare_write(page, from, to,
get_block);
} else if (!PageUptodate(page))
error = stuffed_readpage(ip, page);
} else
error = block_prepare_write(page, from, to, get_block);
return error;
}
/**
* gfs2_commit_write - Commit write to a file
* @file: The file to write to
* @page: The page containing the data
* @from: From (byte range within page)
* @to: To (byte range within page)
*
* Returns: errno
*/
static int gfs2_commit_write(struct file *file, struct page *page,
unsigned from, unsigned to)
{
struct inode *inode = page->mapping->host;
struct gfs2_inode *ip = get_v2ip(inode);
struct gfs2_sbd *sdp = ip->i_sbd;
int error;
atomic_inc(&sdp->sd_ops_address);
if (gfs2_is_stuffed(ip)) {
struct buffer_head *dibh;
uint64_t file_size;
void *kaddr;
file_size = ((uint64_t)page->index << PAGE_CACHE_SHIFT) + to;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
goto fail;
gfs2_trans_add_bh(ip->i_gl, dibh);
kaddr = kmap(page);
memcpy(dibh->b_data + sizeof(struct gfs2_dinode) + from,
(char *)kaddr + from,
to - from);
kunmap(page);
brelse(dibh);
SetPageUptodate(page);
if (inode->i_size < file_size)
i_size_write(inode, file_size);
} else {
if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED)
gfs2_page_add_databufs(sdp, page, from, to);
error = generic_commit_write(file, page, from, to);
if (error)
goto fail;
}
return 0;
fail:
ClearPageUptodate(page);
return error;
}
/**
* gfs2_bmap - Block map function
* @mapping: Address space info
* @lblock: The block to map
*
* Returns: The disk address for the block or 0 on hole or error
*/
static sector_t gfs2_bmap(struct address_space *mapping, sector_t lblock)
{
struct gfs2_inode *ip = get_v2ip(mapping->host);
struct gfs2_holder i_gh;
sector_t dblock = 0;
int error;
atomic_inc(&ip->i_sbd->sd_ops_address);
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
if (error)
return 0;
if (!gfs2_is_stuffed(ip))
dblock = generic_block_bmap(mapping, lblock, get_block);
gfs2_glock_dq_uninit(&i_gh);
return dblock;
}
static void discard_buffer(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
struct gfs2_databuf *db;
gfs2_log_lock(sdp);
db = get_v2db(bh);
if (db) {
db->db_bh = NULL;
set_v2db(bh, NULL);
gfs2_log_unlock(sdp);
brelse(bh);
} else
gfs2_log_unlock(sdp);
lock_buffer(bh);
clear_buffer_dirty(bh);
bh->b_bdev = NULL;
clear_buffer_mapped(bh);
clear_buffer_req(bh);
clear_buffer_new(bh);
clear_buffer_delay(bh);
unlock_buffer(bh);
}
static int gfs2_invalidatepage(struct page *page, unsigned long offset)
{
struct gfs2_sbd *sdp = get_v2sdp(page->mapping->host->i_sb);
struct buffer_head *head, *bh, *next;
unsigned int curr_off = 0;
int ret = 1;
BUG_ON(!PageLocked(page));
if (!page_has_buffers(page))
return 1;
bh = head = page_buffers(page);
do {
unsigned int next_off = curr_off + bh->b_size;
next = bh->b_this_page;
if (offset <= curr_off)
discard_buffer(sdp, bh);
curr_off = next_off;
bh = next;
} while (bh != head);
if (!offset)
ret = try_to_release_page(page, 0);
return ret;
}
static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs)
{
struct file *file = iocb->ki_filp;
struct inode *inode = file->f_mapping->host;
struct gfs2_inode *ip = get_v2ip(inode);
struct gfs2_sbd *sdp = ip->i_sbd;
get_blocks_t *gb = get_blocks;
atomic_inc(&sdp->sd_ops_address);
if (gfs2_assert_warn(sdp, gfs2_glock_is_locked_by_me(ip->i_gl)) ||
gfs2_assert_warn(sdp, !gfs2_is_stuffed(ip)))
return -EINVAL;
if (rw == WRITE && !get_transaction)
gb = get_blocks_noalloc;
return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
offset, nr_segs, gb, NULL);
}
struct address_space_operations gfs2_file_aops = {
.writepage = gfs2_writepage,
.readpage = gfs2_readpage,
.sync_page = block_sync_page,
.prepare_write = gfs2_prepare_write,
.commit_write = gfs2_commit_write,
.bmap = gfs2_bmap,
.invalidatepage = gfs2_invalidatepage,
.direct_IO = gfs2_direct_IO,
};

15
fs/gfs2/ops_address.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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 __OPS_ADDRESS_DOT_H__
#define __OPS_ADDRESS_DOT_H__
extern struct address_space_operations gfs2_file_aops;
#endif /* __OPS_ADDRESS_DOT_H__ */

117
fs/gfs2/ops_dentry.c Normal file
View File

@ -0,0 +1,117 @@
/*
* 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 <linux/smp_lock.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "dir.h"
#include "glock.h"
#include "ops_dentry.h"
/**
* gfs2_drevalidate - Check directory lookup consistency
* @dentry: the mapping to check
* @nd:
*
* Check to make sure the lookup necessary to arrive at this inode from its
* parent is still good.
*
* Returns: 1 if the dentry is ok, 0 if it isn't
*/
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd)
{
struct dentry *parent = dget_parent(dentry);
struct gfs2_inode *dip = get_v2ip(parent->d_inode);
struct gfs2_sbd *sdp = dip->i_sbd;
struct inode *inode;
struct gfs2_holder d_gh;
struct gfs2_inode *ip;
struct gfs2_inum inum;
unsigned int type;
int error;
lock_kernel();
atomic_inc(&sdp->sd_ops_dentry);
inode = dentry->d_inode;
if (inode && is_bad_inode(inode))
goto invalid;
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
if (error)
goto fail;
error = gfs2_dir_search(dip, &dentry->d_name, &inum, &type);
switch (error) {
case 0:
if (!inode)
goto invalid_gunlock;
break;
case -ENOENT:
if (!inode)
goto valid_gunlock;
goto invalid_gunlock;
default:
goto fail_gunlock;
}
ip = get_v2ip(inode);
if (!gfs2_inum_equal(&ip->i_num, &inum))
goto invalid_gunlock;
if (IF2DT(ip->i_di.di_mode) != type) {
gfs2_consist_inode(dip);
goto fail_gunlock;
}
valid_gunlock:
gfs2_glock_dq_uninit(&d_gh);
valid:
unlock_kernel();
dput(parent);
return 1;
invalid_gunlock:
gfs2_glock_dq_uninit(&d_gh);
invalid:
if (inode && S_ISDIR(inode->i_mode)) {
if (have_submounts(dentry))
goto valid;
shrink_dcache_parent(dentry);
}
d_drop(dentry);
unlock_kernel();
dput(parent);
return 0;
fail_gunlock:
gfs2_glock_dq_uninit(&d_gh);
fail:
unlock_kernel();
dput(parent);
return 0;
}
struct dentry_operations gfs2_dops = {
.d_revalidate = gfs2_drevalidate,
};

15
fs/gfs2/ops_dentry.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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 __OPS_DENTRY_DOT_H__
#define __OPS_DENTRY_DOT_H__
extern struct dentry_operations gfs2_dops;
#endif /* __OPS_DENTRY_DOT_H__ */

310
fs/gfs2/ops_export.c Normal file
View File

@ -0,0 +1,310 @@
/*
* 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 "gfs2.h"
#include "dir.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
#include "ops_export.h"
#include "rgrp.h"
static struct dentry *gfs2_decode_fh(struct super_block *sb,
__u32 *fh,
int fh_len,
int fh_type,
int (*acceptable)(void *context,
struct dentry *dentry),
void *context)
{
struct gfs2_inum this, parent;
atomic_inc(&get_v2sdp(sb)->sd_ops_export);
if (fh_type != fh_len)
return NULL;
memset(&parent, 0, sizeof(struct gfs2_inum));
switch (fh_type) {
case 8:
parent.no_formal_ino = ((uint64_t)be32_to_cpu(fh[4])) << 32;
parent.no_formal_ino |= be32_to_cpu(fh[5]);
parent.no_addr = ((uint64_t)be32_to_cpu(fh[6])) << 32;
parent.no_addr |= be32_to_cpu(fh[7]);
case 4:
this.no_formal_ino = ((uint64_t)be32_to_cpu(fh[0])) << 32;
this.no_formal_ino |= be32_to_cpu(fh[1]);
this.no_addr = ((uint64_t)be32_to_cpu(fh[2])) << 32;
this.no_addr |= be32_to_cpu(fh[3]);
break;
default:
return NULL;
}
return gfs2_export_ops.find_exported_dentry(sb, &this, &parent,
acceptable, context);
}
static int gfs2_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
int connectable)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = get_v2ip(inode);
struct gfs2_sbd *sdp = ip->i_sbd;
atomic_inc(&sdp->sd_ops_export);
if (*len < 4 || (connectable && *len < 8))
return 255;
fh[0] = ip->i_num.no_formal_ino >> 32;
fh[0] = cpu_to_be32(fh[0]);
fh[1] = ip->i_num.no_formal_ino & 0xFFFFFFFF;
fh[1] = cpu_to_be32(fh[1]);
fh[2] = ip->i_num.no_addr >> 32;
fh[2] = cpu_to_be32(fh[2]);
fh[3] = ip->i_num.no_addr & 0xFFFFFFFF;
fh[3] = cpu_to_be32(fh[3]);
*len = 4;
if (!connectable || ip == sdp->sd_root_dir)
return *len;
spin_lock(&dentry->d_lock);
inode = dentry->d_parent->d_inode;
ip = get_v2ip(inode);
gfs2_inode_hold(ip);
spin_unlock(&dentry->d_lock);
fh[4] = ip->i_num.no_formal_ino >> 32;
fh[4] = cpu_to_be32(fh[4]);
fh[5] = ip->i_num.no_formal_ino & 0xFFFFFFFF;
fh[5] = cpu_to_be32(fh[5]);
fh[6] = ip->i_num.no_addr >> 32;
fh[6] = cpu_to_be32(fh[6]);
fh[7] = ip->i_num.no_addr & 0xFFFFFFFF;
fh[7] = cpu_to_be32(fh[7]);
*len = 8;
gfs2_inode_put(ip);
return *len;
}
struct get_name_filldir {
struct gfs2_inum inum;
char *name;
};
static int get_name_filldir(void *opaque, const char *name, unsigned int length,
uint64_t offset, struct gfs2_inum *inum,
unsigned int type)
{
struct get_name_filldir *gnfd = (struct get_name_filldir *)opaque;
if (!gfs2_inum_equal(inum, &gnfd->inum))
return 0;
memcpy(gnfd->name, name, length);
gnfd->name[length] = 0;
return 1;
}
static int gfs2_get_name(struct dentry *parent, char *name,
struct dentry *child)
{
struct inode *dir = parent->d_inode;
struct inode *inode = child->d_inode;
struct gfs2_inode *dip, *ip;
struct get_name_filldir gnfd;
struct gfs2_holder gh;
uint64_t offset = 0;
int error;
if (!dir)
return -EINVAL;
atomic_inc(&get_v2sdp(dir->i_sb)->sd_ops_export);
if (!S_ISDIR(dir->i_mode) || !inode)
return -EINVAL;
dip = get_v2ip(dir);
ip = get_v2ip(inode);
*name = 0;
gnfd.inum = ip->i_num;
gnfd.name = name;
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &gh);
if (error)
return error;
error = gfs2_dir_read(dip, &offset, &gnfd, get_name_filldir);
gfs2_glock_dq_uninit(&gh);
if (!error && !*name)
error = -ENOENT;
return error;
}
static struct dentry *gfs2_get_parent(struct dentry *child)
{
struct gfs2_inode *dip = get_v2ip(child->d_inode);
struct qstr dotdot = { .name = "..", .len = 2 };
struct gfs2_inode *ip;
struct inode *inode;
struct dentry *dentry;
int error;
atomic_inc(&dip->i_sbd->sd_ops_export);
error = gfs2_lookupi(dip, &dotdot, 1, &ip);
if (error)
return ERR_PTR(error);
inode = gfs2_ip2v(ip);
gfs2_inode_put(ip);
if (!inode)
return ERR_PTR(-ENOMEM);
dentry = d_alloc_anon(inode);
if (!dentry) {
iput(inode);
return ERR_PTR(-ENOMEM);
}
return dentry;
}
static struct dentry *gfs2_get_dentry(struct super_block *sb, void *inum_p)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
struct gfs2_inum *inum = (struct gfs2_inum *)inum_p;
struct gfs2_holder i_gh, ri_gh, rgd_gh;
struct gfs2_rgrpd *rgd;
struct gfs2_inode *ip;
struct inode *inode;
struct dentry *dentry;
int error;
atomic_inc(&sdp->sd_ops_export);
/* System files? */
inode = gfs2_iget(sb, inum);
if (inode) {
ip = get_v2ip(inode);
if (ip->i_num.no_formal_ino != inum->no_formal_ino) {
iput(inode);
return ERR_PTR(-ESTALE);
}
goto out_inode;
}
error = gfs2_glock_nq_num(sdp,
inum->no_addr, &gfs2_inode_glops,
LM_ST_SHARED, LM_FLAG_ANY | GL_LOCAL_EXCL,
&i_gh);
if (error)
return ERR_PTR(error);
error = gfs2_inode_get(i_gh.gh_gl, inum, NO_CREATE, &ip);
if (error)
goto fail;
if (ip)
goto out_ip;
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
goto fail;
error = -EINVAL;
rgd = gfs2_blk2rgrpd(sdp, inum->no_addr);
if (!rgd)
goto fail_rindex;
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
if (error)
goto fail_rindex;
error = -ESTALE;
if (gfs2_get_block_type(rgd, inum->no_addr) != GFS2_BLKST_DINODE)
goto fail_rgd;
gfs2_glock_dq_uninit(&rgd_gh);
gfs2_glock_dq_uninit(&ri_gh);
error = gfs2_inode_get(i_gh.gh_gl, inum, CREATE, &ip);
if (error)
goto fail;
error = gfs2_inode_refresh(ip);
if (error) {
gfs2_inode_put(ip);
goto fail;
}
atomic_inc(&sdp->sd_fh2dentry_misses);
out_ip:
error = -EIO;
if (ip->i_di.di_flags & GFS2_DIF_SYSTEM) {
gfs2_inode_put(ip);
goto fail;
}
gfs2_glock_dq_uninit(&i_gh);
inode = gfs2_ip2v(ip);
gfs2_inode_put(ip);
if (!inode)
return ERR_PTR(-ENOMEM);
out_inode:
dentry = d_alloc_anon(inode);
if (!dentry) {
iput(inode);
return ERR_PTR(-ENOMEM);
}
return dentry;
fail_rgd:
gfs2_glock_dq_uninit(&rgd_gh);
fail_rindex:
gfs2_glock_dq_uninit(&ri_gh);
fail:
gfs2_glock_dq_uninit(&i_gh);
return ERR_PTR(error);
}
struct export_operations gfs2_export_ops = {
.decode_fh = gfs2_decode_fh,
.encode_fh = gfs2_encode_fh,
.get_name = gfs2_get_name,
.get_parent = gfs2_get_parent,
.get_dentry = gfs2_get_dentry,
};

15
fs/gfs2/ops_export.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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 __OPS_EXPORT_DOT_H__
#define __OPS_EXPORT_DOT_H__
extern struct export_operations gfs2_export_ops;
#endif /* __OPS_EXPORT_DOT_H__ */

1597
fs/gfs2/ops_file.c Normal file

File diff suppressed because it is too large Load Diff

16
fs/gfs2/ops_file.h Normal file
View File

@ -0,0 +1,16 @@
/*
* 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 __OPS_FILE_DOT_H__
#define __OPS_FILE_DOT_H__
extern struct file_operations gfs2_file_fops;
extern struct file_operations gfs2_dir_fops;
#endif /* __OPS_FILE_DOT_H__ */

879
fs/gfs2/ops_fstype.c Normal file
View File

@ -0,0 +1,879 @@
/*
* 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 <linux/vmalloc.h>
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "daemon.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
#include "lm.h"
#include "mount.h"
#include "ops_export.h"
#include "ops_fstype.h"
#include "ops_super.h"
#include "recovery.h"
#include "rgrp.h"
#include "super.h"
#include "unlinked.h"
#include "sys.h"
#define DO 0
#define UNDO 1
static struct gfs2_sbd *init_sbd(struct super_block *sb)
{
struct gfs2_sbd *sdp;
unsigned int x;
sdp = vmalloc(sizeof(struct gfs2_sbd));
if (!sdp)
return NULL;
memset(sdp, 0, sizeof(struct gfs2_sbd));
set_v2sdp(sb, sdp);
sdp->sd_vfs = sb;
gfs2_tune_init(&sdp->sd_tune);
for (x = 0; x < GFS2_GL_HASH_SIZE; x++) {
sdp->sd_gl_hash[x].hb_lock = RW_LOCK_UNLOCKED;
INIT_LIST_HEAD(&sdp->sd_gl_hash[x].hb_list);
}
INIT_LIST_HEAD(&sdp->sd_reclaim_list);
spin_lock_init(&sdp->sd_reclaim_lock);
init_waitqueue_head(&sdp->sd_reclaim_wq);
init_MUTEX(&sdp->sd_invalidate_inodes_mutex);
init_MUTEX(&sdp->sd_inum_mutex);
spin_lock_init(&sdp->sd_statfs_spin);
init_MUTEX(&sdp->sd_statfs_mutex);
spin_lock_init(&sdp->sd_rindex_spin);
init_MUTEX(&sdp->sd_rindex_mutex);
INIT_LIST_HEAD(&sdp->sd_rindex_list);
INIT_LIST_HEAD(&sdp->sd_rindex_mru_list);
INIT_LIST_HEAD(&sdp->sd_rindex_recent_list);
INIT_LIST_HEAD(&sdp->sd_jindex_list);
spin_lock_init(&sdp->sd_jindex_spin);
init_MUTEX(&sdp->sd_jindex_mutex);
INIT_LIST_HEAD(&sdp->sd_unlinked_list);
spin_lock_init(&sdp->sd_unlinked_spin);
init_MUTEX(&sdp->sd_unlinked_mutex);
INIT_LIST_HEAD(&sdp->sd_quota_list);
spin_lock_init(&sdp->sd_quota_spin);
init_MUTEX(&sdp->sd_quota_mutex);
spin_lock_init(&sdp->sd_log_lock);
init_waitqueue_head(&sdp->sd_log_trans_wq);
init_waitqueue_head(&sdp->sd_log_flush_wq);
INIT_LIST_HEAD(&sdp->sd_log_le_gl);
INIT_LIST_HEAD(&sdp->sd_log_le_buf);
INIT_LIST_HEAD(&sdp->sd_log_le_revoke);
INIT_LIST_HEAD(&sdp->sd_log_le_rg);
INIT_LIST_HEAD(&sdp->sd_log_le_databuf);
INIT_LIST_HEAD(&sdp->sd_log_blks_list);
init_waitqueue_head(&sdp->sd_log_blks_wait);
INIT_LIST_HEAD(&sdp->sd_ail1_list);
INIT_LIST_HEAD(&sdp->sd_ail2_list);
init_MUTEX(&sdp->sd_log_flush_lock);
INIT_LIST_HEAD(&sdp->sd_log_flush_list);
INIT_LIST_HEAD(&sdp->sd_revoke_list);
init_MUTEX(&sdp->sd_freeze_lock);
return sdp;
}
static void init_vfs(struct gfs2_sbd *sdp)
{
struct super_block *sb = sdp->sd_vfs;
sb->s_magic = GFS2_MAGIC;
sb->s_op = &gfs2_super_ops;
sb->s_export_op = &gfs2_export_ops;
sb->s_maxbytes = MAX_LFS_FILESIZE;
if (sb->s_flags & (MS_NOATIME | MS_NODIRATIME))
set_bit(SDF_NOATIME, &sdp->sd_flags);
/* Don't let the VFS update atimes. GFS2 handles this itself. */
sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
/* Set up the buffer cache and fill in some fake block size values
to allow us to read-in the on-disk superblock. */
sdp->sd_sb.sb_bsize = sb_min_blocksize(sb, GFS2_BASIC_BLOCK);
sdp->sd_sb.sb_bsize_shift = sb->s_blocksize_bits;
sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift - GFS2_BASIC_BLOCK_SHIFT;
sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift;
}
static int init_names(struct gfs2_sbd *sdp, int silent)
{
struct gfs2_sb *sb = NULL;
char *proto, *table;
int error = 0;
proto = sdp->sd_args.ar_lockproto;
table = sdp->sd_args.ar_locktable;
/* Try to autodetect */
if (!proto[0] || !table[0]) {
struct buffer_head *bh;
bh = sb_getblk(sdp->sd_vfs,
GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift);
lock_buffer(bh);
clear_buffer_uptodate(bh);
clear_buffer_dirty(bh);
unlock_buffer(bh);
ll_rw_block(READ, 1, &bh);
wait_on_buffer(bh);
if (!buffer_uptodate(bh)) {
brelse(bh);
return -EIO;
}
sb = kmalloc(sizeof(struct gfs2_sb), GFP_KERNEL);
if (!sb) {
brelse(bh);
return -ENOMEM;
}
gfs2_sb_in(sb, bh->b_data);
brelse(bh);
error = gfs2_check_sb(sdp, sb, silent);
if (error)
goto out;
if (!proto[0])
proto = sb->sb_lockproto;
if (!table[0])
table = sb->sb_locktable;
}
if (!table[0])
table = sdp->sd_vfs->s_id;
snprintf(sdp->sd_proto_name, GFS2_FSNAME_LEN, "%s", proto);
snprintf(sdp->sd_table_name, GFS2_FSNAME_LEN, "%s", table);
out:
kfree(sb);
return error;
}
static int init_locking(struct gfs2_sbd *sdp, struct gfs2_holder *mount_gh,
int undo)
{
struct task_struct *p;
int error = 0;
if (undo)
goto fail_trans;
p = kthread_run(gfs2_scand, sdp, "gfs2_scand");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start scand thread: %d\n", error);
return error;
}
sdp->sd_scand_process = p;
for (sdp->sd_glockd_num = 0;
sdp->sd_glockd_num < sdp->sd_args.ar_num_glockd;
sdp->sd_glockd_num++) {
p = kthread_run(gfs2_glockd, sdp, "gfs2_glockd");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start glockd thread: %d\n", error);
goto fail;
}
sdp->sd_glockd_process[sdp->sd_glockd_num] = p;
}
error = gfs2_glock_nq_num(sdp,
GFS2_MOUNT_LOCK, &gfs2_nondisk_glops,
LM_ST_EXCLUSIVE, LM_FLAG_NOEXP | GL_NOCACHE,
mount_gh);
if (error) {
fs_err(sdp, "can't acquire mount glock: %d\n", error);
goto fail;
}
error = gfs2_glock_nq_num(sdp,
GFS2_LIVE_LOCK, &gfs2_nondisk_glops,
LM_ST_SHARED,
LM_FLAG_NOEXP | GL_EXACT | GL_NEVER_RECURSE,
&sdp->sd_live_gh);
if (error) {
fs_err(sdp, "can't acquire live glock: %d\n", error);
goto fail_mount;
}
error = gfs2_glock_get(sdp, GFS2_RENAME_LOCK, &gfs2_nondisk_glops,
CREATE, &sdp->sd_rename_gl);
if (error) {
fs_err(sdp, "can't create rename glock: %d\n", error);
goto fail_live;
}
error = gfs2_glock_get(sdp, GFS2_TRANS_LOCK, &gfs2_trans_glops,
CREATE, &sdp->sd_trans_gl);
if (error) {
fs_err(sdp, "can't create transaction glock: %d\n", error);
goto fail_rename;
}
set_bit(GLF_STICKY, &sdp->sd_trans_gl->gl_flags);
return 0;
fail_trans:
gfs2_glock_put(sdp->sd_trans_gl);
fail_rename:
gfs2_glock_put(sdp->sd_rename_gl);
fail_live:
gfs2_glock_dq_uninit(&sdp->sd_live_gh);
fail_mount:
gfs2_glock_dq_uninit(mount_gh);
fail:
while (sdp->sd_glockd_num--)
kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]);
kthread_stop(sdp->sd_scand_process);
return error;
}
static int init_sb(struct gfs2_sbd *sdp, int silent, int undo)
{
struct super_block *sb = sdp->sd_vfs;
struct gfs2_holder sb_gh;
int error = 0;
if (undo) {
gfs2_inode_put(sdp->sd_master_dir);
return 0;
}
error = gfs2_glock_nq_num(sdp,
GFS2_SB_LOCK, &gfs2_meta_glops,
LM_ST_SHARED, 0, &sb_gh);
if (error) {
fs_err(sdp, "can't acquire superblock glock: %d\n", error);
return error;
}
error = gfs2_read_sb(sdp, sb_gh.gh_gl, silent);
if (error) {
fs_err(sdp, "can't read superblock: %d\n", error);
goto out;
}
/* Set up the buffer cache and SB for real */
error = -EINVAL;
if (sdp->sd_sb.sb_bsize < bdev_hardsect_size(sb->s_bdev)) {
fs_err(sdp, "FS block size (%u) is too small for device "
"block size (%u)\n",
sdp->sd_sb.sb_bsize, bdev_hardsect_size(sb->s_bdev));
goto out;
}
if (sdp->sd_sb.sb_bsize > PAGE_SIZE) {
fs_err(sdp, "FS block size (%u) is too big for machine "
"page size (%u)\n",
sdp->sd_sb.sb_bsize, (unsigned int)PAGE_SIZE);
goto out;
}
/* Get rid of buffers from the original block size */
sb_gh.gh_gl->gl_ops->go_inval(sb_gh.gh_gl, DIO_METADATA | DIO_DATA);
sb_gh.gh_gl->gl_aspace->i_blkbits = sdp->sd_sb.sb_bsize_shift;
sb_set_blocksize(sb, sdp->sd_sb.sb_bsize);
error = gfs2_lookup_master_dir(sdp);
if (error)
fs_err(sdp, "can't read in master directory: %d\n", error);
out:
gfs2_glock_dq_uninit(&sb_gh);
return error;
}
static int init_journal(struct gfs2_sbd *sdp, int undo)
{
struct gfs2_holder ji_gh;
struct task_struct *p;
int jindex = 1;
int error = 0;
if (undo) {
jindex = 0;
goto fail_recoverd;
}
error = gfs2_lookup_simple(sdp->sd_master_dir, "jindex",
&sdp->sd_jindex);
if (error) {
fs_err(sdp, "can't lookup journal index: %d\n", error);
return error;
}
set_bit(GLF_STICKY, &sdp->sd_jindex->i_gl->gl_flags);
/* Load in the journal index special file */
error = gfs2_jindex_hold(sdp, &ji_gh);
if (error) {
fs_err(sdp, "can't read journal index: %d\n", error);
goto fail;
}
error = -EINVAL;
if (!gfs2_jindex_size(sdp)) {
fs_err(sdp, "no journals!\n");
goto fail_jindex;
}
if (sdp->sd_args.ar_spectator) {
sdp->sd_jdesc = gfs2_jdesc_find(sdp, 0);
sdp->sd_log_blks_free = sdp->sd_jdesc->jd_blocks;
} else {
if (sdp->sd_lockstruct.ls_jid >= gfs2_jindex_size(sdp)) {
fs_err(sdp, "can't mount journal #%u\n",
sdp->sd_lockstruct.ls_jid);
fs_err(sdp, "there are only %u journals (0 - %u)\n",
gfs2_jindex_size(sdp),
gfs2_jindex_size(sdp) - 1);
goto fail_jindex;
}
sdp->sd_jdesc = gfs2_jdesc_find(sdp, sdp->sd_lockstruct.ls_jid);
error = gfs2_glock_nq_num(sdp,
sdp->sd_lockstruct.ls_jid,
&gfs2_journal_glops,
LM_ST_EXCLUSIVE, LM_FLAG_NOEXP,
&sdp->sd_journal_gh);
if (error) {
fs_err(sdp, "can't acquire journal glock: %d\n", error);
goto fail_jindex;
}
error = gfs2_glock_nq_init(sdp->sd_jdesc->jd_inode->i_gl,
LM_ST_SHARED,
LM_FLAG_NOEXP | GL_EXACT,
&sdp->sd_jinode_gh);
if (error) {
fs_err(sdp, "can't acquire journal inode glock: %d\n",
error);
goto fail_journal_gh;
}
error = gfs2_jdesc_check(sdp->sd_jdesc);
if (error) {
fs_err(sdp, "my journal (%u) is bad: %d\n",
sdp->sd_jdesc->jd_jid, error);
goto fail_jinode_gh;
}
sdp->sd_log_blks_free = sdp->sd_jdesc->jd_blocks;
}
if (sdp->sd_lockstruct.ls_first) {
unsigned int x;
for (x = 0; x < sdp->sd_journals; x++) {
error = gfs2_recover_journal(gfs2_jdesc_find(sdp, x),
WAIT);
if (error) {
fs_err(sdp, "error recovering journal %u: %d\n",
x, error);
goto fail_jinode_gh;
}
}
gfs2_lm_others_may_mount(sdp);
} else if (!sdp->sd_args.ar_spectator) {
error = gfs2_recover_journal(sdp->sd_jdesc, WAIT);
if (error) {
fs_err(sdp, "error recovering my journal: %d\n", error);
goto fail_jinode_gh;
}
}
set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags);
gfs2_glock_dq_uninit(&ji_gh);
jindex = 0;
/* Disown my Journal glock */
sdp->sd_journal_gh.gh_owner = NULL;
sdp->sd_jinode_gh.gh_owner = NULL;
p = kthread_run(gfs2_recoverd, sdp, "gfs2_recoverd");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start recoverd thread: %d\n", error);
goto fail_jinode_gh;
}
sdp->sd_recoverd_process = p;
return 0;
fail_recoverd:
kthread_stop(sdp->sd_recoverd_process);
fail_jinode_gh:
if (!sdp->sd_args.ar_spectator)
gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
fail_journal_gh:
if (!sdp->sd_args.ar_spectator)
gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
fail_jindex:
gfs2_jindex_free(sdp);
if (jindex)
gfs2_glock_dq_uninit(&ji_gh);
fail:
gfs2_inode_put(sdp->sd_jindex);
return error;
}
int gfs2_lookup_root(struct gfs2_sbd *sdp)
{
int error;
struct gfs2_glock *gl;
error = gfs2_glock_get(sdp, sdp->sd_sb.sb_root_dir.no_addr,
&gfs2_inode_glops, CREATE, &gl);
if (!error) {
error = gfs2_inode_get(gl, &sdp->sd_sb.sb_root_dir,
CREATE, &sdp->sd_root_dir);
if (!error)
gfs2_inode_min_init(sdp->sd_root_dir, DT_DIR);
gfs2_glock_put(gl);
}
return error;
}
static int init_inodes(struct gfs2_sbd *sdp, int undo)
{
struct inode *inode;
struct dentry **dentry = &sdp->sd_vfs->s_root;
int error = 0;
if (undo)
goto fail_dput;
/* Read in the master inode number inode */
error = gfs2_lookup_simple(sdp->sd_master_dir, "inum",
&sdp->sd_inum_inode);
if (error) {
fs_err(sdp, "can't read in inum inode: %d\n", error);
return error;
}
/* Read in the master statfs inode */
error = gfs2_lookup_simple(sdp->sd_master_dir, "statfs",
&sdp->sd_statfs_inode);
if (error) {
fs_err(sdp, "can't read in statfs inode: %d\n", error);
goto fail;
}
/* Read in the resource index inode */
error = gfs2_lookup_simple(sdp->sd_master_dir, "rindex",
&sdp->sd_rindex);
if (error) {
fs_err(sdp, "can't get resource index inode: %d\n", error);
goto fail_statfs;
}
set_bit(GLF_STICKY, &sdp->sd_rindex->i_gl->gl_flags);
sdp->sd_rindex_vn = sdp->sd_rindex->i_gl->gl_vn - 1;
/* Read in the quota inode */
error = gfs2_lookup_simple(sdp->sd_master_dir, "quota",
&sdp->sd_quota_inode);
if (error) {
fs_err(sdp, "can't get quota file inode: %d\n", error);
goto fail_rindex;
}
/* Get the root inode */
error = gfs2_lookup_root(sdp);
if (error) {
fs_err(sdp, "can't read in root inode: %d\n", error);
goto fail_qinode;
}
/* Get the root inode/dentry */
inode = gfs2_ip2v(sdp->sd_root_dir);
if (!inode) {
fs_err(sdp, "can't get root inode\n");
error = -ENOMEM;
goto fail_rooti;
}
*dentry = d_alloc_root(inode);
if (!*dentry) {
iput(inode);
fs_err(sdp, "can't get root dentry\n");
error = -ENOMEM;
goto fail_rooti;
}
return 0;
fail_dput:
dput(*dentry);
*dentry = NULL;
fail_rooti:
gfs2_inode_put(sdp->sd_root_dir);
fail_qinode:
gfs2_inode_put(sdp->sd_quota_inode);
fail_rindex:
gfs2_clear_rgrpd(sdp);
gfs2_inode_put(sdp->sd_rindex);
fail_statfs:
gfs2_inode_put(sdp->sd_statfs_inode);
fail:
gfs2_inode_put(sdp->sd_inum_inode);
return error;
}
static int init_per_node(struct gfs2_sbd *sdp, int undo)
{
struct gfs2_inode *pn = NULL;
char buf[30];
int error = 0;
if (sdp->sd_args.ar_spectator)
return 0;
if (undo)
goto fail_qc_gh;
error = gfs2_lookup_simple(sdp->sd_master_dir, "per_node", &pn);
if (error) {
fs_err(sdp, "can't find per_node directory: %d\n", error);
return error;
}
sprintf(buf, "inum_range%u", sdp->sd_jdesc->jd_jid);
error = gfs2_lookup_simple(pn, buf, &sdp->sd_ir_inode);
if (error) {
fs_err(sdp, "can't find local \"ir\" file: %d\n", error);
goto fail;
}
sprintf(buf, "statfs_change%u", sdp->sd_jdesc->jd_jid);
error = gfs2_lookup_simple(pn, buf, &sdp->sd_sc_inode);
if (error) {
fs_err(sdp, "can't find local \"sc\" file: %d\n", error);
goto fail_ir_i;
}
sprintf(buf, "unlinked_tag%u", sdp->sd_jdesc->jd_jid);
error = gfs2_lookup_simple(pn, buf, &sdp->sd_ut_inode);
if (error) {
fs_err(sdp, "can't find local \"ut\" file: %d\n", error);
goto fail_sc_i;
}
sprintf(buf, "quota_change%u", sdp->sd_jdesc->jd_jid);
error = gfs2_lookup_simple(pn, buf, &sdp->sd_qc_inode);
if (error) {
fs_err(sdp, "can't find local \"qc\" file: %d\n", error);
goto fail_ut_i;
}
gfs2_inode_put(pn);
pn = NULL;
error = gfs2_glock_nq_init(sdp->sd_ir_inode->i_gl,
LM_ST_EXCLUSIVE, GL_NEVER_RECURSE,
&sdp->sd_ir_gh);
if (error) {
fs_err(sdp, "can't lock local \"ir\" file: %d\n", error);
goto fail_qc_i;
}
error = gfs2_glock_nq_init(sdp->sd_sc_inode->i_gl,
LM_ST_EXCLUSIVE, GL_NEVER_RECURSE,
&sdp->sd_sc_gh);
if (error) {
fs_err(sdp, "can't lock local \"sc\" file: %d\n", error);
goto fail_ir_gh;
}
error = gfs2_glock_nq_init(sdp->sd_ut_inode->i_gl,
LM_ST_EXCLUSIVE, GL_NEVER_RECURSE,
&sdp->sd_ut_gh);
if (error) {
fs_err(sdp, "can't lock local \"ut\" file: %d\n", error);
goto fail_sc_gh;
}
error = gfs2_glock_nq_init(sdp->sd_qc_inode->i_gl,
LM_ST_EXCLUSIVE, GL_NEVER_RECURSE,
&sdp->sd_qc_gh);
if (error) {
fs_err(sdp, "can't lock local \"qc\" file: %d\n", error);
goto fail_ut_gh;
}
return 0;
fail_qc_gh:
gfs2_glock_dq_uninit(&sdp->sd_qc_gh);
fail_ut_gh:
gfs2_glock_dq_uninit(&sdp->sd_ut_gh);
fail_sc_gh:
gfs2_glock_dq_uninit(&sdp->sd_sc_gh);
fail_ir_gh:
gfs2_glock_dq_uninit(&sdp->sd_ir_gh);
fail_qc_i:
gfs2_inode_put(sdp->sd_qc_inode);
fail_ut_i:
gfs2_inode_put(sdp->sd_ut_inode);
fail_sc_i:
gfs2_inode_put(sdp->sd_sc_inode);
fail_ir_i:
gfs2_inode_put(sdp->sd_ir_inode);
fail:
if (pn)
gfs2_inode_put(pn);
return error;
}
static int init_threads(struct gfs2_sbd *sdp, int undo)
{
struct task_struct *p;
int error = 0;
if (undo)
goto fail_inoded;
sdp->sd_log_flush_time = jiffies;
sdp->sd_jindex_refresh_time = jiffies;
p = kthread_run(gfs2_logd, sdp, "gfs2_logd");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start logd thread: %d\n", error);
return error;
}
sdp->sd_logd_process = p;
sdp->sd_statfs_sync_time = jiffies;
sdp->sd_quota_sync_time = jiffies;
p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start quotad thread: %d\n", error);
goto fail;
}
sdp->sd_quotad_process = p;
p = kthread_run(gfs2_inoded, sdp, "gfs2_inoded");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start inoded thread: %d\n", error);
goto fail_quotad;
}
sdp->sd_inoded_process = p;
return 0;
fail_inoded:
kthread_stop(sdp->sd_inoded_process);
fail_quotad:
kthread_stop(sdp->sd_quotad_process);
fail:
kthread_stop(sdp->sd_logd_process);
return error;
}
/**
* fill_super - Read in superblock
* @sb: The VFS superblock
* @data: Mount options
* @silent: Don't complain if it's not a GFS2 filesystem
*
* Returns: errno
*/
static int fill_super(struct super_block *sb, void *data, int silent)
{
struct gfs2_sbd *sdp;
struct gfs2_holder mount_gh;
int error;
sdp = init_sbd(sb);
if (!sdp) {
printk("GFS2: can't alloc struct gfs2_sbd\n");
return -ENOMEM;
}
error = gfs2_mount_args(sdp, (char *)data, 0);
if (error) {
printk("GFS2: can't parse mount arguments\n");
goto fail;
}
init_vfs(sdp);
error = init_names(sdp, silent);
if (error)
goto fail;
error = gfs2_sys_fs_add(sdp);
if (error)
goto fail;
error = gfs2_lm_mount(sdp, silent);
if (error)
goto fail_sys;
error = init_locking(sdp, &mount_gh, DO);
if (error)
goto fail_lm;
error = init_sb(sdp, silent, DO);
if (error)
goto fail_locking;
error = init_journal(sdp, DO);
if (error)
goto fail_sb;
error = init_inodes(sdp, DO);
if (error)
goto fail_journals;
error = init_per_node(sdp, DO);
if (error)
goto fail_inodes;
error = gfs2_statfs_init(sdp);
if (error) {
fs_err(sdp, "can't initialize statfs subsystem: %d\n", error);
goto fail_per_node;
}
error = init_threads(sdp, DO);
if (error)
goto fail_per_node;
if (!(sb->s_flags & MS_RDONLY)) {
error = gfs2_make_fs_rw(sdp);
if (error) {
fs_err(sdp, "can't make FS RW: %d\n", error);
goto fail_threads;
}
}
gfs2_glock_dq_uninit(&mount_gh);
return 0;
fail_threads:
init_threads(sdp, UNDO);
fail_per_node:
init_per_node(sdp, UNDO);
fail_inodes:
init_inodes(sdp, UNDO);
fail_journals:
init_journal(sdp, UNDO);
fail_sb:
init_sb(sdp, 0, UNDO);
fail_locking:
init_locking(sdp, &mount_gh, UNDO);
fail_lm:
gfs2_gl_hash_clear(sdp, WAIT);
gfs2_lm_unmount(sdp);
while (invalidate_inodes(sb))
yield();
fail_sys:
gfs2_sys_fs_del(sdp);
fail:
vfree(sdp);
set_v2sdp(sb, NULL);
return error;
}
static struct super_block *gfs2_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data)
{
return get_sb_bdev(fs_type, flags, dev_name, data, fill_super);
}
struct file_system_type gfs2_fs_type = {
.name = "gfs2",
.fs_flags = FS_REQUIRES_DEV,
.get_sb = gfs2_get_sb,
.kill_sb = kill_block_super,
.owner = THIS_MODULE,
};

15
fs/gfs2/ops_fstype.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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 __OPS_FSTYPE_DOT_H__
#define __OPS_FSTYPE_DOT_H__
extern struct file_system_type gfs2_fs_type;
#endif /* __OPS_FSTYPE_DOT_H__ */

1265
fs/gfs2/ops_inode.c Normal file

File diff suppressed because it is too large Load Diff

18
fs/gfs2/ops_inode.h Normal file
View File

@ -0,0 +1,18 @@
/*
* 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 __OPS_INODE_DOT_H__
#define __OPS_INODE_DOT_H__
extern struct inode_operations gfs2_file_iops;
extern struct inode_operations gfs2_dir_iops;
extern struct inode_operations gfs2_symlink_iops;
extern struct inode_operations gfs2_dev_iops;
#endif /* __OPS_INODE_DOT_H__ */

401
fs/gfs2/ops_super.c Normal file
View File

@ -0,0 +1,401 @@
/*
* 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 <linux/vmalloc.h>
#include <linux/statfs.h>
#include <linux/seq_file.h>
#include <linux/mount.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "glock.h"
#include "inode.h"
#include "lm.h"
#include "log.h"
#include "mount.h"
#include "ops_super.h"
#include "page.h"
#include "quota.h"
#include "recovery.h"
#include "rgrp.h"
#include "super.h"
#include "sys.h"
/**
* gfs2_write_inode - Make sure the inode is stable on the disk
* @inode: The inode
* @sync: synchronous write flag
*
* Returns: errno
*/
static int gfs2_write_inode(struct inode *inode, int sync)
{
struct gfs2_inode *ip = get_v2ip(inode);
atomic_inc(&ip->i_sbd->sd_ops_super);
if (current->flags & PF_MEMALLOC)
return 0;
if (ip && sync)
gfs2_log_flush_glock(ip->i_gl);
return 0;
}
/**
* gfs2_put_super - Unmount the filesystem
* @sb: The VFS superblock
*
*/
static void gfs2_put_super(struct super_block *sb)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
int error;
if (!sdp)
return;
atomic_inc(&sdp->sd_ops_super);
/* Unfreeze the filesystem, if we need to */
down(&sdp->sd_freeze_lock);
if (sdp->sd_freeze_count)
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
up(&sdp->sd_freeze_lock);
kthread_stop(sdp->sd_inoded_process);
kthread_stop(sdp->sd_quotad_process);
kthread_stop(sdp->sd_logd_process);
kthread_stop(sdp->sd_recoverd_process);
while (sdp->sd_glockd_num--)
kthread_stop(sdp->sd_glockd_process[sdp->sd_glockd_num]);
kthread_stop(sdp->sd_scand_process);
if (!(sb->s_flags & MS_RDONLY)) {
error = gfs2_make_fs_ro(sdp);
if (error)
gfs2_io_error(sdp);
}
/* At this point, we're through modifying the disk */
/* Release stuff */
gfs2_inode_put(sdp->sd_master_dir);
gfs2_inode_put(sdp->sd_jindex);
gfs2_inode_put(sdp->sd_inum_inode);
gfs2_inode_put(sdp->sd_statfs_inode);
gfs2_inode_put(sdp->sd_rindex);
gfs2_inode_put(sdp->sd_quota_inode);
gfs2_inode_put(sdp->sd_root_dir);
gfs2_glock_put(sdp->sd_rename_gl);
gfs2_glock_put(sdp->sd_trans_gl);
if (!sdp->sd_args.ar_spectator) {
gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
gfs2_glock_dq_uninit(&sdp->sd_ir_gh);
gfs2_glock_dq_uninit(&sdp->sd_sc_gh);
gfs2_glock_dq_uninit(&sdp->sd_ut_gh);
gfs2_glock_dq_uninit(&sdp->sd_qc_gh);
gfs2_inode_put(sdp->sd_ir_inode);
gfs2_inode_put(sdp->sd_sc_inode);
gfs2_inode_put(sdp->sd_ut_inode);
gfs2_inode_put(sdp->sd_qc_inode);
}
gfs2_glock_dq_uninit(&sdp->sd_live_gh);
gfs2_clear_rgrpd(sdp);
gfs2_jindex_free(sdp);
/* Take apart glock structures and buffer lists */
gfs2_gl_hash_clear(sdp, WAIT);
/* Unmount the locking protocol */
gfs2_lm_unmount(sdp);
/* At this point, we're through participating in the lockspace */
gfs2_sys_fs_del(sdp);
/* Get rid of any extra inodes */
while (invalidate_inodes(sb))
yield();
vfree(sdp);
set_v2sdp(sb, NULL);
}
/**
* gfs2_write_super - disk commit all incore transactions
* @sb: the filesystem
*
* This function is called every time sync(2) is called.
* After this exits, all dirty buffers and synced.
*/
static void gfs2_write_super(struct super_block *sb)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
atomic_inc(&sdp->sd_ops_super);
gfs2_log_flush(sdp);
}
/**
* gfs2_write_super_lockfs - prevent further writes to the filesystem
* @sb: the VFS structure for the filesystem
*
*/
static void gfs2_write_super_lockfs(struct super_block *sb)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
int error;
atomic_inc(&sdp->sd_ops_super);
for (;;) {
error = gfs2_freeze_fs(sdp);
if (!error)
break;
switch (error) {
case -EBUSY:
fs_err(sdp, "waiting for recovery before freeze\n");
break;
default:
fs_err(sdp, "error freezing FS: %d\n", error);
break;
}
fs_err(sdp, "retrying...\n");
msleep(1000);
}
}
/**
* gfs2_unlockfs - reallow writes to the filesystem
* @sb: the VFS structure for the filesystem
*
*/
static void gfs2_unlockfs(struct super_block *sb)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
atomic_inc(&sdp->sd_ops_super);
gfs2_unfreeze_fs(sdp);
}
/**
* gfs2_statfs - Gather and return stats about the filesystem
* @sb: The superblock
* @statfsbuf: The buffer
*
* Returns: 0 on success or error code
*/
static int gfs2_statfs(struct super_block *sb, struct kstatfs *buf)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
struct gfs2_statfs_change sc;
int error;
atomic_inc(&sdp->sd_ops_super);
if (gfs2_tune_get(sdp, gt_statfs_slow))
error = gfs2_statfs_slow(sdp, &sc);
else
error = gfs2_statfs_i(sdp, &sc);
if (error)
return error;
memset(buf, 0, sizeof(struct kstatfs));
buf->f_type = GFS2_MAGIC;
buf->f_bsize = sdp->sd_sb.sb_bsize;
buf->f_blocks = sc.sc_total;
buf->f_bfree = sc.sc_free;
buf->f_bavail = sc.sc_free;
buf->f_files = sc.sc_dinodes + sc.sc_free;
buf->f_ffree = sc.sc_free;
buf->f_namelen = GFS2_FNAMESIZE;
return 0;
}
/**
* gfs2_remount_fs - called when the FS is remounted
* @sb: the filesystem
* @flags: the remount flags
* @data: extra data passed in (not used right now)
*
* Returns: errno
*/
static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
{
struct gfs2_sbd *sdp = get_v2sdp(sb);
int error;
atomic_inc(&sdp->sd_ops_super);
error = gfs2_mount_args(sdp, data, 1);
if (error)
return error;
if (sdp->sd_args.ar_spectator)
*flags |= MS_RDONLY;
else {
if (*flags & MS_RDONLY) {
if (!(sb->s_flags & MS_RDONLY))
error = gfs2_make_fs_ro(sdp);
} else if (!(*flags & MS_RDONLY) &&
(sb->s_flags & MS_RDONLY)) {
error = gfs2_make_fs_rw(sdp);
}
}
if (*flags & (MS_NOATIME | MS_NODIRATIME))
set_bit(SDF_NOATIME, &sdp->sd_flags);
else
clear_bit(SDF_NOATIME, &sdp->sd_flags);
/* Don't let the VFS update atimes. GFS2 handles this itself. */
*flags |= MS_NOATIME | MS_NODIRATIME;
return error;
}
/**
* gfs2_clear_inode - Deallocate an inode when VFS is done with it
* @inode: The VFS inode
*
*/
static void gfs2_clear_inode(struct inode *inode)
{
struct gfs2_inode *ip = get_v2ip(inode);
atomic_inc(&get_v2sdp(inode->i_sb)->sd_ops_super);
if (ip) {
spin_lock(&ip->i_spin);
ip->i_vnode = NULL;
set_v2ip(inode, NULL);
spin_unlock(&ip->i_spin);
gfs2_glock_schedule_for_reclaim(ip->i_gl);
gfs2_inode_put(ip);
}
}
/**
* gfs2_show_options - Show mount options for /proc/mounts
* @s: seq_file structure
* @mnt: vfsmount
*
* Returns: 0 on success or error code
*/
static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
{
struct gfs2_sbd *sdp = get_v2sdp(mnt->mnt_sb);
struct gfs2_args *args = &sdp->sd_args;
atomic_inc(&sdp->sd_ops_super);
if (args->ar_lockproto[0])
seq_printf(s, ",lockproto=%s", args->ar_lockproto);
if (args->ar_locktable[0])
seq_printf(s, ",locktable=%s", args->ar_locktable);
if (args->ar_hostdata[0])
seq_printf(s, ",hostdata=%s", args->ar_hostdata);
if (args->ar_spectator)
seq_printf(s, ",spectator");
if (args->ar_ignore_local_fs)
seq_printf(s, ",ignore_local_fs");
if (args->ar_localflocks)
seq_printf(s, ",localflocks");
if (args->ar_localcaching)
seq_printf(s, ",localcaching");
if (args->ar_debug)
seq_printf(s, ",debug");
if (args->ar_upgrade)
seq_printf(s, ",upgrade");
if (args->ar_num_glockd != GFS2_GLOCKD_DEFAULT)
seq_printf(s, ",num_glockd=%u", args->ar_num_glockd);
if (args->ar_posix_acl)
seq_printf(s, ",acl");
if (args->ar_quota != GFS2_QUOTA_DEFAULT) {
char *state;
switch (args->ar_quota) {
case GFS2_QUOTA_OFF:
state = "off";
break;
case GFS2_QUOTA_ACCOUNT:
state = "account";
break;
case GFS2_QUOTA_ON:
state = "on";
break;
default:
state = "unknown";
break;
}
seq_printf(s, ",quota=%s", state);
}
if (args->ar_suiddir)
seq_printf(s, ",suiddir");
if (args->ar_data != GFS2_DATA_DEFAULT) {
char *state;
switch (args->ar_data) {
case GFS2_DATA_WRITEBACK:
state = "writeback";
break;
case GFS2_DATA_ORDERED:
state = "ordered";
break;
default:
state = "unknown";
break;
}
seq_printf(s, ",data=%s", state);
}
return 0;
}
struct super_operations gfs2_super_ops = {
.write_inode = gfs2_write_inode,
.put_super = gfs2_put_super,
.write_super = gfs2_write_super,
.write_super_lockfs = gfs2_write_super_lockfs,
.unlockfs = gfs2_unlockfs,
.statfs = gfs2_statfs,
.remount_fs = gfs2_remount_fs,
.clear_inode = gfs2_clear_inode,
.show_options = gfs2_show_options,
};

15
fs/gfs2/ops_super.h Normal file
View File

@ -0,0 +1,15 @@
/*
* 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 __OPS_SUPER_DOT_H__
#define __OPS_SUPER_DOT_H__
extern struct super_operations gfs2_super_ops;
#endif /* __OPS_SUPER_DOT_H__ */

199
fs/gfs2/ops_vm.c Normal file
View File

@ -0,0 +1,199 @@
/*
* 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 <linux/mm.h>
#include <linux/pagemap.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "bmap.h"
#include "glock.h"
#include "inode.h"
#include "ops_vm.h"
#include "page.h"
#include "quota.h"
#include "rgrp.h"
#include "trans.h"
static void pfault_be_greedy(struct gfs2_inode *ip)
{
unsigned int time;
spin_lock(&ip->i_spin);
time = ip->i_greedy;
ip->i_last_pfault = jiffies;
spin_unlock(&ip->i_spin);
gfs2_inode_hold(ip);
if (gfs2_glock_be_greedy(ip->i_gl, time))
gfs2_inode_put(ip);
}
static struct page *gfs2_private_nopage(struct vm_area_struct *area,
unsigned long address, int *type)
{
struct gfs2_inode *ip = get_v2ip(area->vm_file->f_mapping->host);
struct gfs2_holder i_gh;
struct page *result;
int error;
atomic_inc(&ip->i_sbd->sd_ops_vm);
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
if (error)
return NULL;
set_bit(GIF_PAGED, &ip->i_flags);
result = filemap_nopage(area, address, type);
if (result && result != NOPAGE_OOM)
pfault_be_greedy(ip);
gfs2_glock_dq_uninit(&i_gh);
return result;
}
static int alloc_page_backing(struct gfs2_inode *ip, struct page *page)
{
struct gfs2_sbd *sdp = ip->i_sbd;
unsigned long index = page->index;
uint64_t lblock = index << (PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift);
unsigned int blocks = PAGE_CACHE_SIZE >> sdp->sd_sb.sb_bsize_shift;
struct gfs2_alloc *al;
unsigned int data_blocks, ind_blocks;
unsigned int x;
int error;
al = gfs2_alloc_get(ip);
error = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
if (error)
goto out;
error = gfs2_quota_check(ip, ip->i_di.di_uid, ip->i_di.di_gid);
if (error)
goto out_gunlock_q;
gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE,
&data_blocks, &ind_blocks);
al->al_requested = data_blocks + ind_blocks;
error = gfs2_inplace_reserve(ip);
if (error)
goto out_gunlock_q;
error = gfs2_trans_begin(sdp,
al->al_rgd->rd_ri.ri_length +
ind_blocks + RES_DINODE +
RES_STATFS + RES_QUOTA, 0);
if (error)
goto out_ipres;
if (gfs2_is_stuffed(ip)) {
error = gfs2_unstuff_dinode(ip, gfs2_unstuffer_page, NULL);
if (error)
goto out_trans;
}
for (x = 0; x < blocks; ) {
uint64_t dblock;
unsigned int extlen;
int new = 1;
error = gfs2_block_map(ip, lblock, &new, &dblock, &extlen);
if (error)
goto out_trans;
lblock += extlen;
x += extlen;
}
gfs2_assert_warn(sdp, al->al_alloced);
out_trans:
gfs2_trans_end(sdp);
out_ipres:
gfs2_inplace_release(ip);
out_gunlock_q:
gfs2_quota_unlock(ip);
out:
gfs2_alloc_put(ip);
return error;
}
static struct page *gfs2_sharewrite_nopage(struct vm_area_struct *area,
unsigned long address, int *type)
{
struct gfs2_inode *ip = get_v2ip(area->vm_file->f_mapping->host);
struct gfs2_holder i_gh;
struct page *result = NULL;
unsigned long index = ((address - area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
int alloc_required;
int error;
atomic_inc(&ip->i_sbd->sd_ops_vm);
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
if (error)
return NULL;
if (gfs2_is_jdata(ip))
goto out;
set_bit(GIF_PAGED, &ip->i_flags);
set_bit(GIF_SW_PAGED, &ip->i_flags);
error = gfs2_write_alloc_required(ip,
(uint64_t)index << PAGE_CACHE_SHIFT,
PAGE_CACHE_SIZE, &alloc_required);
if (error)
goto out;
result = filemap_nopage(area, address, type);
if (!result || result == NOPAGE_OOM)
goto out;
if (alloc_required) {
error = alloc_page_backing(ip, result);
if (error) {
page_cache_release(result);
result = NULL;
goto out;
}
set_page_dirty(result);
}
pfault_be_greedy(ip);
out:
gfs2_glock_dq_uninit(&i_gh);
return result;
}
struct vm_operations_struct gfs2_vm_ops_private = {
.nopage = gfs2_private_nopage,
};
struct vm_operations_struct gfs2_vm_ops_sharewrite = {
.nopage = gfs2_sharewrite_nopage,
};

16
fs/gfs2/ops_vm.h Normal file
View File

@ -0,0 +1,16 @@
/*
* 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 __OPS_VM_DOT_H__
#define __OPS_VM_DOT_H__
extern struct vm_operations_struct gfs2_vm_ops_private;
extern struct vm_operations_struct gfs2_vm_ops_sharewrite;
#endif /* __OPS_VM_DOT_H__ */

273
fs/gfs2/page.c Normal file
View File

@ -0,0 +1,273 @@
/*
* 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 <linux/pagemap.h>
#include <linux/mm.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "bmap.h"
#include "inode.h"
#include "page.h"
#include "trans.h"
/**
* gfs2_pte_inval - Sync and invalidate all PTEs associated with a glock
* @gl: the glock
*
*/
void gfs2_pte_inval(struct gfs2_glock *gl)
{
struct gfs2_inode *ip;
struct inode *inode;
ip = get_gl2ip(gl);
if (!ip || !S_ISREG(ip->i_di.di_mode))
return;
if (!test_bit(GIF_PAGED, &ip->i_flags))
return;
inode = gfs2_ip2v_lookup(ip);
if (inode) {
unmap_shared_mapping_range(inode->i_mapping, 0, 0);
iput(inode);
if (test_bit(GIF_SW_PAGED, &ip->i_flags))
set_bit(GLF_DIRTY, &gl->gl_flags);
}
clear_bit(GIF_SW_PAGED, &ip->i_flags);
}
/**
* gfs2_page_inval - Invalidate all pages associated with a glock
* @gl: the glock
*
*/
void gfs2_page_inval(struct gfs2_glock *gl)
{
struct gfs2_inode *ip;
struct inode *inode;
ip = get_gl2ip(gl);
if (!ip || !S_ISREG(ip->i_di.di_mode))
return;
inode = gfs2_ip2v_lookup(ip);
if (inode) {
struct address_space *mapping = inode->i_mapping;
truncate_inode_pages(mapping, 0);
gfs2_assert_withdraw(ip->i_sbd, !mapping->nrpages);
iput(inode);
}
clear_bit(GIF_PAGED, &ip->i_flags);
}
/**
* gfs2_page_sync - Sync the data pages (not metadata) associated with a glock
* @gl: the glock
* @flags: DIO_START | DIO_WAIT
*
* Syncs data (not metadata) for a regular file.
* No-op for all other types.
*/
void gfs2_page_sync(struct gfs2_glock *gl, int flags)
{
struct gfs2_inode *ip;
struct inode *inode;
ip = get_gl2ip(gl);
if (!ip || !S_ISREG(ip->i_di.di_mode))
return;
inode = gfs2_ip2v_lookup(ip);
if (inode) {
struct address_space *mapping = inode->i_mapping;
int error = 0;
if (flags & DIO_START)
filemap_fdatawrite(mapping);
if (!error && (flags & DIO_WAIT))
error = filemap_fdatawait(mapping);
/* Put back any errors cleared by filemap_fdatawait()
so they can be caught by someone who can pass them
up to user space. */
if (error == -ENOSPC)
set_bit(AS_ENOSPC, &mapping->flags);
else if (error)
set_bit(AS_EIO, &mapping->flags);
iput(inode);
}
}
/**
* 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
* @private: any locked page held by the caller process
*
* Returns: errno
*/
int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
uint64_t block, void *private)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct inode *inode = ip->i_vnode;
struct page *page = (struct page *)private;
struct buffer_head *bh;
int release = 0;
if (!page || page->index) {
page = grab_cache_page(inode->i_mapping, 0);
if (!page)
return -ENOMEM;
release = 1;
}
if (!PageUptodate(page)) {
void *kaddr = kmap(page);
memcpy(kaddr,
dibh->b_data + sizeof(struct gfs2_dinode),
ip->i_di.di_size);
memset(kaddr + ip->i_di.di_size,
0,
PAGE_CACHE_SIZE - ip->i_di.di_size);
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 (sdp->sd_args.ar_data == GFS2_DATA_ORDERED)
gfs2_trans_add_databuf(sdp, bh);
mark_buffer_dirty(bh);
if (release) {
unlock_page(page);
page_cache_release(page);
}
return 0;
}
/**
* gfs2_truncator_page - truncate a partial data block in the page cache
* @ip: the inode
* @size: the size the file should be
*
* Returns: errno
*/
int gfs2_truncator_page(struct gfs2_inode *ip, uint64_t size)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct inode *inode = ip->i_vnode;
struct page *page;
struct buffer_head *bh;
void *kaddr;
uint64_t lbn, dbn;
unsigned long index;
unsigned int offset;
unsigned int bufnum;
int new = 0;
int error;
lbn = size >> inode->i_blkbits;
error = gfs2_block_map(ip, lbn, &new, &dbn, NULL);
if (error || !dbn)
return error;
index = size >> PAGE_CACHE_SHIFT;
offset = size & (PAGE_CACHE_SIZE - 1);
bufnum = lbn - (index << (PAGE_CACHE_SHIFT - inode->i_blkbits));
page = read_cache_page(inode->i_mapping, index,
(filler_t *)inode->i_mapping->a_ops->readpage,
NULL);
if (IS_ERR(page))
return PTR_ERR(page);
lock_page(page);
if (!PageUptodate(page) || PageError(page)) {
error = -EIO;
goto out;
}
kaddr = kmap(page);
memset(kaddr + offset, 0, PAGE_CACHE_SIZE - offset);
kunmap(page);
if (!page_has_buffers(page))
create_empty_buffers(page, 1 << inode->i_blkbits,
(1 << BH_Uptodate));
for (bh = page_buffers(page); bufnum--; bh = bh->b_this_page)
/* Do nothing */;
if (!buffer_mapped(bh))
map_bh(bh, inode->i_sb, dbn);
set_buffer_uptodate(bh);
if (sdp->sd_args.ar_data == GFS2_DATA_ORDERED)
gfs2_trans_add_databuf(sdp, bh);
mark_buffer_dirty(bh);
out:
unlock_page(page);
page_cache_release(page);
return error;
}
void gfs2_page_add_databufs(struct gfs2_sbd *sdp, struct page *page,
unsigned int from, unsigned int to)
{
struct buffer_head *head = page_buffers(page);
unsigned int bsize = head->b_size;
struct buffer_head *bh;
unsigned int start, end;
for (bh = head, start = 0;
bh != head || !start;
bh = bh->b_this_page, start = end) {
end = start + bsize;
if (end <= from || start >= to)
continue;
gfs2_trans_add_databuf(sdp, bh);
}
}

23
fs/gfs2/page.h Normal file
View File

@ -0,0 +1,23 @@
/*
* 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 __PAGE_DOT_H__
#define __PAGE_DOT_H__
void gfs2_pte_inval(struct gfs2_glock *gl);
void gfs2_page_inval(struct gfs2_glock *gl);
void gfs2_page_sync(struct gfs2_glock *gl, int flags);
int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
uint64_t block, void *private);
int gfs2_truncator_page(struct gfs2_inode *ip, uint64_t size);
void gfs2_page_add_databufs(struct gfs2_sbd *sdp, struct page *page,
unsigned int from, unsigned int to);
#endif /* __PAGE_DOT_H__ */

1238
fs/gfs2/quota.c Normal file

File diff suppressed because it is too large Load Diff

34
fs/gfs2/quota.h Normal file
View File

@ -0,0 +1,34 @@
/*
* 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 __QUOTA_DOT_H__
#define __QUOTA_DOT_H__
#define NO_QUOTA_CHANGE ((uint32_t)-1)
int gfs2_quota_hold(struct gfs2_inode *ip, uint32_t uid, uint32_t gid);
void gfs2_quota_unhold(struct gfs2_inode *ip);
int gfs2_quota_lock(struct gfs2_inode *ip, uint32_t uid, uint32_t gid);
void gfs2_quota_unlock(struct gfs2_inode *ip);
int gfs2_quota_check(struct gfs2_inode *ip, uint32_t uid, uint32_t gid);
void gfs2_quota_change(struct gfs2_inode *ip, int64_t change,
uint32_t uid, uint32_t gid);
int gfs2_quota_sync(struct gfs2_sbd *sdp);
int gfs2_quota_refresh(struct gfs2_sbd *sdp, int user, uint32_t id);
int gfs2_quota_read(struct gfs2_sbd *sdp, int user, uint32_t id,
struct gfs2_quota *q);
int gfs2_quota_init(struct gfs2_sbd *sdp);
void gfs2_quota_scan(struct gfs2_sbd *sdp);
void gfs2_quota_cleanup(struct gfs2_sbd *sdp);
#endif /* __QUOTA_DOT_H__ */

570
fs/gfs2/recovery.c Normal file
View File

@ -0,0 +1,570 @@
/*
* 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 "gfs2.h"
#include "bmap.h"
#include "glock.h"
#include "glops.h"
#include "lm.h"
#include "lops.h"
#include "meta_io.h"
#include "recovery.h"
#include "super.h"
int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
struct buffer_head **bh)
{
struct gfs2_glock *gl = jd->jd_inode->i_gl;
int new = 0;
uint64_t dblock;
uint32_t extlen;
int error;
error = gfs2_block_map(jd->jd_inode, blk, &new, &dblock, &extlen);
if (error)
return error;
if (!dblock) {
gfs2_consist_inode(jd->jd_inode);
return -EIO;
}
gfs2_meta_ra(gl, dblock, extlen);
error = gfs2_meta_read(gl, dblock, DIO_START | DIO_WAIT, bh);
return error;
}
int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where)
{
struct list_head *head = &sdp->sd_revoke_list;
struct gfs2_revoke_replay *rr;
int found = 0;
list_for_each_entry(rr, head, rr_list) {
if (rr->rr_blkno == blkno) {
found = 1;
break;
}
}
if (found) {
rr->rr_where = where;
return 0;
}
rr = kmalloc(sizeof(struct gfs2_revoke_replay), GFP_KERNEL);
if (!rr)
return -ENOMEM;
rr->rr_blkno = blkno;
rr->rr_where = where;
list_add(&rr->rr_list, head);
return 1;
}
int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where)
{
struct gfs2_revoke_replay *rr;
int wrap, a, b, revoke;
int found = 0;
list_for_each_entry(rr, &sdp->sd_revoke_list, rr_list) {
if (rr->rr_blkno == blkno) {
found = 1;
break;
}
}
if (!found)
return 0;
wrap = (rr->rr_where < sdp->sd_replay_tail);
a = (sdp->sd_replay_tail < where);
b = (where < rr->rr_where);
revoke = (wrap) ? (a || b) : (a && b);
return revoke;
}
void gfs2_revoke_clean(struct gfs2_sbd *sdp)
{
struct list_head *head = &sdp->sd_revoke_list;
struct gfs2_revoke_replay *rr;
while (!list_empty(head)) {
rr = list_entry(head->next, struct gfs2_revoke_replay, rr_list);
list_del(&rr->rr_list);
kfree(rr);
}
}
/**
* get_log_header - read the log header for a given segment
* @jd: the journal
* @blk: the block to look at
* @lh: the log header to return
*
* Read the log header for a given segement in a given journal. Do a few
* sanity checks on it.
*
* Returns: 0 on success,
* 1 if the header was invalid or incomplete,
* errno on error
*/
static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
struct gfs2_log_header *head)
{
struct buffer_head *bh;
struct gfs2_log_header lh;
uint32_t hash;
int error;
error = gfs2_replay_read_block(jd, blk, &bh);
if (error)
return error;
memcpy(&lh, bh->b_data, sizeof(struct gfs2_log_header));
lh.lh_hash = 0;
hash = gfs2_disk_hash((char *)&lh, sizeof(struct gfs2_log_header));
gfs2_log_header_in(&lh, bh->b_data);
brelse(bh);
if (lh.lh_header.mh_magic != GFS2_MAGIC ||
lh.lh_header.mh_type != GFS2_METATYPE_LH ||
lh.lh_blkno != blk ||
lh.lh_hash != hash)
return 1;
*head = lh;
return 0;
}
/**
* find_good_lh - find a good log header
* @jd: the journal
* @blk: the segment to start searching from
* @lh: the log header to fill in
* @forward: if true search forward in the log, else search backward
*
* Call get_log_header() to get a log header for a segment, but if the
* segment is bad, either scan forward or backward until we find a good one.
*
* Returns: errno
*/
static int find_good_lh(struct gfs2_jdesc *jd, unsigned int *blk,
struct gfs2_log_header *head)
{
unsigned int orig_blk = *blk;
int error;
for (;;) {
error = get_log_header(jd, *blk, head);
if (error <= 0)
return error;
if (++*blk == jd->jd_blocks)
*blk = 0;
if (*blk == orig_blk) {
gfs2_consist_inode(jd->jd_inode);
return -EIO;
}
}
}
/**
* jhead_scan - make sure we've found the head of the log
* @jd: the journal
* @head: this is filled in with the log descriptor of the head
*
* At this point, seg and lh should be either the head of the log or just
* before. Scan forward until we find the head.
*
* Returns: errno
*/
static int jhead_scan(struct gfs2_jdesc *jd, struct gfs2_log_header *head)
{
unsigned int blk = head->lh_blkno;
struct gfs2_log_header lh;
int error;
for (;;) {
if (++blk == jd->jd_blocks)
blk = 0;
error = get_log_header(jd, blk, &lh);
if (error < 0)
return error;
if (error == 1)
continue;
if (lh.lh_sequence == head->lh_sequence) {
gfs2_consist_inode(jd->jd_inode);
return -EIO;
}
if (lh.lh_sequence < head->lh_sequence)
break;
*head = lh;
}
return 0;
}
/**
* gfs2_find_jhead - find the head of a log
* @jd: the journal
* @head: the log descriptor for the head of the log is returned here
*
* Do a binary search of a journal and find the valid log entry with the
* highest sequence number. (i.e. the log head)
*
* Returns: errno
*/
int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header *head)
{
struct gfs2_log_header lh_1, lh_m;
uint32_t blk_1, blk_2, blk_m;
int error;
blk_1 = 0;
blk_2 = jd->jd_blocks - 1;
for (;;) {
blk_m = (blk_1 + blk_2) / 2;
error = find_good_lh(jd, &blk_1, &lh_1);
if (error)
return error;
error = find_good_lh(jd, &blk_m, &lh_m);
if (error)
return error;
if (blk_1 == blk_m || blk_m == blk_2)
break;
if (lh_1.lh_sequence <= lh_m.lh_sequence)
blk_1 = blk_m;
else
blk_2 = blk_m;
}
error = jhead_scan(jd, &lh_1);
if (error)
return error;
*head = lh_1;
return error;
}
/**
* foreach_descriptor - go through the active part of the log
* @jd: the journal
* @start: the first log header in the active region
* @end: the last log header (don't process the contents of this entry))
*
* Call a given function once for every log descriptor in the active
* portion of the log.
*
* Returns: errno
*/
static int foreach_descriptor(struct gfs2_jdesc *jd, unsigned int start,
unsigned int end, int pass)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
struct buffer_head *bh;
struct gfs2_log_descriptor *ld;
int error = 0;
u32 length;
__be64 *ptr;
unsigned int offset = sizeof(struct gfs2_log_descriptor);
offset += (sizeof(__be64)-1);
offset &= ~(sizeof(__be64)-1);
while (start != end) {
error = gfs2_replay_read_block(jd, start, &bh);
if (error)
return error;
if (gfs2_meta_check(sdp, bh)) {
brelse(bh);
return -EIO;
}
ld = (struct gfs2_log_descriptor *)bh->b_data;
length = be32_to_cpu(ld->ld_length);
if (be16_to_cpu(ld->ld_header.mh_type) == GFS2_METATYPE_LH) {
struct gfs2_log_header lh;
error = get_log_header(jd, start, &lh);
if (!error) {
gfs2_replay_incr_blk(sdp, &start);
continue;
}
if (error == 1) {
gfs2_consist_inode(jd->jd_inode);
error = -EIO;
}
brelse(bh);
return error;
} else if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_LD)) {
brelse(bh);
return -EIO;
}
ptr = (__be64 *)(bh->b_data + offset);
error = lops_scan_elements(jd, start, ld, ptr, pass);
if (error) {
brelse(bh);
return error;
}
while (length--)
gfs2_replay_incr_blk(sdp, &start);
brelse(bh);
}
return 0;
}
/**
* clean_journal - mark a dirty journal as being clean
* @sdp: the filesystem
* @jd: the journal
* @gl: the journal's glock
* @head: the head journal to start from
*
* Returns: errno
*/
static int clean_journal(struct gfs2_jdesc *jd, struct gfs2_log_header *head)
{
struct gfs2_inode *ip = jd->jd_inode;
struct gfs2_sbd *sdp = ip->i_sbd;
unsigned int lblock;
int new = 0;
uint64_t dblock;
struct gfs2_log_header *lh;
uint32_t hash;
struct buffer_head *bh;
int error;
lblock = head->lh_blkno;
gfs2_replay_incr_blk(sdp, &lblock);
error = gfs2_block_map(ip, lblock, &new, &dblock, NULL);
if (error)
return error;
if (!dblock) {
gfs2_consist_inode(ip);
return -EIO;
}
bh = sb_getblk(sdp->sd_vfs, dblock);
lock_buffer(bh);
memset(bh->b_data, 0, bh->b_size);
set_buffer_uptodate(bh);
clear_buffer_dirty(bh);
unlock_buffer(bh);
lh = (struct gfs2_log_header *)bh->b_data;
memset(lh, 0, sizeof(struct gfs2_log_header));
lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
lh->lh_header.mh_type = cpu_to_be16(GFS2_METATYPE_LH);
lh->lh_header.mh_format = cpu_to_be16(GFS2_FORMAT_LH);
lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1);
lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT);
lh->lh_blkno = cpu_to_be32(lblock);
hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header));
lh->lh_hash = cpu_to_be32(hash);
set_buffer_dirty(bh);
if (sync_dirty_buffer(bh))
gfs2_io_error_bh(sdp, bh);
brelse(bh);
return error;
}
/**
* gfs2_recover_journal - recovery a given journal
* @jd: the struct gfs2_jdesc describing the journal
* @wait: Don't return until the journal is clean (or an error is encountered)
*
* Acquire the journal's lock, check to see if the journal is clean, and
* do recovery if necessary.
*
* Returns: errno
*/
int gfs2_recover_journal(struct gfs2_jdesc *jd, int wait)
{
struct gfs2_sbd *sdp = jd->jd_inode->i_sbd;
struct gfs2_log_header head;
struct gfs2_holder j_gh, ji_gh, t_gh;
unsigned long t;
int ro = 0;
unsigned int pass;
int error;
fs_info(sdp, "jid=%u: Trying to acquire journal lock...\n", jd->jd_jid);
/* Aquire the journal lock so we can do recovery */
error = gfs2_glock_nq_num(sdp,
jd->jd_jid, &gfs2_journal_glops,
LM_ST_EXCLUSIVE,
LM_FLAG_NOEXP |
((wait) ? 0 : LM_FLAG_TRY) |
GL_NOCACHE, &j_gh);
switch (error) {
case 0:
break;
case GLR_TRYFAILED:
fs_info(sdp, "jid=%u: Busy\n", jd->jd_jid);
error = 0;
default:
goto fail;
};
error = gfs2_glock_nq_init(jd->jd_inode->i_gl, LM_ST_SHARED,
LM_FLAG_NOEXP, &ji_gh);
if (error)
goto fail_gunlock_j;
fs_info(sdp, "jid=%u: Looking at journal...\n", jd->jd_jid);
error = gfs2_jdesc_check(jd);
if (error)
goto fail_gunlock_ji;
error = gfs2_find_jhead(jd, &head);
if (error)
goto fail_gunlock_ji;
if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) {
fs_info(sdp, "jid=%u: Acquiring the transaction lock...\n",
jd->jd_jid);
t = jiffies;
/* Acquire a shared hold on the transaction lock */
error = gfs2_glock_nq_init(sdp->sd_trans_gl,
LM_ST_SHARED,
LM_FLAG_NOEXP |
LM_FLAG_PRIORITY |
GL_NEVER_RECURSE |
GL_NOCANCEL |
GL_NOCACHE,
&t_gh);
if (error)
goto fail_gunlock_ji;
if (test_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags)) {
if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags))
ro = 1;
} else {
if (sdp->sd_vfs->s_flags & MS_RDONLY)
ro = 1;
}
if (ro) {
fs_warn(sdp, "jid=%u: Can't replay: read-only FS\n",
jd->jd_jid);
error = -EROFS;
goto fail_gunlock_tr;
}
fs_info(sdp, "jid=%u: Replaying journal...\n", jd->jd_jid);
for (pass = 0; pass < 2; pass++) {
lops_before_scan(jd, &head, pass);
error = foreach_descriptor(jd, head.lh_tail,
head.lh_blkno, pass);
lops_after_scan(jd, error, pass);
if (error)
goto fail_gunlock_tr;
}
error = clean_journal(jd, &head);
if (error)
goto fail_gunlock_tr;
gfs2_glock_dq_uninit(&t_gh);
t = DIV_RU(jiffies - t, HZ);
fs_info(sdp, "jid=%u: Journal replayed in %lus\n",
jd->jd_jid, t);
}
gfs2_glock_dq_uninit(&ji_gh);
gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_SUCCESS);
gfs2_glock_dq_uninit(&j_gh);
fs_info(sdp, "jid=%u: Done\n", jd->jd_jid);
return 0;
fail_gunlock_tr:
gfs2_glock_dq_uninit(&t_gh);
fail_gunlock_ji:
gfs2_glock_dq_uninit(&ji_gh);
fail_gunlock_j:
gfs2_glock_dq_uninit(&j_gh);
fs_info(sdp, "jid=%u: %s\n", jd->jd_jid, (error) ? "Failed" : "Done");
fail:
gfs2_lm_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP);
return error;
}
/**
* gfs2_check_journals - Recover any dirty journals
* @sdp: the filesystem
*
*/
void gfs2_check_journals(struct gfs2_sbd *sdp)
{
struct gfs2_jdesc *jd;
for (;;) {
jd = gfs2_jdesc_find_dirty(sdp);
if (!jd)
break;
if (jd != sdp->sd_jdesc)
gfs2_recover_journal(jd, NO_WAIT);
}
}

32
fs/gfs2/recovery.h Normal file
View File

@ -0,0 +1,32 @@
/*
* 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 __RECOVERY_DOT_H__
#define __RECOVERY_DOT_H__
static inline void gfs2_replay_incr_blk(struct gfs2_sbd *sdp, unsigned int *blk)
{
if (++*blk == sdp->sd_jdesc->jd_blocks)
*blk = 0;
}
int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk,
struct buffer_head **bh);
int gfs2_revoke_add(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where);
int gfs2_revoke_check(struct gfs2_sbd *sdp, uint64_t blkno, unsigned int where);
void gfs2_revoke_clean(struct gfs2_sbd *sdp);
int gfs2_find_jhead(struct gfs2_jdesc *jd,
struct gfs2_log_header *head);
int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd, int wait);
void gfs2_check_journals(struct gfs2_sbd *sdp);
#endif /* __RECOVERY_DOT_H__ */

291
fs/gfs2/resize.c Normal file
View File

@ -0,0 +1,291 @@
/*
* 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 "gfs2.h"
#include "bmap.h"
#include "dir.h"
#include "glock.h"
#include "inode.h"
#include "jdata.h"
#include "meta_io.h"
#include "quota.h"
#include "resize.h"
#include "rgrp.h"
#include "super.h"
#include "trans.h"
/* A single transaction needs to add the structs to rindex and make the
statfs change. */
int gfs2_resize_add_rgrps(struct gfs2_sbd *sdp, char __user *buf,
unsigned int size)
{
unsigned int num = size / sizeof(struct gfs2_rindex);
struct gfs2_inode *ip = sdp->sd_rindex;
struct gfs2_alloc *al = NULL;
struct gfs2_holder i_gh;
unsigned int data_blocks, ind_blocks;
int alloc_required;
unsigned int x;
int error;
gfs2_write_calc_reserv(ip, size, &data_blocks, &ind_blocks);
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE,
LM_FLAG_PRIORITY | GL_SYNC, &i_gh);
if (error)
return error;
if (!gfs2_is_jdata(ip)) {
gfs2_consist_inode(ip);
error = -EIO;
goto out;
}
error = gfs2_write_alloc_required(ip, ip->i_di.di_size, size,
&alloc_required);
if (error)
goto out;
if (alloc_required) {
al = gfs2_alloc_get(ip);
al->al_requested = data_blocks + ind_blocks;
error = gfs2_inplace_reserve(ip);
if (error)
goto out_alloc;
error = gfs2_trans_begin(sdp,
al->al_rgd->rd_ri.ri_length +
data_blocks + ind_blocks +
RES_DINODE + RES_STATFS, 0);
if (error)
goto out_relse;
} else {
error = gfs2_trans_begin(sdp, data_blocks +
RES_DINODE + RES_STATFS, 0);
if (error)
goto out;
}
for (x = 0; x < num; x++) {
struct gfs2_rindex ri;
char ri_buf[sizeof(struct gfs2_rindex)];
error = copy_from_user(&ri, buf, sizeof(struct gfs2_rindex));
if (error) {
error = -EFAULT;
goto out_trans;
}
gfs2_rindex_out(&ri, ri_buf);
error = gfs2_jdata_write_mem(ip, ri_buf, ip->i_di.di_size,
sizeof(struct gfs2_rindex));
if (error < 0)
goto out_trans;
gfs2_assert_withdraw(sdp, error == sizeof(struct gfs2_rindex));
error = 0;
gfs2_statfs_change(sdp, ri.ri_data, ri.ri_data, 0);
buf += sizeof(struct gfs2_rindex);
}
out_trans:
gfs2_trans_end(sdp);
out_relse:
if (alloc_required)
gfs2_inplace_release(ip);
out_alloc:
if (alloc_required)
gfs2_alloc_put(ip);
out:
ip->i_gl->gl_vn++;
gfs2_glock_dq_uninit(&i_gh);
return error;
}
static void drop_dentries(struct gfs2_inode *ip)
{
struct inode *inode;
struct dentry *d;
inode = gfs2_ip2v_lookup(ip);
if (!inode)
return;
restart:
spin_lock(&dcache_lock);
list_for_each_entry(d, &inode->i_dentry, d_alias) {
if (d_unhashed(d))
continue;
dget_locked(d);
__d_drop(d);
spin_unlock(&dcache_lock);
dput(d);
goto restart;
}
spin_unlock(&dcache_lock);
iput(inode);
}
/* This is called by an ioctl to rename an ordinary file that's represented
in the vfs to a hidden system file that isn't represented in the vfs. It's
used to add journals, along with the associated system files, to a fs. */
int gfs2_rename2system(struct gfs2_inode *ip,
struct gfs2_inode *old_dip, char *old_name,
struct gfs2_inode *new_dip, char *new_name)
{
struct gfs2_sbd *sdp = ip->i_sbd;
struct gfs2_holder ghs[3];
struct qstr old_qstr, new_qstr;
struct gfs2_inum inum;
int alloc_required;
struct buffer_head *dibh;
int error;
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE, ghs);
gfs2_holder_init(old_dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
gfs2_holder_init(new_dip->i_gl, LM_ST_EXCLUSIVE, GL_SYNC, ghs + 2);
error = gfs2_glock_nq_m(3, ghs);
if (error)
goto out;
error = -EMLINK;
if (ip->i_di.di_nlink != 1)
goto out_gunlock;
error = -EINVAL;
if (!S_ISREG(ip->i_di.di_mode))
goto out_gunlock;
old_qstr.name = old_name;
old_qstr.len = strlen(old_name);
error = gfs2_dir_search(old_dip, &old_qstr, &inum, NULL);
switch (error) {
case 0:
break;
default:
goto out_gunlock;
}
error = -EINVAL;
if (!gfs2_inum_equal(&inum, &ip->i_num))
goto out_gunlock;
new_qstr.name = new_name;
new_qstr.len = strlen(new_name);
error = gfs2_dir_search(new_dip, &new_qstr, NULL, NULL);
switch (error) {
case -ENOENT:
break;
case 0:
error = -EEXIST;
default:
goto out_gunlock;
}
gfs2_alloc_get(ip);
error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
if (error)
goto out_alloc;
error = gfs2_diradd_alloc_required(new_dip, &new_qstr, &alloc_required);
if (error)
goto out_unhold;
if (alloc_required) {
struct gfs2_alloc *al = gfs2_alloc_get(new_dip);
al->al_requested = sdp->sd_max_dirres;
error = gfs2_inplace_reserve(new_dip);
if (error)
goto out_alloc2;
error = gfs2_trans_begin(sdp,
sdp->sd_max_dirres +
al->al_rgd->rd_ri.ri_length +
3 * RES_DINODE + RES_LEAF +
RES_STATFS + RES_QUOTA, 0);
if (error)
goto out_ipreserv;
} else {
error = gfs2_trans_begin(sdp,
3 * RES_DINODE + 2 * RES_LEAF +
RES_QUOTA, 0);
if (error)
goto out_unhold;
}
error = gfs2_dir_del(old_dip, &old_qstr);
if (error)
goto out_trans;
error = gfs2_dir_add(new_dip, &new_qstr, &ip->i_num,
IF2DT(ip->i_di.di_mode));
if (error)
goto out_trans;
gfs2_quota_change(ip, -ip->i_di.di_blocks, ip->i_di.di_uid,
ip->i_di.di_gid);
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
goto out_trans;
ip->i_di.di_flags |= GFS2_DIF_SYSTEM;
gfs2_trans_add_bh(ip->i_gl, dibh);
gfs2_dinode_out(&ip->i_di, dibh->b_data);
brelse(dibh);
drop_dentries(ip);
out_trans:
gfs2_trans_end(sdp);
out_ipreserv:
if (alloc_required)
gfs2_inplace_release(new_dip);
out_alloc2:
if (alloc_required)
gfs2_alloc_put(new_dip);
out_unhold:
gfs2_quota_unhold(ip);
out_alloc:
gfs2_alloc_put(ip);
out_gunlock:
gfs2_glock_dq_m(3, ghs);
out:
gfs2_holder_uninit(ghs);
gfs2_holder_uninit(ghs + 1);
gfs2_holder_uninit(ghs + 2);
return error;
}

19
fs/gfs2/resize.h Normal file
View File

@ -0,0 +1,19 @@
/*
* 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 __RESIZE_DOT_H__
#define __RESIZE_DOT_H__
int gfs2_resize_add_rgrps(struct gfs2_sbd *sdp, char __user *buf,
unsigned int size);
int gfs2_rename2system(struct gfs2_inode *ip,
struct gfs2_inode *old_dip, char *old_name,
struct gfs2_inode *new_dip, char *new_name);
#endif /* __RESIZE_DOT_H__ */

1361
fs/gfs2/rgrp.c Normal file

File diff suppressed because it is too large Load Diff

62
fs/gfs2/rgrp.h Normal file
View File

@ -0,0 +1,62 @@
/*
* 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 __RGRP_DOT_H__
#define __RGRP_DOT_H__
void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd);
struct gfs2_rgrpd *gfs2_blk2rgrpd(struct gfs2_sbd *sdp, uint64_t blk);
struct gfs2_rgrpd *gfs2_rgrpd_get_first(struct gfs2_sbd *sdp);
struct gfs2_rgrpd *gfs2_rgrpd_get_next(struct gfs2_rgrpd *rgd);
void gfs2_clear_rgrpd(struct gfs2_sbd *sdp);
int gfs2_rindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ri_gh);
int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd);
void gfs2_rgrp_bh_hold(struct gfs2_rgrpd *rgd);
void gfs2_rgrp_bh_put(struct gfs2_rgrpd *rgd);
void gfs2_rgrp_repolish_clones(struct gfs2_rgrpd *rgd);
struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip);
void gfs2_alloc_put(struct gfs2_inode *ip);
int gfs2_inplace_reserve_i(struct gfs2_inode *ip,
char *file, unsigned int line);
#define gfs2_inplace_reserve(ip) \
gfs2_inplace_reserve_i((ip), __FILE__, __LINE__)
void gfs2_inplace_release(struct gfs2_inode *ip);
unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, uint64_t block);
uint64_t gfs2_alloc_data(struct gfs2_inode *ip);
uint64_t gfs2_alloc_meta(struct gfs2_inode *ip);
uint64_t gfs2_alloc_di(struct gfs2_inode *ip);
void gfs2_free_data(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen);
void gfs2_free_meta(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen);
void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, uint64_t blkno);
void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip);
struct gfs2_rgrp_list {
unsigned int rl_rgrps;
unsigned int rl_space;
struct gfs2_rgrpd **rl_rgd;
struct gfs2_holder *rl_ghs;
};
void gfs2_rlist_add(struct gfs2_sbd *sdp, struct gfs2_rgrp_list *rlist,
uint64_t block);
void gfs2_rlist_alloc(struct gfs2_rgrp_list *rlist, unsigned int state,
int flags);
void gfs2_rlist_free(struct gfs2_rgrp_list *rlist);
#endif /* __RGRP_DOT_H__ */

944
fs/gfs2/super.c Normal file
View File

@ -0,0 +1,944 @@
/*
* 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 "gfs2.h"
#include "bmap.h"
#include "dir.h"
#include "format.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
#include "log.h"
#include "meta_io.h"
#include "quota.h"
#include "recovery.h"
#include "rgrp.h"
#include "super.h"
#include "trans.h"
#include "unlinked.h"
/**
* gfs2_tune_init - Fill a gfs2_tune structure with default values
* @gt: tune
*
*/
void gfs2_tune_init(struct gfs2_tune *gt)
{
spin_lock_init(&gt->gt_spin);
gt->gt_ilimit = 100;
gt->gt_ilimit_tries = 3;
gt->gt_ilimit_min = 1;
gt->gt_demote_secs = 300;
gt->gt_incore_log_blocks = 1024;
gt->gt_log_flush_secs = 60;
gt->gt_jindex_refresh_secs = 60;
gt->gt_scand_secs = 15;
gt->gt_recoverd_secs = 60;
gt->gt_logd_secs = 1;
gt->gt_quotad_secs = 5;
gt->gt_inoded_secs = 15;
gt->gt_quota_simul_sync = 64;
gt->gt_quota_warn_period = 10;
gt->gt_quota_scale_num = 1;
gt->gt_quota_scale_den = 1;
gt->gt_quota_cache_secs = 300;
gt->gt_quota_quantum = 60;
gt->gt_atime_quantum = 3600;
gt->gt_new_files_jdata = 0;
gt->gt_new_files_directio = 0;
gt->gt_max_atomic_write = 4 << 20;
gt->gt_max_readahead = 1 << 18;
gt->gt_lockdump_size = 131072;
gt->gt_stall_secs = 600;
gt->gt_complain_secs = 10;
gt->gt_reclaim_limit = 5000;
gt->gt_entries_per_readdir = 32;
gt->gt_prefetch_secs = 10;
gt->gt_greedy_default = HZ / 10;
gt->gt_greedy_quantum = HZ / 40;
gt->gt_greedy_max = HZ / 4;
gt->gt_statfs_quantum = 30;
gt->gt_statfs_slow = 0;
}
/**
* gfs2_check_sb - Check superblock
* @sdp: the filesystem
* @sb: The superblock
* @silent: Don't print a message if the check fails
*
* Checks the version code of the FS is one that we understand how to
* read and that the sizes of the various on-disk structures have not
* changed.
*/
int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb *sb, int silent)
{
unsigned int x;
if (sb->sb_header.mh_magic != GFS2_MAGIC ||
sb->sb_header.mh_type != GFS2_METATYPE_SB) {
if (!silent)
printk("GFS2: not a GFS2 filesystem\n");
return -EINVAL;
}
/* If format numbers match exactly, we're done. */
if (sb->sb_fs_format == GFS2_FORMAT_FS &&
sb->sb_multihost_format == GFS2_FORMAT_MULTI)
return 0;
if (sb->sb_fs_format != GFS2_FORMAT_FS) {
for (x = 0; gfs2_old_fs_formats[x]; x++)
if (gfs2_old_fs_formats[x] == sb->sb_fs_format)
break;
if (!gfs2_old_fs_formats[x]) {
printk("GFS2: code version (%u, %u) is incompatible "
"with ondisk format (%u, %u)\n",
GFS2_FORMAT_FS, GFS2_FORMAT_MULTI,
sb->sb_fs_format, sb->sb_multihost_format);
printk("GFS2: I don't know how to upgrade this FS\n");
return -EINVAL;
}
}
if (sb->sb_multihost_format != GFS2_FORMAT_MULTI) {
for (x = 0; gfs2_old_multihost_formats[x]; x++)
if (gfs2_old_multihost_formats[x] == sb->sb_multihost_format)
break;
if (!gfs2_old_multihost_formats[x]) {
printk("GFS2: code version (%u, %u) is incompatible "
"with ondisk format (%u, %u)\n",
GFS2_FORMAT_FS, GFS2_FORMAT_MULTI,
sb->sb_fs_format, sb->sb_multihost_format);
printk("GFS2: I don't know how to upgrade this FS\n");
return -EINVAL;
}
}
if (!sdp->sd_args.ar_upgrade) {
printk("GFS2: code version (%u, %u) is incompatible "
"with ondisk format (%u, %u)\n",
GFS2_FORMAT_FS, GFS2_FORMAT_MULTI,
sb->sb_fs_format, sb->sb_multihost_format);
printk("GFS2: Use the \"upgrade\" mount option to upgrade "
"the FS\n");
printk("GFS2: See the manual for more details\n");
return -EINVAL;
}
return 0;
}
/**
* gfs2_read_sb - Read super block
* @sdp: The GFS2 superblock
* @gl: the glock for the superblock (assumed to be held)
* @silent: Don't print message if mount fails
*
*/
int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent)
{
struct buffer_head *bh;
uint32_t hash_blocks, ind_blocks, leaf_blocks;
uint32_t tmp_blocks;
unsigned int x;
int error;
error = gfs2_meta_read(gl, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift,
DIO_FORCE | DIO_START | DIO_WAIT, &bh);
if (error) {
if (!silent)
fs_err(sdp, "can't read superblock\n");
return error;
}
gfs2_assert(sdp, sizeof(struct gfs2_sb) <= bh->b_size);
gfs2_sb_in(&sdp->sd_sb, bh->b_data);
brelse(bh);
error = gfs2_check_sb(sdp, &sdp->sd_sb, silent);
if (error)
return error;
sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift -
GFS2_BASIC_BLOCK_SHIFT;
sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift;
sdp->sd_diptrs = (sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_dinode)) / sizeof(uint64_t);
sdp->sd_inptrs = (sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_meta_header)) / sizeof(uint64_t);
sdp->sd_jbsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header);
sdp->sd_hash_bsize = sdp->sd_sb.sb_bsize / 2;
sdp->sd_hash_bsize_shift = sdp->sd_sb.sb_bsize_shift - 1;
sdp->sd_hash_ptrs = sdp->sd_hash_bsize / sizeof(uint64_t);
sdp->sd_ut_per_block = (sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_meta_header)) /
sizeof(struct gfs2_unlinked_tag);
sdp->sd_qc_per_block = (sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_meta_header)) /
sizeof(struct gfs2_quota_change);
/* Compute maximum reservation required to add a entry to a directory */
hash_blocks = DIV_RU(sizeof(uint64_t) * (1 << GFS2_DIR_MAX_DEPTH),
sdp->sd_jbsize);
ind_blocks = 0;
for (tmp_blocks = hash_blocks; tmp_blocks > sdp->sd_diptrs;) {
tmp_blocks = DIV_RU(tmp_blocks, sdp->sd_inptrs);
ind_blocks += tmp_blocks;
}
leaf_blocks = 2 + GFS2_DIR_MAX_DEPTH;
sdp->sd_max_dirres = hash_blocks + ind_blocks + leaf_blocks;
sdp->sd_heightsize[0] = sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_dinode);
sdp->sd_heightsize[1] = sdp->sd_sb.sb_bsize * sdp->sd_diptrs;
for (x = 2;; x++) {
uint64_t space, d;
uint32_t m;
space = sdp->sd_heightsize[x - 1] * sdp->sd_inptrs;
d = space;
m = do_div(d, sdp->sd_inptrs);
if (d != sdp->sd_heightsize[x - 1] || m)
break;
sdp->sd_heightsize[x] = space;
}
sdp->sd_max_height = x;
gfs2_assert(sdp, sdp->sd_max_height <= GFS2_MAX_META_HEIGHT);
sdp->sd_jheightsize[0] = sdp->sd_sb.sb_bsize -
sizeof(struct gfs2_dinode);
sdp->sd_jheightsize[1] = sdp->sd_jbsize * sdp->sd_diptrs;
for (x = 2;; x++) {
uint64_t space, d;
uint32_t m;
space = sdp->sd_jheightsize[x - 1] * sdp->sd_inptrs;
d = space;
m = do_div(d, sdp->sd_inptrs);
if (d != sdp->sd_jheightsize[x - 1] || m)
break;
sdp->sd_jheightsize[x] = space;
}
sdp->sd_max_jheight = x;
gfs2_assert(sdp, sdp->sd_max_jheight <= GFS2_MAX_META_HEIGHT);
return 0;
}
int gfs2_do_upgrade(struct gfs2_sbd *sdp, struct gfs2_glock *sb_gl)
{
return 0;
}
/**
* gfs2_jindex_hold - Grab a lock on the jindex
* @sdp: The GFS2 superblock
* @ji_gh: the holder for the jindex glock
*
* This is very similar to the gfs2_rindex_hold() function, except that
* in general we hold the jindex lock for longer periods of time and
* we grab it far less frequently (in general) then the rgrp lock.
*
* Returns: errno
*/
int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
{
struct gfs2_inode *dip = sdp->sd_jindex;
struct qstr name;
char buf[20];
struct gfs2_jdesc *jd;
int error;
name.name = buf;
down(&sdp->sd_jindex_mutex);
for (;;) {
error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED,
GL_LOCAL_EXCL, ji_gh);
if (error)
break;
name.len = sprintf(buf, "journal%u", sdp->sd_journals);
error = gfs2_dir_search(sdp->sd_jindex, &name, NULL, NULL);
if (error == -ENOENT) {
error = 0;
break;
}
gfs2_glock_dq_uninit(ji_gh);
if (error)
break;
error = -ENOMEM;
jd = kzalloc(sizeof(struct gfs2_jdesc), GFP_KERNEL);
if (!jd)
break;
error = gfs2_lookupi(dip, &name, 1, &jd->jd_inode);
if (error) {
kfree(jd);
break;
}
spin_lock(&sdp->sd_jindex_spin);
jd->jd_jid = sdp->sd_journals++;
list_add_tail(&jd->jd_list, &sdp->sd_jindex_list);
spin_unlock(&sdp->sd_jindex_spin);
}
up(&sdp->sd_jindex_mutex);
return error;
}
/**
* gfs2_jindex_free - Clear all the journal index information
* @sdp: The GFS2 superblock
*
*/
void gfs2_jindex_free(struct gfs2_sbd *sdp)
{
struct list_head list;
struct gfs2_jdesc *jd;
spin_lock(&sdp->sd_jindex_spin);
list_add(&list, &sdp->sd_jindex_list);
list_del_init(&sdp->sd_jindex_list);
sdp->sd_journals = 0;
spin_unlock(&sdp->sd_jindex_spin);
while (!list_empty(&list)) {
jd = list_entry(list.next, struct gfs2_jdesc, jd_list);
list_del(&jd->jd_list);
gfs2_inode_put(jd->jd_inode);
kfree(jd);
}
}
static struct gfs2_jdesc *jdesc_find_i(struct list_head *head, unsigned int jid)
{
struct gfs2_jdesc *jd;
int found = 0;
list_for_each_entry(jd, head, jd_list) {
if (jd->jd_jid == jid) {
found = 1;
break;
}
}
if (!found)
jd = NULL;
return jd;
}
struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid)
{
struct gfs2_jdesc *jd;
spin_lock(&sdp->sd_jindex_spin);
jd = jdesc_find_i(&sdp->sd_jindex_list, jid);
spin_unlock(&sdp->sd_jindex_spin);
return jd;
}
void gfs2_jdesc_make_dirty(struct gfs2_sbd *sdp, unsigned int jid)
{
struct gfs2_jdesc *jd;
spin_lock(&sdp->sd_jindex_spin);
jd = jdesc_find_i(&sdp->sd_jindex_list, jid);
if (jd)
jd->jd_dirty = 1;
spin_unlock(&sdp->sd_jindex_spin);
}
struct gfs2_jdesc *gfs2_jdesc_find_dirty(struct gfs2_sbd *sdp)
{
struct gfs2_jdesc *jd;
int found = 0;
spin_lock(&sdp->sd_jindex_spin);
list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
if (jd->jd_dirty) {
jd->jd_dirty = 0;
found = 1;
break;
}
}
spin_unlock(&sdp->sd_jindex_spin);
if (!found)
jd = NULL;
return jd;
}
int gfs2_jdesc_check(struct gfs2_jdesc *jd)
{
struct gfs2_inode *ip = jd->jd_inode;
struct gfs2_sbd *sdp = ip->i_sbd;
int ar;
int error;
if (ip->i_di.di_size < (8 << 20) ||
ip->i_di.di_size > (1 << 30) ||
(ip->i_di.di_size & (sdp->sd_sb.sb_bsize - 1))) {
gfs2_consist_inode(ip);
return -EIO;
}
jd->jd_blocks = ip->i_di.di_size >> sdp->sd_sb.sb_bsize_shift;
error = gfs2_write_alloc_required(ip,
0, ip->i_di.di_size,
&ar);
if (!error && ar) {
gfs2_consist_inode(ip);
error = -EIO;
}
return error;
}
int gfs2_lookup_master_dir(struct gfs2_sbd *sdp)
{
struct gfs2_glock *gl;
int error;
error = gfs2_glock_get(sdp,
sdp->sd_sb.sb_master_dir.no_addr,
&gfs2_inode_glops, CREATE, &gl);
if (!error) {
error = gfs2_inode_get(gl, &sdp->sd_sb.sb_master_dir, CREATE,
&sdp->sd_master_dir);
gfs2_glock_put(gl);
}
return error;
}
/**
* gfs2_make_fs_rw - Turn a Read-Only FS into a Read-Write one
* @sdp: the filesystem
*
* Returns: errno
*/
int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
{
struct gfs2_glock *j_gl = sdp->sd_jdesc->jd_inode->i_gl;
struct gfs2_holder t_gh;
struct gfs2_log_header head;
int error;
error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED,
GL_LOCAL_EXCL | GL_NEVER_RECURSE, &t_gh);
if (error)
return error;
gfs2_meta_cache_flush(sdp->sd_jdesc->jd_inode);
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA | DIO_DATA);
error = gfs2_find_jhead(sdp->sd_jdesc, &head);
if (error)
goto fail;
if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) {
gfs2_consist(sdp);
error = -EIO;
goto fail;
}
/* Initialize some head of the log stuff */
sdp->sd_log_sequence = head.lh_sequence + 1;
gfs2_log_pointers_init(sdp, head.lh_blkno);
error = gfs2_unlinked_init(sdp);
if (error)
goto fail;
error = gfs2_quota_init(sdp);
if (error)
goto fail_unlinked;
set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
gfs2_glock_dq_uninit(&t_gh);
return 0;
fail_unlinked:
gfs2_unlinked_cleanup(sdp);
fail:
t_gh.gh_flags |= GL_NOCACHE;
gfs2_glock_dq_uninit(&t_gh);
return error;
}
/**
* gfs2_make_fs_ro - Turn a Read-Write FS into a Read-Only one
* @sdp: the filesystem
*
* Returns: errno
*/
int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
{
struct gfs2_holder t_gh;
int error;
gfs2_unlinked_dealloc(sdp);
gfs2_quota_sync(sdp);
gfs2_statfs_sync(sdp);
error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED,
GL_LOCAL_EXCL | GL_NEVER_RECURSE | GL_NOCACHE,
&t_gh);
if (error && !test_bit(SDF_SHUTDOWN, &sdp->sd_flags))
return error;
gfs2_meta_syncfs(sdp);
gfs2_log_shutdown(sdp);
clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
if (t_gh.gh_gl)
gfs2_glock_dq_uninit(&t_gh);
gfs2_unlinked_cleanup(sdp);
gfs2_quota_cleanup(sdp);
return error;
}
int gfs2_statfs_init(struct gfs2_sbd *sdp)
{
struct gfs2_inode *m_ip = sdp->sd_statfs_inode;
struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master;
struct gfs2_inode *l_ip = sdp->sd_sc_inode;
struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local;
struct buffer_head *m_bh, *l_bh;
struct gfs2_holder gh;
int error;
error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE,
&gh);
if (error)
return error;
error = gfs2_meta_inode_buffer(m_ip, &m_bh);
if (error)
goto out;
if (sdp->sd_args.ar_spectator) {
spin_lock(&sdp->sd_statfs_spin);
gfs2_statfs_change_in(m_sc, m_bh->b_data +
sizeof(struct gfs2_dinode));
spin_unlock(&sdp->sd_statfs_spin);
} else {
error = gfs2_meta_inode_buffer(l_ip, &l_bh);
if (error)
goto out_m_bh;
spin_lock(&sdp->sd_statfs_spin);
gfs2_statfs_change_in(m_sc, m_bh->b_data +
sizeof(struct gfs2_dinode));
gfs2_statfs_change_in(l_sc, l_bh->b_data +
sizeof(struct gfs2_dinode));
spin_unlock(&sdp->sd_statfs_spin);
brelse(l_bh);
}
out_m_bh:
brelse(m_bh);
out:
gfs2_glock_dq_uninit(&gh);
return 0;
}
void gfs2_statfs_change(struct gfs2_sbd *sdp, int64_t total, int64_t free,
int64_t dinodes)
{
struct gfs2_inode *l_ip = sdp->sd_sc_inode;
struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local;
struct buffer_head *l_bh;
int error;
error = gfs2_meta_inode_buffer(l_ip, &l_bh);
if (error)
return;
down(&sdp->sd_statfs_mutex);
gfs2_trans_add_bh(l_ip->i_gl, l_bh);
up(&sdp->sd_statfs_mutex);
spin_lock(&sdp->sd_statfs_spin);
l_sc->sc_total += total;
l_sc->sc_free += free;
l_sc->sc_dinodes += dinodes;
gfs2_statfs_change_out(l_sc, l_bh->b_data +
sizeof(struct gfs2_dinode));
spin_unlock(&sdp->sd_statfs_spin);
brelse(l_bh);
}
int gfs2_statfs_sync(struct gfs2_sbd *sdp)
{
struct gfs2_inode *m_ip = sdp->sd_statfs_inode;
struct gfs2_inode *l_ip = sdp->sd_sc_inode;
struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master;
struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local;
struct gfs2_holder gh;
struct buffer_head *m_bh, *l_bh;
int error;
error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, GL_NOCACHE,
&gh);
if (error)
return error;
error = gfs2_meta_inode_buffer(m_ip, &m_bh);
if (error)
goto out;
spin_lock(&sdp->sd_statfs_spin);
gfs2_statfs_change_in(m_sc, m_bh->b_data +
sizeof(struct gfs2_dinode));
if (!l_sc->sc_total && !l_sc->sc_free && !l_sc->sc_dinodes) {
spin_unlock(&sdp->sd_statfs_spin);
goto out_bh;
}
spin_unlock(&sdp->sd_statfs_spin);
error = gfs2_meta_inode_buffer(l_ip, &l_bh);
if (error)
goto out_bh;
error = gfs2_trans_begin(sdp, 2 * RES_DINODE, 0);
if (error)
goto out_bh2;
down(&sdp->sd_statfs_mutex);
gfs2_trans_add_bh(l_ip->i_gl, l_bh);
up(&sdp->sd_statfs_mutex);
spin_lock(&sdp->sd_statfs_spin);
m_sc->sc_total += l_sc->sc_total;
m_sc->sc_free += l_sc->sc_free;
m_sc->sc_dinodes += l_sc->sc_dinodes;
memset(l_sc, 0, sizeof(struct gfs2_statfs_change));
memset(l_bh->b_data + sizeof(struct gfs2_dinode),
0, sizeof(struct gfs2_statfs_change));
spin_unlock(&sdp->sd_statfs_spin);
gfs2_trans_add_bh(m_ip->i_gl, m_bh);
gfs2_statfs_change_out(m_sc, m_bh->b_data + sizeof(struct gfs2_dinode));
gfs2_trans_end(sdp);
out_bh2:
brelse(l_bh);
out_bh:
brelse(m_bh);
out:
gfs2_glock_dq_uninit(&gh);
return error;
}
/**
* gfs2_statfs_i - Do a statfs
* @sdp: the filesystem
* @sg: the sg structure
*
* Returns: errno
*/
int gfs2_statfs_i(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc)
{
struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master;
struct gfs2_statfs_change *l_sc = &sdp->sd_statfs_local;
spin_lock(&sdp->sd_statfs_spin);
*sc = *m_sc;
sc->sc_total += l_sc->sc_total;
sc->sc_free += l_sc->sc_free;
sc->sc_dinodes += l_sc->sc_dinodes;
spin_unlock(&sdp->sd_statfs_spin);
if (sc->sc_free < 0)
sc->sc_free = 0;
if (sc->sc_free > sc->sc_total)
sc->sc_free = sc->sc_total;
if (sc->sc_dinodes < 0)
sc->sc_dinodes = 0;
return 0;
}
/**
* statfs_fill - fill in the sg for a given RG
* @rgd: the RG
* @sc: the sc structure
*
* Returns: 0 on success, -ESTALE if the LVB is invalid
*/
static int statfs_slow_fill(struct gfs2_rgrpd *rgd,
struct gfs2_statfs_change *sc)
{
gfs2_rgrp_verify(rgd);
sc->sc_total += rgd->rd_ri.ri_data;
sc->sc_free += rgd->rd_rg.rg_free;
sc->sc_dinodes += rgd->rd_rg.rg_dinodes;
return 0;
}
/**
* gfs2_statfs_slow - Stat a filesystem using asynchronous locking
* @sdp: the filesystem
* @sc: the sc info that will be returned
*
* Any error (other than a signal) will cause this routine to fall back
* to the synchronous version.
*
* FIXME: This really shouldn't busy wait like this.
*
* Returns: errno
*/
int gfs2_statfs_slow(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc)
{
struct gfs2_holder ri_gh;
struct gfs2_rgrpd *rgd_next;
struct gfs2_holder *gha, *gh;
unsigned int slots = 64;
unsigned int x;
int done;
int error = 0, err;
memset(sc, 0, sizeof(struct gfs2_statfs_change));
gha = kcalloc(slots, sizeof(struct gfs2_holder), GFP_KERNEL);
if (!gha)
return -ENOMEM;
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
goto out;
rgd_next = gfs2_rgrpd_get_first(sdp);
for (;;) {
done = 1;
for (x = 0; x < slots; x++) {
gh = gha + x;
if (gh->gh_gl && gfs2_glock_poll(gh)) {
err = gfs2_glock_wait(gh);
if (err) {
gfs2_holder_uninit(gh);
error = err;
} else {
if (!error)
error = statfs_slow_fill(get_gl2rgd(gh->gh_gl), sc);
gfs2_glock_dq_uninit(gh);
}
}
if (gh->gh_gl)
done = 0;
else if (rgd_next && !error) {
error = gfs2_glock_nq_init(rgd_next->rd_gl,
LM_ST_SHARED,
GL_ASYNC,
gh);
rgd_next = gfs2_rgrpd_get_next(rgd_next);
done = 0;
}
if (signal_pending(current))
error = -ERESTARTSYS;
}
if (done)
break;
yield();
}
gfs2_glock_dq_uninit(&ri_gh);
out:
kfree(gha);
return error;
}
struct lfcc {
struct list_head list;
struct gfs2_holder gh;
};
/**
* gfs2_lock_fs_check_clean - Stop all writes to the FS and check that all
* journals are clean
* @sdp: the file system
* @state: the state to put the transaction lock into
* @t_gh: the hold on the transaction lock
*
* Returns: errno
*/
int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp, struct gfs2_holder *t_gh)
{
struct gfs2_holder ji_gh;
struct gfs2_jdesc *jd;
struct lfcc *lfcc;
LIST_HEAD(list);
struct gfs2_log_header lh;
int error;
error = gfs2_jindex_hold(sdp, &ji_gh);
if (error)
return error;
list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
lfcc = kmalloc(sizeof(struct lfcc), GFP_KERNEL);
if (!lfcc) {
error = -ENOMEM;
goto out;
}
error = gfs2_glock_nq_init(jd->jd_inode->i_gl, LM_ST_SHARED, 0,
&lfcc->gh);
if (error) {
kfree(lfcc);
goto out;
}
list_add(&lfcc->list, &list);
}
error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_DEFERRED,
LM_FLAG_PRIORITY | GL_NEVER_RECURSE | GL_NOCACHE,
t_gh);
list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
error = gfs2_jdesc_check(jd);
if (error)
break;
error = gfs2_find_jhead(jd, &lh);
if (error)
break;
if (!(lh.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) {
error = -EBUSY;
break;
}
}
if (error)
gfs2_glock_dq_uninit(t_gh);
out:
while (!list_empty(&list)) {
lfcc = list_entry(list.next, struct lfcc, list);
list_del(&lfcc->list);
gfs2_glock_dq_uninit(&lfcc->gh);
kfree(lfcc);
}
gfs2_glock_dq_uninit(&ji_gh);
return error;
}
/**
* gfs2_freeze_fs - freezes the file system
* @sdp: the file system
*
* This function flushes data and meta data for all machines by
* aquiring the transaction log exclusively. All journals are
* ensured to be in a clean state as well.
*
* Returns: errno
*/
int gfs2_freeze_fs(struct gfs2_sbd *sdp)
{
int error = 0;
down(&sdp->sd_freeze_lock);
if (!sdp->sd_freeze_count++) {
error = gfs2_lock_fs_check_clean(sdp, &sdp->sd_freeze_gh);
if (error)
sdp->sd_freeze_count--;
}
up(&sdp->sd_freeze_lock);
return error;
}
/**
* gfs2_unfreeze_fs - unfreezes the file system
* @sdp: the file system
*
* This function allows the file system to proceed by unlocking
* the exclusively held transaction lock. Other GFS2 nodes are
* now free to acquire the lock shared and go on with their lives.
*
*/
void gfs2_unfreeze_fs(struct gfs2_sbd *sdp)
{
down(&sdp->sd_freeze_lock);
if (sdp->sd_freeze_count && !--sdp->sd_freeze_count)
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
up(&sdp->sd_freeze_lock);
}

55
fs/gfs2/super.h Normal file
View File

@ -0,0 +1,55 @@
/*
* 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 __SUPER_DOT_H__
#define __SUPER_DOT_H__
void gfs2_tune_init(struct gfs2_tune *gt);
int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb *sb, int silent);
int gfs2_read_sb(struct gfs2_sbd *sdp, struct gfs2_glock *gl, int silent);
int gfs2_do_upgrade(struct gfs2_sbd *sdp, struct gfs2_glock *gl_sb);
static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
{
unsigned int x;
spin_lock(&sdp->sd_jindex_spin);
x = sdp->sd_journals;
spin_unlock(&sdp->sd_jindex_spin);
return x;
}
int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh);
void gfs2_jindex_free(struct gfs2_sbd *sdp);
struct gfs2_jdesc *gfs2_jdesc_find(struct gfs2_sbd *sdp, unsigned int jid);
void gfs2_jdesc_make_dirty(struct gfs2_sbd *sdp, unsigned int jid);
struct gfs2_jdesc *gfs2_jdesc_find_dirty(struct gfs2_sbd *sdp);
int gfs2_jdesc_check(struct gfs2_jdesc *jd);
int gfs2_lookup_master_dir(struct gfs2_sbd *sdp);
int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
struct gfs2_inode **ipp);
int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
int gfs2_make_fs_ro(struct gfs2_sbd *sdp);
int gfs2_statfs_init(struct gfs2_sbd *sdp);
void gfs2_statfs_change(struct gfs2_sbd *sdp,
int64_t total, int64_t free, int64_t dinodes);
int gfs2_statfs_sync(struct gfs2_sbd *sdp);
int gfs2_statfs_i(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc);
int gfs2_statfs_slow(struct gfs2_sbd *sdp, struct gfs2_statfs_change *sc);
int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp, struct gfs2_holder *t_gh);
int gfs2_freeze_fs(struct gfs2_sbd *sdp);
void gfs2_unfreeze_fs(struct gfs2_sbd *sdp);
#endif /* __SUPER_DOT_H__ */

640
fs/gfs2/sys.c Normal file
View File

@ -0,0 +1,640 @@
/*
* 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 <linux/module.h>
#include <linux/kobject.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include "gfs2.h"
#include "lm.h"
#include "sys.h"
#include "super.h"
#include "glock.h"
#include "quota.h"
char *gfs2_sys_margs;
spinlock_t gfs2_sys_margs_lock;
static ssize_t id_show(struct gfs2_sbd *sdp, char *buf)
{
return sprintf(buf, "%s\n", sdp->sd_vfs->s_id);
}
static ssize_t fsname_show(struct gfs2_sbd *sdp, char *buf)
{
return sprintf(buf, "%s\n", sdp->sd_fsname);
}
static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
{
unsigned int count;
down(&sdp->sd_freeze_lock);
count = sdp->sd_freeze_count;
up(&sdp->sd_freeze_lock);
return sprintf(buf, "%u\n", count);
}
static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
{
ssize_t ret = len;
int error = 0;
int n = simple_strtol(buf, NULL, 0);
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
switch (n) {
case 0:
gfs2_unfreeze_fs(sdp);
break;
case 1:
error = gfs2_freeze_fs(sdp);
break;
default:
ret = -EINVAL;
}
if (error)
fs_warn(sdp, "freeze %d error %d", n, error);
return ret;
}
static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf)
{
unsigned int b = test_bit(SDF_SHUTDOWN, &sdp->sd_flags);
return sprintf(buf, "%u\n", b);
}
static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (simple_strtol(buf, NULL, 0) != 1)
return -EINVAL;
gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: withdrawing from cluster at user's request\n",
sdp->sd_fsname);
return len;
}
static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf,
size_t len)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (simple_strtol(buf, NULL, 0) != 1)
return -EINVAL;
gfs2_statfs_sync(sdp);
return len;
}
static ssize_t shrink_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (simple_strtol(buf, NULL, 0) != 1)
return -EINVAL;
gfs2_gl_hash_clear(sdp, NO_WAIT);
return len;
}
static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf,
size_t len)
{
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (simple_strtol(buf, NULL, 0) != 1)
return -EINVAL;
gfs2_quota_sync(sdp);
return len;
}
static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf,
size_t len)
{
uint32_t id;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
id = simple_strtoul(buf, NULL, 0);
gfs2_quota_refresh(sdp, 1, id);
return len;
}
static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf,
size_t len)
{
uint32_t id;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
id = simple_strtoul(buf, NULL, 0);
gfs2_quota_refresh(sdp, 0, id);
return len;
}
struct gfs2_attr {
struct attribute attr;
ssize_t (*show)(struct gfs2_sbd *, char *);
ssize_t (*store)(struct gfs2_sbd *, const char *, size_t);
};
#define GFS2_ATTR(name, mode, show, store) \
static struct gfs2_attr gfs2_attr_##name = __ATTR(name, mode, show, store)
GFS2_ATTR(id, 0444, id_show, NULL);
GFS2_ATTR(fsname, 0444, fsname_show, NULL);
GFS2_ATTR(freeze, 0644, freeze_show, freeze_store);
GFS2_ATTR(shrink, 0200, NULL, shrink_store);
GFS2_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
GFS2_ATTR(statfs_sync, 0200, NULL, statfs_sync_store);
GFS2_ATTR(quota_sync, 0200, NULL, quota_sync_store);
GFS2_ATTR(quota_refresh_user, 0200, NULL, quota_refresh_user_store);
GFS2_ATTR(quota_refresh_group, 0200, NULL, quota_refresh_group_store);
static struct attribute *gfs2_attrs[] = {
&gfs2_attr_id.attr,
&gfs2_attr_fsname.attr,
&gfs2_attr_freeze.attr,
&gfs2_attr_shrink.attr,
&gfs2_attr_withdraw.attr,
&gfs2_attr_statfs_sync.attr,
&gfs2_attr_quota_sync.attr,
&gfs2_attr_quota_refresh_user.attr,
&gfs2_attr_quota_refresh_group.attr,
NULL,
};
static ssize_t gfs2_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr);
return a->show ? a->show(sdp, buf) : 0;
}
static ssize_t gfs2_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t len)
{
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr);
return a->store ? a->store(sdp, buf, len) : len;
}
static struct sysfs_ops gfs2_attr_ops = {
.show = gfs2_attr_show,
.store = gfs2_attr_store,
};
static struct kobj_type gfs2_ktype = {
.default_attrs = gfs2_attrs,
.sysfs_ops = &gfs2_attr_ops,
};
static struct kset gfs2_kset = {
.subsys = &fs_subsys,
.kobj = {.name = "gfs2",},
.ktype = &gfs2_ktype,
};
/*
* display struct lm_lockstruct fields
*/
struct lockstruct_attr {
struct attribute attr;
ssize_t (*show)(struct gfs2_sbd *, char *);
};
#define LOCKSTRUCT_ATTR(name, fmt) \
static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \
{ \
return sprintf(buf, fmt, sdp->sd_lockstruct.ls_##name); \
} \
static struct lockstruct_attr lockstruct_attr_##name = __ATTR_RO(name)
LOCKSTRUCT_ATTR(jid, "%u\n");
LOCKSTRUCT_ATTR(first, "%u\n");
LOCKSTRUCT_ATTR(lvb_size, "%u\n");
LOCKSTRUCT_ATTR(flags, "%d\n");
static struct attribute *lockstruct_attrs[] = {
&lockstruct_attr_jid.attr,
&lockstruct_attr_first.attr,
&lockstruct_attr_lvb_size.attr,
&lockstruct_attr_flags.attr,
NULL
};
/*
* display struct gfs2_args fields
*/
struct args_attr {
struct attribute attr;
ssize_t (*show)(struct gfs2_sbd *, char *);
};
#define ARGS_ATTR(name, fmt) \
static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \
{ \
return sprintf(buf, fmt, sdp->sd_args.ar_##name); \
} \
static struct args_attr args_attr_##name = __ATTR_RO(name)
ARGS_ATTR(lockproto, "%s\n");
ARGS_ATTR(locktable, "%s\n");
ARGS_ATTR(hostdata, "%s\n");
ARGS_ATTR(spectator, "%d\n");
ARGS_ATTR(ignore_local_fs, "%d\n");
ARGS_ATTR(localcaching, "%d\n");
ARGS_ATTR(localflocks, "%d\n");
ARGS_ATTR(debug, "%d\n");
ARGS_ATTR(upgrade, "%d\n");
ARGS_ATTR(num_glockd, "%u\n");
ARGS_ATTR(posix_acl, "%d\n");
ARGS_ATTR(quota, "%u\n");
ARGS_ATTR(suiddir, "%d\n");
ARGS_ATTR(data, "%d\n");
/* one oddball doesn't fit the macro mold */
static ssize_t noatime_show(struct gfs2_sbd *sdp, char *buf)
{
return sprintf(buf, "%d\n", !!test_bit(SDF_NOATIME, &sdp->sd_flags));
}
static struct args_attr args_attr_noatime = __ATTR_RO(noatime);
static struct attribute *args_attrs[] = {
&args_attr_lockproto.attr,
&args_attr_locktable.attr,
&args_attr_hostdata.attr,
&args_attr_spectator.attr,
&args_attr_ignore_local_fs.attr,
&args_attr_localcaching.attr,
&args_attr_localflocks.attr,
&args_attr_debug.attr,
&args_attr_upgrade.attr,
&args_attr_num_glockd.attr,
&args_attr_posix_acl.attr,
&args_attr_quota.attr,
&args_attr_suiddir.attr,
&args_attr_data.attr,
&args_attr_noatime.attr,
NULL
};
/*
* display counters from superblock
*/
struct counters_attr {
struct attribute attr;
ssize_t (*show)(struct gfs2_sbd *, char *);
};
#define COUNTERS_ATTR_GENERAL(name, fmt, val) \
static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \
{ \
return sprintf(buf, fmt, val); \
} \
static struct counters_attr counters_attr_##name = __ATTR_RO(name)
#define COUNTERS_ATTR_SIMPLE(name, fmt) \
COUNTERS_ATTR_GENERAL(name, fmt, sdp->sd_##name)
#define COUNTERS_ATTR_ATOMIC(name, fmt) \
COUNTERS_ATTR_GENERAL(name, fmt, (unsigned int)atomic_read(&sdp->sd_##name))
COUNTERS_ATTR_ATOMIC(glock_count, "%u\n");
COUNTERS_ATTR_ATOMIC(glock_held_count, "%u\n");
COUNTERS_ATTR_ATOMIC(inode_count, "%u\n");
COUNTERS_ATTR_ATOMIC(bufdata_count, "%u\n");
COUNTERS_ATTR_ATOMIC(unlinked_count, "%u\n");
COUNTERS_ATTR_ATOMIC(quota_count, "%u\n");
COUNTERS_ATTR_SIMPLE(log_num_gl, "%u\n");
COUNTERS_ATTR_SIMPLE(log_num_buf, "%u\n");
COUNTERS_ATTR_SIMPLE(log_num_revoke, "%u\n");
COUNTERS_ATTR_SIMPLE(log_num_rg, "%u\n");
COUNTERS_ATTR_SIMPLE(log_num_databuf, "%u\n");
COUNTERS_ATTR_SIMPLE(log_blks_free, "%u\n");
COUNTERS_ATTR_GENERAL(jd_blocks, "%u\n", sdp->sd_jdesc->jd_blocks);
COUNTERS_ATTR_ATOMIC(reclaim_count, "%u\n");
COUNTERS_ATTR_SIMPLE(log_wraps, "%llu\n");
COUNTERS_ATTR_ATOMIC(fh2dentry_misses, "%u\n");
COUNTERS_ATTR_ATOMIC(reclaimed, "%u\n");
COUNTERS_ATTR_ATOMIC(log_flush_incore, "%u\n");
COUNTERS_ATTR_ATOMIC(log_flush_ondisk, "%u\n");
COUNTERS_ATTR_ATOMIC(glock_nq_calls, "%u\n");
COUNTERS_ATTR_ATOMIC(glock_dq_calls, "%u\n");
COUNTERS_ATTR_ATOMIC(glock_prefetch_calls, "%u\n");
COUNTERS_ATTR_ATOMIC(lm_lock_calls, "%u\n");
COUNTERS_ATTR_ATOMIC(lm_unlock_calls, "%u\n");
COUNTERS_ATTR_ATOMIC(lm_callbacks, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_address, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_dentry, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_export, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_file, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_inode, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_super, "%u\n");
COUNTERS_ATTR_ATOMIC(ops_vm, "%u\n");
static struct attribute *counters_attrs[] = {
&counters_attr_glock_count.attr,
&counters_attr_glock_held_count.attr,
&counters_attr_inode_count.attr,
&counters_attr_bufdata_count.attr,
&counters_attr_unlinked_count.attr,
&counters_attr_quota_count.attr,
&counters_attr_log_num_gl.attr,
&counters_attr_log_num_buf.attr,
&counters_attr_log_num_revoke.attr,
&counters_attr_log_num_rg.attr,
&counters_attr_log_num_databuf.attr,
&counters_attr_log_blks_free.attr,
&counters_attr_jd_blocks.attr,
&counters_attr_reclaim_count.attr,
&counters_attr_log_wraps.attr,
&counters_attr_fh2dentry_misses.attr,
&counters_attr_reclaimed.attr,
&counters_attr_log_flush_incore.attr,
&counters_attr_log_flush_ondisk.attr,
&counters_attr_glock_nq_calls.attr,
&counters_attr_glock_dq_calls.attr,
&counters_attr_glock_prefetch_calls.attr,
&counters_attr_lm_lock_calls.attr,
&counters_attr_lm_unlock_calls.attr,
&counters_attr_lm_callbacks.attr,
&counters_attr_ops_address.attr,
&counters_attr_ops_dentry.attr,
&counters_attr_ops_export.attr,
&counters_attr_ops_file.attr,
&counters_attr_ops_inode.attr,
&counters_attr_ops_super.attr,
&counters_attr_ops_vm.attr,
NULL
};
/*
* get and set struct gfs2_tune fields
*/
static ssize_t quota_scale_show(struct gfs2_sbd *sdp, char *buf)
{
return sprintf(buf, "%u %u\n", sdp->sd_tune.gt_quota_scale_num,
sdp->sd_tune.gt_quota_scale_den);
}
static ssize_t quota_scale_store(struct gfs2_sbd *sdp, const char *buf,
size_t len)
{
struct gfs2_tune *gt = &sdp->sd_tune;
unsigned int x, y;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (sscanf(buf, "%u %u", &x, &y) != 2 || !y)
return -EINVAL;
spin_lock(&gt->gt_spin);
gt->gt_quota_scale_num = x;
gt->gt_quota_scale_den = y;
spin_unlock(&gt->gt_spin);
return len;
}
static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field,
int check_zero, const char *buf, size_t len)
{
struct gfs2_tune *gt = &sdp->sd_tune;
unsigned int x;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
x = simple_strtoul(buf, NULL, 0);
if (check_zero && !x)
return -EINVAL;
spin_lock(&gt->gt_spin);
*field = x;
spin_unlock(&gt->gt_spin);
return len;
}
struct tune_attr {
struct attribute attr;
ssize_t (*show)(struct gfs2_sbd *, char *);
ssize_t (*store)(struct gfs2_sbd *, const char *, size_t);
};
#define TUNE_ATTR_3(name, show, store) \
static struct tune_attr tune_attr_##name = __ATTR(name, 0644, show, store)
#define TUNE_ATTR_2(name, store) \
static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \
{ \
return sprintf(buf, "%u\n", sdp->sd_tune.gt_##name); \
} \
TUNE_ATTR_3(name, name##_show, store)
#define TUNE_ATTR(name, check_zero) \
static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\
{ \
return tune_set(sdp, &sdp->sd_tune.gt_##name, check_zero, buf, len); \
} \
TUNE_ATTR_2(name, name##_store)
#define TUNE_ATTR_DAEMON(name, process) \
static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\
{ \
ssize_t r = tune_set(sdp, &sdp->sd_tune.gt_##name, 1, buf, len); \
wake_up_process(sdp->sd_##process); \
return r; \
} \
TUNE_ATTR_2(name, name##_store)
TUNE_ATTR(ilimit, 0);
TUNE_ATTR(ilimit_tries, 0);
TUNE_ATTR(ilimit_min, 0);
TUNE_ATTR(demote_secs, 0);
TUNE_ATTR(incore_log_blocks, 0);
TUNE_ATTR(log_flush_secs, 0);
TUNE_ATTR(jindex_refresh_secs, 0);
TUNE_ATTR(quota_warn_period, 0);
TUNE_ATTR(quota_quantum, 0);
TUNE_ATTR(atime_quantum, 0);
TUNE_ATTR(max_readahead, 0);
TUNE_ATTR(complain_secs, 0);
TUNE_ATTR(reclaim_limit, 0);
TUNE_ATTR(prefetch_secs, 0);
TUNE_ATTR(statfs_slow, 0);
TUNE_ATTR(new_files_jdata, 0);
TUNE_ATTR(new_files_directio, 0);
TUNE_ATTR(quota_simul_sync, 1);
TUNE_ATTR(quota_cache_secs, 1);
TUNE_ATTR(max_atomic_write, 1);
TUNE_ATTR(stall_secs, 1);
TUNE_ATTR(entries_per_readdir, 1);
TUNE_ATTR(greedy_default, 1);
TUNE_ATTR(greedy_quantum, 1);
TUNE_ATTR(greedy_max, 1);
TUNE_ATTR(statfs_quantum, 1);
TUNE_ATTR_DAEMON(scand_secs, scand_process);
TUNE_ATTR_DAEMON(recoverd_secs, recoverd_process);
TUNE_ATTR_DAEMON(logd_secs, logd_process);
TUNE_ATTR_DAEMON(quotad_secs, quotad_process);
TUNE_ATTR_DAEMON(inoded_secs, inoded_process);
TUNE_ATTR_3(quota_scale, quota_scale_show, quota_scale_store);
static struct attribute *tune_attrs[] = {
&tune_attr_ilimit.attr,
&tune_attr_ilimit_tries.attr,
&tune_attr_ilimit_min.attr,
&tune_attr_demote_secs.attr,
&tune_attr_incore_log_blocks.attr,
&tune_attr_log_flush_secs.attr,
&tune_attr_jindex_refresh_secs.attr,
&tune_attr_quota_warn_period.attr,
&tune_attr_quota_quantum.attr,
&tune_attr_atime_quantum.attr,
&tune_attr_max_readahead.attr,
&tune_attr_complain_secs.attr,
&tune_attr_reclaim_limit.attr,
&tune_attr_prefetch_secs.attr,
&tune_attr_statfs_slow.attr,
&tune_attr_quota_simul_sync.attr,
&tune_attr_quota_cache_secs.attr,
&tune_attr_max_atomic_write.attr,
&tune_attr_stall_secs.attr,
&tune_attr_entries_per_readdir.attr,
&tune_attr_greedy_default.attr,
&tune_attr_greedy_quantum.attr,
&tune_attr_greedy_max.attr,
&tune_attr_statfs_quantum.attr,
&tune_attr_scand_secs.attr,
&tune_attr_recoverd_secs.attr,
&tune_attr_logd_secs.attr,
&tune_attr_quotad_secs.attr,
&tune_attr_inoded_secs.attr,
&tune_attr_quota_scale.attr,
&tune_attr_new_files_jdata.attr,
&tune_attr_new_files_directio.attr,
NULL
};
static struct attribute_group lockstruct_group = {
.name = "lockstruct",
.attrs = lockstruct_attrs
};
static struct attribute_group counters_group = {
.name = "counters",
.attrs = counters_attrs
};
static struct attribute_group args_group = {
.name = "args",
.attrs = args_attrs
};
static struct attribute_group tune_group = {
.name = "tune",
.attrs = tune_attrs
};
int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
{
int error;
sdp->sd_kobj.kset = &gfs2_kset;
sdp->sd_kobj.ktype = &gfs2_ktype;
error = kobject_set_name(&sdp->sd_kobj, "%s", sdp->sd_table_name);
if (error)
goto fail;
error = kobject_register(&sdp->sd_kobj);
if (error)
goto fail;
error = sysfs_create_group(&sdp->sd_kobj, &lockstruct_group);
if (error)
goto fail_reg;
error = sysfs_create_group(&sdp->sd_kobj, &counters_group);
if (error)
goto fail_lockstruct;
error = sysfs_create_group(&sdp->sd_kobj, &args_group);
if (error)
goto fail_counters;
error = sysfs_create_group(&sdp->sd_kobj, &tune_group);
if (error)
goto fail_args;
return 0;
fail_args:
sysfs_remove_group(&sdp->sd_kobj, &args_group);
fail_counters:
sysfs_remove_group(&sdp->sd_kobj, &counters_group);
fail_lockstruct:
sysfs_remove_group(&sdp->sd_kobj, &lockstruct_group);
fail_reg:
kobject_unregister(&sdp->sd_kobj);
fail:
return error;
}
void gfs2_sys_fs_del(struct gfs2_sbd *sdp)
{
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
sysfs_remove_group(&sdp->sd_kobj, &args_group);
sysfs_remove_group(&sdp->sd_kobj, &counters_group);
sysfs_remove_group(&sdp->sd_kobj, &lockstruct_group);
kobject_unregister(&sdp->sd_kobj);
}
int gfs2_sys_init(void)
{
gfs2_sys_margs = NULL;
spin_lock_init(&gfs2_sys_margs_lock);
return kset_register(&gfs2_kset);
}
void gfs2_sys_uninit(void)
{
kfree(gfs2_sys_margs);
kset_unregister(&gfs2_kset);
}

24
fs/gfs2/sys.h Normal file
View File

@ -0,0 +1,24 @@
/*
* 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 __SYS_DOT_H__
#define __SYS_DOT_H__
/* Allow args to be passed to GFS2 when using an initial ram disk */
extern char *gfs2_sys_margs;
extern spinlock_t gfs2_sys_margs_lock;
int gfs2_sys_fs_add(struct gfs2_sbd *sdp);
void gfs2_sys_fs_del(struct gfs2_sbd *sdp);
int gfs2_sys_init(void);
void gfs2_sys_uninit(void);
#endif /* __SYS_DOT_H__ */

214
fs/gfs2/trans.c Normal file
View File

@ -0,0 +1,214 @@
/*
* 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 "gfs2.h"
#include "glock.h"
#include "log.h"
#include "lops.h"
#include "meta_io.h"
#include "trans.h"
int gfs2_trans_begin_i(struct gfs2_sbd *sdp, unsigned int blocks,
unsigned int revokes, char *file, unsigned int line)
{
struct gfs2_trans *tr;
int error;
if (gfs2_assert_warn(sdp, !get_transaction) ||
gfs2_assert_warn(sdp, blocks || revokes)) {
fs_warn(sdp, "(%s, %u)\n", file, line);
return -EINVAL;
}
tr = kzalloc(sizeof(struct gfs2_trans), GFP_KERNEL);
if (!tr)
return -ENOMEM;
tr->tr_file = file;
tr->tr_line = line;
tr->tr_blocks = blocks;
tr->tr_revokes = revokes;
tr->tr_reserved = 1;
if (blocks)
tr->tr_reserved += 1 + blocks;
if (revokes)
tr->tr_reserved += gfs2_struct2blk(sdp, revokes,
sizeof(uint64_t));
INIT_LIST_HEAD(&tr->tr_list_buf);
error = -ENOMEM;
tr->tr_t_gh = gfs2_holder_get(sdp->sd_trans_gl, LM_ST_SHARED,
GL_NEVER_RECURSE, GFP_KERNEL);
if (!tr->tr_t_gh)
goto fail;
error = gfs2_glock_nq(tr->tr_t_gh);
if (error)
goto fail_holder_put;
if (!test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) {
tr->tr_t_gh->gh_flags |= GL_NOCACHE;
error = -EROFS;
goto fail_gunlock;
}
error = gfs2_log_reserve(sdp, tr->tr_reserved);
if (error)
goto fail_gunlock;
set_transaction(tr);
return 0;
fail_gunlock:
gfs2_glock_dq(tr->tr_t_gh);
fail_holder_put:
gfs2_holder_put(tr->tr_t_gh);
fail:
kfree(tr);
return error;
}
void gfs2_trans_end(struct gfs2_sbd *sdp)
{
struct gfs2_trans *tr;
struct gfs2_holder *t_gh;
tr = get_transaction;
set_transaction(NULL);
if (gfs2_assert_warn(sdp, tr))
return;
t_gh = tr->tr_t_gh;
tr->tr_t_gh = NULL;
if (!tr->tr_touched) {
gfs2_log_release(sdp, tr->tr_reserved);
kfree(tr);
gfs2_glock_dq(t_gh);
gfs2_holder_put(t_gh);
return;
}
if (gfs2_assert_withdraw(sdp, tr->tr_num_buf <= tr->tr_blocks))
fs_err(sdp, "tr_num_buf = %u, tr_blocks = %u "
"tr_file = %s, tr_line = %u\n",
tr->tr_num_buf, tr->tr_blocks,
tr->tr_file, tr->tr_line);
if (gfs2_assert_withdraw(sdp, tr->tr_num_revoke <= tr->tr_revokes))
fs_err(sdp, "tr_num_revoke = %u, tr_revokes = %u "
"tr_file = %s, tr_line = %u\n",
tr->tr_num_revoke, tr->tr_revokes,
tr->tr_file, tr->tr_line);
gfs2_log_commit(sdp, tr);
gfs2_glock_dq(t_gh);
gfs2_holder_put(t_gh);
if (sdp->sd_vfs->s_flags & MS_SYNCHRONOUS)
gfs2_log_flush(sdp);
}
void gfs2_trans_add_gl(struct gfs2_glock *gl)
{
lops_add(gl->gl_sbd, &gl->gl_le);
}
/**
* gfs2_trans_add_bh - Add a to-be-modified buffer to the current transaction
* @gl: the glock the buffer belongs to
* @bh: The buffer to add
*
*/
void gfs2_trans_add_bh(struct gfs2_glock *gl, struct buffer_head *bh)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct gfs2_bufdata *bd;
bd = get_v2bd(bh);
if (bd)
gfs2_assert(sdp, bd->bd_gl == gl);
else {
gfs2_meta_attach_bufdata(gl, bh);
bd = get_v2bd(bh);
}
lops_add(sdp, &bd->bd_le);
}
void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, uint64_t blkno)
{
struct gfs2_revoke *rv = kmalloc(sizeof(struct gfs2_revoke),
GFP_KERNEL | __GFP_NOFAIL);
lops_init_le(&rv->rv_le, &gfs2_revoke_lops);
rv->rv_blkno = blkno;
lops_add(sdp, &rv->rv_le);
}
void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, uint64_t blkno)
{
struct gfs2_revoke *rv;
int found = 0;
gfs2_log_lock(sdp);
list_for_each_entry(rv, &sdp->sd_log_le_revoke, rv_le.le_list) {
if (rv->rv_blkno == blkno) {
list_del(&rv->rv_le.le_list);
gfs2_assert_withdraw(sdp, sdp->sd_log_num_revoke);
sdp->sd_log_num_revoke--;
found = 1;
break;
}
}
gfs2_log_unlock(sdp);
if (found) {
kfree(rv);
get_transaction->tr_num_revoke_rm++;
}
}
void gfs2_trans_add_rg(struct gfs2_rgrpd *rgd)
{
lops_add(rgd->rd_sbd, &rgd->rd_le);
}
void gfs2_trans_add_databuf(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
struct gfs2_databuf *db;
db = get_v2db(bh);
if (!db) {
db = kmalloc(sizeof(struct gfs2_databuf),
GFP_KERNEL | __GFP_NOFAIL);
lops_init_le(&db->db_le, &gfs2_databuf_lops);
get_bh(bh);
db->db_bh = bh;
set_v2db(bh, db);
lops_add(sdp, &db->db_le);
}
}

40
fs/gfs2/trans.h Normal file
View File

@ -0,0 +1,40 @@
/*
* 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 __TRANS_DOT_H__
#define __TRANS_DOT_H__
#define RES_DINODE 1
#define RES_INDIRECT 1
#define RES_JDATA 1
#define RES_DATA 1
#define RES_LEAF 1
#define RES_RG_BIT 2
#define RES_EATTR 1
#define RES_UNLINKED 1
#define RES_STATFS 1
#define RES_QUOTA 2
#define gfs2_trans_begin(sdp, blocks, revokes) \
gfs2_trans_begin_i((sdp), (blocks), (revokes), __FILE__, __LINE__)
int gfs2_trans_begin_i(struct gfs2_sbd *sdp,
unsigned int blocks, unsigned int revokes,
char *file, unsigned int line);
void gfs2_trans_end(struct gfs2_sbd *sdp);
void gfs2_trans_add_gl(struct gfs2_glock *gl);
void gfs2_trans_add_bh(struct gfs2_glock *gl, struct buffer_head *bh);
void gfs2_trans_add_revoke(struct gfs2_sbd *sdp, uint64_t blkno);
void gfs2_trans_add_unrevoke(struct gfs2_sbd *sdp, uint64_t blkno);
void gfs2_trans_add_rg(struct gfs2_rgrpd *rgd);
void gfs2_trans_add_databuf(struct gfs2_sbd *sdp, struct buffer_head *bh);
#endif /* __TRANS_DOT_H__ */

453
fs/gfs2/unlinked.c Normal file
View File

@ -0,0 +1,453 @@
/*
* 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 <linux/kthread.h>
#include <asm/semaphore.h>
#include "gfs2.h"
#include "bmap.h"
#include "inode.h"
#include "meta_io.h"
#include "trans.h"
#include "unlinked.h"
static int munge_ondisk(struct gfs2_sbd *sdp, unsigned int slot,
struct gfs2_unlinked_tag *ut)
{
struct gfs2_inode *ip = sdp->sd_ut_inode;
unsigned int block, offset;
uint64_t dblock;
int new = 0;
struct buffer_head *bh;
int error;
block = slot / sdp->sd_ut_per_block;
offset = slot % sdp->sd_ut_per_block;
error = gfs2_block_map(ip, block, &new, &dblock, NULL);
if (error)
return error;
error = gfs2_meta_read(ip->i_gl, dblock, DIO_START | DIO_WAIT, &bh);
if (error)
return error;
if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_UT)) {
error = -EIO;
goto out;
}
down(&sdp->sd_unlinked_mutex);
gfs2_trans_add_bh(ip->i_gl, bh);
gfs2_unlinked_tag_out(ut, bh->b_data +
sizeof(struct gfs2_meta_header) +
offset * sizeof(struct gfs2_unlinked_tag));
up(&sdp->sd_unlinked_mutex);
out:
brelse(bh);
return error;
}
static void ul_hash(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
{
spin_lock(&sdp->sd_unlinked_spin);
list_add(&ul->ul_list, &sdp->sd_unlinked_list);
gfs2_assert(sdp, ul->ul_count);
ul->ul_count++;
atomic_inc(&sdp->sd_unlinked_count);
spin_unlock(&sdp->sd_unlinked_spin);
}
static void ul_unhash(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
{
spin_lock(&sdp->sd_unlinked_spin);
list_del_init(&ul->ul_list);
gfs2_assert(sdp, ul->ul_count > 1);
ul->ul_count--;
gfs2_assert_warn(sdp, atomic_read(&sdp->sd_unlinked_count) > 0);
atomic_dec(&sdp->sd_unlinked_count);
spin_unlock(&sdp->sd_unlinked_spin);
}
static struct gfs2_unlinked *ul_fish(struct gfs2_sbd *sdp)
{
struct list_head *head;
struct gfs2_unlinked *ul;
int found = 0;
if (sdp->sd_vfs->s_flags & MS_RDONLY)
return NULL;
spin_lock(&sdp->sd_unlinked_spin);
head = &sdp->sd_unlinked_list;
list_for_each_entry(ul, head, ul_list) {
if (test_bit(ULF_LOCKED, &ul->ul_flags))
continue;
list_move_tail(&ul->ul_list, head);
ul->ul_count++;
set_bit(ULF_LOCKED, &ul->ul_flags);
found = 1;
break;
}
if (!found)
ul = NULL;
spin_unlock(&sdp->sd_unlinked_spin);
return ul;
}
/**
* enforce_limit - limit the number of inodes waiting to be deallocated
* @sdp: the filesystem
*
* Returns: errno
*/
static void enforce_limit(struct gfs2_sbd *sdp)
{
unsigned int tries = 0, min = 0;
int error;
if (atomic_read(&sdp->sd_unlinked_count) >=
gfs2_tune_get(sdp, gt_ilimit)) {
tries = gfs2_tune_get(sdp, gt_ilimit_tries);
min = gfs2_tune_get(sdp, gt_ilimit_min);
}
while (tries--) {
struct gfs2_unlinked *ul = ul_fish(sdp);
if (!ul)
break;
error = gfs2_inode_dealloc(sdp, ul);
gfs2_unlinked_put(sdp, ul);
if (!error) {
if (!--min)
break;
} else if (error != 1)
break;
}
}
static struct gfs2_unlinked *ul_alloc(struct gfs2_sbd *sdp)
{
struct gfs2_unlinked *ul;
ul = kzalloc(sizeof(struct gfs2_unlinked), GFP_KERNEL);
if (ul) {
INIT_LIST_HEAD(&ul->ul_list);
ul->ul_count = 1;
set_bit(ULF_LOCKED, &ul->ul_flags);
}
return ul;
}
int gfs2_unlinked_get(struct gfs2_sbd *sdp, struct gfs2_unlinked **ul)
{
unsigned int c, o = 0, b;
unsigned char byte = 0;
enforce_limit(sdp);
*ul = ul_alloc(sdp);
if (!*ul)
return -ENOMEM;
spin_lock(&sdp->sd_unlinked_spin);
for (c = 0; c < sdp->sd_unlinked_chunks; c++)
for (o = 0; o < PAGE_SIZE; o++) {
byte = sdp->sd_unlinked_bitmap[c][o];
if (byte != 0xFF)
goto found;
}
goto fail;
found:
for (b = 0; b < 8; b++)
if (!(byte & (1 << b)))
break;
(*ul)->ul_slot = c * (8 * PAGE_SIZE) + o * 8 + b;
if ((*ul)->ul_slot >= sdp->sd_unlinked_slots)
goto fail;
sdp->sd_unlinked_bitmap[c][o] |= 1 << b;
spin_unlock(&sdp->sd_unlinked_spin);
return 0;
fail:
spin_unlock(&sdp->sd_unlinked_spin);
kfree(*ul);
return -ENOSPC;
}
void gfs2_unlinked_put(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
{
gfs2_assert_warn(sdp, test_and_clear_bit(ULF_LOCKED, &ul->ul_flags));
spin_lock(&sdp->sd_unlinked_spin);
gfs2_assert(sdp, ul->ul_count);
ul->ul_count--;
if (!ul->ul_count) {
gfs2_icbit_munge(sdp, sdp->sd_unlinked_bitmap, ul->ul_slot, 0);
spin_unlock(&sdp->sd_unlinked_spin);
kfree(ul);
} else
spin_unlock(&sdp->sd_unlinked_spin);
}
int gfs2_unlinked_ondisk_add(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
{
int error;
gfs2_assert_warn(sdp, test_bit(ULF_LOCKED, &ul->ul_flags));
gfs2_assert_warn(sdp, list_empty(&ul->ul_list));
error = munge_ondisk(sdp, ul->ul_slot, &ul->ul_ut);
if (!error)
ul_hash(sdp, ul);
return error;
}
int gfs2_unlinked_ondisk_munge(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
{
int error;
gfs2_assert_warn(sdp, test_bit(ULF_LOCKED, &ul->ul_flags));
gfs2_assert_warn(sdp, !list_empty(&ul->ul_list));
error = munge_ondisk(sdp, ul->ul_slot, &ul->ul_ut);
return error;
}
int gfs2_unlinked_ondisk_rm(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul)
{
struct gfs2_unlinked_tag ut;
int error;
gfs2_assert_warn(sdp, test_bit(ULF_LOCKED, &ul->ul_flags));
gfs2_assert_warn(sdp, !list_empty(&ul->ul_list));
memset(&ut, 0, sizeof(struct gfs2_unlinked_tag));
error = munge_ondisk(sdp, ul->ul_slot, &ut);
if (error)
return error;
ul_unhash(sdp, ul);
return 0;
}
/**
* gfs2_unlinked_dealloc - Go through the list of inodes to be deallocated
* @sdp: the filesystem
*
* Returns: errno
*/
int gfs2_unlinked_dealloc(struct gfs2_sbd *sdp)
{
unsigned int hits, strikes;
int error;
for (;;) {
hits = 0;
strikes = 0;
for (;;) {
struct gfs2_unlinked *ul = ul_fish(sdp);
if (!ul)
return 0;
error = gfs2_inode_dealloc(sdp, ul);
gfs2_unlinked_put(sdp, ul);
if (!error) {
hits++;
if (strikes)
strikes--;
} else if (error == 1) {
strikes++;
if (strikes >=
atomic_read(&sdp->sd_unlinked_count)) {
error = 0;
break;
}
} else
return error;
}
if (!hits || kthread_should_stop())
break;
cond_resched();
}
return 0;
}
int gfs2_unlinked_init(struct gfs2_sbd *sdp)
{
struct gfs2_inode *ip = sdp->sd_ut_inode;
unsigned int blocks = ip->i_di.di_size >> sdp->sd_sb.sb_bsize_shift;
unsigned int x, slot = 0;
unsigned int found = 0;
uint64_t dblock;
uint32_t extlen = 0;
int error;
if (!ip->i_di.di_size ||
ip->i_di.di_size > (64 << 20) ||
ip->i_di.di_size & (sdp->sd_sb.sb_bsize - 1)) {
gfs2_consist_inode(ip);
return -EIO;
}
sdp->sd_unlinked_slots = blocks * sdp->sd_ut_per_block;
sdp->sd_unlinked_chunks = DIV_RU(sdp->sd_unlinked_slots, 8 * PAGE_SIZE);
error = -ENOMEM;
sdp->sd_unlinked_bitmap = kcalloc(sdp->sd_unlinked_chunks,
sizeof(unsigned char *),
GFP_KERNEL);
if (!sdp->sd_unlinked_bitmap)
return error;
for (x = 0; x < sdp->sd_unlinked_chunks; x++) {
sdp->sd_unlinked_bitmap[x] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!sdp->sd_unlinked_bitmap[x])
goto fail;
}
for (x = 0; x < blocks; x++) {
struct buffer_head *bh;
unsigned int y;
if (!extlen) {
int new = 0;
error = gfs2_block_map(ip, x, &new, &dblock, &extlen);
if (error)
goto fail;
}
gfs2_meta_ra(ip->i_gl, dblock, extlen);
error = gfs2_meta_read(ip->i_gl, dblock, DIO_START | DIO_WAIT,
&bh);
if (error)
goto fail;
error = -EIO;
if (gfs2_metatype_check(sdp, bh, GFS2_METATYPE_UT)) {
brelse(bh);
goto fail;
}
for (y = 0;
y < sdp->sd_ut_per_block && slot < sdp->sd_unlinked_slots;
y++, slot++) {
struct gfs2_unlinked_tag ut;
struct gfs2_unlinked *ul;
gfs2_unlinked_tag_in(&ut, bh->b_data +
sizeof(struct gfs2_meta_header) +
y * sizeof(struct gfs2_unlinked_tag));
if (!ut.ut_inum.no_addr)
continue;
error = -ENOMEM;
ul = ul_alloc(sdp);
if (!ul) {
brelse(bh);
goto fail;
}
ul->ul_ut = ut;
ul->ul_slot = slot;
spin_lock(&sdp->sd_unlinked_spin);
gfs2_icbit_munge(sdp, sdp->sd_unlinked_bitmap, slot, 1);
spin_unlock(&sdp->sd_unlinked_spin);
ul_hash(sdp, ul);
gfs2_unlinked_put(sdp, ul);
found++;
}
brelse(bh);
dblock++;
extlen--;
}
if (found)
fs_info(sdp, "found %u unlinked inodes\n", found);
return 0;
fail:
gfs2_unlinked_cleanup(sdp);
return error;
}
/**
* gfs2_unlinked_cleanup - get rid of any extra struct gfs2_unlinked structures
* @sdp: the filesystem
*
*/
void gfs2_unlinked_cleanup(struct gfs2_sbd *sdp)
{
struct list_head *head = &sdp->sd_unlinked_list;
struct gfs2_unlinked *ul;
unsigned int x;
spin_lock(&sdp->sd_unlinked_spin);
while (!list_empty(head)) {
ul = list_entry(head->next, struct gfs2_unlinked, ul_list);
if (ul->ul_count > 1) {
list_move_tail(&ul->ul_list, head);
spin_unlock(&sdp->sd_unlinked_spin);
schedule();
spin_lock(&sdp->sd_unlinked_spin);
continue;
}
list_del_init(&ul->ul_list);
atomic_dec(&sdp->sd_unlinked_count);
gfs2_assert_warn(sdp, ul->ul_count == 1);
gfs2_assert_warn(sdp, !test_bit(ULF_LOCKED, &ul->ul_flags));
kfree(ul);
}
spin_unlock(&sdp->sd_unlinked_spin);
gfs2_assert_warn(sdp, !atomic_read(&sdp->sd_unlinked_count));
if (sdp->sd_unlinked_bitmap) {
for (x = 0; x < sdp->sd_unlinked_chunks; x++)
kfree(sdp->sd_unlinked_bitmap[x]);
kfree(sdp->sd_unlinked_bitmap);
}
}

25
fs/gfs2/unlinked.h Normal file
View File

@ -0,0 +1,25 @@
/*
* 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 __UNLINKED_DOT_H__
#define __UNLINKED_DOT_H__
int gfs2_unlinked_get(struct gfs2_sbd *sdp, struct gfs2_unlinked **ul);
void gfs2_unlinked_put(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul);
int gfs2_unlinked_ondisk_add(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul);
int gfs2_unlinked_ondisk_munge(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul);
int gfs2_unlinked_ondisk_rm(struct gfs2_sbd *sdp, struct gfs2_unlinked *ul);
int gfs2_unlinked_dealloc(struct gfs2_sbd *sdp);
int gfs2_unlinked_init(struct gfs2_sbd *sdp);
void gfs2_unlinked_cleanup(struct gfs2_sbd *sdp);
#endif /* __UNLINKED_DOT_H__ */

273
fs/gfs2/util.c Normal file
View File

@ -0,0 +1,273 @@
/*
* 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 <linux/crc32.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include "gfs2.h"
#include "glock.h"
#include "lm.h"
kmem_cache_t *gfs2_glock_cachep __read_mostly;
kmem_cache_t *gfs2_inode_cachep __read_mostly;
kmem_cache_t *gfs2_bufdata_cachep __read_mostly;
uint32_t gfs2_disk_hash(const char *data, int len)
{
return crc32_le(0xFFFFFFFF, data, len) ^ 0xFFFFFFFF;
}
void gfs2_assert_i(struct gfs2_sbd *sdp)
{
printk(KERN_EMERG "GFS2: fsid=%s: fatal assertion failed\n",
sdp->sd_fsname);
}
/**
* gfs2_assert_withdraw_i - Cause the machine to withdraw if @assertion is false
* Returns: -1 if this call withdrew the machine,
* -2 if it was already withdrawn
*/
int gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion,
const char *function, char *file, unsigned int line)
{
int me;
me = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: assertion \"%s\" failed\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname, assertion,
sdp->sd_fsname, function, file, line);
return (me) ? -1 : -2;
}
/**
* gfs2_assert_warn_i - Print a message to the console if @assertion is false
* Returns: -1 if we printed something
* -2 if we didn't
*/
int gfs2_assert_warn_i(struct gfs2_sbd *sdp, char *assertion,
const char *function, char *file, unsigned int line)
{
if (time_before(jiffies,
sdp->sd_last_warning +
gfs2_tune_get(sdp, gt_complain_secs) * HZ))
return -2;
printk(KERN_WARNING
"GFS2: fsid=%s: warning: assertion \"%s\" failed\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname, assertion,
sdp->sd_fsname, function, file, line);
if (sdp->sd_args.ar_debug)
BUG();
sdp->sd_last_warning = jiffies;
return -1;
}
/**
* gfs2_consist_i - Flag a filesystem consistency error and withdraw
* Returns: -1 if this call withdrew the machine,
* 0 if it was already withdrawn
*/
int gfs2_consist_i(struct gfs2_sbd *sdp, int cluster_wide, const char *function,
char *file, unsigned int line)
{
int rv;
rv = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: filesystem consistency error\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, function, file, line);
return rv;
}
/**
* gfs2_consist_inode_i - Flag an inode consistency error and withdraw
* Returns: -1 if this call withdrew the machine,
* 0 if it was already withdrawn
*/
int gfs2_consist_inode_i(struct gfs2_inode *ip, int cluster_wide,
const char *function, char *file, unsigned int line)
{
struct gfs2_sbd *sdp = ip->i_sbd;
int rv;
rv = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: filesystem consistency error\n"
"GFS2: fsid=%s: inode = %llu %llu\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, ip->i_num.no_formal_ino, ip->i_num.no_addr,
sdp->sd_fsname, function, file, line);
return rv;
}
/**
* gfs2_consist_rgrpd_i - Flag a RG consistency error and withdraw
* Returns: -1 if this call withdrew the machine,
* 0 if it was already withdrawn
*/
int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide,
const char *function, char *file, unsigned int line)
{
struct gfs2_sbd *sdp = rgd->rd_sbd;
int rv;
rv = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: filesystem consistency error\n"
"GFS2: fsid=%s: RG = %llu\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, rgd->rd_ri.ri_addr,
sdp->sd_fsname, function, file, line);
return rv;
}
/**
* gfs2_meta_check_ii - Flag a magic number consistency error and withdraw
* Returns: -1 if this call withdrew the machine,
* -2 if it was already withdrawn
*/
int gfs2_meta_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *type, const char *function, char *file,
unsigned int line)
{
int me;
me = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: invalid metadata block\n"
"GFS2: fsid=%s: bh = %llu (%s)\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, (uint64_t)bh->b_blocknr, type,
sdp->sd_fsname, function, file, line);
return (me) ? -1 : -2;
}
/**
* gfs2_metatype_check_ii - Flag a metadata type consistency error and withdraw
* Returns: -1 if this call withdrew the machine,
* -2 if it was already withdrawn
*/
int gfs2_metatype_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh,
uint16_t type, uint16_t t, const char *function,
char *file, unsigned int line)
{
int me;
me = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: invalid metadata block\n"
"GFS2: fsid=%s: bh = %llu (type: exp=%u, found=%u)\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, (uint64_t)bh->b_blocknr, type, t,
sdp->sd_fsname, function, file, line);
return (me) ? -1 : -2;
}
/**
* gfs2_io_error_i - Flag an I/O error and withdraw
* Returns: -1 if this call withdrew the machine,
* 0 if it was already withdrawn
*/
int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, char *file,
unsigned int line)
{
int rv;
rv = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: I/O error\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, function, file, line);
return rv;
}
/**
* gfs2_io_error_bh_i - Flag a buffer I/O error and withdraw
* Returns: -1 if this call withdrew the machine,
* 0 if it was already withdrawn
*/
int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *function, char *file, unsigned int line)
{
int rv;
rv = gfs2_lm_withdraw(sdp,
"GFS2: fsid=%s: fatal: I/O error\n"
"GFS2: fsid=%s: block = %llu\n"
"GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
sdp->sd_fsname,
sdp->sd_fsname, (uint64_t)bh->b_blocknr,
sdp->sd_fsname, function, file, line);
return rv;
}
/**
* gfs2_add_bh_to_ub - copy a buffer up to user space
* @ub: the structure representing where to copy
* @bh: the buffer
*
* Returns: errno
*/
int gfs2_add_bh_to_ub(struct gfs2_user_buffer *ub, struct buffer_head *bh)
{
uint64_t blkno = bh->b_blocknr;
if (ub->ub_count + sizeof(uint64_t) + bh->b_size > ub->ub_size)
return -ENOMEM;
if (copy_to_user(ub->ub_data + ub->ub_count,
&blkno,
sizeof(uint64_t)))
return -EFAULT;
ub->ub_count += sizeof(uint64_t);
if (copy_to_user(ub->ub_data + ub->ub_count,
bh->b_data,
bh->b_size))
return -EFAULT;
ub->ub_count += bh->b_size;
return 0;
}
void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap,
unsigned int bit, int new_value)
{
unsigned int c, o, b = bit;
int old_value;
c = b / (8 * PAGE_SIZE);
b %= 8 * PAGE_SIZE;
o = b / 8;
b %= 8;
old_value = (bitmap[c][o] & (1 << b));
gfs2_assert_withdraw(sdp, !old_value != !new_value);
if (new_value)
bitmap[c][o] |= 1 << b;
else
bitmap[c][o] &= ~(1 << b);
}

180
fs/gfs2/util.h Normal file
View File

@ -0,0 +1,180 @@
/*
* 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 __UTIL_DOT_H__
#define __UTIL_DOT_H__
uint32_t gfs2_disk_hash(const char *data, int len);
#define fs_printk(level, fs, fmt, arg...) \
printk(level "GFS2: fsid=%s: " fmt , (fs)->sd_fsname , ## arg)
#define fs_info(fs, fmt, arg...) \
fs_printk(KERN_INFO , fs , fmt , ## arg)
#define fs_warn(fs, fmt, arg...) \
fs_printk(KERN_WARNING , fs , fmt , ## arg)
#define fs_err(fs, fmt, arg...) \
fs_printk(KERN_ERR, fs , fmt , ## arg)
void gfs2_assert_i(struct gfs2_sbd *sdp);
#define gfs2_assert(sdp, assertion) \
do { \
if (unlikely(!(assertion))) { \
gfs2_assert_i(sdp); \
BUG(); \
} \
} while (0)
int gfs2_assert_withdraw_i(struct gfs2_sbd *sdp, char *assertion,
const char *function, char *file, unsigned int line);
#define gfs2_assert_withdraw(sdp, assertion) \
((likely(assertion)) ? 0 : gfs2_assert_withdraw_i((sdp), #assertion, \
__FUNCTION__, __FILE__, __LINE__))
int gfs2_assert_warn_i(struct gfs2_sbd *sdp, char *assertion,
const char *function, char *file, unsigned int line);
#define gfs2_assert_warn(sdp, assertion) \
((likely(assertion)) ? 0 : gfs2_assert_warn_i((sdp), #assertion, \
__FUNCTION__, __FILE__, __LINE__))
int gfs2_consist_i(struct gfs2_sbd *sdp, int cluster_wide,
const char *function, char *file, unsigned int line);
#define gfs2_consist(sdp) \
gfs2_consist_i((sdp), 0, __FUNCTION__, __FILE__, __LINE__)
int gfs2_consist_inode_i(struct gfs2_inode *ip, int cluster_wide,
const char *function, char *file, unsigned int line);
#define gfs2_consist_inode(ip) \
gfs2_consist_inode_i((ip), 0, __FUNCTION__, __FILE__, __LINE__)
int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide,
const char *function, char *file, unsigned int line);
#define gfs2_consist_rgrpd(rgd) \
gfs2_consist_rgrpd_i((rgd), 0, __FUNCTION__, __FILE__, __LINE__)
int gfs2_meta_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *type, const char *function,
char *file, unsigned int line);
static inline int gfs2_meta_check_i(struct gfs2_sbd *sdp,
struct buffer_head *bh,
const char *function,
char *file, unsigned int line)
{
struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data;
uint32_t magic = mh->mh_magic;
magic = be32_to_cpu(magic);
if (unlikely(magic != GFS2_MAGIC))
return gfs2_meta_check_ii(sdp, bh, "magic number", function,
file, line);
return 0;
}
#define gfs2_meta_check(sdp, bh) \
gfs2_meta_check_i((sdp), (bh), __FUNCTION__, __FILE__, __LINE__)
int gfs2_metatype_check_ii(struct gfs2_sbd *sdp, struct buffer_head *bh,
uint16_t type, uint16_t t,
const char *function,
char *file, unsigned int line);
static inline int gfs2_metatype_check_i(struct gfs2_sbd *sdp,
struct buffer_head *bh,
uint16_t type,
const char *function,
char *file, unsigned int line)
{
struct gfs2_meta_header *mh = (struct gfs2_meta_header *)bh->b_data;
uint32_t magic = mh->mh_magic;
uint16_t t = mh->mh_type;
magic = be32_to_cpu(magic);
if (unlikely(magic != GFS2_MAGIC))
return gfs2_meta_check_ii(sdp, bh, "magic number", function,
file, line);
t = be16_to_cpu(t);
if (unlikely(t != type))
return gfs2_metatype_check_ii(sdp, bh, type, t, function,
file, line);
return 0;
}
#define gfs2_metatype_check(sdp, bh, type) \
gfs2_metatype_check_i((sdp), (bh), (type), __FUNCTION__, __FILE__, __LINE__)
static inline void gfs2_metatype_set(struct buffer_head *bh, uint16_t type,
uint16_t format)
{
struct gfs2_meta_header *mh;
mh = (struct gfs2_meta_header *)bh->b_data;
mh->mh_type = cpu_to_be16(type);
mh->mh_format = cpu_to_be16(format);
}
int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function,
char *file, unsigned int line);
#define gfs2_io_error(sdp) \
gfs2_io_error_i((sdp), __FUNCTION__, __FILE__, __LINE__);
int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *function, char *file, unsigned int line);
#define gfs2_io_error_bh(sdp, bh) \
gfs2_io_error_bh_i((sdp), (bh), __FUNCTION__, __FILE__, __LINE__);
extern kmem_cache_t *gfs2_glock_cachep;
extern kmem_cache_t *gfs2_inode_cachep;
extern kmem_cache_t *gfs2_bufdata_cachep;
struct gfs2_user_buffer {
char __user *ub_data;
unsigned int ub_size;
unsigned int ub_count;
};
int gfs2_add_bh_to_ub(struct gfs2_user_buffer *ub, struct buffer_head *bh);
static inline unsigned int gfs2_tune_get_i(struct gfs2_tune *gt,
unsigned int *p)
{
unsigned int x;
spin_lock(&gt->gt_spin);
x = *p;
spin_unlock(&gt->gt_spin);
return x;
}
#define gfs2_tune_get(sdp, field) \
gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap,
unsigned int bit, int new_value);
#endif /* __UTIL_DOT_H__ */

View File

@ -0,0 +1,32 @@
/*
* 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 __GFS2_IOCTL_DOT_H__
#define __GFS2_IOCTL_DOT_H__
#define _GFS2C_(x) (('G' << 16) | ('2' << 8) | (x))
/* Ioctls implemented */
#define GFS2_IOCTL_IDENTIFY _GFS2C_(1)
#define GFS2_IOCTL_SUPER _GFS2C_(2)
#define GFS2_IOCTL_SETFLAGS _GFS2C_(3)
#define GFS2_IOCTL_GETFLAGS _GFS2C_(4)
struct gfs2_ioctl {
unsigned int gi_argc;
const char **gi_argv;
char __user *gi_data;
unsigned int gi_size;
uint64_t gi_offset;
};
#endif /* ___GFS2_IOCTL_DOT_H__ */

454
include/linux/gfs2_ondisk.h Normal file
View File

@ -0,0 +1,454 @@
/*
* 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 __GFS2_ONDISK_DOT_H__
#define __GFS2_ONDISK_DOT_H__
#define GFS2_MAGIC 0x01161970
#define GFS2_BASIC_BLOCK 512
#define GFS2_BASIC_BLOCK_SHIFT 9
/* Lock numbers of the LM_TYPE_NONDISK type */
#define GFS2_MOUNT_LOCK 0
#define GFS2_LIVE_LOCK 1
#define GFS2_TRANS_LOCK 2
#define GFS2_RENAME_LOCK 3
/* Format numbers for various metadata types */
#define GFS2_FORMAT_NONE 0
#define GFS2_FORMAT_SB 100
#define GFS2_FORMAT_RG 200
#define GFS2_FORMAT_RB 300
#define GFS2_FORMAT_DI 400
#define GFS2_FORMAT_IN 500
#define GFS2_FORMAT_LF 600
#define GFS2_FORMAT_JD 700
#define GFS2_FORMAT_LH 800
#define GFS2_FORMAT_LD 900
#define GFS2_FORMAT_LB 1000
#define GFS2_FORMAT_EA 1100
#define GFS2_FORMAT_ED 1200
#define GFS2_FORMAT_UT 1300
#define GFS2_FORMAT_QC 1400
/* These are format numbers for entities contained in files */
#define GFS2_FORMAT_RI 1500
#define GFS2_FORMAT_DE 1600
#define GFS2_FORMAT_QU 1700
/* These are part of the superblock */
#define GFS2_FORMAT_FS 1801
#define GFS2_FORMAT_MULTI 1900
/*
* An on-disk inode number
*/
#define gfs2_inum_equal(ino1, ino2) \
(((ino1)->no_formal_ino == (ino2)->no_formal_ino) && \
((ino1)->no_addr == (ino2)->no_addr))
struct gfs2_inum {
__be64 no_formal_ino;
__be64 no_addr;
};
/*
* Generic metadata head structure
* Every inplace buffer logged in the journal must start with this.
*/
#define GFS2_METATYPE_NONE 0
#define GFS2_METATYPE_SB 1
#define GFS2_METATYPE_RG 2
#define GFS2_METATYPE_RB 3
#define GFS2_METATYPE_DI 4
#define GFS2_METATYPE_IN 5
#define GFS2_METATYPE_LF 6
#define GFS2_METATYPE_JD 7
#define GFS2_METATYPE_LH 8
#define GFS2_METATYPE_LD 9
#define GFS2_METATYPE_LB 10
#define GFS2_METATYPE_EA 11
#define GFS2_METATYPE_ED 12
#define GFS2_METATYPE_UT 13
#define GFS2_METATYPE_QC 14
struct gfs2_meta_header {
__be32 mh_magic;
__be32 mh_type;
__be64 __pad0; /* Was generation number in gfs1 */
__be32 mh_format;
__be32 __pad1; /* Was incarnation number in gfs1 */
};
/*
* super-block structure
*
* It's probably good if SIZEOF_SB <= GFS2_BASIC_BLOCK (512 bytes)
*
* Order is important, need to be able to read old superblocks to do on-disk
* version upgrades.
*/
/* Address of superblock in GFS2 basic blocks */
#define GFS2_SB_ADDR 128
/* The lock number for the superblock (must be zero) */
#define GFS2_SB_LOCK 0
/* Requirement: GFS2_LOCKNAME_LEN % 8 == 0
Includes: the fencing zero at the end */
#define GFS2_LOCKNAME_LEN 64
struct gfs2_sb {
struct gfs2_meta_header sb_header;
__be32 sb_fs_format;
__be32 sb_multihost_format;
__u32 __pad0; /* Was superblock flags in gfs1 */
__be32 sb_bsize;
__be32 sb_bsize_shift;
__u32 __pad1; /* Was journal segment size in gfs1 */
struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */
struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */
struct gfs2_inum sb_root_dir;
char sb_lockproto[GFS2_LOCKNAME_LEN];
char sb_locktable[GFS2_LOCKNAME_LEN];
/* In gfs1, quota and license dinodes followed */
};
/*
* resource index structure
*/
struct gfs2_rindex {
__be64 ri_addr; /* grp block disk address */
__be32 ri_length; /* length of rgrp header in fs blocks */
__u32 __pad;
__be64 ri_data0; /* first data location */
__be32 ri_data; /* num of data blocks in rgrp */
__be32 ri_bitbytes; /* number of bytes in data bitmaps */
__u8 ri_reserved[64];
};
/*
* resource group header structure
*/
/* Number of blocks per byte in rgrp */
#define GFS2_NBBY 4
#define GFS2_BIT_SIZE 2
#define GFS2_BIT_MASK 0x00000003
#define GFS2_BLKST_FREE 0
#define GFS2_BLKST_USED 1
#define GFS2_BLKST_INVALID 2
#define GFS2_BLKST_DINODE 3
#define GFS2_RGF_JOURNAL 0x00000001
#define GFS2_RGF_METAONLY 0x00000002
#define GFS2_RGF_DATAONLY 0x00000004
#define GFS2_RGF_NOALLOC 0x00000008
struct gfs2_rgrp {
struct gfs2_meta_header rg_header;
__be32 rg_flags;
__be32 rg_free;
__be32 rg_dinodes;
__u8 rg_reserved[92]; /* Several fields from gfs1 now reserved */
};
/*
* quota structure
*/
struct gfs2_quota {
__be64 qu_limit;
__be64 qu_warn;
__be64 qu_value;
};
/*
* dinode structure
*/
#define GFS2_MAX_META_HEIGHT 10
#define GFS2_DIR_MAX_DEPTH 17
#define DT2IF(dt) (((dt) << 12) & S_IFMT)
#define IF2DT(sif) (((sif) & S_IFMT) >> 12)
/* Dinode flags */
#define GFS2_DIF_JDATA 0x00000001
#define GFS2_DIF_EXHASH 0x00000002
#define GFS2_DIF_UNUSED 0x00000004 /* only in gfs1 */
#define GFS2_DIF_EA_INDIRECT 0x00000008
#define GFS2_DIF_DIRECTIO 0x00000010
#define GFS2_DIF_IMMUTABLE 0x00000020
#define GFS2_DIF_APPENDONLY 0x00000040
#define GFS2_DIF_NOATIME 0x00000080
#define GFS2_DIF_SYNC 0x00000100
#define GFS2_DIF_SYSTEM 0x00000200 /* New in gfs2 */
#define GFS2_DIF_TRUNC_IN_PROG 0x20000000 /* New in gfs2 */
#define GFS2_DIF_INHERIT_DIRECTIO 0x40000000
#define GFS2_DIF_INHERIT_JDATA 0x80000000
struct gfs2_dinode {
struct gfs2_meta_header di_header;
struct gfs2_inum di_num;
__be32 di_mode; /* mode of file */
__be32 di_uid; /* owner's user id */
__be32 di_gid; /* owner's group id */
__be32 di_nlink; /* number of links to this file */
__be64 di_size; /* number of bytes in file */
__be64 di_blocks; /* number of blocks in file */
__be64 di_atime; /* time last accessed */
__be64 di_mtime; /* time last modified */
__be64 di_ctime; /* time last changed */
__be32 di_major; /* device major number */
__be32 di_minor; /* device minor number */
/* This section varies from gfs1. Padding added to align with
* remainder of dinode
*/
__be64 di_goal_meta; /* rgrp to alloc from next */
__be64 di_goal_data; /* data block goal */
__u32 __pad[2];
__be32 di_flags; /* GFS2_DIF_... */
__be32 di_payload_format; /* GFS2_FORMAT_... */
__u16 __pad1; /* Was ditype in gfs1 */
__be16 di_height; /* height of metadata */
__u32 __pad2; /* Unused incarnation number from gfs1 */
/* These only apply to directories */
__u16 __pad3; /* Padding */
__be16 di_depth; /* Number of bits in the table */
__be32 di_entries; /* The number of entries in the directory */
struct gfs2_inum __pad4; /* Unused even in current gfs1 */
__be64 di_eattr; /* extended attribute block number */
__u8 di_reserved[56];
};
/*
* directory structure - many of these per directory file
*/
#define GFS2_FNAMESIZE 255
#define GFS2_DIRENT_SIZE(name_len) ((sizeof(struct gfs2_dirent) + (name_len) + 7) & ~7)
struct gfs2_dirent {
struct gfs2_inum de_inum;
__be32 de_hash;
__be32 de_rec_len;
__u8 de_name_len;
__u8 de_type;
__u16 __pad1;
__u32 __pad2;
};
/*
* Header of leaf directory nodes
*/
struct gfs2_leaf {
struct gfs2_meta_header lf_header;
__be16 lf_depth; /* Depth of leaf */
__be16 lf_entries; /* Number of dirents in leaf */
__be32 lf_dirent_format; /* Format of the dirents */
__be64 lf_next; /* Next leaf, if overflow */
__u8 lf_reserved[32];
};
/*
* Extended attribute header format
*/
#define GFS2_EA_MAX_NAME_LEN 255
#define GFS2_EA_MAX_DATA_LEN 65536
#define GFS2_EATYPE_UNUSED 0
#define GFS2_EATYPE_USR 1
#define GFS2_EATYPE_SYS 2
#define GFS2_EATYPE_LAST 2
#define GFS2_EATYPE_VALID(x) ((x) <= GFS2_EATYPE_LAST)
#define GFS2_EAFLAG_LAST 0x01 /* last ea in block */
struct gfs2_ea_header {
__be32 ea_rec_len;
__be32 ea_data_len;
__u8 ea_name_len; /* no NULL pointer after the string */
__u8 ea_type; /* GFS2_EATYPE_... */
__u8 ea_flags; /* GFS2_EAFLAG_... */
__u8 ea_num_ptrs;
__u32 __pad;
};
/*
* Log header structure
*/
#define GFS2_LOG_HEAD_UNMOUNT 0x00000001 /* log is clean */
struct gfs2_log_header {
struct gfs2_meta_header lh_header;
__be64 lh_sequence; /* Sequence number of this transaction */
__be32 lh_flags; /* GFS2_LOG_HEAD_... */
__be32 lh_tail; /* Block number of log tail */
__be32 lh_blkno;
__be32 lh_hash;
};
/*
* Log type descriptor
*/
#define GFS2_LOG_DESC_METADATA 300
/* ld_data1 is the number of metadata blocks in the descriptor.
ld_data2 is unused. */
#define GFS2_LOG_DESC_REVOKE 301
/* ld_data1 is the number of revoke blocks in the descriptor.
ld_data2 is unused. */
struct gfs2_log_descriptor {
struct gfs2_meta_header ld_header;
__be32 ld_type; /* GFS2_LOG_DESC_... */
__be32 ld_length; /* Number of buffers in this chunk */
__be32 ld_data1; /* descriptor-specific field */
__be32 ld_data2; /* descriptor-specific field */
__u8 ld_reserved[32];
};
/*
* Inum Range
* Describe a range of formal inode numbers allocated to
* one machine to assign to inodes.
*/
#define GFS2_INUM_QUANTUM 1048576
struct gfs2_inum_range {
__be64 ir_start;
__be64 ir_length;
};
/*
* Statfs change
* Describes an change to the pool of free and allocated
* blocks.
*/
struct gfs2_statfs_change {
__be64 sc_total;
__be64 sc_free;
__be64 sc_dinodes;
};
/*
* Unlinked Tag
* Describes an allocated inode that isn't linked into
* the directory tree and might need to be deallocated.
*/
#define GFS2_UTF_UNINIT 0x00000001
struct gfs2_unlinked_tag {
struct gfs2_inum ut_inum;
__be32 ut_flags; /* GFS2_UTF_... */
__u32 __pad;
};
/*
* Quota change
* Describes an allocation change for a particular
* user or group.
*/
#define GFS2_QCF_USER 0x00000001
struct gfs2_quota_change {
__be64 qc_change;
__be32 qc_flags; /* GFS2_QCF_... */
__be32 qc_id;
};
/* Translation functions */
extern void gfs2_inum_in(struct gfs2_inum *no, char *buf);
extern void gfs2_inum_out(struct gfs2_inum *no, char *buf);
extern void gfs2_meta_header_in(struct gfs2_meta_header *mh, char *buf);
extern void gfs2_meta_header_out(struct gfs2_meta_header *mh, char *buf);
extern void gfs2_sb_in(struct gfs2_sb *sb, char *buf);
extern void gfs2_sb_out(struct gfs2_sb *sb, char *buf);
extern void gfs2_rindex_in(struct gfs2_rindex *ri, char *buf);
extern void gfs2_rindex_out(struct gfs2_rindex *ri, char *buf);
extern void gfs2_rgrp_in(struct gfs2_rgrp *rg, char *buf);
extern void gfs2_rgrp_out(struct gfs2_rgrp *rg, char *buf);
extern void gfs2_quota_in(struct gfs2_quota *qu, char *buf);
extern void gfs2_quota_out(struct gfs2_quota *qu, char *buf);
extern void gfs2_dinode_in(struct gfs2_dinode *di, char *buf);
extern void gfs2_dinode_out(struct gfs2_dinode *di, char *buf);
extern void gfs2_dirent_in(struct gfs2_dirent *de, char *buf);
extern void gfs2_dirent_out(struct gfs2_dirent *de, char *buf);
extern void gfs2_leaf_in(struct gfs2_leaf *lf, char *buf);
extern void gfs2_leaf_out(struct gfs2_leaf *lf, char *buf);
extern void gfs2_ea_header_in(struct gfs2_ea_header *ea, char *buf);
extern void gfs2_ea_header_out(struct gfs2_ea_header *ea, char *buf);
extern void gfs2_log_header_in(struct gfs2_log_header *lh, char *buf);
extern void gfs2_inum_range_in(struct gfs2_inum_range *ir, char *buf);
extern void gfs2_inum_range_out(struct gfs2_inum_range *ir, char *buf);
extern void gfs2_statfs_change_in(struct gfs2_statfs_change *sc, char *buf);
extern void gfs2_statfs_change_out(struct gfs2_statfs_change *sc, char *buf);
extern void gfs2_unlinked_tag_in(struct gfs2_unlinked_tag *ut, char *buf);
extern void gfs2_unlinked_tag_out(struct gfs2_unlinked_tag *ut, char *buf);
extern void gfs2_quota_change_in(struct gfs2_quota_change *qc, char *buf);
extern void gfs2_quota_change_out(struct gfs2_quota_change *qc, char *buf);
/* Printing functions */
extern void gfs2_inum_print(struct gfs2_inum *no);
extern void gfs2_meta_header_print(struct gfs2_meta_header *mh);
extern void gfs2_sb_print(struct gfs2_sb *sb);
extern void gfs2_rindex_print(struct gfs2_rindex *ri);
extern void gfs2_rgrp_print(struct gfs2_rgrp *rg);
extern void gfs2_quota_print(struct gfs2_quota *qu);
extern void gfs2_dinode_print(struct gfs2_dinode *di);
extern void gfs2_dirent_print(struct gfs2_dirent *de, char *name);
extern void gfs2_leaf_print(struct gfs2_leaf *lf);
extern void gfs2_ea_header_print(struct gfs2_ea_header *ea, char *name);
extern void gfs2_log_header_print(struct gfs2_log_header *lh);
extern void gfs2_log_descriptor_print(struct gfs2_log_descriptor *ld);
extern void gfs2_inum_range_print(struct gfs2_inum_range *ir);
extern void gfs2_statfs_change_print(struct gfs2_statfs_change *sc);
extern void gfs2_unlinked_tag_print(struct gfs2_unlinked_tag *ut);
extern void gfs2_quota_change_print(struct gfs2_quota_change *qc);
#endif /* __GFS2_ONDISK_DOT_H__ */