![Steven Whitehouse](/assets/img/avatar_default.png)
This patch fixes the way we have been dealing with unlinked, but still open files. It removes all limits (other than memory for inodes, as per every other filesystem) on numbers of these which we can support on GFS2. It also means that (like other fs) its the responsibility of the last process to close the file to deallocate the storage, rather than the person who did the unlinking. Note that with GFS2, those two events might take place on different nodes. Also there are a number of other changes: o We use the Linux inode subsystem as it was intended to be used, wrt allocating GFS2 inodes o The Linux inode cache is now the point which we use for local enforcement of only holding one copy of the inode in core at once (previous to this we used the glock layer). o We no longer use the unlinked "special" file. We just ignore it completely. This makes unlinking more efficient. o We now use the 4th block allocation state. The previously unused state is used to track unlinked but still open inodes. o gfs2_inoded is no longer needed o Several fields are now no longer needed (and removed) from the in core struct gfs2_inode o Several fields are no longer needed (and removed) from the in core superblock There are a number of future possible optimisations and clean ups which have been made possible by this patch. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
929 lines
21 KiB
C
929 lines
21 KiB
C
/*
|
|
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
|
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU General Public License 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 <linux/gfs2_ondisk.h>
|
|
|
|
#include "gfs2.h"
|
|
#include "lm_interface.h"
|
|
#include "incore.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 "util.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_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(KERN_WARNING "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(KERN_WARNING
|
|
"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(KERN_WARNING
|
|
"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(KERN_WARNING
|
|
"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(KERN_WARNING
|
|
"GFS2: I don't know how to upgrade this FS\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (!sdp->sd_args.ar_upgrade) {
|
|
printk(KERN_WARNING
|
|
"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(KERN_INFO
|
|
"GFS2: Use the \"upgrade\" mount option to upgrade "
|
|
"the FS\n");
|
|
printk(KERN_INFO "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_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_ROUND_UP(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_ROUND_UP(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;
|
|
}
|
|
|
|
/**
|
|
* 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 = GFS2_I(sdp->sd_jindex);
|
|
struct qstr name;
|
|
char buf[20];
|
|
struct gfs2_jdesc *jd;
|
|
int error;
|
|
|
|
name.name = buf;
|
|
|
|
mutex_lock(&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);
|
|
name.hash = gfs2_disk_hash(name.name, name.len);
|
|
|
|
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;
|
|
|
|
jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1, NULL);
|
|
if (!jd->jd_inode || IS_ERR(jd->jd_inode)) {
|
|
if (!jd->jd_inode)
|
|
error = -ENOENT;
|
|
else
|
|
error = PTR_ERR(jd->jd_inode);
|
|
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);
|
|
}
|
|
|
|
mutex_unlock(&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);
|
|
iput(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 = GFS2_I(jd->jd_inode);
|
|
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 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_inode *ip = GFS2_I(sdp->sd_jdesc->jd_inode);
|
|
struct gfs2_glock *j_gl = ip->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, &t_gh);
|
|
if (error)
|
|
return error;
|
|
|
|
gfs2_meta_cache_flush(ip);
|
|
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_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:
|
|
|
|
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_quota_sync(sdp);
|
|
gfs2_statfs_sync(sdp);
|
|
|
|
error = gfs2_glock_nq_init(sdp->sd_trans_gl, LM_ST_SHARED,
|
|
GL_LOCAL_EXCL | 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_quota_cleanup(sdp);
|
|
|
|
return error;
|
|
}
|
|
|
|
int gfs2_statfs_init(struct gfs2_sbd *sdp)
|
|
{
|
|
struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
|
|
struct gfs2_statfs_change *m_sc = &sdp->sd_statfs_master;
|
|
struct gfs2_inode *l_ip = GFS2_I(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 = GFS2_I(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;
|
|
|
|
mutex_lock(&sdp->sd_statfs_mutex);
|
|
gfs2_trans_add_bh(l_ip->i_gl, l_bh, 1);
|
|
mutex_unlock(&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 = GFS2_I(sdp->sd_statfs_inode);
|
|
struct gfs2_inode *l_ip = GFS2_I(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;
|
|
|
|
mutex_lock(&sdp->sd_statfs_mutex);
|
|
gfs2_trans_add_bh(l_ip->i_gl, l_bh, 1);
|
|
mutex_unlock(&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, 1);
|
|
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(
|
|
gh->gh_gl->gl_object, 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
|
|
*/
|
|
|
|
static int gfs2_lock_fs_check_clean(struct gfs2_sbd *sdp,
|
|
struct gfs2_holder *t_gh)
|
|
{
|
|
struct gfs2_inode *ip;
|
|
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;
|
|
}
|
|
ip = GFS2_I(jd->jd_inode);
|
|
error = gfs2_glock_nq_init(ip->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_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;
|
|
|
|
mutex_lock(&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--;
|
|
}
|
|
|
|
mutex_unlock(&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)
|
|
{
|
|
mutex_lock(&sdp->sd_freeze_lock);
|
|
|
|
if (sdp->sd_freeze_count && !--sdp->sd_freeze_count)
|
|
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
|
|
|
|
mutex_unlock(&sdp->sd_freeze_lock);
|
|
}
|
|
|