mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
[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:
parent
f7825dcf8c
commit
b3b94faa5f
46
fs/gfs2/Kconfig
Normal file
46
fs/gfs2/Kconfig
Normal 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
44
fs/gfs2/Makefile
Normal 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
312
fs/gfs2/acl.c
Normal 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
37
fs/gfs2/acl.h
Normal 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
178
fs/gfs2/bits.c
Normal 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
28
fs/gfs2/bits.h
Normal 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
1206
fs/gfs2/bmap.c
Normal file
File diff suppressed because it is too large
Load Diff
39
fs/gfs2/bmap.h
Normal file
39
fs/gfs2/bmap.h
Normal 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
225
fs/gfs2/daemon.c
Normal 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
20
fs/gfs2/daemon.h
Normal 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
2157
fs/gfs2/dir.c
Normal file
File diff suppressed because it is too large
Load Diff
51
fs/gfs2/dir.h
Normal file
51
fs/gfs2/dir.h
Normal 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
185
fs/gfs2/eaops.c
Normal 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
30
fs/gfs2/eaops.h
Normal 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
1620
fs/gfs2/eattr.c
Normal file
File diff suppressed because it is too large
Load Diff
90
fs/gfs2/eattr.h
Normal file
90
fs/gfs2/eattr.h
Normal 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
21
fs/gfs2/format.h
Normal 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
62
fs/gfs2/gfs2.h
Normal 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
2513
fs/gfs2/glock.c
Normal file
File diff suppressed because it is too large
Load Diff
143
fs/gfs2/glock.h
Normal file
143
fs/gfs2/glock.h
Normal 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
487
fs/gfs2/glops.c
Normal 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
23
fs/gfs2/glops.h
Normal 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
703
fs/gfs2/incore.h
Normal 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
1805
fs/gfs2/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
74
fs/gfs2/inode.h
Normal file
74
fs/gfs2/inode.h
Normal 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
382
fs/gfs2/jdata.c
Normal 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
52
fs/gfs2/jdata.h
Normal 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
235
fs/gfs2/lm.c
Normal 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
42
fs/gfs2/lm.h
Normal 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
295
fs/gfs2/lm_interface.h
Normal 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
192
fs/gfs2/locking.c
Normal 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
659
fs/gfs2/log.c
Normal 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
68
fs/gfs2/log.h
Normal 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
534
fs/gfs2/lops.c
Normal 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
96
fs/gfs2/lops.h
Normal 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
48
fs/gfs2/lvb.c
Normal 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
28
fs/gfs2/lvb.h
Normal 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
103
fs/gfs2/main.c
Normal 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
876
fs/gfs2/meta_io.c
Normal 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
88
fs/gfs2/meta_io.h
Normal 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
211
fs/gfs2/mount.c
Normal 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
15
fs/gfs2/mount.h
Normal 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
590
fs/gfs2/ondisk.c
Normal 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
515
fs/gfs2/ops_address.c
Normal 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
15
fs/gfs2/ops_address.h
Normal 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
117
fs/gfs2/ops_dentry.c
Normal 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
15
fs/gfs2/ops_dentry.h
Normal 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
310
fs/gfs2/ops_export.c
Normal 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
15
fs/gfs2/ops_export.h
Normal 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
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
16
fs/gfs2/ops_file.h
Normal 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
879
fs/gfs2/ops_fstype.c
Normal 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
15
fs/gfs2/ops_fstype.h
Normal 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
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
18
fs/gfs2/ops_inode.h
Normal 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
401
fs/gfs2/ops_super.c
Normal 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
15
fs/gfs2/ops_super.h
Normal 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
199
fs/gfs2/ops_vm.c
Normal 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
16
fs/gfs2/ops_vm.h
Normal 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
273
fs/gfs2/page.c
Normal 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
23
fs/gfs2/page.h
Normal 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
1238
fs/gfs2/quota.c
Normal file
File diff suppressed because it is too large
Load Diff
34
fs/gfs2/quota.h
Normal file
34
fs/gfs2/quota.h
Normal 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
570
fs/gfs2/recovery.c
Normal 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
32
fs/gfs2/recovery.h
Normal 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
291
fs/gfs2/resize.c
Normal 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
19
fs/gfs2/resize.h
Normal 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
1361
fs/gfs2/rgrp.c
Normal file
File diff suppressed because it is too large
Load Diff
62
fs/gfs2/rgrp.h
Normal file
62
fs/gfs2/rgrp.h
Normal 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
944
fs/gfs2/super.c
Normal 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_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
55
fs/gfs2/super.h
Normal 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
640
fs/gfs2/sys.c
Normal 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_spin);
|
||||
gt->gt_quota_scale_num = x;
|
||||
gt->gt_quota_scale_den = y;
|
||||
spin_unlock(>->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_spin);
|
||||
*field = x;
|
||||
spin_unlock(>->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
24
fs/gfs2/sys.h
Normal 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
214
fs/gfs2/trans.c
Normal 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
40
fs/gfs2/trans.h
Normal 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
453
fs/gfs2/unlinked.c
Normal 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
25
fs/gfs2/unlinked.h
Normal 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
273
fs/gfs2/util.c
Normal 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
180
fs/gfs2/util.h
Normal 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_spin);
|
||||
x = *p;
|
||||
spin_unlock(>->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__ */
|
||||
|
32
include/linux/gfs2_ioctl.h
Normal file
32
include/linux/gfs2_ioctl.h
Normal 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
454
include/linux/gfs2_ondisk.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user