Commit 6c1b8d94 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-2.6-nmw

* git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-2.6-nmw: (32 commits)
  GFS2: Move all locking inside the inode creation function
  GFS2: Clean up symlink creation
  GFS2: Clean up mkdir
  GFS2: Use UUID field in generic superblock
  GFS2: Rename ops_inode.c to inode.c
  GFS2: Inode.c is empty now, remove it
  GFS2: Move final part of inode.c into super.c
  GFS2: Move most of the remaining inode.c into ops_inode.c
  GFS2: Move gfs2_refresh_inode() and friends into glops.c
  GFS2: Remove gfs2_dinode_print() function
  GFS2: When adding a new dir entry, inc link count if it is a subdir
  GFS2: Make gfs2_dir_del update link count when required
  GFS2: Don't use gfs2_change_nlink in link syscall
  GFS2: Don't use a try lock when promoting to a higher mode
  GFS2: Double check link count under glock
  GFS2: Improve bug trap code in ->releasepage()
  GFS2: Fix ail list traversal
  GFS2: make sure fallocate bytes is a multiple of blksize
  GFS2: Add an AIL writeback tracepoint
  GFS2: Make writeback more responsive to system conditions
  ...
parents 82aff107 f2741d98
ccflags-y := -I$(src) ccflags-y := -I$(src)
obj-$(CONFIG_GFS2_FS) += gfs2.o obj-$(CONFIG_GFS2_FS) += gfs2.o
gfs2-y := acl.o bmap.o dir.o xattr.o glock.o \ gfs2-y := acl.o bmap.o dir.o xattr.o glock.o \
glops.o inode.o log.o lops.o main.o meta_io.o \ glops.o log.o lops.o main.o meta_io.o \
aops.o dentry.o export.o file.o \ aops.o dentry.o export.o file.o \
ops_fstype.o ops_inode.o quota.o \ ops_fstype.o inode.o quota.o \
recovery.o rgrp.o super.o sys.o trans.o util.o recovery.o rgrp.o super.o sys.o trans.o util.o
gfs2-$(CONFIG_GFS2_FS_LOCKING_DLM) += lock_dlm.o gfs2-$(CONFIG_GFS2_FS_LOCKING_DLM) += lock_dlm.o
......
...@@ -1076,8 +1076,8 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask) ...@@ -1076,8 +1076,8 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
bd = bh->b_private; bd = bh->b_private;
if (bd && bd->bd_ail) if (bd && bd->bd_ail)
goto cannot_release; goto cannot_release;
gfs2_assert_warn(sdp, !buffer_pinned(bh)); if (buffer_pinned(bh) || buffer_dirty(bh))
gfs2_assert_warn(sdp, !buffer_dirty(bh)); goto not_possible;
bh = bh->b_this_page; bh = bh->b_this_page;
} while(bh != head); } while(bh != head);
gfs2_log_unlock(sdp); gfs2_log_unlock(sdp);
...@@ -1107,6 +1107,10 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask) ...@@ -1107,6 +1107,10 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
} while (bh != head); } while (bh != head);
return try_to_free_buffers(page); return try_to_free_buffers(page);
not_possible: /* Should never happen */
WARN_ON(buffer_dirty(bh));
WARN_ON(buffer_pinned(bh));
cannot_release: cannot_release:
gfs2_log_unlock(sdp); gfs2_log_unlock(sdp);
return 0; return 0;
......
...@@ -82,12 +82,9 @@ ...@@ -82,12 +82,9 @@
struct qstr gfs2_qdot __read_mostly; struct qstr gfs2_qdot __read_mostly;
struct qstr gfs2_qdotdot __read_mostly; struct qstr gfs2_qdotdot __read_mostly;
typedef int (*leaf_call_t) (struct gfs2_inode *dip, u32 index, u32 len,
u64 leaf_no, void *data);
typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent, typedef int (*gfs2_dscan_t)(const struct gfs2_dirent *dent,
const struct qstr *name, void *opaque); const struct qstr *name, void *opaque);
int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block, int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block,
struct buffer_head **bhp) struct buffer_head **bhp)
{ {
...@@ -1600,7 +1597,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name) ...@@ -1600,7 +1597,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
*/ */
int gfs2_dir_add(struct inode *inode, const struct qstr *name, int gfs2_dir_add(struct inode *inode, const struct qstr *name,
const struct gfs2_inode *nip, unsigned type) const struct gfs2_inode *nip)
{ {
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
struct buffer_head *bh; struct buffer_head *bh;
...@@ -1616,7 +1613,7 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, ...@@ -1616,7 +1613,7 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
return PTR_ERR(dent); return PTR_ERR(dent);
dent = gfs2_init_dirent(inode, dent, name, bh); dent = gfs2_init_dirent(inode, dent, name, bh);
gfs2_inum_out(nip, dent); gfs2_inum_out(nip, dent);
dent->de_type = cpu_to_be16(type); dent->de_type = cpu_to_be16(IF2DT(nip->i_inode.i_mode));
if (ip->i_diskflags & GFS2_DIF_EXHASH) { if (ip->i_diskflags & GFS2_DIF_EXHASH) {
leaf = (struct gfs2_leaf *)bh->b_data; leaf = (struct gfs2_leaf *)bh->b_data;
be16_add_cpu(&leaf->lf_entries, 1); be16_add_cpu(&leaf->lf_entries, 1);
...@@ -1628,6 +1625,8 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, ...@@ -1628,6 +1625,8 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
gfs2_trans_add_bh(ip->i_gl, bh, 1); gfs2_trans_add_bh(ip->i_gl, bh, 1);
ip->i_entries++; ip->i_entries++;
ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
if (S_ISDIR(nip->i_inode.i_mode))
inc_nlink(&ip->i_inode);
gfs2_dinode_out(ip, bh->b_data); gfs2_dinode_out(ip, bh->b_data);
brelse(bh); brelse(bh);
error = 0; error = 0;
...@@ -1672,8 +1671,9 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name, ...@@ -1672,8 +1671,9 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
* Returns: 0 on success, error code on failure * Returns: 0 on success, error code on failure
*/ */
int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
{ {
const struct qstr *name = &dentry->d_name;
struct gfs2_dirent *dent, *prev = NULL; struct gfs2_dirent *dent, *prev = NULL;
struct buffer_head *bh; struct buffer_head *bh;
int error; int error;
...@@ -1714,6 +1714,8 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name) ...@@ -1714,6 +1714,8 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *name)
gfs2_trans_add_bh(dip->i_gl, bh, 1); gfs2_trans_add_bh(dip->i_gl, bh, 1);
dip->i_entries--; dip->i_entries--;
dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME;
if (S_ISDIR(dentry->d_inode->i_mode))
drop_nlink(&dip->i_inode);
gfs2_dinode_out(dip, bh->b_data); gfs2_dinode_out(dip, bh->b_data);
brelse(bh); brelse(bh);
mark_inode_dirty(&dip->i_inode); mark_inode_dirty(&dip->i_inode);
...@@ -1767,95 +1769,21 @@ int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, ...@@ -1767,95 +1769,21 @@ int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
return 0; return 0;
} }
/**
* foreach_leaf - call a function for each leaf in a directory
* @dip: the directory
* @lc: the function to call for each each
* @data: private data to pass to it
*
* Returns: errno
*/
static int foreach_leaf(struct gfs2_inode *dip, leaf_call_t lc, void *data)
{
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct buffer_head *bh;
struct gfs2_leaf *leaf;
u32 hsize, len;
u32 ht_offset, lp_offset, ht_offset_cur = -1;
u32 index = 0;
__be64 *lp;
u64 leaf_no;
int error = 0;
hsize = 1 << dip->i_depth;
if (hsize * sizeof(u64) != i_size_read(&dip->i_inode)) {
gfs2_consist_inode(dip);
return -EIO;
}
lp = kmalloc(sdp->sd_hash_bsize, GFP_NOFS);
if (!lp)
return -ENOMEM;
while (index < hsize) {
lp_offset = index & (sdp->sd_hash_ptrs - 1);
ht_offset = index - lp_offset;
if (ht_offset_cur != ht_offset) {
error = gfs2_dir_read_data(dip, (char *)lp,
ht_offset * sizeof(__be64),
sdp->sd_hash_bsize, 1);
if (error != sdp->sd_hash_bsize) {
if (error >= 0)
error = -EIO;
goto out;
}
ht_offset_cur = ht_offset;
}
leaf_no = be64_to_cpu(lp[lp_offset]);
if (leaf_no) {
error = get_leaf(dip, leaf_no, &bh);
if (error)
goto out;
leaf = (struct gfs2_leaf *)bh->b_data;
len = 1 << (dip->i_depth - be16_to_cpu(leaf->lf_depth));
brelse(bh);
error = lc(dip, index, len, leaf_no, data);
if (error)
goto out;
index = (index & ~(len - 1)) + len;
} else
index++;
}
if (index != hsize) {
gfs2_consist_inode(dip);
error = -EIO;
}
out:
kfree(lp);
return error;
}
/** /**
* leaf_dealloc - Deallocate a directory leaf * leaf_dealloc - Deallocate a directory leaf
* @dip: the directory * @dip: the directory
* @index: the hash table offset in the directory * @index: the hash table offset in the directory
* @len: the number of pointers to this leaf * @len: the number of pointers to this leaf
* @leaf_no: the leaf number * @leaf_no: the leaf number
* @data: not used * @leaf_bh: buffer_head for the starting leaf
* last_dealloc: 1 if this is the final dealloc for the leaf, else 0
* *
* Returns: errno * Returns: errno
*/ */
static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
u64 leaf_no, void *data) u64 leaf_no, struct buffer_head *leaf_bh,
int last_dealloc)
{ {
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct gfs2_leaf *tmp_leaf; struct gfs2_leaf *tmp_leaf;
...@@ -1887,14 +1815,18 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, ...@@ -1887,14 +1815,18 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
goto out_qs; goto out_qs;
/* Count the number of leaves */ /* Count the number of leaves */
bh = leaf_bh;
for (blk = leaf_no; blk; blk = nblk) { for (blk = leaf_no; blk; blk = nblk) {
error = get_leaf(dip, blk, &bh); if (blk != leaf_no) {
if (error) error = get_leaf(dip, blk, &bh);
goto out_rlist; if (error)
goto out_rlist;
}
tmp_leaf = (struct gfs2_leaf *)bh->b_data; tmp_leaf = (struct gfs2_leaf *)bh->b_data;
nblk = be64_to_cpu(tmp_leaf->lf_next); nblk = be64_to_cpu(tmp_leaf->lf_next);
brelse(bh); if (blk != leaf_no)
brelse(bh);
gfs2_rlist_add(sdp, &rlist, blk); gfs2_rlist_add(sdp, &rlist, blk);
l_blocks++; l_blocks++;
...@@ -1918,13 +1850,18 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, ...@@ -1918,13 +1850,18 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
if (error) if (error)
goto out_rg_gunlock; goto out_rg_gunlock;
bh = leaf_bh;
for (blk = leaf_no; blk; blk = nblk) { for (blk = leaf_no; blk; blk = nblk) {
error = get_leaf(dip, blk, &bh); if (blk != leaf_no) {
if (error) error = get_leaf(dip, blk, &bh);
goto out_end_trans; if (error)
goto out_end_trans;
}
tmp_leaf = (struct gfs2_leaf *)bh->b_data; tmp_leaf = (struct gfs2_leaf *)bh->b_data;
nblk = be64_to_cpu(tmp_leaf->lf_next); nblk = be64_to_cpu(tmp_leaf->lf_next);
brelse(bh); if (blk != leaf_no)
brelse(bh);
gfs2_free_meta(dip, blk, 1); gfs2_free_meta(dip, blk, 1);
gfs2_add_inode_blocks(&dip->i_inode, -1); gfs2_add_inode_blocks(&dip->i_inode, -1);
...@@ -1942,6 +1879,10 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len, ...@@ -1942,6 +1879,10 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
goto out_end_trans; goto out_end_trans;
gfs2_trans_add_bh(dip->i_gl, dibh, 1); gfs2_trans_add_bh(dip->i_gl, dibh, 1);
/* On the last dealloc, make this a regular file in case we crash.
(We don't want to free these blocks a second time.) */
if (last_dealloc)
dip->i_inode.i_mode = S_IFREG;
gfs2_dinode_out(dip, dibh->b_data); gfs2_dinode_out(dip, dibh->b_data);
brelse(dibh); brelse(dibh);
...@@ -1975,29 +1916,67 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip) ...@@ -1975,29 +1916,67 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip)
{ {
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct buffer_head *bh; struct buffer_head *bh;
int error; struct gfs2_leaf *leaf;
u32 hsize, len;
u32 ht_offset, lp_offset, ht_offset_cur = -1;
u32 index = 0, next_index;
__be64 *lp;
u64 leaf_no;
int error = 0, last;
/* Dealloc on-disk leaves to FREEMETA state */ hsize = 1 << dip->i_depth;
error = foreach_leaf(dip, leaf_dealloc, NULL); if (hsize * sizeof(u64) != i_size_read(&dip->i_inode)) {
if (error) gfs2_consist_inode(dip);
return error; return -EIO;
}
/* Make this a regular file in case we crash. lp = kmalloc(sdp->sd_hash_bsize, GFP_NOFS);
(We don't want to free these blocks a second time.) */ if (!lp)
return -ENOMEM;
error = gfs2_trans_begin(sdp, RES_DINODE, 0); while (index < hsize) {
if (error) lp_offset = index & (sdp->sd_hash_ptrs - 1);
return error; ht_offset = index - lp_offset;
error = gfs2_meta_inode_buffer(dip, &bh); if (ht_offset_cur != ht_offset) {
if (!error) { error = gfs2_dir_read_data(dip, (char *)lp,
gfs2_trans_add_bh(dip->i_gl, bh, 1); ht_offset * sizeof(__be64),
((struct gfs2_dinode *)bh->b_data)->di_mode = sdp->sd_hash_bsize, 1);
cpu_to_be32(S_IFREG); if (error != sdp->sd_hash_bsize) {
brelse(bh); if (error >= 0)
error = -EIO;
goto out;
}
ht_offset_cur = ht_offset;
}
leaf_no = be64_to_cpu(lp[lp_offset]);
if (leaf_no) {
error = get_leaf(dip, leaf_no, &bh);
if (error)
goto out;
leaf = (struct gfs2_leaf *)bh->b_data;
len = 1 << (dip->i_depth - be16_to_cpu(leaf->lf_depth));
next_index = (index & ~(len - 1)) + len;
last = ((next_index >= hsize) ? 1 : 0);
error = leaf_dealloc(dip, index, len, leaf_no, bh,
last);
brelse(bh);
if (error)
goto out;
index = next_index;
} else
index++;
} }
gfs2_trans_end(sdp); if (index != hsize) {
gfs2_consist_inode(dip);
error = -EIO;
}
out:
kfree(lp);
return error; return error;
} }
......
...@@ -22,8 +22,8 @@ extern struct inode *gfs2_dir_search(struct inode *dir, ...@@ -22,8 +22,8 @@ extern struct inode *gfs2_dir_search(struct inode *dir,
extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename, extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename,
const struct gfs2_inode *ip); const struct gfs2_inode *ip);
extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename, extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename,
const struct gfs2_inode *ip, unsigned int type); const struct gfs2_inode *ip);
extern int gfs2_dir_del(struct gfs2_inode *dip, const struct qstr *filename); extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry);
extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque, extern int gfs2_dir_read(struct inode *inode, u64 *offset, void *opaque,
filldir_t filldir); filldir_t filldir);
extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename, extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
......
...@@ -139,7 +139,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb, ...@@ -139,7 +139,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
struct gfs2_sbd *sdp = sb->s_fs_info; struct gfs2_sbd *sdp = sb->s_fs_info;
struct inode *inode; struct inode *inode;
inode = gfs2_ilookup(sb, inum->no_addr); inode = gfs2_ilookup(sb, inum->no_addr, 0);
if (inode) { if (inode) {
if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) { if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) {
iput(inode); iput(inode);
......
...@@ -545,18 +545,10 @@ static int gfs2_close(struct inode *inode, struct file *file) ...@@ -545,18 +545,10 @@ static int gfs2_close(struct inode *inode, struct file *file)
/** /**
* gfs2_fsync - sync the dirty data for a file (across the cluster) * gfs2_fsync - sync the dirty data for a file (across the cluster)
* @file: the file that points to the dentry (we ignore this) * @file: the file that points to the dentry (we ignore this)
* @dentry: the dentry that points to the inode to sync * @datasync: set if we can ignore timestamp changes
* *
* The VFS will flush "normal" data for us. We only need to worry * The VFS will flush data for us. We only need to worry
* about metadata here. For journaled data, we just do a log flush * about metadata here.
* as we can't avoid it. Otherwise we can just bale out if datasync
* is set. For stuffed inodes we must flush the log in order to
* ensure that all data is on disk.
*
* The call to write_inode_now() is there to write back metadata and
* the inode itself. It does also try and write the data, but thats
* (hopefully) a no-op due to the VFS having already called filemap_fdatawrite()
* for us.
* *
* Returns: errno * Returns: errno
*/ */
...@@ -565,22 +557,20 @@ static int gfs2_fsync(struct file *file, int datasync) ...@@ -565,22 +557,20 @@ static int gfs2_fsync(struct file *file, int datasync)
{ {
struct inode *inode = file->f_mapping->host; struct inode *inode = file->f_mapping->host;
int sync_state = inode->i_state & (I_DIRTY_SYNC|I_DIRTY_DATASYNC); int sync_state = inode->i_state & (I_DIRTY_SYNC|I_DIRTY_DATASYNC);
int ret = 0; struct gfs2_inode *ip = GFS2_I(inode);
int ret;
if (gfs2_is_jdata(GFS2_I(inode))) {
gfs2_log_flush(GFS2_SB(inode), GFS2_I(inode)->i_gl);
return 0;
}
if (sync_state != 0) { if (datasync)
if (!datasync) sync_state &= ~I_DIRTY_SYNC;
ret = write_inode_now(inode, 0);
if (gfs2_is_stuffed(GFS2_I(inode))) if (sync_state) {
gfs2_log_flush(GFS2_SB(inode), GFS2_I(inode)->i_gl); ret = sync_inode_metadata(inode, 1);
if (ret)
return ret;
gfs2_ail_flush(ip->i_gl);
} }
return ret; return 0;
} }
/** /**
...@@ -826,6 +816,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, ...@@ -826,6 +816,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
loff_t bytes, max_bytes; loff_t bytes, max_bytes;
struct gfs2_alloc *al; struct gfs2_alloc *al;
int error; int error;
loff_t bsize_mask = ~((loff_t)sdp->sd_sb.sb_bsize - 1);
loff_t next = (offset + len - 1) >> sdp->sd_sb.sb_bsize_shift; loff_t next = (offset + len - 1) >> sdp->sd_sb.sb_bsize_shift;
next = (next + 1) << sdp->sd_sb.sb_bsize_shift; next = (next + 1) << sdp->sd_sb.sb_bsize_shift;
...@@ -833,13 +824,15 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, ...@@ -833,13 +824,15 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
if (mode & ~FALLOC_FL_KEEP_SIZE) if (mode & ~FALLOC_FL_KEEP_SIZE)
return -EOPNOTSUPP; return -EOPNOTSUPP;
offset = (offset >> sdp->sd_sb.sb_bsize_shift) << offset &= bsize_mask;
sdp->sd_sb.sb_bsize_shift;
len = next - offset; len = next - offset;
bytes = sdp->sd_max_rg_data * sdp->sd_sb.sb_bsize / 2; bytes = sdp->sd_max_rg_data * sdp->sd_sb.sb_bsize / 2;
if (!bytes) if (!bytes)
bytes = UINT_MAX; bytes = UINT_MAX;
bytes &= bsize_mask;
if (bytes == 0)
bytes = sdp->sd_sb.sb_bsize;
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh); gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &ip->i_gh);
error = gfs2_glock_nq(&ip->i_gh); error = gfs2_glock_nq(&ip->i_gh);
...@@ -870,6 +863,9 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, ...@@ -870,6 +863,9 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset,
if (error) { if (error) {
if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) {
bytes >>= 1; bytes >>= 1;
bytes &= bsize_mask;
if (bytes == 0)
bytes = sdp->sd_sb.sb_bsize;
goto retry; goto retry;
} }
goto out_qunlock; goto out_qunlock;
......
...@@ -143,14 +143,9 @@ static int demote_ok(const struct gfs2_glock *gl) ...@@ -143,14 +143,9 @@ static int demote_ok(const struct gfs2_glock *gl)
{ {
const struct gfs2_glock_operations *glops = gl->gl_ops; const struct gfs2_glock_operations *glops = gl->gl_ops;
/* assert_spin_locked(&gl->gl_spin); */
if (gl->gl_state == LM_ST_UNLOCKED) if (gl->gl_state == LM_ST_UNLOCKED)
return 0; return 0;
if (test_bit(GLF_LFLUSH, &gl->gl_flags)) if (!list_empty(&gl->gl_holders))
return 0;
if ((gl->gl_name.ln_type != LM_TYPE_INODE) &&
!list_empty(&gl->gl_holders))
return 0; return 0;
if (glops->go_demote_ok) if (glops->go_demote_ok)
return glops->go_demote_ok(gl); return glops->go_demote_ok(gl);
...@@ -158,6 +153,31 @@ static int demote_ok(const struct gfs2_glock *gl) ...@@ -158,6 +153,31 @@ static int demote_ok(const struct gfs2_glock *gl)
} }
void gfs2_glock_add_to_lru(struct gfs2_glock *gl)
{
spin_lock(&lru_lock);
if (!list_empty(&gl->gl_lru))
list_del_init(&gl->gl_lru);
else
atomic_inc(&lru_count);
list_add_tail(&gl->gl_lru, &lru_list);
set_bit(GLF_LRU, &gl->gl_flags);
spin_unlock(&lru_lock);
}
static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl)
{
spin_lock(&lru_lock);
if (!list_empty(&gl->gl_lru)) {
list_del_init(&gl->gl_lru);
atomic_dec(&lru_count);
clear_bit(GLF_LRU, &gl->gl_flags);
}
spin_unlock(&lru_lock);
}
/** /**
* __gfs2_glock_schedule_for_reclaim - Add a glock to the reclaim list * __gfs2_glock_schedule_for_reclaim - Add a glock to the reclaim list
* @gl: the glock * @gl: the glock
...@@ -168,24 +188,8 @@ static int demote_ok(const struct gfs2_glock *gl) ...@@ -168,24 +188,8 @@ static int demote_ok(const struct gfs2_glock *gl)
static void __gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl) static void __gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl)
{ {
if (demote_ok(gl)) { if (demote_ok(gl))
spin_lock(&lru_lock); gfs2_glock_add_to_lru(gl);
if (!list_empty(&gl->gl_lru))
list_del_init(&gl->gl_lru);
else
atomic_inc(&lru_count);
list_add_tail(&gl->gl_lru, &lru_list);
spin_unlock(&lru_lock);
}
}
void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl)
{
spin_lock(&gl->gl_spin);
__gfs2_glock_schedule_for_reclaim(gl);
spin_unlock(&gl->gl_spin);
} }
/** /**
...@@ -217,12 +221,7 @@ void gfs2_glock_put(struct gfs2_glock *gl) ...@@ -217,12 +221,7 @@ void gfs2_glock_put(struct gfs2_glock *gl)
spin_lock_bucket(gl->gl_hash); spin_lock_bucket(gl->gl_hash);
hlist_bl_del_rcu(&gl->gl_list); hlist_bl_del_rcu(&gl->gl_list);
spin_unlock_bucket(gl->gl_hash); spin_unlock_bucket(gl->gl_hash);
spin_lock(&lru_lock); gfs2_glock_remove_from_lru(gl);
if (!list_empty(&gl->gl_lru)) {
list_del_init(&gl->gl_lru);
atomic_dec(&lru_count);
}
spin_unlock(&lru_lock);
GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders)); GLOCK_BUG_ON(gl, !list_empty(&gl->gl_holders));
GLOCK_BUG_ON(gl, mapping && mapping->nrpages); GLOCK_BUG_ON(gl, mapping && mapping->nrpages);
trace_gfs2_glock_put(gl); trace_gfs2_glock_put(gl);
...@@ -542,11 +541,6 @@ __acquires(&gl->gl_spin) ...@@ -542,11 +541,6 @@ __acquires(&gl->gl_spin)
clear_bit(GLF_INVALIDATE_IN_PROGRESS, &gl->gl_flags); clear_bit(GLF_INVALIDATE_IN_PROGRESS, &gl->gl_flags);
gfs2_glock_hold(gl); gfs2_glock_hold(gl);
if (target != LM_ST_UNLOCKED && (gl->gl_state == LM_ST_SHARED ||
gl->gl_state == LM_ST_DEFERRED) &&
!(lck_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)))
lck_flags |= LM_FLAG_TRY_1CB;
if (sdp->sd_lockstruct.ls_ops->lm_lock) { if (sdp->sd_lockstruct.ls_ops->lm_lock) {
/* lock_dlm */ /* lock_dlm */
ret = sdp->sd_lockstruct.ls_ops->lm_lock(gl, target, lck_flags); ret = sdp->sd_lockstruct.ls_ops->lm_lock(gl, target, lck_flags);
...@@ -648,7 +642,7 @@ static void delete_work_func(struct work_struct *work) ...@@ -648,7 +642,7 @@ static void delete_work_func(struct work_struct *work)
/* Note: Unsafe to dereference ip as we don't hold right refs/locks */ /* Note: Unsafe to dereference ip as we don't hold right refs/locks */
if (ip) if (ip)
inode = gfs2_ilookup(sdp->sd_vfs, no_addr); inode = gfs2_ilookup(sdp->sd_vfs, no_addr, 1);
else else
inode = gfs2_lookup_by_inum(sdp, no_addr, NULL, GFS2_BLKST_UNLINKED); inode = gfs2_lookup_by_inum(sdp, no_addr, NULL, GFS2_BLKST_UNLINKED);
if (inode && !IS_ERR(inode)) { if (inode && !IS_ERR(inode)) {
...@@ -1025,6 +1019,9 @@ int gfs2_glock_nq(struct gfs2_holder *gh) ...@@ -1025,6 +1019,9 @@ int gfs2_glock_nq(struct gfs2_holder *gh)
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
return -EIO; return -EIO;
if (test_bit(GLF_LRU, &gl->gl_flags))
gfs2_glock_remove_from_lru(gl);
spin_lock(&gl->gl_spin); spin_lock(&gl->gl_spin);
add_to_queue(gh); add_to_queue(gh);
if ((LM_FLAG_NOEXP & gh->gh_flags) && if ((LM_FLAG_NOEXP & gh->gh_flags) &&
...@@ -1082,7 +1079,8 @@ void gfs2_glock_dq(struct gfs2_holder *gh) ...@@ -1082,7 +1079,8 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
!test_bit(GLF_DEMOTE, &gl->gl_flags)) !test_bit(GLF_DEMOTE, &gl->gl_flags))
fast_path = 1; fast_path = 1;
} }
__gfs2_glock_schedule_for_reclaim(gl); if (!test_bit(GLF_LFLUSH, &gl->gl_flags))
__gfs2_glock_schedule_for_reclaim(gl);
trace_gfs2_glock_queue(gh, 0); trace_gfs2_glock_queue(gh, 0);
spin_unlock(&gl->gl_spin); spin_unlock(&gl->gl_spin);
if (likely(fast_path)) if (likely(fast_path))
...@@ -1365,6 +1363,7 @@ static int gfs2_shrink_glock_memory(struct shrinker *shrink, int nr, gfp_t gfp_m ...@@ -1365,6 +1363,7 @@ static int gfs2_shrink_glock_memory(struct shrinker *shrink, int nr, gfp_t gfp_m
while(nr && !list_empty(&lru_list)) { while(nr && !list_empty(&lru_list)) {
gl = list_entry(lru_list.next, struct gfs2_glock, gl_lru); gl = list_entry(lru_list.next, struct gfs2_glock, gl_lru);
list_del_init(&gl->gl_lru); list_del_init(&gl->gl_lru);
clear_bit(GLF_LRU, &gl->gl_flags);
atomic_dec(&lru_count); atomic_dec(&lru_count);
/* Test for being demotable */ /* Test for being demotable */
...@@ -1387,6 +1386,7 @@ static int gfs2_shrink_glock_memory(struct shrinker *shrink, int nr, gfp_t gfp_m ...@@ -1387,6 +1386,7 @@ static int gfs2_shrink_glock_memory(struct shrinker *shrink, int nr, gfp_t gfp_m
} }
nr_skipped++; nr_skipped++;
list_add(&gl->gl_lru, &skipped); list_add(&gl->gl_lru, &skipped);
set_bit(GLF_LRU, &gl->gl_flags);
} }
list_splice(&skipped, &lru_list); list_splice(&skipped, &lru_list);
atomic_add(nr_skipped, &lru_count); atomic_add(nr_skipped, &lru_count);
...@@ -1459,12 +1459,7 @@ static void thaw_glock(struct gfs2_glock *gl) ...@@ -1459,12 +1459,7 @@ static void thaw_glock(struct gfs2_glock *gl)
static void clear_glock(struct gfs2_glock *gl) static void clear_glock(struct gfs2_glock *gl)
{ {
spin_lock(&lru_lock); gfs2_glock_remove_from_lru(gl);
if (!list_empty(&gl->gl_lru)) {
list_del_init(&gl->gl_lru);
atomic_dec(&lru_count);
}
spin_unlock(&lru_lock);
spin_lock(&gl->gl_spin); spin_lock(&gl->gl_spin);
if (gl->gl_state != LM_ST_UNLOCKED) if (gl->gl_state != LM_ST_UNLOCKED)
...@@ -1599,9 +1594,11 @@ static int dump_holder(struct seq_file *seq, const struct gfs2_holder *gh) ...@@ -1599,9 +1594,11 @@ static int dump_holder(struct seq_file *seq, const struct gfs2_holder *gh)
return 0; return 0;
} }
static const char *gflags2str(char *buf, const unsigned long *gflags) static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
{ {
const unsigned long *gflags = &gl->gl_flags;
char *p = buf; char *p = buf;
if (test_bit(GLF_LOCK, gflags)) if (test_bit(GLF_LOCK, gflags))
*p++ = 'l'; *p++ = 'l';
if (test_bit(GLF_DEMOTE, gflags)) if (test_bit(GLF_DEMOTE, gflags))
...@@ -1624,6 +1621,10 @@ static const char *gflags2str(char *buf, const unsigned long *gflags) ...@@ -1624,6 +1621,10 @@ static const char *gflags2str(char *buf, const unsigned long *gflags)
*p++ = 'F'; *p++ = 'F';
if (test_bit(GLF_QUEUED, gflags)) if (test_bit(GLF_QUEUED, gflags))
*p++ = 'q'; *p++ = 'q';
if (test_bit(GLF_LRU, gflags))
*p++ = 'L';
if (gl->gl_object)
*p++ = 'o';
*p = 0; *p = 0;
return buf; return buf;
} }
...@@ -1658,14 +1659,15 @@ static int __dump_glock(struct seq_file *seq, const struct gfs2_glock *gl) ...@@ -1658,14 +1659,15 @@ static int __dump_glock(struct seq_file *seq, const struct gfs2_glock *gl)
dtime *= 1000000/HZ; /* demote time in uSec */ dtime *= 1000000/HZ; /* demote time in uSec */
if (!test_bit(GLF_DEMOTE, &gl->gl_flags)) if (!test_bit(GLF_DEMOTE, &gl->gl_flags))
dtime = 0; dtime = 0;
gfs2_print_dbg(seq, "G: s:%s n:%u/%llx f:%s t:%s d:%s/%llu a:%d r:%d\n", gfs2_print_dbg(seq, "G: s:%s n:%u/%llx f:%s t:%s d:%s/%llu a:%d v:%d r:%d\n",
state2str(gl->gl_state), state2str(gl->gl_state),
gl->gl_name.ln_type, gl->gl_name.ln_type,
(unsigned long long)gl->gl_name.ln_number, (unsigned long long)gl->gl_name.ln_number,
gflags2str(gflags_buf, &gl->gl_flags), gflags2str(gflags_buf, gl),
state2str(gl->gl_target), state2str(gl->gl_target),
state2str(gl->gl_demote_state), dtime, state2str(gl->gl_demote_state), dtime,
atomic_read(&gl->gl_ail_count), atomic_read(&gl->gl_ail_count),
atomic_read(&gl->gl_revokes),
atomic_read(&gl->gl_ref)); atomic_read(&gl->gl_ref));
list_for_each_entry(gh, &gl->gl_holders, gh_list) { list_for_each_entry(gh, &gl->gl_holders, gh_list) {
......
...@@ -225,11 +225,10 @@ static inline int gfs2_glock_nq_init(struct gfs2_glock *gl, ...@@ -225,11 +225,10 @@ static inline int gfs2_glock_nq_init(struct gfs2_glock *gl,
extern void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state); extern void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state);
extern void gfs2_glock_complete(struct gfs2_glock *gl, int ret); extern void gfs2_glock_complete(struct gfs2_glock *gl, int ret);
extern void gfs2_reclaim_glock(struct gfs2_sbd *sdp);
extern void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); extern void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip); extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip);
extern void gfs2_glock_thaw(struct gfs2_sbd *sdp); extern void gfs2_glock_thaw(struct gfs2_sbd *sdp);
extern void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); extern void gfs2_glock_add_to_lru(struct gfs2_glock *gl);
extern void gfs2_glock_free(struct gfs2_glock *gl); extern void gfs2_glock_free(struct gfs2_glock *gl);
extern int __init gfs2_glock_init(void); extern int __init gfs2_glock_init(void);
......
...@@ -28,33 +28,18 @@ ...@@ -28,33 +28,18 @@
#include "trans.h" #include "trans.h"
/** /**
* ail_empty_gl - remove all buffers for a given lock from the AIL * __gfs2_ail_flush - remove all buffers for a given lock from the AIL
* @gl: the glock * @gl: the glock
* *
* None of the buffers should be dirty, locked, or pinned. * None of the buffers should be dirty, locked, or pinned.
*/ */
static void gfs2_ail_empty_gl(struct gfs2_glock *gl) static void __gfs2_ail_flush(struct gfs2_glock *gl)
{ {
struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_sbd *sdp = gl->gl_sbd;
struct list_head *head = &gl->gl_ail_list; struct list_head *head = &gl->gl_ail_list;
struct gfs2_bufdata *bd; struct gfs2_bufdata *bd;
struct buffer_head *bh; struct buffer_head *bh;
struct gfs2_trans tr;
memset(&tr, 0, sizeof(tr));
tr.tr_revokes = atomic_read(&gl->gl_ail_count);
if (!tr.tr_revokes)
return;
/* A shortened, inline version of gfs2_trans_begin() */
tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
tr.tr_ip = (unsigned long)__builtin_return_address(0);
INIT_LIST_HEAD(&tr.tr_list_buf);
gfs2_log_reserve(sdp, tr.tr_reserved);
BUG_ON(current->journal_info);
current->journal_info = &tr;
spin_lock(&sdp->sd_ail_lock); spin_lock(&sdp->sd_ail_lock);
while (!list_empty(head)) { while (!list_empty(head)) {
...@@ -76,7 +61,47 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) ...@@ -76,7 +61,47 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
} }
gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count));
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
}
static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct gfs2_trans tr;
memset(&tr, 0, sizeof(tr));
tr.tr_revokes = atomic_read(&gl->gl_ail_count);
if (!tr.tr_revokes)
return;
/* A shortened, inline version of gfs2_trans_begin() */
tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
tr.tr_ip = (unsigned long)__builtin_return_address(0);
INIT_LIST_HEAD(&tr.tr_list_buf);
gfs2_log_reserve(sdp, tr.tr_reserved);
BUG_ON(current->journal_info);
current->journal_info = &tr;
__gfs2_ail_flush(gl);
gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL);
}
void gfs2_ail_flush(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
unsigned int revokes = atomic_read(&gl->gl_ail_count);
int ret;
if (!revokes)
return;
ret = gfs2_trans_begin(sdp, 0, revokes);
if (ret)
return;
__gfs2_ail_flush(gl);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
} }
...@@ -226,6 +251,119 @@ static int inode_go_demote_ok(const struct gfs2_glock *gl) ...@@ -226,6 +251,119 @@ static int inode_go_demote_ok(const struct gfs2_glock *gl)
return 1; return 1;
} }
/**
* gfs2_set_nlink - Set the inode's link count based on on-disk info
* @inode: The inode in question
* @nlink: The link count
*
* If the link count has hit zero, it must never be raised, whatever the
* on-disk inode might say. When new struct inodes are created the link
* count is set to 1, so that we can safely use this test even when reading
* in on disk information for the first time.
*/
static void gfs2_set_nlink(struct inode *inode, u32 nlink)
{
/*
* We will need to review setting the nlink count here in the
* light of the forthcoming ro bind mount work. This is a reminder
* to do that.
*/
if ((inode->i_nlink != nlink) && (inode->i_nlink != 0)) {
if (nlink == 0)
clear_nlink(inode);
else
inode->i_nlink = nlink;
}
}
static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
{
const struct gfs2_dinode *str = buf;
struct timespec atime;
u16 height, depth;
if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
goto corrupt;
ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino);
ip->i_inode.i_mode = be32_to_cpu(str->di_mode);
ip->i_inode.i_rdev = 0;
switch (ip->i_inode.i_mode & S_IFMT) {
case S_IFBLK:
case S_IFCHR:
ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
be32_to_cpu(str->di_minor));
break;
};
ip->i_inode.i_uid = be32_to_cpu(str->di_uid);
ip->i_inode.i_gid = be32_to_cpu(str->di_gid);
gfs2_set_nlink(&ip->i_inode, be32_to_cpu(str->di_nlink));
i_size_write(&ip->i_inode, be64_to_cpu(str->di_size));
gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks));
atime.tv_sec = be64_to_cpu(str->di_atime);
atime.tv_nsec = be32_to_cpu(str->di_atime_nsec);
if (timespec_compare(&ip->i_inode.i_atime, &atime) < 0)
ip->i_inode.i_atime = atime;
ip->i_inode.i_mtime.tv_sec = be64_to_cpu(str->di_mtime);
ip->i_inode.i_mtime.tv_nsec = be32_to_cpu(str->di_mtime_nsec);
ip->i_inode.i_ctime.tv_sec = be64_to_cpu(str->di_ctime);
ip->i_inode.i_ctime.tv_nsec = be32_to_cpu(str->di_ctime_nsec);
ip->i_goal = be64_to_cpu(str->di_goal_meta);
ip->i_generation = be64_to_cpu(str->di_generation);
ip->i_diskflags = be32_to_cpu(str->di_flags);
gfs2_set_inode_flags(&ip->i_inode);
height = be16_to_cpu(str->di_height);
if (unlikely(height > GFS2_MAX_META_HEIGHT))
goto corrupt;
ip->i_height = (u8)height;
depth = be16_to_cpu(str->di_depth);
if (unlikely(depth > GFS2_DIR_MAX_DEPTH))
goto corrupt;
ip->i_depth = (u8)depth;
ip->i_entries = be32_to_cpu(str->di_entries);
ip->i_eattr = be64_to_cpu(str->di_eattr);
if (S_ISREG(ip->i_inode.i_mode))
gfs2_set_aops(&ip->i_inode);
return 0;
corrupt:
gfs2_consist_inode(ip);
return -EIO;
}
/**
* gfs2_inode_refresh - Refresh the incore copy of the dinode
* @ip: The GFS2 inode
*
* Returns: errno
*/
int gfs2_inode_refresh(struct gfs2_inode *ip)
{
struct buffer_head *dibh;
int error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), dibh, GFS2_METATYPE_DI)) {
brelse(dibh);
return -EIO;
}
error = gfs2_dinode_in(ip, dibh->b_data);
brelse(dibh);
clear_bit(GIF_INVALID, &ip->i_flags);
return error;
}
/** /**
* inode_go_lock - operation done after an inode lock is locked by a process * inode_go_lock - operation done after an inode lock is locked by a process
* @gl: the glock * @gl: the glock
......
...@@ -23,4 +23,6 @@ extern const struct gfs2_glock_operations gfs2_quota_glops; ...@@ -23,4 +23,6 @@ extern const struct gfs2_glock_operations gfs2_quota_glops;
extern const struct gfs2_glock_operations gfs2_journal_glops; extern const struct gfs2_glock_operations gfs2_journal_glops;
extern const struct gfs2_glock_operations *gfs2_glops_list[]; extern const struct gfs2_glock_operations *gfs2_glops_list[];
extern void gfs2_ail_flush(struct gfs2_glock *gl);
#endif /* __GLOPS_DOT_H__ */ #endif /* __GLOPS_DOT_H__ */
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#define DIO_WAIT 0x00000010 #define DIO_WAIT 0x00000010
#define DIO_METADATA 0x00000020 #define DIO_METADATA 0x00000020
#define DIO_ALL 0x00000100
struct gfs2_log_operations; struct gfs2_log_operations;
struct gfs2_log_element; struct gfs2_log_element;
...@@ -200,6 +199,8 @@ enum { ...@@ -200,6 +199,8 @@ enum {
GLF_INITIAL = 10, GLF_INITIAL = 10,
GLF_FROZEN = 11, GLF_FROZEN = 11,
GLF_QUEUED = 12, GLF_QUEUED = 12,
GLF_LRU = 13,
GLF_OBJECT = 14, /* Used only for tracing */
}; };
struct gfs2_glock { struct gfs2_glock {
...@@ -234,6 +235,7 @@ struct gfs2_glock { ...@@ -234,6 +235,7 @@ struct gfs2_glock {
struct list_head gl_ail_list; struct list_head gl_ail_list;
atomic_t gl_ail_count; atomic_t gl_ail_count;
atomic_t gl_revokes;
struct delayed_work gl_work; struct delayed_work gl_work;
struct work_struct gl_delete; struct work_struct gl_delete;
struct rcu_head gl_rcu; struct rcu_head gl_rcu;
...@@ -374,8 +376,6 @@ struct gfs2_ail { ...@@ -374,8 +376,6 @@ struct gfs2_ail {
unsigned int ai_first; unsigned int ai_first;
struct list_head ai_ail1_list; struct list_head ai_ail1_list;
struct list_head ai_ail2_list; struct list_head ai_ail2_list;
u64 ai_sync_gen;
}; };
struct gfs2_journal_extent { struct gfs2_journal_extent {
...@@ -488,7 +488,6 @@ struct gfs2_sb_host { ...@@ -488,7 +488,6 @@ struct gfs2_sb_host {
char sb_lockproto[GFS2_LOCKNAME_LEN]; char sb_lockproto[GFS2_LOCKNAME_LEN];
char sb_locktable[GFS2_LOCKNAME_LEN]; char sb_locktable[GFS2_LOCKNAME_LEN];
u8 sb_uuid[16];
}; };
/* /*
...@@ -654,7 +653,6 @@ struct gfs2_sbd { ...@@ -654,7 +653,6 @@ struct gfs2_sbd {
spinlock_t sd_ail_lock; spinlock_t sd_ail_lock;
struct list_head sd_ail1_list; struct list_head sd_ail1_list;
struct list_head sd_ail2_list; struct list_head sd_ail2_list;
u64 sd_ail_sync_gen;
/* Replay stuff */ /* Replay stuff */
......
/* /*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved. * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved. * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
* *
* This copyrighted material is made available to anyone wishing to use, * This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions * modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2. * of the GNU General Public License version 2.
*/ */
#include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/namei.h>
#include <linux/mm.h>
#include <linux/xattr.h>
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include <linux/sort.h>
#include <linux/gfs2_ondisk.h> #include <linux/gfs2_ondisk.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/fiemap.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/time.h> #include <asm/uaccess.h>
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
...@@ -26,19 +28,14 @@ ...@@ -26,19 +28,14 @@
#include "dir.h" #include "dir.h"
#include "xattr.h" #include "xattr.h"
#include "glock.h" #include "glock.h"
#include "glops.h"
#include "inode.h" #include "inode.h"
#include "log.h"
#include "meta_io.h" #include "meta_io.h"
#include "quota.h" #include "quota.h"
#include "rgrp.h" #include "rgrp.h"
#include "trans.h" #include "trans.h"
#include "util.h" #include "util.h"
#include "super.h"
struct gfs2_inum_range_host { #include "glops.h"
u64 ir_start;
u64 ir_length;
};
struct gfs2_skip_data { struct gfs2_skip_data {
u64 no_addr; u64 no_addr;
...@@ -74,14 +71,14 @@ static int iget_set(struct inode *inode, void *opaque) ...@@ -74,14 +71,14 @@ static int iget_set(struct inode *inode, void *opaque)
return 0; return 0;
} }
struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr) struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block)
{ {
unsigned long hash = (unsigned long)no_addr; unsigned long hash = (unsigned long)no_addr;
struct gfs2_skip_data data; struct gfs2_skip_data data;
data.no_addr = no_addr; data.no_addr = no_addr;
data.skipped = 0; data.skipped = 0;
data.non_block = 0; data.non_block = non_block;
return ilookup5(sb, hash, iget_test, &data); return ilookup5(sb, hash, iget_test, &data);
} }
...@@ -248,203 +245,6 @@ struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, ...@@ -248,203 +245,6 @@ struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr,
goto fail; goto fail;
} }
static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
{
const struct gfs2_dinode *str = buf;
struct timespec atime;
u16 height, depth;
if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
goto corrupt;
ip->i_no_formal_ino = be64_to_cpu(str->di_num.no_formal_ino);
ip->i_inode.i_mode = be32_to_cpu(str->di_mode);
ip->i_inode.i_rdev = 0;
switch (ip->i_inode.i_mode & S_IFMT) {
case S_IFBLK:
case S_IFCHR:
ip->i_inode.i_rdev = MKDEV(be32_to_cpu(str->di_major),
be32_to_cpu(str->di_minor));
break;
};
ip->i_inode.i_uid = be32_to_cpu(str->di_uid);
ip->i_inode.i_gid = be32_to_cpu(str->di_gid);
/*
* We will need to review setting the nlink count here in the
* light of the forthcoming ro bind mount work. This is a reminder
* to do that.
*/
ip->i_inode.i_nlink = be32_to_cpu(str->di_nlink);
i_size_write(&ip->i_inode, be64_to_cpu(str->di_size));
gfs2_set_inode_blocks(&ip->i_inode, be64_to_cpu(str->di_blocks));
atime.tv_sec = be64_to_cpu(str->di_atime);
atime.tv_nsec = be32_to_cpu(str->di_atime_nsec);
if (timespec_compare(&ip->i_inode.i_atime, &atime) < 0)
ip->i_inode.i_atime = atime;
ip->i_inode.i_mtime.tv_sec = be64_to_cpu(str->di_mtime);
ip->i_inode.i_mtime.tv_nsec = be32_to_cpu(str->di_mtime_nsec);
ip->i_inode.i_ctime.tv_sec = be64_to_cpu(str->di_ctime);
ip->i_inode.i_ctime.tv_nsec = be32_to_cpu(str->di_ctime_nsec);
ip->i_goal = be64_to_cpu(str->di_goal_meta);
ip->i_generation = be64_to_cpu(str->di_generation);
ip->i_diskflags = be32_to_cpu(str->di_flags);
gfs2_set_inode_flags(&ip->i_inode);
height = be16_to_cpu(str->di_height);
if (unlikely(height > GFS2_MAX_META_HEIGHT))
goto corrupt;
ip->i_height = (u8)height;
depth = be16_to_cpu(str->di_depth);
if (unlikely(depth > GFS2_DIR_MAX_DEPTH))
goto corrupt;
ip->i_depth = (u8)depth;
ip->i_entries = be32_to_cpu(str->di_entries);
ip->i_eattr = be64_to_cpu(str->di_eattr);
if (S_ISREG(ip->i_inode.i_mode))
gfs2_set_aops(&ip->i_inode);
return 0;
corrupt:
if (gfs2_consist_inode(ip))
gfs2_dinode_print(ip);
return -EIO;
}
/**
* gfs2_inode_refresh - Refresh the incore copy of the dinode
* @ip: The GFS2 inode
*
* Returns: errno
*/
int gfs2_inode_refresh(struct gfs2_inode *ip)
{
struct buffer_head *dibh;
int error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
if (gfs2_metatype_check(GFS2_SB(&ip->i_inode), dibh, GFS2_METATYPE_DI)) {
brelse(dibh);
return -EIO;
}
error = gfs2_dinode_in(ip, dibh->b_data);
brelse(dibh);
clear_bit(GIF_INVALID, &ip->i_flags);
return error;
}
int gfs2_dinode_dealloc(struct gfs2_inode *ip)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_alloc *al;
struct gfs2_rgrpd *rgd;
int error;
if (gfs2_get_inode_blocks(&ip->i_inode) != 1) {
if (gfs2_consist_inode(ip))
gfs2_dinode_print(ip);
return -EIO;
}
al = gfs2_alloc_get(ip);
if (!al)
return -ENOMEM;
error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
if (error)
goto out;
error = gfs2_rindex_hold(sdp, &al->al_ri_gh);
if (error)
goto out_qs;
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr);
if (!rgd) {
gfs2_consist_inode(ip);
error = -EIO;
goto out_rindex_relse;
}
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0,
&al->al_rgd_gh);
if (error)
goto out_rindex_relse;
error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA, 1);
if (error)
goto out_rg_gunlock;
set_bit(GLF_DIRTY, &ip->i_gl->gl_flags);
set_bit(GLF_LFLUSH, &ip->i_gl->gl_flags);
gfs2_free_di(rgd, ip);
gfs2_trans_end(sdp);
out_rg_gunlock:
gfs2_glock_dq_uninit(&al->al_rgd_gh);
out_rindex_relse:
gfs2_glock_dq_uninit(&al->al_ri_gh);
out_qs:
gfs2_quota_unhold(ip);
out:
gfs2_alloc_put(ip);
return error;
}
/**
* gfs2_change_nlink - Change nlink count on inode
* @ip: The GFS2 inode
* @diff: The change in the nlink count required
*
* Returns: errno
*/
int gfs2_change_nlink(struct gfs2_inode *ip, int diff)
{
struct buffer_head *dibh;
u32 nlink;
int error;
BUG_ON(diff != 1 && diff != -1);
nlink = ip->i_inode.i_nlink + diff;
/* If we are reducing the nlink count, but the new value ends up being
bigger than the old one, we must have underflowed. */
if (diff < 0 && nlink > ip->i_inode.i_nlink) {
if (gfs2_consist_inode(ip))
gfs2_dinode_print(ip);
return -EIO;
}
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
if (diff > 0)
inc_nlink(&ip->i_inode);
else
drop_nlink(&ip->i_inode);
ip->i_inode.i_ctime = CURRENT_TIME;
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh);
mark_inode_dirty(&ip->i_inode);
if (ip->i_inode.i_nlink == 0)
gfs2_unlink_di(&ip->i_inode); /* mark inode unlinked */
return error;
}
struct inode *gfs2_lookup_simple(struct inode *dip, const char *name) struct inode *gfs2_lookup_simple(struct inode *dip, const char *name)
{ {
...@@ -543,7 +343,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name, ...@@ -543,7 +343,7 @@ static int create_ok(struct gfs2_inode *dip, const struct qstr *name,
/* Don't create entries in an unlinked directory */ /* Don't create entries in an unlinked directory */
if (!dip->i_inode.i_nlink) if (!dip->i_inode.i_nlink)
return -EPERM; return -ENOENT;
error = gfs2_dir_check(&dip->i_inode, name, NULL); error = gfs2_dir_check(&dip->i_inode, name, NULL);
switch (error) { switch (error) {
...@@ -613,21 +413,44 @@ static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation) ...@@ -613,21 +413,44 @@ static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation)
return error; return error;
} }
static void gfs2_init_dir(struct buffer_head *dibh,
const struct gfs2_inode *parent)
{
struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data;
struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1);
gfs2_qstr2dirent(&gfs2_qdot, GFS2_DIRENT_SIZE(gfs2_qdot.len), dent);
dent->de_inum = di->di_num; /* already GFS2 endian */
dent->de_type = cpu_to_be16(DT_DIR);
dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1));
gfs2_qstr2dirent(&gfs2_qdotdot, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent);
gfs2_inum_out(parent, dent);
dent->de_type = cpu_to_be16(DT_DIR);
}
/** /**
* init_dinode - Fill in a new dinode structure * init_dinode - Fill in a new dinode structure
* @dip: the directory this inode is being created in * @dip: The directory this inode is being created in
* @gl: The glock covering the new inode * @gl: The glock covering the new inode
* @inum: the inode number * @inum: The inode number
* @mode: the file permissions * @mode: The file permissions
* @uid: * @uid: The uid of the new inode
* @gid: * @gid: The gid of the new inode
* @generation: The generation number of the new inode
* @dev: The device number (if a device node)
* @symname: The symlink destination (if a symlink)
* @size: The inode size (ignored for directories)
* @bhp: The buffer head (returned to caller)
* *
*/ */
static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
const struct gfs2_inum_host *inum, unsigned int mode, const struct gfs2_inum_host *inum, unsigned int mode,
unsigned int uid, unsigned int gid, unsigned int uid, unsigned int gid,
const u64 *generation, dev_t dev, struct buffer_head **bhp) const u64 *generation, dev_t dev, const char *symname,
unsigned size, struct buffer_head **bhp)
{ {
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct gfs2_dinode *di; struct gfs2_dinode *di;
...@@ -646,7 +469,7 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, ...@@ -646,7 +469,7 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
di->di_uid = cpu_to_be32(uid); di->di_uid = cpu_to_be32(uid);
di->di_gid = cpu_to_be32(gid); di->di_gid = cpu_to_be32(gid);
di->di_nlink = 0; di->di_nlink = 0;
di->di_size = 0; di->di_size = cpu_to_be64(size);
di->di_blocks = cpu_to_be64(1); di->di_blocks = cpu_to_be64(1);
di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec); di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(tv.tv_sec);
di->di_major = cpu_to_be32(MAJOR(dev)); di->di_major = cpu_to_be32(MAJOR(dev));
...@@ -654,16 +477,6 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, ...@@ -654,16 +477,6 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr);
di->di_generation = cpu_to_be64(*generation); di->di_generation = cpu_to_be64(*generation);
di->di_flags = 0; di->di_flags = 0;
if (S_ISREG(mode)) {
if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) ||
gfs2_tune_get(sdp, gt_new_files_jdata))
di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA);
} else if (S_ISDIR(mode)) {
di->di_flags |= cpu_to_be32(dip->i_diskflags &
GFS2_DIF_INHERIT_JDATA);
}
di->__pad1 = 0; di->__pad1 = 0;
di->di_payload_format = cpu_to_be32(S_ISDIR(mode) ? GFS2_FORMAT_DE : 0); di->di_payload_format = cpu_to_be32(S_ISDIR(mode) ? GFS2_FORMAT_DE : 0);
di->di_height = 0; di->di_height = 0;
...@@ -677,7 +490,26 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, ...@@ -677,7 +490,26 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec); di->di_mtime_nsec = cpu_to_be32(tv.tv_nsec);
di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec); di->di_ctime_nsec = cpu_to_be32(tv.tv_nsec);
memset(&di->di_reserved, 0, sizeof(di->di_reserved)); memset(&di->di_reserved, 0, sizeof(di->di_reserved));
switch(mode & S_IFMT) {
case S_IFREG:
if ((dip->i_diskflags & GFS2_DIF_INHERIT_JDATA) ||
gfs2_tune_get(sdp, gt_new_files_jdata))
di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA);
break;
case S_IFDIR:
di->di_flags |= cpu_to_be32(dip->i_diskflags &
GFS2_DIF_INHERIT_JDATA);
di->di_flags |= cpu_to_be32(GFS2_DIF_JDATA);
di->di_size = cpu_to_be64(sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode));
di->di_entries = cpu_to_be32(2);
gfs2_init_dir(dibh, dip);
break;
case S_IFLNK:
memcpy(dibh->b_data + sizeof(struct gfs2_dinode), symname, size);
break;
}
set_buffer_uptodate(dibh); set_buffer_uptodate(dibh);
*bhp = dibh; *bhp = dibh;
...@@ -685,7 +517,8 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, ...@@ -685,7 +517,8 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
unsigned int mode, const struct gfs2_inum_host *inum, unsigned int mode, const struct gfs2_inum_host *inum,
const u64 *generation, dev_t dev, struct buffer_head **bhp) const u64 *generation, dev_t dev, const char *symname,
unsigned int size, struct buffer_head **bhp)
{ {
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
unsigned int uid, gid; unsigned int uid, gid;
...@@ -707,7 +540,7 @@ static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, ...@@ -707,7 +540,7 @@ static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl,
if (error) if (error)
goto out_quota; goto out_quota;
init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, bhp); init_dinode(dip, gl, inum, mode, uid, gid, generation, dev, symname, size, bhp);
gfs2_quota_change(dip, +1, uid, gid); gfs2_quota_change(dip, +1, uid, gid);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
...@@ -761,14 +594,16 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, ...@@ -761,14 +594,16 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
goto fail_quota_locks; goto fail_quota_locks;
} }
error = gfs2_dir_add(&dip->i_inode, name, ip, IF2DT(ip->i_inode.i_mode)); error = gfs2_dir_add(&dip->i_inode, name, ip);
if (error) if (error)
goto fail_end_trans; goto fail_end_trans;
error = gfs2_meta_inode_buffer(ip, &dibh); error = gfs2_meta_inode_buffer(ip, &dibh);
if (error) if (error)
goto fail_end_trans; goto fail_end_trans;
ip->i_inode.i_nlink = 1; inc_nlink(&ip->i_inode);
if (S_ISDIR(ip->i_inode.i_mode))
inc_nlink(&ip->i_inode);
gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data); gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh); brelse(dibh);
...@@ -815,27 +650,25 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip, ...@@ -815,27 +650,25 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip,
} }
/** /**
* gfs2_createi - Create a new inode * gfs2_create_inode - Create a new inode
* @ghs: An array of two holders * @dir: The parent directory
* @name: The name of the new file * @dentry: The new dentry
* @mode: the permissions on the new inode * @mode: The permissions on the new inode
* * @dev: For device nodes, this is the device number
* @ghs[0] is an initialized holder for the directory * @symname: For symlinks, this is the link destination
* @ghs[1] is the holder for the inode lock * @size: The initial size of the inode (ignored for directories)
* *
* If the return value is not NULL, the glocks on both the directory and the new * Returns: 0 on success, or error code
* file are held. A transaction has been started and an inplace reservation
* is held, as well.
*
* Returns: An inode
*/ */
struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
unsigned int mode, dev_t dev) unsigned int mode, dev_t dev, const char *symname,
unsigned int size)
{ {
const struct qstr *name = &dentry->d_name;
struct gfs2_holder ghs[2];
struct inode *inode = NULL; struct inode *inode = NULL;
struct gfs2_inode *dip = ghs->gh_gl->gl_object; struct gfs2_inode *dip = GFS2_I(dir);
struct inode *dir = &dip->i_inode;
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 }; struct gfs2_inum_host inum = { .no_addr = 0, .no_formal_ino = 0 };
int error; int error;
...@@ -843,10 +676,9 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, ...@@ -843,10 +676,9 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
struct buffer_head *bh = NULL; struct buffer_head *bh = NULL;
if (!name->len || name->len > GFS2_FNAMESIZE) if (!name->len || name->len > GFS2_FNAMESIZE)
return ERR_PTR(-ENAMETOOLONG); return -ENAMETOOLONG;
gfs2_holder_reinit(LM_ST_EXCLUSIVE, 0, ghs); error = gfs2_glock_nq_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
error = gfs2_glock_nq(ghs);
if (error) if (error)
goto fail; goto fail;
...@@ -864,7 +696,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, ...@@ -864,7 +696,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
if (error) if (error)
goto fail_gunlock; goto fail_gunlock;
error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, &bh); error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation, dev, symname, size, &bh);
if (error) if (error)
goto fail_gunlock2; goto fail_gunlock2;
...@@ -891,118 +723,1170 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, ...@@ -891,118 +723,1170 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
if (bh) if (bh)
brelse(bh); brelse(bh);
return inode;
gfs2_trans_end(sdp);
if (dip->i_alloc->al_rgd)
gfs2_inplace_release(dip);
gfs2_quota_unlock(dip);
gfs2_alloc_put(dip);
gfs2_glock_dq_uninit_m(2, ghs);
mark_inode_dirty(inode);
d_instantiate(dentry, inode);
return 0;
fail_gunlock2: fail_gunlock2:
gfs2_glock_dq_uninit(ghs + 1); gfs2_glock_dq_uninit(ghs + 1);
if (inode && !IS_ERR(inode)) if (inode && !IS_ERR(inode))
iput(inode); iput(inode);
fail_gunlock: fail_gunlock:
gfs2_glock_dq(ghs); gfs2_glock_dq_uninit(ghs);
fail: fail:
if (bh) if (bh)
brelse(bh); brelse(bh);
return ERR_PTR(error); return error;
} }
static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) /**
* gfs2_create - Create a file
* @dir: The directory in which to create the file
* @dentry: The dentry of the new file
* @mode: The mode of the new file
*
* Returns: errno
*/
static int gfs2_create(struct inode *dir, struct dentry *dentry,
int mode, struct nameidata *nd)
{ {
struct inode *inode = &ip->i_inode; struct inode *inode;
int ret;
for (;;) {
ret = gfs2_create_inode(dir, dentry, S_IFREG | mode, 0, NULL, 0);
if (ret != -EEXIST || (nd && (nd->flags & LOOKUP_EXCL)))
return ret;
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
if (inode) {
if (!IS_ERR(inode))
break;
return PTR_ERR(inode);
}
}
d_instantiate(dentry, inode);
return 0;
}
/**
* gfs2_lookup - Look up a filename in a directory and return its inode
* @dir: The directory inode
* @dentry: The dentry of the new inode
* @nd: passed from Linux VFS, ignored by us
*
* Called by the VFS layer. Lock dir and call gfs2_lookupi()
*
* Returns: errno
*/
static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct inode *inode = NULL;
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
if (inode && IS_ERR(inode))
return ERR_CAST(inode);
if (inode) {
struct gfs2_glock *gl = GFS2_I(inode)->i_gl;
struct gfs2_holder gh;
int error;
error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
if (error) {
iput(inode);
return ERR_PTR(error);
}
gfs2_glock_dq_uninit(&gh);
return d_splice_alias(inode, dentry);
}
d_add(dentry, inode);
return NULL;
}
/**
* gfs2_link - Link to a file
* @old_dentry: The inode to link
* @dir: Add link to this directory
* @dentry: The name of the link
*
* Link the inode in "old_dentry" into the directory "dir" with the
* name in "dentry".
*
* Returns: errno
*/
static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct inode *inode = old_dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder ghs[2];
struct buffer_head *dibh; struct buffer_head *dibh;
int alloc_required;
int error; int error;
if (S_ISDIR(inode->i_mode))
return -EPERM;
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
error = gfs2_glock_nq(ghs); /* parent */
if (error)
goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = -ENOENT;
if (inode->i_nlink == 0)
goto out_gunlock;
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
if (error)
goto out_gunlock;
error = gfs2_dir_check(dir, &dentry->d_name, NULL);
switch (error) {
case -ENOENT:
break;
case 0:
error = -EEXIST;
default:
goto out_gunlock;
}
error = -EINVAL;
if (!dip->i_inode.i_nlink)
goto out_gunlock;
error = -EFBIG;
if (dip->i_entries == (u32)-1)
goto out_gunlock;
error = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
goto out_gunlock;
error = -EINVAL;
if (!ip->i_inode.i_nlink)
goto out_gunlock;
error = -EMLINK;
if (ip->i_inode.i_nlink == (u32)-1)
goto out_gunlock;
alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name);
if (error < 0)
goto out_gunlock;
error = 0;
if (alloc_required) {
struct gfs2_alloc *al = gfs2_alloc_get(dip);
if (!al) {
error = -ENOMEM;
goto out_gunlock;
}
error = gfs2_quota_lock_check(dip);
if (error)
goto out_alloc;
al->al_requested = sdp->sd_max_dirres;
error = gfs2_inplace_reserve(dip);
if (error)
goto out_gunlock_q;
error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
gfs2_rg_blocks(al) +
2 * RES_DINODE + RES_STATFS +
RES_QUOTA, 0);
if (error)
goto out_ipres;
} else {
error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0);
if (error)
goto out_ipres;
}
error = gfs2_meta_inode_buffer(ip, &dibh); error = gfs2_meta_inode_buffer(ip, &dibh);
if (error) if (error)
return error; goto out_end_trans;
error = gfs2_dir_add(dir, &dentry->d_name, ip);
if (error)
goto out_brelse;
setattr_copy(inode, attr);
mark_inode_dirty(inode);
gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_trans_add_bh(ip->i_gl, dibh, 1);
inc_nlink(&ip->i_inode);
ip->i_inode.i_ctime = CURRENT_TIME;
gfs2_dinode_out(ip, dibh->b_data); gfs2_dinode_out(ip, dibh->b_data);
mark_inode_dirty(&ip->i_inode);
out_brelse:
brelse(dibh); brelse(dibh);
return 0; out_end_trans:
gfs2_trans_end(sdp);
out_ipres:
if (alloc_required)
gfs2_inplace_release(dip);
out_gunlock_q:
if (alloc_required)
gfs2_quota_unlock(dip);
out_alloc:
if (alloc_required)
gfs2_alloc_put(dip);
out_gunlock:
gfs2_glock_dq(ghs + 1);
out_child:
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs);
gfs2_holder_uninit(ghs + 1);
if (!error) {
ihold(inode);
d_instantiate(dentry, inode);
mark_inode_dirty(inode);
}
return error;
} }
/** /*
* gfs2_setattr_simple - * gfs2_unlink_ok - check to see that a inode is still in a directory
* @ip: * @dip: the directory
* @attr: * @name: the name of the file
* @ip: the inode
* *
* Called with a reference on the vnode. * Assumes that the lock on (at least) @dip is held.
* *
* Returns: errno * Returns: 0 if the parent/child relationship is correct, errno if it isn't
*/ */
int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
const struct gfs2_inode *ip)
{ {
int error; int error;
if (current->journal_info) if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode))
return __gfs2_setattr_simple(ip, attr); return -EPERM;
error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0); if ((dip->i_inode.i_mode & S_ISVTX) &&
dip->i_inode.i_uid != current_fsuid() &&
ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER))
return -EPERM;
if (IS_APPEND(&dip->i_inode))
return -EPERM;
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
if (error) if (error)
return error; return error;
error = __gfs2_setattr_simple(ip, attr); error = gfs2_dir_check(&dip->i_inode, name, ip);
gfs2_trans_end(GFS2_SB(&ip->i_inode)); if (error)
return error; return error;
}
void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf) return 0;
{
struct gfs2_dinode *str = buf;
str->di_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
str->di_header.mh_type = cpu_to_be32(GFS2_METATYPE_DI);
str->di_header.mh_format = cpu_to_be32(GFS2_FORMAT_DI);
str->di_num.no_addr = cpu_to_be64(ip->i_no_addr);
str->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino);
str->di_mode = cpu_to_be32(ip->i_inode.i_mode);
str->di_uid = cpu_to_be32(ip->i_inode.i_uid);
str->di_gid = cpu_to_be32(ip->i_inode.i_gid);
str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink);
str->di_size = cpu_to_be64(i_size_read(&ip->i_inode));
str->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode));
str->di_atime = cpu_to_be64(ip->i_inode.i_atime.tv_sec);
str->di_mtime = cpu_to_be64(ip->i_inode.i_mtime.tv_sec);
str->di_ctime = cpu_to_be64(ip->i_inode.i_ctime.tv_sec);
str->di_goal_meta = cpu_to_be64(ip->i_goal);
str->di_goal_data = cpu_to_be64(ip->i_goal);
str->di_generation = cpu_to_be64(ip->i_generation);
str->di_flags = cpu_to_be32(ip->i_diskflags);
str->di_height = cpu_to_be16(ip->i_height);
str->di_payload_format = cpu_to_be32(S_ISDIR(ip->i_inode.i_mode) &&
!(ip->i_diskflags & GFS2_DIF_EXHASH) ?
GFS2_FORMAT_DE : 0);
str->di_depth = cpu_to_be16(ip->i_depth);
str->di_entries = cpu_to_be32(ip->i_entries);
str->di_eattr = cpu_to_be64(ip->i_eattr);
str->di_atime_nsec = cpu_to_be32(ip->i_inode.i_atime.tv_nsec);
str->di_mtime_nsec = cpu_to_be32(ip->i_inode.i_mtime.tv_nsec);
str->di_ctime_nsec = cpu_to_be32(ip->i_inode.i_ctime.tv_nsec);
}
void gfs2_dinode_print(const struct gfs2_inode *ip)
{
printk(KERN_INFO " no_formal_ino = %llu\n",
(unsigned long long)ip->i_no_formal_ino);
printk(KERN_INFO " no_addr = %llu\n",
(unsigned long long)ip->i_no_addr);
printk(KERN_INFO " i_size = %llu\n",
(unsigned long long)i_size_read(&ip->i_inode));
printk(KERN_INFO " blocks = %llu\n",
(unsigned long long)gfs2_get_inode_blocks(&ip->i_inode));
printk(KERN_INFO " i_goal = %llu\n",
(unsigned long long)ip->i_goal);
printk(KERN_INFO " i_diskflags = 0x%.8X\n", ip->i_diskflags);
printk(KERN_INFO " i_height = %u\n", ip->i_height);
printk(KERN_INFO " i_depth = %u\n", ip->i_depth);
printk(KERN_INFO " i_entries = %u\n", ip->i_entries);
printk(KERN_INFO " i_eattr = %llu\n",
(unsigned long long)ip->i_eattr);
} }
/**
* gfs2_unlink_inode - Removes an inode from its parent dir and unlinks it
* @dip: The parent directory
* @name: The name of the entry in the parent directory
* @bh: The inode buffer for the inode to be removed
* @inode: The inode to be removed
*
* Called with all the locks and in a transaction. This will only be
* called for a directory after it has been checked to ensure it is empty.
*
* Returns: 0 on success, or an error
*/
static int gfs2_unlink_inode(struct gfs2_inode *dip,
const struct dentry *dentry,
struct buffer_head *bh)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
int error;
error = gfs2_dir_del(dip, dentry);
if (error)
return error;
ip->i_entries = 0;
inode->i_ctime = CURRENT_TIME;
if (S_ISDIR(inode->i_mode))
clear_nlink(inode);
else
drop_nlink(inode);
gfs2_trans_add_bh(ip->i_gl, bh, 1);
gfs2_dinode_out(ip, bh->b_data);
mark_inode_dirty(inode);
if (inode->i_nlink == 0)
gfs2_unlink_di(inode);
return 0;
}
/**
* gfs2_unlink - Unlink an inode (this does rmdir as well)
* @dir: The inode of the directory containing the inode to unlink
* @dentry: The file itself
*
* This routine uses the type of the inode as a flag to figure out
* whether this is an unlink or an rmdir.
*
* Returns: errno
*/
static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct buffer_head *bh;
struct gfs2_holder ghs[3];
struct gfs2_rgrpd *rgd;
struct gfs2_holder ri_gh;
int error;
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
return error;
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr);
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2);
error = gfs2_glock_nq(ghs); /* parent */
if (error)
goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = -ENOENT;
if (inode->i_nlink == 0)
goto out_rgrp;
if (S_ISDIR(inode->i_mode)) {
error = -ENOTEMPTY;
if (ip->i_entries > 2 || inode->i_nlink > 2)
goto out_rgrp;
}
error = gfs2_glock_nq(ghs + 2); /* rgrp */
if (error)
goto out_rgrp;
error = gfs2_unlink_ok(dip, &dentry->d_name, ip);
if (error)
goto out_gunlock;
error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0);
if (error)
goto out_gunlock;
error = gfs2_meta_inode_buffer(ip, &bh);
if (error)
goto out_end_trans;
error = gfs2_unlink_inode(dip, dentry, bh);
brelse(bh);
out_end_trans:
gfs2_trans_end(sdp);
out_gunlock:
gfs2_glock_dq(ghs + 2);
out_rgrp:
gfs2_holder_uninit(ghs + 2);
gfs2_glock_dq(ghs + 1);
out_child:
gfs2_holder_uninit(ghs + 1);
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs);
gfs2_glock_dq_uninit(&ri_gh);
return error;
}
/**
* gfs2_symlink - Create a symlink
* @dir: The directory to create the symlink in
* @dentry: The dentry to put the symlink in
* @symname: The thing which the link points to
*
* Returns: errno
*/
static int gfs2_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
{
struct gfs2_sbd *sdp = GFS2_SB(dir);
unsigned int size;
size = strlen(symname);
if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1)
return -ENAMETOOLONG;
return gfs2_create_inode(dir, dentry, S_IFLNK | S_IRWXUGO, 0, symname, size);
}
/**
* gfs2_mkdir - Make a directory
* @dir: The parent directory of the new one
* @dentry: The dentry of the new directory
* @mode: The mode of the new directory
*
* Returns: errno
*/
static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
return gfs2_create_inode(dir, dentry, S_IFDIR | mode, 0, NULL, 0);
}
/**
* gfs2_mknod - Make a special file
* @dir: The directory in which the special file will reside
* @dentry: The dentry of the special file
* @mode: The mode of the special file
* @dev: The device specification of the special file
*
*/
static int gfs2_mknod(struct inode *dir, struct dentry *dentry, int mode,
dev_t dev)
{
return gfs2_create_inode(dir, dentry, mode, dev, NULL, 0);
}
/*
* gfs2_ok_to_move - check if it's ok to move a directory to another directory
* @this: move this
* @to: to here
*
* Follow @to back to the root and make sure we don't encounter @this
* Assumes we already hold the rename lock.
*
* Returns: errno
*/
static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
{
struct inode *dir = &to->i_inode;
struct super_block *sb = dir->i_sb;
struct inode *tmp;
int error = 0;
igrab(dir);
for (;;) {
if (dir == &this->i_inode) {
error = -EINVAL;
break;
}
if (dir == sb->s_root->d_inode) {
error = 0;
break;
}
tmp = gfs2_lookupi(dir, &gfs2_qdotdot, 1);
if (IS_ERR(tmp)) {
error = PTR_ERR(tmp);
break;
}
iput(dir);
dir = tmp;
}
iput(dir);
return error;
}
/**
* gfs2_rename - Rename a file
* @odir: Parent directory of old file name
* @odentry: The old dentry of the file
* @ndir: Parent directory of new file name
* @ndentry: The new dentry of the file
*
* Returns: errno
*/
static int gfs2_rename(struct inode *odir, struct dentry *odentry,
struct inode *ndir, struct dentry *ndentry)
{
struct gfs2_inode *odip = GFS2_I(odir);
struct gfs2_inode *ndip = GFS2_I(ndir);
struct gfs2_inode *ip = GFS2_I(odentry->d_inode);
struct gfs2_inode *nip = NULL;
struct gfs2_sbd *sdp = GFS2_SB(odir);
struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }, ri_gh;
struct gfs2_rgrpd *nrgd;
unsigned int num_gh;
int dir_rename = 0;
int alloc_required = 0;
unsigned int x;
int error;
if (ndentry->d_inode) {
nip = GFS2_I(ndentry->d_inode);
if (ip == nip)
return 0;
}
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
return error;
if (odip != ndip) {
error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE,
0, &r_gh);
if (error)
goto out;
if (S_ISDIR(ip->i_inode.i_mode)) {
dir_rename = 1;
/* don't move a dirctory into it's subdir */
error = gfs2_ok_to_move(ip, ndip);
if (error)
goto out_gunlock_r;
}
}
num_gh = 1;
gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
if (odip != ndip) {
gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
num_gh++;
}
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
num_gh++;
if (nip) {
gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
num_gh++;
/* grab the resource lock for unlink flag twiddling
* this is the case of the target file already existing
* so we unlink before doing the rename
*/
nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr);
if (nrgd)
gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++);
}
for (x = 0; x < num_gh; x++) {
error = gfs2_glock_nq(ghs + x);
if (error)
goto out_gunlock;
}
error = -ENOENT;
if (ip->i_inode.i_nlink == 0)
goto out_gunlock;
/* Check out the old directory */
error = gfs2_unlink_ok(odip, &odentry->d_name, ip);
if (error)
goto out_gunlock;
/* Check out the new directory */
if (nip) {
error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip);
if (error)
goto out_gunlock;
if (nip->i_inode.i_nlink == 0) {
error = -EAGAIN;
goto out_gunlock;
}
if (S_ISDIR(nip->i_inode.i_mode)) {
if (nip->i_entries < 2) {
gfs2_consist_inode(nip);
error = -EIO;
goto out_gunlock;
}
if (nip->i_entries > 2) {
error = -ENOTEMPTY;
goto out_gunlock;
}
}
} else {
error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
if (error)
goto out_gunlock;
error = gfs2_dir_check(ndir, &ndentry->d_name, NULL);
switch (error) {
case -ENOENT:
error = 0;
break;
case 0:
error = -EEXIST;
default:
goto out_gunlock;
};
if (odip != ndip) {
if (!ndip->i_inode.i_nlink) {
error = -ENOENT;
goto out_gunlock;
}
if (ndip->i_entries == (u32)-1) {
error = -EFBIG;
goto out_gunlock;
}
if (S_ISDIR(ip->i_inode.i_mode) &&
ndip->i_inode.i_nlink == (u32)-1) {
error = -EMLINK;
goto out_gunlock;
}
}
}
/* Check out the dir to be renamed */
if (dir_rename) {
error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
if (error)
goto out_gunlock;
}
if (nip == NULL)
alloc_required = gfs2_diradd_alloc_required(ndir, &ndentry->d_name);
error = alloc_required;
if (error < 0)
goto out_gunlock;
error = 0;
if (alloc_required) {
struct gfs2_alloc *al = gfs2_alloc_get(ndip);
if (!al) {
error = -ENOMEM;
goto out_gunlock;
}
error = gfs2_quota_lock_check(ndip);
if (error)
goto out_alloc;
al->al_requested = sdp->sd_max_dirres;
error = gfs2_inplace_reserve_ri(ndip);
if (error)
goto out_gunlock_q;
error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
gfs2_rg_blocks(al) +
4 * RES_DINODE + 4 * RES_LEAF +
RES_STATFS + RES_QUOTA + 4, 0);
if (error)
goto out_ipreserv;
} else {
error = gfs2_trans_begin(sdp, 4 * RES_DINODE +
5 * RES_LEAF + 4, 0);
if (error)
goto out_gunlock;
}
/* Remove the target file, if it exists */
if (nip) {
struct buffer_head *bh;
error = gfs2_meta_inode_buffer(nip, &bh);
if (error)
goto out_end_trans;
error = gfs2_unlink_inode(ndip, ndentry, bh);
brelse(bh);
}
if (dir_rename) {
error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR);
if (error)
goto out_end_trans;
} else {
struct buffer_head *dibh;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
goto out_end_trans;
ip->i_inode.i_ctime = CURRENT_TIME;
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh);
}
error = gfs2_dir_del(odip, odentry);
if (error)
goto out_end_trans;
error = gfs2_dir_add(ndir, &ndentry->d_name, ip);
if (error)
goto out_end_trans;
out_end_trans:
gfs2_trans_end(sdp);
out_ipreserv:
if (alloc_required)
gfs2_inplace_release(ndip);
out_gunlock_q:
if (alloc_required)
gfs2_quota_unlock(ndip);
out_alloc:
if (alloc_required)
gfs2_alloc_put(ndip);
out_gunlock:
while (x--) {
gfs2_glock_dq(ghs + x);
gfs2_holder_uninit(ghs + x);
}
out_gunlock_r:
if (r_gh.gh_gl)
gfs2_glock_dq_uninit(&r_gh);
out:
gfs2_glock_dq_uninit(&ri_gh);
return error;
}
/**
* gfs2_follow_link - Follow a symbolic link
* @dentry: The dentry of the link
* @nd: Data that we pass to vfs_follow_link()
*
* This can handle symlinks of any size.
*
* Returns: 0 on success or error code
*/
static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
struct gfs2_holder i_gh;
struct buffer_head *dibh;
unsigned int size;
char *buf;
int error;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
error = gfs2_glock_nq(&i_gh);
if (error) {
gfs2_holder_uninit(&i_gh);
nd_set_link(nd, ERR_PTR(error));
return NULL;
}
size = (unsigned int)i_size_read(&ip->i_inode);
if (size == 0) {
gfs2_consist_inode(ip);
buf = ERR_PTR(-EIO);
goto out;
}
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error) {
buf = ERR_PTR(error);
goto out;
}
buf = kzalloc(size + 1, GFP_NOFS);
if (!buf)
buf = ERR_PTR(-ENOMEM);
else
memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), size);
brelse(dibh);
out:
gfs2_glock_dq_uninit(&i_gh);
nd_set_link(nd, buf);
return NULL;
}
static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
char *s = nd_get_link(nd);
if (!IS_ERR(s))
kfree(s);
}
/**
* gfs2_permission -
* @inode: The inode
* @mask: The mask to be tested
* @flags: Indicates whether this is an RCU path walk or not
*
* This may be called from the VFS directly, or from within GFS2 with the
* inode locked, so we look to see if the glock is already locked and only
* lock the glock if its not already been done.
*
* Returns: errno
*/
int gfs2_permission(struct inode *inode, int mask, unsigned int flags)
{
struct gfs2_inode *ip;
struct gfs2_holder i_gh;
int error;
int unlock = 0;
ip = GFS2_I(inode);
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
if (error)
return error;
unlock = 1;
}
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
error = -EACCES;
else
error = generic_permission(inode, mask, flags, gfs2_check_acl);
if (unlock)
gfs2_glock_dq_uninit(&i_gh);
return error;
}
static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr)
{
struct inode *inode = &ip->i_inode;
struct buffer_head *dibh;
int error;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
setattr_copy(inode, attr);
mark_inode_dirty(inode);
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh);
return 0;
}
/**
* gfs2_setattr_simple -
* @ip:
* @attr:
*
* Returns: errno
*/
int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr)
{
int error;
if (current->journal_info)
return __gfs2_setattr_simple(ip, attr);
error = gfs2_trans_begin(GFS2_SB(&ip->i_inode), RES_DINODE, 0);
if (error)
return error;
error = __gfs2_setattr_simple(ip, attr);
gfs2_trans_end(GFS2_SB(&ip->i_inode));
return error;
}
static int setattr_chown(struct inode *inode, struct iattr *attr)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
u32 ouid, ogid, nuid, ngid;
int error;
ouid = inode->i_uid;
ogid = inode->i_gid;
nuid = attr->ia_uid;
ngid = attr->ia_gid;
if (!(attr->ia_valid & ATTR_UID) || ouid == nuid)
ouid = nuid = NO_QUOTA_CHANGE;
if (!(attr->ia_valid & ATTR_GID) || ogid == ngid)
ogid = ngid = NO_QUOTA_CHANGE;
if (!gfs2_alloc_get(ip))
return -ENOMEM;
error = gfs2_quota_lock(ip, nuid, ngid);
if (error)
goto out_alloc;
if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) {
error = gfs2_quota_check(ip, nuid, ngid);
if (error)
goto out_gunlock_q;
}
error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_QUOTA, 0);
if (error)
goto out_gunlock_q;
error = gfs2_setattr_simple(ip, attr);
if (error)
goto out_end_trans;
if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) {
u64 blocks = gfs2_get_inode_blocks(&ip->i_inode);
gfs2_quota_change(ip, -blocks, ouid, ogid);
gfs2_quota_change(ip, blocks, nuid, ngid);
}
out_end_trans:
gfs2_trans_end(sdp);
out_gunlock_q:
gfs2_quota_unlock(ip);
out_alloc:
gfs2_alloc_put(ip);
return error;
}
/**
* gfs2_setattr - Change attributes on an inode
* @dentry: The dentry which is changing
* @attr: The structure describing the change
*
* The VFS layer wants to change one or more of an inodes attributes. Write
* that change out to disk.
*
* Returns: errno
*/
static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder i_gh;
int error;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
if (error)
return error;
error = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
goto out;
error = inode_change_ok(inode, attr);
if (error)
goto out;
if (attr->ia_valid & ATTR_SIZE)
error = gfs2_setattr_size(inode, attr->ia_size);
else if (attr->ia_valid & (ATTR_UID | ATTR_GID))
error = setattr_chown(inode, attr);
else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode))
error = gfs2_acl_chmod(ip, attr);
else
error = gfs2_setattr_simple(ip, attr);
out:
gfs2_glock_dq_uninit(&i_gh);
if (!error)
mark_inode_dirty(inode);
return error;
}
/**
* gfs2_getattr - Read out an inode's attributes
* @mnt: The vfsmount the inode is being accessed from
* @dentry: The dentry to stat
* @stat: The inode's stats
*
* This may be called from the VFS directly, or from within GFS2 with the
* inode locked, so we look to see if the glock is already locked and only
* lock the glock if its not already been done. Note that its the NFS
* readdirplus operation which causes this to be called (from filldir)
* with the glock already held.
*
* Returns: errno
*/
static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int error;
int unlock = 0;
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
if (error)
return error;
unlock = 1;
}
generic_fillattr(inode, stat);
if (unlock)
gfs2_glock_dq_uninit(&gh);
return 0;
}
static int gfs2_setxattr(struct dentry *dentry, const char *name,
const void *data, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
ret = generic_setxattr(dentry, name, data, size, flags);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
return ret;
}
static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name,
void *data, size_t size)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
ret = generic_getxattr(dentry, name, data, size);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
return ret;
}
static int gfs2_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
ret = generic_removexattr(dentry, name);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
return ret;
}
static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
if (ret)
return ret;
mutex_lock(&inode->i_mutex);
ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
if (ret)
goto out;
if (gfs2_is_stuffed(ip)) {
u64 phys = ip->i_no_addr << inode->i_blkbits;
u64 size = i_size_read(inode);
u32 flags = FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_NOT_ALIGNED|
FIEMAP_EXTENT_DATA_INLINE;
phys += sizeof(struct gfs2_dinode);
phys += start;
if (start + len > size)
len = size - start;
if (start < size)
ret = fiemap_fill_next_extent(fieinfo, start, phys,
len, flags);
if (ret == 1)
ret = 0;
} else {
ret = __generic_block_fiemap(inode, fieinfo, start, len,
gfs2_block_map);
}
gfs2_glock_dq_uninit(&gh);
out:
mutex_unlock(&inode->i_mutex);
return ret;
}
const struct inode_operations gfs2_file_iops = {
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
.getxattr = gfs2_getxattr,
.listxattr = gfs2_listxattr,
.removexattr = gfs2_removexattr,
.fiemap = gfs2_fiemap,
};
const struct inode_operations gfs2_dir_iops = {
.create = gfs2_create,
.lookup = gfs2_lookup,
.link = gfs2_link,
.unlink = gfs2_unlink,
.symlink = gfs2_symlink,
.mkdir = gfs2_mkdir,
.rmdir = gfs2_unlink,
.mknod = gfs2_mknod,
.rename = gfs2_rename,
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
.getxattr = gfs2_getxattr,
.listxattr = gfs2_listxattr,
.removexattr = gfs2_removexattr,
.fiemap = gfs2_fiemap,
};
const struct inode_operations gfs2_symlink_iops = {
.readlink = generic_readlink,
.follow_link = gfs2_follow_link,
.put_link = gfs2_put_link,
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
.getxattr = gfs2_getxattr,
.listxattr = gfs2_listxattr,
.removexattr = gfs2_removexattr,
.fiemap = gfs2_fiemap,
};
...@@ -102,22 +102,16 @@ extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, ...@@ -102,22 +102,16 @@ extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type,
extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr,
u64 *no_formal_ino, u64 *no_formal_ino,
unsigned int blktype); unsigned int blktype);
extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr); extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int nonblock);
extern int gfs2_inode_refresh(struct gfs2_inode *ip); extern int gfs2_inode_refresh(struct gfs2_inode *ip);
extern int gfs2_dinode_dealloc(struct gfs2_inode *inode);
extern int gfs2_change_nlink(struct gfs2_inode *ip, int diff);
extern struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name, extern struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
int is_root); int is_root);
extern struct inode *gfs2_createi(struct gfs2_holder *ghs,
const struct qstr *name,
unsigned int mode, dev_t dev);
extern int gfs2_permission(struct inode *inode, int mask, unsigned int flags); extern int gfs2_permission(struct inode *inode, int mask, unsigned int flags);
extern int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr); extern int gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr);
extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name); extern struct inode *gfs2_lookup_simple(struct inode *dip, const char *name);
extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf); extern void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf);
extern void gfs2_dinode_print(const struct gfs2_inode *ip);
extern const struct inode_operations gfs2_file_iops; extern const struct inode_operations gfs2_file_iops;
extern const struct inode_operations gfs2_dir_iops; extern const struct inode_operations gfs2_dir_iops;
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/bio.h> #include <linux/bio.h>
#include <linux/writeback.h>
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
...@@ -83,55 +84,97 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd) ...@@ -83,55 +84,97 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
/** /**
* gfs2_ail1_start_one - Start I/O on a part of the AIL * gfs2_ail1_start_one - Start I/O on a part of the AIL
* @sdp: the filesystem * @sdp: the filesystem
* @tr: the part of the AIL * @wbc: The writeback control structure
* @ai: The ail structure
* *
*/ */
static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) static int gfs2_ail1_start_one(struct gfs2_sbd *sdp,
struct writeback_control *wbc,
struct gfs2_ail *ai)
__releases(&sdp->sd_ail_lock) __releases(&sdp->sd_ail_lock)
__acquires(&sdp->sd_ail_lock) __acquires(&sdp->sd_ail_lock)
{ {
struct gfs2_glock *gl = NULL;
struct address_space *mapping;
struct gfs2_bufdata *bd, *s; struct gfs2_bufdata *bd, *s;
struct buffer_head *bh; struct buffer_head *bh;
int retry;
do { list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, bd_ail_st_list) {
retry = 0; bh = bd->bd_bh;
list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, gfs2_assert(sdp, bd->bd_ail == ai);
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_busy(bh)) { if (!buffer_dirty(bh))
if (!buffer_uptodate(bh)) continue;
gfs2_io_error_bh(sdp, bh); if (gl == bd->bd_gl)
list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); continue;
continue; gl = bd->bd_gl;
} list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
mapping = bh->b_page->mapping;
if (!mapping)
continue;
spin_unlock(&sdp->sd_ail_lock);
generic_writepages(mapping, wbc);
spin_lock(&sdp->sd_ail_lock);
if (wbc->nr_to_write <= 0)
break;
return 1;
}
if (!buffer_dirty(bh)) return 0;
continue; }
list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
get_bh(bh); /**
spin_unlock(&sdp->sd_ail_lock); * gfs2_ail1_flush - start writeback of some ail1 entries
lock_buffer(bh); * @sdp: The super block
if (test_clear_buffer_dirty(bh)) { * @wbc: The writeback control structure
bh->b_end_io = end_buffer_write_sync; *
submit_bh(WRITE_SYNC, bh); * Writes back some ail1 entries, according to the limits in the
} else { * writeback control structure
unlock_buffer(bh); */
brelse(bh);
} void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
spin_lock(&sdp->sd_ail_lock); {
struct list_head *head = &sdp->sd_ail1_list;
retry = 1; struct gfs2_ail *ai;
trace_gfs2_ail_flush(sdp, wbc, 1);
spin_lock(&sdp->sd_ail_lock);
restart:
list_for_each_entry_reverse(ai, head, ai_list) {
if (wbc->nr_to_write <= 0)
break; break;
} if (gfs2_ail1_start_one(sdp, wbc, ai))
} while (retry); goto restart;
}
spin_unlock(&sdp->sd_ail_lock);
trace_gfs2_ail_flush(sdp, wbc, 0);
}
/**
* gfs2_ail1_start - start writeback of all ail1 entries
* @sdp: The superblock
*/
static void gfs2_ail1_start(struct gfs2_sbd *sdp)
{
struct writeback_control wbc = {
.sync_mode = WB_SYNC_NONE,
.nr_to_write = LONG_MAX,
.range_start = 0,
.range_end = LLONG_MAX,
};
return gfs2_ail1_flush(sdp, &wbc);
} }
/** /**
...@@ -141,7 +184,7 @@ __acquires(&sdp->sd_ail_lock) ...@@ -141,7 +184,7 @@ __acquires(&sdp->sd_ail_lock)
* *
*/ */
static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags) static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{ {
struct gfs2_bufdata *bd, *s; struct gfs2_bufdata *bd, *s;
struct buffer_head *bh; struct buffer_head *bh;
...@@ -149,71 +192,37 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl ...@@ -149,71 +192,37 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,
bd_ail_st_list) { bd_ail_st_list) {
bh = bd->bd_bh; bh = bd->bd_bh;
gfs2_assert(sdp, bd->bd_ail == ai); gfs2_assert(sdp, bd->bd_ail == ai);
if (buffer_busy(bh))
if (buffer_busy(bh)) { continue;
if (flags & DIO_ALL)
continue;
else
break;
}
if (!buffer_uptodate(bh)) if (!buffer_uptodate(bh))
gfs2_io_error_bh(sdp, bh); gfs2_io_error_bh(sdp, bh);
list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
} }
return list_empty(&ai->ai_ail1_list);
} }
static void gfs2_ail1_start(struct gfs2_sbd *sdp) /**
{ * gfs2_ail1_empty - Try to empty the ail1 lists
struct list_head *head; * @sdp: The superblock
u64 sync_gen; *
struct gfs2_ail *ai; * Tries to empty the ail1 lists, starting with the oldest first
int done = 0; */
spin_lock(&sdp->sd_ail_lock);
head = &sdp->sd_ail1_list;
if (list_empty(head)) {
spin_unlock(&sdp->sd_ail_lock);
return;
}
sync_gen = sdp->sd_ail_sync_gen++;
while(!done) {
done = 1;
list_for_each_entry_reverse(ai, head, ai_list) {
if (ai->ai_sync_gen >= sync_gen)
continue;
ai->ai_sync_gen = sync_gen;
gfs2_ail1_start_one(sdp, ai); /* This may drop ail lock */
done = 0;
break;
}
}
spin_unlock(&sdp->sd_ail_lock);
}
static int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags) static int gfs2_ail1_empty(struct gfs2_sbd *sdp)
{ {
struct gfs2_ail *ai, *s; struct gfs2_ail *ai, *s;
int ret; int ret;
spin_lock(&sdp->sd_ail_lock); spin_lock(&sdp->sd_ail_lock);
list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) { list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) {
if (gfs2_ail1_empty_one(sdp, ai, flags)) gfs2_ail1_empty_one(sdp, ai);
if (list_empty(&ai->ai_ail1_list))
list_move(&ai->ai_list, &sdp->sd_ail2_list); list_move(&ai->ai_list, &sdp->sd_ail2_list);
else if (!(flags & DIO_ALL)) else
break; break;
} }
ret = list_empty(&sdp->sd_ail1_list); ret = list_empty(&sdp->sd_ail1_list);
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
return ret; return ret;
...@@ -574,7 +583,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) ...@@ -574,7 +583,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
clear_buffer_dirty(bh); clear_buffer_dirty(bh);
gfs2_ail1_empty(sdp, 0); gfs2_ail1_empty(sdp);
tail = current_tail(sdp); tail = current_tail(sdp);
lh = (struct gfs2_log_header *)bh->b_data; lh = (struct gfs2_log_header *)bh->b_data;
...@@ -869,7 +878,7 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp) ...@@ -869,7 +878,7 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
for (;;) { for (;;) {
gfs2_ail1_start(sdp); gfs2_ail1_start(sdp);
if (gfs2_ail1_empty(sdp, DIO_ALL)) if (gfs2_ail1_empty(sdp))
break; break;
msleep(10); msleep(10);
} }
...@@ -905,17 +914,15 @@ int gfs2_logd(void *data) ...@@ -905,17 +914,15 @@ int gfs2_logd(void *data)
preflush = atomic_read(&sdp->sd_log_pinned); preflush = atomic_read(&sdp->sd_log_pinned);
if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
gfs2_ail1_empty(sdp, DIO_ALL); gfs2_ail1_empty(sdp);
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
gfs2_ail1_empty(sdp, DIO_ALL);
} }
if (gfs2_ail_flush_reqd(sdp)) { if (gfs2_ail_flush_reqd(sdp)) {
gfs2_ail1_start(sdp); gfs2_ail1_start(sdp);
io_schedule(); io_schedule();
gfs2_ail1_empty(sdp, 0); gfs2_ail1_empty(sdp);
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
gfs2_ail1_empty(sdp, DIO_ALL);
} }
wake_up(&sdp->sd_log_waitq); wake_up(&sdp->sd_log_waitq);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/writeback.h>
#include "incore.h" #include "incore.h"
/** /**
...@@ -59,6 +60,7 @@ extern struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp, ...@@ -59,6 +60,7 @@ extern struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl); extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl);
extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd); extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc);
extern void gfs2_log_shutdown(struct gfs2_sbd *sdp); extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp); extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp);
......
...@@ -40,7 +40,7 @@ static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) ...@@ -40,7 +40,7 @@ static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh)
{ {
struct gfs2_bufdata *bd; struct gfs2_bufdata *bd;
gfs2_assert_withdraw(sdp, test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)); BUG_ON(!current->journal_info);
clear_buffer_dirty(bh); clear_buffer_dirty(bh);
if (test_set_buffer_pinned(bh)) if (test_set_buffer_pinned(bh))
...@@ -65,6 +65,7 @@ static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) ...@@ -65,6 +65,7 @@ static void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh)
* @sdp: the filesystem the buffer belongs to * @sdp: the filesystem the buffer belongs to
* @bh: The buffer to unpin * @bh: The buffer to unpin
* @ai: * @ai:
* @flags: The inode dirty flags
* *
*/ */
...@@ -73,10 +74,8 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, ...@@ -73,10 +74,8 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh,
{ {
struct gfs2_bufdata *bd = bh->b_private; struct gfs2_bufdata *bd = bh->b_private;
gfs2_assert_withdraw(sdp, buffer_uptodate(bh)); BUG_ON(!buffer_uptodate(bh));
BUG_ON(!buffer_pinned(bh));
if (!buffer_pinned(bh))
gfs2_assert_withdraw(sdp, 0);
lock_buffer(bh); lock_buffer(bh);
mark_buffer_dirty(bh); mark_buffer_dirty(bh);
...@@ -95,8 +94,7 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh, ...@@ -95,8 +94,7 @@ static void gfs2_unpin(struct gfs2_sbd *sdp, struct buffer_head *bh,
list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list); list_add(&bd->bd_ail_st_list, &ai->ai_ail1_list);
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
if (test_and_clear_bit(GLF_LFLUSH, &bd->bd_gl->gl_flags)) clear_bit(GLF_LFLUSH, &bd->bd_gl->gl_flags);
gfs2_glock_schedule_for_reclaim(bd->bd_gl);
trace_gfs2_pin(bd, 0); trace_gfs2_pin(bd, 0);
unlock_buffer(bh); unlock_buffer(bh);
atomic_dec(&sdp->sd_log_pinned); atomic_dec(&sdp->sd_log_pinned);
...@@ -322,12 +320,16 @@ static void buf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass) ...@@ -322,12 +320,16 @@ static void buf_lo_after_scan(struct gfs2_jdesc *jd, int error, int pass)
static void revoke_lo_add(struct gfs2_sbd *sdp, struct gfs2_log_element *le) static void revoke_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_glock *gl = bd->bd_gl;
struct gfs2_trans *tr; struct gfs2_trans *tr;
tr = current->journal_info; tr = current->journal_info;
tr->tr_touched = 1; tr->tr_touched = 1;
tr->tr_num_revoke++; tr->tr_num_revoke++;
sdp->sd_log_num_revoke++; sdp->sd_log_num_revoke++;
atomic_inc(&gl->gl_revokes);
set_bit(GLF_LFLUSH, &gl->gl_flags);
list_add(&le->le_list, &sdp->sd_log_le_revoke); list_add(&le->le_list, &sdp->sd_log_le_revoke);
} }
...@@ -350,9 +352,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) ...@@ -350,9 +352,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp)
ld->ld_data1 = cpu_to_be32(sdp->sd_log_num_revoke); ld->ld_data1 = cpu_to_be32(sdp->sd_log_num_revoke);
offset = sizeof(struct gfs2_log_descriptor); offset = sizeof(struct gfs2_log_descriptor);
while (!list_empty(head)) { list_for_each_entry(bd, head, bd_le.le_list) {
bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list);
list_del_init(&bd->bd_le.le_list);
sdp->sd_log_num_revoke--; sdp->sd_log_num_revoke--;
if (offset + sizeof(u64) > sdp->sd_sb.sb_bsize) { if (offset + sizeof(u64) > sdp->sd_sb.sb_bsize) {
...@@ -367,8 +367,6 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) ...@@ -367,8 +367,6 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp)
} }
*(__be64 *)(bh->b_data + offset) = cpu_to_be64(bd->bd_blkno); *(__be64 *)(bh->b_data + offset) = cpu_to_be64(bd->bd_blkno);
kmem_cache_free(gfs2_bufdata_cachep, bd);
offset += sizeof(u64); offset += sizeof(u64);
} }
gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke); gfs2_assert_withdraw(sdp, !sdp->sd_log_num_revoke);
...@@ -376,6 +374,22 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp) ...@@ -376,6 +374,22 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp)
submit_bh(WRITE_SYNC, bh); submit_bh(WRITE_SYNC, bh);
} }
static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
{
struct list_head *head = &sdp->sd_log_le_revoke;
struct gfs2_bufdata *bd;
struct gfs2_glock *gl;
while (!list_empty(head)) {
bd = list_entry(head->next, struct gfs2_bufdata, bd_le.le_list);
list_del_init(&bd->bd_le.le_list);
gl = bd->bd_gl;
atomic_dec(&gl->gl_revokes);
clear_bit(GLF_LFLUSH, &gl->gl_flags);
kmem_cache_free(gfs2_bufdata_cachep, bd);
}
}
static void revoke_lo_before_scan(struct gfs2_jdesc *jd, static void revoke_lo_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head, int pass) struct gfs2_log_header_host *head, int pass)
{ {
...@@ -749,6 +763,7 @@ const struct gfs2_log_operations gfs2_buf_lops = { ...@@ -749,6 +763,7 @@ const struct gfs2_log_operations gfs2_buf_lops = {
const struct gfs2_log_operations gfs2_revoke_lops = { const struct gfs2_log_operations gfs2_revoke_lops = {
.lo_add = revoke_lo_add, .lo_add = revoke_lo_add,
.lo_before_commit = revoke_lo_before_commit, .lo_before_commit = revoke_lo_before_commit,
.lo_after_commit = revoke_lo_after_commit,
.lo_before_scan = revoke_lo_before_scan, .lo_before_scan = revoke_lo_before_scan,
.lo_scan_elements = revoke_lo_scan_elements, .lo_scan_elements = revoke_lo_scan_elements,
.lo_after_scan = revoke_lo_after_scan, .lo_after_scan = revoke_lo_after_scan,
......
...@@ -53,6 +53,7 @@ static void gfs2_init_glock_once(void *foo) ...@@ -53,6 +53,7 @@ static void gfs2_init_glock_once(void *foo)
INIT_LIST_HEAD(&gl->gl_lru); INIT_LIST_HEAD(&gl->gl_lru);
INIT_LIST_HEAD(&gl->gl_ail_list); INIT_LIST_HEAD(&gl->gl_ail_list);
atomic_set(&gl->gl_ail_count, 0); atomic_set(&gl->gl_ail_count, 0);
atomic_set(&gl->gl_revokes, 0);
} }
static void gfs2_init_gl_aspace_once(void *foo) static void gfs2_init_gl_aspace_once(void *foo)
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "rgrp.h" #include "rgrp.h"
#include "trans.h" #include "trans.h"
#include "util.h" #include "util.h"
#include "trace_gfs2.h"
static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc) static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wbc)
{ {
...@@ -310,6 +311,7 @@ void gfs2_remove_from_journal(struct buffer_head *bh, struct gfs2_trans *tr, int ...@@ -310,6 +311,7 @@ void gfs2_remove_from_journal(struct buffer_head *bh, struct gfs2_trans *tr, int
struct gfs2_bufdata *bd = bh->b_private; struct gfs2_bufdata *bd = bh->b_private;
if (test_clear_buffer_pinned(bh)) { if (test_clear_buffer_pinned(bh)) {
trace_gfs2_pin(bd, 0);
atomic_dec(&sdp->sd_log_pinned); atomic_dec(&sdp->sd_log_pinned);
list_del_init(&bd->bd_le.le_list); list_del_init(&bd->bd_le.le_list);
if (meta) { if (meta) {
......
...@@ -77,8 +77,6 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen); ...@@ -77,8 +77,6 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen);
#define buffer_busy(bh) \ #define buffer_busy(bh) \
((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock) | (1ul << BH_Pinned))) ((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)))
#endif /* __DIO_DOT_H__ */ #endif /* __DIO_DOT_H__ */
...@@ -126,8 +126,10 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) ...@@ -126,8 +126,10 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
* changed. * changed.
*/ */
static int gfs2_check_sb(struct gfs2_sbd *sdp, struct gfs2_sb_host *sb, int silent) static int gfs2_check_sb(struct gfs2_sbd *sdp, int silent)
{ {
struct gfs2_sb_host *sb = &sdp->sd_sb;
if (sb->sb_magic != GFS2_MAGIC || if (sb->sb_magic != GFS2_MAGIC ||
sb->sb_type != GFS2_METATYPE_SB) { sb->sb_type != GFS2_METATYPE_SB) {
if (!silent) if (!silent)
...@@ -157,8 +159,10 @@ static void end_bio_io_page(struct bio *bio, int error) ...@@ -157,8 +159,10 @@ static void end_bio_io_page(struct bio *bio, int error)
unlock_page(page); unlock_page(page);
} }
static void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf) static void gfs2_sb_in(struct gfs2_sbd *sdp, const void *buf)
{ {
struct gfs2_sb_host *sb = &sdp->sd_sb;
struct super_block *s = sdp->sd_vfs;
const struct gfs2_sb *str = buf; const struct gfs2_sb *str = buf;
sb->sb_magic = be32_to_cpu(str->sb_header.mh_magic); sb->sb_magic = be32_to_cpu(str->sb_header.mh_magic);
...@@ -175,7 +179,7 @@ static void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf) ...@@ -175,7 +179,7 @@ static void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf)
memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN); memcpy(sb->sb_lockproto, str->sb_lockproto, GFS2_LOCKNAME_LEN);
memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN); memcpy(sb->sb_locktable, str->sb_locktable, GFS2_LOCKNAME_LEN);
memcpy(sb->sb_uuid, str->sb_uuid, 16); memcpy(s->s_uuid, str->sb_uuid, 16);
} }
/** /**
...@@ -197,7 +201,7 @@ static void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf) ...@@ -197,7 +201,7 @@ static void gfs2_sb_in(struct gfs2_sb_host *sb, const void *buf)
* Returns: 0 on success or error * Returns: 0 on success or error
*/ */
static int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector) static int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector, int silent)
{ {
struct super_block *sb = sdp->sd_vfs; struct super_block *sb = sdp->sd_vfs;
struct gfs2_sb *p; struct gfs2_sb *p;
...@@ -227,10 +231,10 @@ static int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector) ...@@ -227,10 +231,10 @@ static int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector)
return -EIO; return -EIO;
} }
p = kmap(page); p = kmap(page);
gfs2_sb_in(&sdp->sd_sb, p); gfs2_sb_in(sdp, p);
kunmap(page); kunmap(page);
__free_page(page); __free_page(page);
return 0; return gfs2_check_sb(sdp, silent);
} }
/** /**
...@@ -247,17 +251,13 @@ static int gfs2_read_sb(struct gfs2_sbd *sdp, int silent) ...@@ -247,17 +251,13 @@ static int gfs2_read_sb(struct gfs2_sbd *sdp, int silent)
unsigned int x; unsigned int x;
int error; int error;
error = gfs2_read_super(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); error = gfs2_read_super(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift, silent);
if (error) { if (error) {
if (!silent) if (!silent)
fs_err(sdp, "can't read superblock\n"); fs_err(sdp, "can't read superblock\n");
return error; return error;
} }
error = gfs2_check_sb(sdp, &sdp->sd_sb, silent);
if (error)
return error;
sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift - sdp->sd_fsb2bb_shift = sdp->sd_sb.sb_bsize_shift -
GFS2_BASIC_BLOCK_SHIFT; GFS2_BASIC_BLOCK_SHIFT;
sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift; sdp->sd_fsb2bb = 1 << sdp->sd_fsb2bb_shift;
...@@ -340,14 +340,10 @@ static int init_names(struct gfs2_sbd *sdp, int silent) ...@@ -340,14 +340,10 @@ static int init_names(struct gfs2_sbd *sdp, int silent)
/* Try to autodetect */ /* Try to autodetect */
if (!proto[0] || !table[0]) { if (!proto[0] || !table[0]) {
error = gfs2_read_super(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift); error = gfs2_read_super(sdp, GFS2_SB_ADDR >> sdp->sd_fsb2bb_shift, silent);
if (error) if (error)
return error; return error;
error = gfs2_check_sb(sdp, &sdp->sd_sb, silent);
if (error)
goto out;
if (!proto[0]) if (!proto[0])
proto = sdp->sd_sb.sb_lockproto; proto = sdp->sd_sb.sb_lockproto;
if (!table[0]) if (!table[0])
...@@ -364,7 +360,6 @@ static int init_names(struct gfs2_sbd *sdp, int silent) ...@@ -364,7 +360,6 @@ static int init_names(struct gfs2_sbd *sdp, int silent)
while ((table = strchr(table, '/'))) while ((table = strchr(table, '/')))
*table = '_'; *table = '_';
out:
return error; return error;
} }
...@@ -1119,8 +1114,7 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent ...@@ -1119,8 +1114,7 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent
if (sdp->sd_args.ar_statfs_quantum) { if (sdp->sd_args.ar_statfs_quantum) {
sdp->sd_tune.gt_statfs_slow = 0; sdp->sd_tune.gt_statfs_slow = 0;
sdp->sd_tune.gt_statfs_quantum = sdp->sd_args.ar_statfs_quantum; sdp->sd_tune.gt_statfs_quantum = sdp->sd_args.ar_statfs_quantum;
} } else {
else {
sdp->sd_tune.gt_statfs_slow = 1; sdp->sd_tune.gt_statfs_slow = 1;
sdp->sd_tune.gt_statfs_quantum = 30; sdp->sd_tune.gt_statfs_quantum = 30;
} }
......
/*
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*/
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <linux/buffer_head.h>
#include <linux/namei.h>
#include <linux/mm.h>
#include <linux/xattr.h>
#include <linux/posix_acl.h>
#include <linux/gfs2_ondisk.h>
#include <linux/crc32.h>
#include <linux/fiemap.h>
#include <asm/uaccess.h>
#include "gfs2.h"
#include "incore.h"
#include "acl.h"
#include "bmap.h"
#include "dir.h"
#include "xattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
#include "quota.h"
#include "rgrp.h"
#include "trans.h"
#include "util.h"
#include "super.h"
/**
* gfs2_create - Create a file
* @dir: The directory in which to create the file
* @dentry: The dentry of the new file
* @mode: The mode of the new file
*
* Returns: errno
*/
static int gfs2_create(struct inode *dir, struct dentry *dentry,
int mode, struct nameidata *nd)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct gfs2_holder ghs[2];
struct inode *inode;
gfs2_holder_init(dip->i_gl, 0, 0, ghs);
for (;;) {
inode = gfs2_createi(ghs, &dentry->d_name, S_IFREG | mode, 0);
if (!IS_ERR(inode)) {
gfs2_trans_end(sdp);
if (dip->i_alloc->al_rgd)
gfs2_inplace_release(dip);
gfs2_quota_unlock(dip);
gfs2_alloc_put(dip);
gfs2_glock_dq_uninit_m(2, ghs);
mark_inode_dirty(inode);
break;
} else if (PTR_ERR(inode) != -EEXIST ||
(nd && nd->flags & LOOKUP_EXCL)) {
gfs2_holder_uninit(ghs);
return PTR_ERR(inode);
}
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
if (inode) {
if (!IS_ERR(inode)) {
gfs2_holder_uninit(ghs);
break;
} else {
gfs2_holder_uninit(ghs);
return PTR_ERR(inode);
}
}
}
d_instantiate(dentry, inode);
return 0;
}
/**
* gfs2_lookup - Look up a filename in a directory and return its inode
* @dir: The directory inode
* @dentry: The dentry of the new inode
* @nd: passed from Linux VFS, ignored by us
*
* Called by the VFS layer. Lock dir and call gfs2_lookupi()
*
* Returns: errno
*/
static struct dentry *gfs2_lookup(struct inode *dir, struct dentry *dentry,
struct nameidata *nd)
{
struct inode *inode = NULL;
inode = gfs2_lookupi(dir, &dentry->d_name, 0);
if (inode && IS_ERR(inode))
return ERR_CAST(inode);
if (inode) {
struct gfs2_glock *gl = GFS2_I(inode)->i_gl;
struct gfs2_holder gh;
int error;
error = gfs2_glock_nq_init(gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
if (error) {
iput(inode);
return ERR_PTR(error);
}
gfs2_glock_dq_uninit(&gh);
return d_splice_alias(inode, dentry);
}
d_add(dentry, inode);
return NULL;
}
/**
* gfs2_link - Link to a file
* @old_dentry: The inode to link
* @dir: Add link to this directory
* @dentry: The name of the link
*
* Link the inode in "old_dentry" into the directory "dir" with the
* name in "dentry".
*
* Returns: errno
*/
static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct inode *inode = old_dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder ghs[2];
int alloc_required;
int error;
if (S_ISDIR(inode->i_mode))
return -EPERM;
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
error = gfs2_glock_nq(ghs); /* parent */
if (error)
goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = gfs2_permission(dir, MAY_WRITE | MAY_EXEC, 0);
if (error)
goto out_gunlock;
error = gfs2_dir_check(dir, &dentry->d_name, NULL);
switch (error) {
case -ENOENT:
break;
case 0:
error = -EEXIST;
default:
goto out_gunlock;
}
error = -EINVAL;
if (!dip->i_inode.i_nlink)
goto out_gunlock;
error = -EFBIG;
if (dip->i_entries == (u32)-1)
goto out_gunlock;
error = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
goto out_gunlock;
error = -EINVAL;
if (!ip->i_inode.i_nlink)
goto out_gunlock;
error = -EMLINK;
if (ip->i_inode.i_nlink == (u32)-1)
goto out_gunlock;
alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name);
if (error < 0)
goto out_gunlock;
error = 0;
if (alloc_required) {
struct gfs2_alloc *al = gfs2_alloc_get(dip);
if (!al) {
error = -ENOMEM;
goto out_gunlock;
}
error = gfs2_quota_lock_check(dip);
if (error)
goto out_alloc;
al->al_requested = sdp->sd_max_dirres;
error = gfs2_inplace_reserve(dip);
if (error)
goto out_gunlock_q;
error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
gfs2_rg_blocks(al) +
2 * RES_DINODE + RES_STATFS +
RES_QUOTA, 0);
if (error)
goto out_ipres;
} else {
error = gfs2_trans_begin(sdp, 2 * RES_DINODE + RES_LEAF, 0);
if (error)
goto out_ipres;
}
error = gfs2_dir_add(dir, &dentry->d_name, ip, IF2DT(inode->i_mode));
if (error)
goto out_end_trans;
error = gfs2_change_nlink(ip, +1);
out_end_trans:
gfs2_trans_end(sdp);
out_ipres:
if (alloc_required)
gfs2_inplace_release(dip);
out_gunlock_q:
if (alloc_required)
gfs2_quota_unlock(dip);
out_alloc:
if (alloc_required)
gfs2_alloc_put(dip);
out_gunlock:
gfs2_glock_dq(ghs + 1);
out_child:
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs);
gfs2_holder_uninit(ghs + 1);
if (!error) {
ihold(inode);
d_instantiate(dentry, inode);
mark_inode_dirty(inode);
}
return error;
}
/*
* gfs2_unlink_ok - check to see that a inode is still in a directory
* @dip: the directory
* @name: the name of the file
* @ip: the inode
*
* Assumes that the lock on (at least) @dip is held.
*
* Returns: 0 if the parent/child relationship is correct, errno if it isn't
*/
static int gfs2_unlink_ok(struct gfs2_inode *dip, const struct qstr *name,
const struct gfs2_inode *ip)
{
int error;
if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode))
return -EPERM;
if ((dip->i_inode.i_mode & S_ISVTX) &&
dip->i_inode.i_uid != current_fsuid() &&
ip->i_inode.i_uid != current_fsuid() && !capable(CAP_FOWNER))
return -EPERM;
if (IS_APPEND(&dip->i_inode))
return -EPERM;
error = gfs2_permission(&dip->i_inode, MAY_WRITE | MAY_EXEC, 0);
if (error)
return error;
error = gfs2_dir_check(&dip->i_inode, name, ip);
if (error)
return error;
return 0;
}
/**
* gfs2_unlink - Unlink a file
* @dir: The inode of the directory containing the file to unlink
* @dentry: The file itself
*
* Unlink a file. Call gfs2_unlinki()
*
* Returns: errno
*/
static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
struct gfs2_holder ghs[3];
struct gfs2_rgrpd *rgd;
struct gfs2_holder ri_gh;
int error;
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
return error;
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr);
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2);
error = gfs2_glock_nq(ghs); /* parent */
if (error)
goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = gfs2_glock_nq(ghs + 2); /* rgrp */
if (error)
goto out_rgrp;
error = gfs2_unlink_ok(dip, &dentry->d_name, ip);
if (error)
goto out_gunlock;
error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0);
if (error)
goto out_gunlock;
error = gfs2_dir_del(dip, &dentry->d_name);
if (error)
goto out_end_trans;
error = gfs2_change_nlink(ip, -1);
out_end_trans:
gfs2_trans_end(sdp);
out_gunlock:
gfs2_glock_dq(ghs + 2);
out_rgrp:
gfs2_holder_uninit(ghs + 2);
gfs2_glock_dq(ghs + 1);
out_child:
gfs2_holder_uninit(ghs + 1);
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs);
gfs2_glock_dq_uninit(&ri_gh);
return error;
}
/**
* gfs2_symlink - Create a symlink
* @dir: The directory to create the symlink in
* @dentry: The dentry to put the symlink in
* @symname: The thing which the link points to
*
* Returns: errno
*/
static int gfs2_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
{
struct gfs2_inode *dip = GFS2_I(dir), *ip;
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct gfs2_holder ghs[2];
struct inode *inode;
struct buffer_head *dibh;
int size;
int error;
/* Must be stuffed with a null terminator for gfs2_follow_link() */
size = strlen(symname);
if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1)
return -ENAMETOOLONG;
gfs2_holder_init(dip->i_gl, 0, 0, ghs);
inode = gfs2_createi(ghs, &dentry->d_name, S_IFLNK | S_IRWXUGO, 0);
if (IS_ERR(inode)) {
gfs2_holder_uninit(ghs);
return PTR_ERR(inode);
}
ip = ghs[1].gh_gl->gl_object;
i_size_write(inode, size);
error = gfs2_meta_inode_buffer(ip, &dibh);
if (!gfs2_assert_withdraw(sdp, !error)) {
gfs2_dinode_out(ip, dibh->b_data);
memcpy(dibh->b_data + sizeof(struct gfs2_dinode), symname,
size);
brelse(dibh);
}
gfs2_trans_end(sdp);
if (dip->i_alloc->al_rgd)
gfs2_inplace_release(dip);
gfs2_quota_unlock(dip);
gfs2_alloc_put(dip);
gfs2_glock_dq_uninit_m(2, ghs);
d_instantiate(dentry, inode);
mark_inode_dirty(inode);
return 0;
}
/**
* gfs2_mkdir - Make a directory
* @dir: The parent directory of the new one
* @dentry: The dentry of the new directory
* @mode: The mode of the new directory
*
* Returns: errno
*/
static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct gfs2_inode *dip = GFS2_I(dir), *ip;
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct gfs2_holder ghs[2];
struct inode *inode;
struct buffer_head *dibh;
int error;
gfs2_holder_init(dip->i_gl, 0, 0, ghs);
inode = gfs2_createi(ghs, &dentry->d_name, S_IFDIR | mode, 0);
if (IS_ERR(inode)) {
gfs2_holder_uninit(ghs);
return PTR_ERR(inode);
}
ip = ghs[1].gh_gl->gl_object;
ip->i_inode.i_nlink = 2;
i_size_write(inode, sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode));
ip->i_diskflags |= GFS2_DIF_JDATA;
ip->i_entries = 2;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (!gfs2_assert_withdraw(sdp, !error)) {
struct gfs2_dinode *di = (struct gfs2_dinode *)dibh->b_data;
struct gfs2_dirent *dent = (struct gfs2_dirent *)(di+1);
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_qstr2dirent(&gfs2_qdot, GFS2_DIRENT_SIZE(gfs2_qdot.len), dent);
dent->de_inum = di->di_num; /* already GFS2 endian */
dent->de_type = cpu_to_be16(DT_DIR);
di->di_entries = cpu_to_be32(1);
dent = (struct gfs2_dirent *)((char*)dent + GFS2_DIRENT_SIZE(1));
gfs2_qstr2dirent(&gfs2_qdotdot, dibh->b_size - GFS2_DIRENT_SIZE(1) - sizeof(struct gfs2_dinode), dent);
gfs2_inum_out(dip, dent);
dent->de_type = cpu_to_be16(DT_DIR);
gfs2_dinode_out(ip, di);
brelse(dibh);
}
error = gfs2_change_nlink(dip, +1);
gfs2_assert_withdraw(sdp, !error); /* dip already pinned */
gfs2_trans_end(sdp);
if (dip->i_alloc->al_rgd)
gfs2_inplace_release(dip);
gfs2_quota_unlock(dip);
gfs2_alloc_put(dip);
gfs2_glock_dq_uninit_m(2, ghs);
d_instantiate(dentry, inode);
mark_inode_dirty(inode);
return 0;
}
/**
* gfs2_rmdiri - Remove a directory
* @dip: The parent directory of the directory to be removed
* @name: The name of the directory to be removed
* @ip: The GFS2 inode of the directory to be removed
*
* Assumes Glocks on dip and ip are held
*
* Returns: errno
*/
static int gfs2_rmdiri(struct gfs2_inode *dip, const struct qstr *name,
struct gfs2_inode *ip)
{
int error;
if (ip->i_entries != 2) {
if (gfs2_consist_inode(ip))
gfs2_dinode_print(ip);
return -EIO;
}
error = gfs2_dir_del(dip, name);
if (error)
return error;
error = gfs2_change_nlink(dip, -1);
if (error)
return error;
error = gfs2_dir_del(ip, &gfs2_qdot);
if (error)
return error;
error = gfs2_dir_del(ip, &gfs2_qdotdot);
if (error)
return error;
/* It looks odd, but it really should be done twice */
error = gfs2_change_nlink(ip, -1);
if (error)
return error;
error = gfs2_change_nlink(ip, -1);
if (error)
return error;
return error;
}
/**
* gfs2_rmdir - Remove a directory
* @dir: The parent directory of the directory to be removed
* @dentry: The dentry of the directory to remove
*
* Remove a directory. Call gfs2_rmdiri()
*
* Returns: errno
*/
static int gfs2_rmdir(struct inode *dir, struct dentry *dentry)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
struct gfs2_holder ghs[3];
struct gfs2_rgrpd *rgd;
struct gfs2_holder ri_gh;
int error;
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
return error;
gfs2_holder_init(dip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + 1);
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr);
gfs2_holder_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + 2);
error = gfs2_glock_nq(ghs); /* parent */
if (error)
goto out_parent;
error = gfs2_glock_nq(ghs + 1); /* child */
if (error)
goto out_child;
error = gfs2_glock_nq(ghs + 2); /* rgrp */
if (error)
goto out_rgrp;
error = gfs2_unlink_ok(dip, &dentry->d_name, ip);
if (error)
goto out_gunlock;
if (ip->i_entries < 2) {
if (gfs2_consist_inode(ip))
gfs2_dinode_print(ip);
error = -EIO;
goto out_gunlock;
}
if (ip->i_entries > 2) {
error = -ENOTEMPTY;
goto out_gunlock;
}
error = gfs2_trans_begin(sdp, 2 * RES_DINODE + 3 * RES_LEAF + RES_RG_BIT, 0);
if (error)
goto out_gunlock;
error = gfs2_rmdiri(dip, &dentry->d_name, ip);
gfs2_trans_end(sdp);
out_gunlock:
gfs2_glock_dq(ghs + 2);
out_rgrp:
gfs2_holder_uninit(ghs + 2);
gfs2_glock_dq(ghs + 1);
out_child:
gfs2_holder_uninit(ghs + 1);
gfs2_glock_dq(ghs);
out_parent:
gfs2_holder_uninit(ghs);
gfs2_glock_dq_uninit(&ri_gh);
return error;
}
/**
* gfs2_mknod - Make a special file
* @dir: The directory in which the special file will reside
* @dentry: The dentry of the special file
* @mode: The mode of the special file
* @rdev: The device specification of the special file
*
*/
static int gfs2_mknod(struct inode *dir, struct dentry *dentry, int mode,
dev_t dev)
{
struct gfs2_inode *dip = GFS2_I(dir);
struct gfs2_sbd *sdp = GFS2_SB(dir);
struct gfs2_holder ghs[2];
struct inode *inode;
gfs2_holder_init(dip->i_gl, 0, 0, ghs);
inode = gfs2_createi(ghs, &dentry->d_name, mode, dev);
if (IS_ERR(inode)) {
gfs2_holder_uninit(ghs);
return PTR_ERR(inode);
}
gfs2_trans_end(sdp);
if (dip->i_alloc->al_rgd)
gfs2_inplace_release(dip);
gfs2_quota_unlock(dip);
gfs2_alloc_put(dip);
gfs2_glock_dq_uninit_m(2, ghs);
d_instantiate(dentry, inode);
mark_inode_dirty(inode);
return 0;
}
/*
* gfs2_ok_to_move - check if it's ok to move a directory to another directory
* @this: move this
* @to: to here
*
* Follow @to back to the root and make sure we don't encounter @this
* Assumes we already hold the rename lock.
*
* Returns: errno
*/
static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to)
{
struct inode *dir = &to->i_inode;
struct super_block *sb = dir->i_sb;
struct inode *tmp;
int error = 0;
igrab(dir);
for (;;) {
if (dir == &this->i_inode) {
error = -EINVAL;
break;
}
if (dir == sb->s_root->d_inode) {
error = 0;
break;
}
tmp = gfs2_lookupi(dir, &gfs2_qdotdot, 1);
if (IS_ERR(tmp)) {
error = PTR_ERR(tmp);
break;
}
iput(dir);
dir = tmp;
}
iput(dir);
return error;
}
/**
* gfs2_rename - Rename a file
* @odir: Parent directory of old file name
* @odentry: The old dentry of the file
* @ndir: Parent directory of new file name
* @ndentry: The new dentry of the file
*
* Returns: errno
*/
static int gfs2_rename(struct inode *odir, struct dentry *odentry,
struct inode *ndir, struct dentry *ndentry)
{
struct gfs2_inode *odip = GFS2_I(odir);
struct gfs2_inode *ndip = GFS2_I(ndir);
struct gfs2_inode *ip = GFS2_I(odentry->d_inode);
struct gfs2_inode *nip = NULL;
struct gfs2_sbd *sdp = GFS2_SB(odir);
struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }, ri_gh;
struct gfs2_rgrpd *nrgd;
unsigned int num_gh;
int dir_rename = 0;
int alloc_required = 0;
unsigned int x;
int error;
if (ndentry->d_inode) {
nip = GFS2_I(ndentry->d_inode);
if (ip == nip)
return 0;
}
error = gfs2_rindex_hold(sdp, &ri_gh);
if (error)
return error;
if (odip != ndip) {
error = gfs2_glock_nq_init(sdp->sd_rename_gl, LM_ST_EXCLUSIVE,
0, &r_gh);
if (error)
goto out;
if (S_ISDIR(ip->i_inode.i_mode)) {
dir_rename = 1;
/* don't move a dirctory into it's subdir */
error = gfs2_ok_to_move(ip, ndip);
if (error)
goto out_gunlock_r;
}
}
num_gh = 1;
gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs);
if (odip != ndip) {
gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
num_gh++;
}
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
num_gh++;
if (nip) {
gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh);
num_gh++;
/* grab the resource lock for unlink flag twiddling
* this is the case of the target file already existing
* so we unlink before doing the rename
*/
nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr);
if (nrgd)
gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++);
}
for (x = 0; x < num_gh; x++) {
error = gfs2_glock_nq(ghs + x);
if (error)
goto out_gunlock;
}
/* Check out the old directory */
error = gfs2_unlink_ok(odip, &odentry->d_name, ip);
if (error)
goto out_gunlock;
/* Check out the new directory */
if (nip) {
error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip);
if (error)
goto out_gunlock;
if (S_ISDIR(nip->i_inode.i_mode)) {
if (nip->i_entries < 2) {
if (gfs2_consist_inode(nip))
gfs2_dinode_print(nip);
error = -EIO;
goto out_gunlock;
}
if (nip->i_entries > 2) {
error = -ENOTEMPTY;
goto out_gunlock;
}
}
} else {
error = gfs2_permission(ndir, MAY_WRITE | MAY_EXEC, 0);
if (error)
goto out_gunlock;
error = gfs2_dir_check(ndir, &ndentry->d_name, NULL);
switch (error) {
case -ENOENT:
error = 0;
break;
case 0:
error = -EEXIST;
default:
goto out_gunlock;
};
if (odip != ndip) {
if (!ndip->i_inode.i_nlink) {
error = -EINVAL;
goto out_gunlock;
}
if (ndip->i_entries == (u32)-1) {
error = -EFBIG;
goto out_gunlock;
}
if (S_ISDIR(ip->i_inode.i_mode) &&
ndip->i_inode.i_nlink == (u32)-1) {
error = -EMLINK;
goto out_gunlock;
}
}
}
/* Check out the dir to be renamed */
if (dir_rename) {
error = gfs2_permission(odentry->d_inode, MAY_WRITE, 0);
if (error)
goto out_gunlock;
}
if (nip == NULL)
alloc_required = gfs2_diradd_alloc_required(ndir, &ndentry->d_name);
error = alloc_required;
if (error < 0)
goto out_gunlock;
error = 0;
if (alloc_required) {
struct gfs2_alloc *al = gfs2_alloc_get(ndip);
if (!al) {
error = -ENOMEM;
goto out_gunlock;
}
error = gfs2_quota_lock_check(ndip);
if (error)
goto out_alloc;
al->al_requested = sdp->sd_max_dirres;
error = gfs2_inplace_reserve_ri(ndip);
if (error)
goto out_gunlock_q;
error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
gfs2_rg_blocks(al) +
4 * RES_DINODE + 4 * RES_LEAF +
RES_STATFS + RES_QUOTA + 4, 0);
if (error)
goto out_ipreserv;
} else {
error = gfs2_trans_begin(sdp, 4 * RES_DINODE +
5 * RES_LEAF + 4, 0);
if (error)
goto out_gunlock;
}
/* Remove the target file, if it exists */
if (nip) {
if (S_ISDIR(nip->i_inode.i_mode))
error = gfs2_rmdiri(ndip, &ndentry->d_name, nip);
else {
error = gfs2_dir_del(ndip, &ndentry->d_name);
if (error)
goto out_end_trans;
error = gfs2_change_nlink(nip, -1);
}
if (error)
goto out_end_trans;
}
if (dir_rename) {
error = gfs2_change_nlink(ndip, +1);
if (error)
goto out_end_trans;
error = gfs2_change_nlink(odip, -1);
if (error)
goto out_end_trans;
error = gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR);
if (error)
goto out_end_trans;
} else {
struct buffer_head *dibh;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
goto out_end_trans;
ip->i_inode.i_ctime = CURRENT_TIME;
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh);
}
error = gfs2_dir_del(odip, &odentry->d_name);
if (error)
goto out_end_trans;
error = gfs2_dir_add(ndir, &ndentry->d_name, ip, IF2DT(ip->i_inode.i_mode));
if (error)
goto out_end_trans;
out_end_trans:
gfs2_trans_end(sdp);
out_ipreserv:
if (alloc_required)
gfs2_inplace_release(ndip);
out_gunlock_q:
if (alloc_required)
gfs2_quota_unlock(ndip);
out_alloc:
if (alloc_required)
gfs2_alloc_put(ndip);
out_gunlock:
while (x--) {
gfs2_glock_dq(ghs + x);
gfs2_holder_uninit(ghs + x);
}
out_gunlock_r:
if (r_gh.gh_gl)
gfs2_glock_dq_uninit(&r_gh);
out:
gfs2_glock_dq_uninit(&ri_gh);
return error;
}
/**
* gfs2_follow_link - Follow a symbolic link
* @dentry: The dentry of the link
* @nd: Data that we pass to vfs_follow_link()
*
* This can handle symlinks of any size.
*
* Returns: 0 on success or error code
*/
static void *gfs2_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
struct gfs2_holder i_gh;
struct buffer_head *dibh;
unsigned int x, size;
char *buf;
int error;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &i_gh);
error = gfs2_glock_nq(&i_gh);
if (error) {
gfs2_holder_uninit(&i_gh);
nd_set_link(nd, ERR_PTR(error));
return NULL;
}
size = (unsigned int)i_size_read(&ip->i_inode);
if (size == 0) {
gfs2_consist_inode(ip);
buf = ERR_PTR(-EIO);
goto out;
}
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error) {
buf = ERR_PTR(error);
goto out;
}
x = size + 1;
buf = kmalloc(x, GFP_NOFS);
if (!buf)
buf = ERR_PTR(-ENOMEM);
else
memcpy(buf, dibh->b_data + sizeof(struct gfs2_dinode), x);
brelse(dibh);
out:
gfs2_glock_dq_uninit(&i_gh);
nd_set_link(nd, buf);
return NULL;
}
static void gfs2_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
char *s = nd_get_link(nd);
if (!IS_ERR(s))
kfree(s);
}
/**
* gfs2_permission -
* @inode: The inode
* @mask: The mask to be tested
* @flags: Indicates whether this is an RCU path walk or not
*
* This may be called from the VFS directly, or from within GFS2 with the
* inode locked, so we look to see if the glock is already locked and only
* lock the glock if its not already been done.
*
* Returns: errno
*/
int gfs2_permission(struct inode *inode, int mask, unsigned int flags)
{
struct gfs2_inode *ip;
struct gfs2_holder i_gh;
int error;
int unlock = 0;
ip = GFS2_I(inode);
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
if (flags & IPERM_FLAG_RCU)
return -ECHILD;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
if (error)
return error;
unlock = 1;
}
if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode))
error = -EACCES;
else
error = generic_permission(inode, mask, flags, gfs2_check_acl);
if (unlock)
gfs2_glock_dq_uninit(&i_gh);
return error;
}
static int setattr_chown(struct inode *inode, struct iattr *attr)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
u32 ouid, ogid, nuid, ngid;
int error;
ouid = inode->i_uid;
ogid = inode->i_gid;
nuid = attr->ia_uid;
ngid = attr->ia_gid;
if (!(attr->ia_valid & ATTR_UID) || ouid == nuid)
ouid = nuid = NO_QUOTA_CHANGE;
if (!(attr->ia_valid & ATTR_GID) || ogid == ngid)
ogid = ngid = NO_QUOTA_CHANGE;
if (!gfs2_alloc_get(ip))
return -ENOMEM;
error = gfs2_quota_lock(ip, nuid, ngid);
if (error)
goto out_alloc;
if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) {
error = gfs2_quota_check(ip, nuid, ngid);
if (error)
goto out_gunlock_q;
}
error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_QUOTA, 0);
if (error)
goto out_gunlock_q;
error = gfs2_setattr_simple(ip, attr);
if (error)
goto out_end_trans;
if (ouid != NO_QUOTA_CHANGE || ogid != NO_QUOTA_CHANGE) {
u64 blocks = gfs2_get_inode_blocks(&ip->i_inode);
gfs2_quota_change(ip, -blocks, ouid, ogid);
gfs2_quota_change(ip, blocks, nuid, ngid);
}
out_end_trans:
gfs2_trans_end(sdp);
out_gunlock_q:
gfs2_quota_unlock(ip);
out_alloc:
gfs2_alloc_put(ip);
return error;
}
/**
* gfs2_setattr - Change attributes on an inode
* @dentry: The dentry which is changing
* @attr: The structure describing the change
*
* The VFS layer wants to change one or more of an inodes attributes. Write
* that change out to disk.
*
* Returns: errno
*/
static int gfs2_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder i_gh;
int error;
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
if (error)
return error;
error = -EPERM;
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
goto out;
error = inode_change_ok(inode, attr);
if (error)
goto out;
if (attr->ia_valid & ATTR_SIZE)
error = gfs2_setattr_size(inode, attr->ia_size);
else if (attr->ia_valid & (ATTR_UID | ATTR_GID))
error = setattr_chown(inode, attr);
else if ((attr->ia_valid & ATTR_MODE) && IS_POSIXACL(inode))
error = gfs2_acl_chmod(ip, attr);
else
error = gfs2_setattr_simple(ip, attr);
out:
gfs2_glock_dq_uninit(&i_gh);
if (!error)
mark_inode_dirty(inode);
return error;
}
/**
* gfs2_getattr - Read out an inode's attributes
* @mnt: The vfsmount the inode is being accessed from
* @dentry: The dentry to stat
* @stat: The inode's stats
*
* This may be called from the VFS directly, or from within GFS2 with the
* inode locked, so we look to see if the glock is already locked and only
* lock the glock if its not already been done. Note that its the NFS
* readdirplus operation which causes this to be called (from filldir)
* with the glock already held.
*
* Returns: errno
*/
static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int error;
int unlock = 0;
if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
if (error)
return error;
unlock = 1;
}
generic_fillattr(inode, stat);
if (unlock)
gfs2_glock_dq_uninit(&gh);
return 0;
}
static int gfs2_setxattr(struct dentry *dentry, const char *name,
const void *data, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
ret = generic_setxattr(dentry, name, data, size, flags);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
return ret;
}
static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name,
void *data, size_t size)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
ret = generic_getxattr(dentry, name, data, size);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
return ret;
}
static int gfs2_removexattr(struct dentry *dentry, const char *name)
{
struct inode *inode = dentry->d_inode;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
ret = generic_removexattr(dentry, name);
gfs2_glock_dq(&gh);
}
gfs2_holder_uninit(&gh);
return ret;
}
static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
u64 start, u64 len)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
int ret;
ret = fiemap_check_flags(fieinfo, FIEMAP_FLAG_SYNC);
if (ret)
return ret;
mutex_lock(&inode->i_mutex);
ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
if (ret)
goto out;
if (gfs2_is_stuffed(ip)) {
u64 phys = ip->i_no_addr << inode->i_blkbits;
u64 size = i_size_read(inode);
u32 flags = FIEMAP_EXTENT_LAST|FIEMAP_EXTENT_NOT_ALIGNED|
FIEMAP_EXTENT_DATA_INLINE;
phys += sizeof(struct gfs2_dinode);
phys += start;
if (start + len > size)
len = size - start;
if (start < size)
ret = fiemap_fill_next_extent(fieinfo, start, phys,
len, flags);
if (ret == 1)
ret = 0;
} else {
ret = __generic_block_fiemap(inode, fieinfo, start, len,
gfs2_block_map);
}
gfs2_glock_dq_uninit(&gh);
out:
mutex_unlock(&inode->i_mutex);
return ret;
}
const struct inode_operations gfs2_file_iops = {
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
.getxattr = gfs2_getxattr,
.listxattr = gfs2_listxattr,
.removexattr = gfs2_removexattr,
.fiemap = gfs2_fiemap,
};
const struct inode_operations gfs2_dir_iops = {
.create = gfs2_create,
.lookup = gfs2_lookup,
.link = gfs2_link,
.unlink = gfs2_unlink,
.symlink = gfs2_symlink,
.mkdir = gfs2_mkdir,
.rmdir = gfs2_rmdir,
.mknod = gfs2_mknod,
.rename = gfs2_rename,
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
.getxattr = gfs2_getxattr,
.listxattr = gfs2_listxattr,
.removexattr = gfs2_removexattr,
.fiemap = gfs2_fiemap,
};
const struct inode_operations gfs2_symlink_iops = {
.readlink = generic_readlink,
.follow_link = gfs2_follow_link,
.put_link = gfs2_put_link,
.permission = gfs2_permission,
.setattr = gfs2_setattr,
.getattr = gfs2_getattr,
.setxattr = gfs2_setxattr,
.getxattr = gfs2_getxattr,
.listxattr = gfs2_listxattr,
.removexattr = gfs2_removexattr,
.fiemap = gfs2_fiemap,
};
...@@ -78,10 +78,11 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, ...@@ -78,10 +78,11 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf1, static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf1,
unsigned char *buf2, unsigned int offset, unsigned char *buf2, unsigned int offset,
unsigned int buflen, u32 block, struct gfs2_bitmap *bi, u32 block,
unsigned char new_state) unsigned char new_state)
{ {
unsigned char *byte1, *byte2, *end, cur_state; unsigned char *byte1, *byte2, *end, cur_state;
unsigned int buflen = bi->bi_len;
const unsigned int bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE; const unsigned int bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
byte1 = buf1 + offset + (block / GFS2_NBBY); byte1 = buf1 + offset + (block / GFS2_NBBY);
...@@ -92,6 +93,16 @@ static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf1, ...@@ -92,6 +93,16 @@ static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf1,
cur_state = (*byte1 >> bit) & GFS2_BIT_MASK; cur_state = (*byte1 >> bit) & GFS2_BIT_MASK;
if (unlikely(!valid_change[new_state * 4 + cur_state])) { if (unlikely(!valid_change[new_state * 4 + cur_state])) {
printk(KERN_WARNING "GFS2: buf_blk = 0x%llx old_state=%d, "
"new_state=%d\n",
(unsigned long long)block, cur_state, new_state);
printk(KERN_WARNING "GFS2: rgrp=0x%llx bi_start=0x%lx\n",
(unsigned long long)rgd->rd_addr,
(unsigned long)bi->bi_start);
printk(KERN_WARNING "GFS2: bi_offset=0x%lx bi_len=0x%lx\n",
(unsigned long)bi->bi_offset,
(unsigned long)bi->bi_len);
dump_stack();
gfs2_consist_rgrpd(rgd); gfs2_consist_rgrpd(rgd);
return; return;
} }
...@@ -381,6 +392,7 @@ static void clear_rgrpdi(struct gfs2_sbd *sdp) ...@@ -381,6 +392,7 @@ static void clear_rgrpdi(struct gfs2_sbd *sdp)
if (gl) { if (gl) {
gl->gl_object = NULL; gl->gl_object = NULL;
gfs2_glock_add_to_lru(gl);
gfs2_glock_put(gl); gfs2_glock_put(gl);
} }
...@@ -1365,7 +1377,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, ...@@ -1365,7 +1377,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
gfs2_setbit(rgd, bi->bi_bh->b_data, bi->bi_clone, bi->bi_offset, gfs2_setbit(rgd, bi->bi_bh->b_data, bi->bi_clone, bi->bi_offset,
bi->bi_len, blk, new_state); bi, blk, new_state);
goal = blk; goal = blk;
while (*n < elen) { while (*n < elen) {
goal++; goal++;
...@@ -1375,7 +1387,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, ...@@ -1375,7 +1387,7 @@ static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
GFS2_BLKST_FREE) GFS2_BLKST_FREE)
break; break;
gfs2_setbit(rgd, bi->bi_bh->b_data, bi->bi_clone, bi->bi_offset, gfs2_setbit(rgd, bi->bi_bh->b_data, bi->bi_clone, bi->bi_offset,
bi->bi_len, goal, new_state); bi, goal, new_state);
(*n)++; (*n)++;
} }
out: out:
...@@ -1432,7 +1444,7 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart, ...@@ -1432,7 +1444,7 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
} }
gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1); gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
gfs2_setbit(rgd, bi->bi_bh->b_data, NULL, bi->bi_offset, gfs2_setbit(rgd, bi->bi_bh->b_data, NULL, bi->bi_offset,
bi->bi_len, buf_blk, new_state); bi, buf_blk, new_state);
} }
return rgd; return rgd;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/time.h> #include <linux/time.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/writeback.h> #include <linux/writeback.h>
#include <linux/backing-dev.h>
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
...@@ -700,11 +701,47 @@ void gfs2_unfreeze_fs(struct gfs2_sbd *sdp) ...@@ -700,11 +701,47 @@ void gfs2_unfreeze_fs(struct gfs2_sbd *sdp)
mutex_unlock(&sdp->sd_freeze_lock); mutex_unlock(&sdp->sd_freeze_lock);
} }
void gfs2_dinode_out(const struct gfs2_inode *ip, void *buf)
{
struct gfs2_dinode *str = buf;
str->di_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
str->di_header.mh_type = cpu_to_be32(GFS2_METATYPE_DI);
str->di_header.mh_format = cpu_to_be32(GFS2_FORMAT_DI);
str->di_num.no_addr = cpu_to_be64(ip->i_no_addr);
str->di_num.no_formal_ino = cpu_to_be64(ip->i_no_formal_ino);
str->di_mode = cpu_to_be32(ip->i_inode.i_mode);
str->di_uid = cpu_to_be32(ip->i_inode.i_uid);
str->di_gid = cpu_to_be32(ip->i_inode.i_gid);
str->di_nlink = cpu_to_be32(ip->i_inode.i_nlink);
str->di_size = cpu_to_be64(i_size_read(&ip->i_inode));
str->di_blocks = cpu_to_be64(gfs2_get_inode_blocks(&ip->i_inode));
str->di_atime = cpu_to_be64(ip->i_inode.i_atime.tv_sec);
str->di_mtime = cpu_to_be64(ip->i_inode.i_mtime.tv_sec);
str->di_ctime = cpu_to_be64(ip->i_inode.i_ctime.tv_sec);
str->di_goal_meta = cpu_to_be64(ip->i_goal);
str->di_goal_data = cpu_to_be64(ip->i_goal);
str->di_generation = cpu_to_be64(ip->i_generation);
str->di_flags = cpu_to_be32(ip->i_diskflags);
str->di_height = cpu_to_be16(ip->i_height);
str->di_payload_format = cpu_to_be32(S_ISDIR(ip->i_inode.i_mode) &&
!(ip->i_diskflags & GFS2_DIF_EXHASH) ?
GFS2_FORMAT_DE : 0);
str->di_depth = cpu_to_be16(ip->i_depth);
str->di_entries = cpu_to_be32(ip->i_entries);
str->di_eattr = cpu_to_be64(ip->i_eattr);
str->di_atime_nsec = cpu_to_be32(ip->i_inode.i_atime.tv_nsec);
str->di_mtime_nsec = cpu_to_be32(ip->i_inode.i_mtime.tv_nsec);
str->di_ctime_nsec = cpu_to_be32(ip->i_inode.i_ctime.tv_nsec);
}
/** /**
* gfs2_write_inode - Make sure the inode is stable on the disk * gfs2_write_inode - Make sure the inode is stable on the disk
* @inode: The inode * @inode: The inode
* @sync: synchronous write flag * @wbc: The writeback control structure
* *
* Returns: errno * Returns: errno
*/ */
...@@ -713,15 +750,17 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -713,15 +750,17 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
{ {
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_sbd *sdp = GFS2_SB(inode);
struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
struct backing_dev_info *bdi = metamapping->backing_dev_info;
struct gfs2_holder gh; struct gfs2_holder gh;
struct buffer_head *bh; struct buffer_head *bh;
struct timespec atime; struct timespec atime;
struct gfs2_dinode *di; struct gfs2_dinode *di;
int ret = 0; int ret = -EAGAIN;
/* Check this is a "normal" inode, etc */ /* Skip timestamp update, if this is from a memalloc */
if (current->flags & PF_MEMALLOC) if (current->flags & PF_MEMALLOC)
return 0; goto do_flush;
ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); ret = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
if (ret) if (ret)
goto do_flush; goto do_flush;
...@@ -745,6 +784,13 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -745,6 +784,13 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
do_flush: do_flush:
if (wbc->sync_mode == WB_SYNC_ALL) if (wbc->sync_mode == WB_SYNC_ALL)
gfs2_log_flush(GFS2_SB(inode), ip->i_gl); gfs2_log_flush(GFS2_SB(inode), ip->i_gl);
filemap_fdatawrite(metamapping);
if (bdi->dirty_exceeded)
gfs2_ail1_flush(sdp, wbc);
if (!ret && (wbc->sync_mode == WB_SYNC_ALL))
ret = filemap_fdatawait(metamapping);
if (ret)
mark_inode_dirty_sync(inode);
return ret; return ret;
} }
...@@ -874,8 +920,9 @@ static void gfs2_put_super(struct super_block *sb) ...@@ -874,8 +920,9 @@ static void gfs2_put_super(struct super_block *sb)
static int gfs2_sync_fs(struct super_block *sb, int wait) static int gfs2_sync_fs(struct super_block *sb, int wait)
{ {
if (wait && sb->s_fs_info) struct gfs2_sbd *sdp = sb->s_fs_info;
gfs2_log_flush(sb->s_fs_info, NULL); if (wait && sdp)
gfs2_log_flush(sdp, NULL);
return 0; return 0;
} }
...@@ -1308,6 +1355,78 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) ...@@ -1308,6 +1355,78 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
return 0; return 0;
} }
static void gfs2_final_release_pages(struct gfs2_inode *ip)
{
struct inode *inode = &ip->i_inode;
struct gfs2_glock *gl = ip->i_gl;
truncate_inode_pages(gfs2_glock2aspace(ip->i_gl), 0);
truncate_inode_pages(&inode->i_data, 0);
if (atomic_read(&gl->gl_revokes) == 0) {
clear_bit(GLF_LFLUSH, &gl->gl_flags);
clear_bit(GLF_DIRTY, &gl->gl_flags);
}
}
static int gfs2_dinode_dealloc(struct gfs2_inode *ip)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_alloc *al;
struct gfs2_rgrpd *rgd;
int error;
if (gfs2_get_inode_blocks(&ip->i_inode) != 1) {
gfs2_consist_inode(ip);
return -EIO;
}
al = gfs2_alloc_get(ip);
if (!al)
return -ENOMEM;
error = gfs2_quota_hold(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
if (error)
goto out;
error = gfs2_rindex_hold(sdp, &al->al_ri_gh);
if (error)
goto out_qs;
rgd = gfs2_blk2rgrpd(sdp, ip->i_no_addr);
if (!rgd) {
gfs2_consist_inode(ip);
error = -EIO;
goto out_rindex_relse;
}
error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, 0,
&al->al_rgd_gh);
if (error)
goto out_rindex_relse;
error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA,
sdp->sd_jdesc->jd_blocks);
if (error)
goto out_rg_gunlock;
gfs2_free_di(rgd, ip);
gfs2_final_release_pages(ip);
gfs2_trans_end(sdp);
out_rg_gunlock:
gfs2_glock_dq_uninit(&al->al_rgd_gh);
out_rindex_relse:
gfs2_glock_dq_uninit(&al->al_ri_gh);
out_qs:
gfs2_quota_unhold(ip);
out:
gfs2_alloc_put(ip);
return error;
}
/* /*
* We have to (at the moment) hold the inodes main lock to cover * We have to (at the moment) hold the inodes main lock to cover
* the gap between unlocking the shared lock on the iopen lock and * the gap between unlocking the shared lock on the iopen lock and
...@@ -1371,15 +1490,13 @@ static void gfs2_evict_inode(struct inode *inode) ...@@ -1371,15 +1490,13 @@ static void gfs2_evict_inode(struct inode *inode)
} }
error = gfs2_dinode_dealloc(ip); error = gfs2_dinode_dealloc(ip);
if (error) goto out_unlock;
goto out_unlock;
out_truncate: out_truncate:
error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks);
if (error) if (error)
goto out_unlock; goto out_unlock;
/* Needs to be done before glock release & also in a transaction */ gfs2_final_release_pages(ip);
truncate_inode_pages(&inode->i_data, 0);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
out_unlock: out_unlock:
...@@ -1394,6 +1511,7 @@ static void gfs2_evict_inode(struct inode *inode) ...@@ -1394,6 +1511,7 @@ static void gfs2_evict_inode(struct inode *inode)
end_writeback(inode); end_writeback(inode);
ip->i_gl->gl_object = NULL; ip->i_gl->gl_object = NULL;
gfs2_glock_add_to_lru(ip->i_gl);
gfs2_glock_put(ip->i_gl); gfs2_glock_put(ip->i_gl);
ip->i_gl = NULL; ip->i_gl = NULL;
if (ip->i_iopen_gh.gh_gl) { if (ip->i_iopen_gh.gh_gl) {
......
...@@ -81,7 +81,8 @@ static int gfs2_uuid_valid(const u8 *uuid) ...@@ -81,7 +81,8 @@ static int gfs2_uuid_valid(const u8 *uuid)
static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf) static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf)
{ {
const u8 *uuid = sdp->sd_sb.sb_uuid; struct super_block *s = sdp->sd_vfs;
const u8 *uuid = s->s_uuid;
buf[0] = '\0'; buf[0] = '\0';
if (!gfs2_uuid_valid(uuid)) if (!gfs2_uuid_valid(uuid))
return 0; return 0;
...@@ -616,7 +617,8 @@ static int gfs2_uevent(struct kset *kset, struct kobject *kobj, ...@@ -616,7 +617,8 @@ static int gfs2_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env) struct kobj_uevent_env *env)
{ {
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj); struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
const u8 *uuid = sdp->sd_sb.sb_uuid; struct super_block *s = sdp->sd_vfs;
const u8 *uuid = s->s_uuid;
add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name); add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name);
add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name); add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/dlmconstants.h> #include <linux/dlmconstants.h>
#include <linux/gfs2_ondisk.h> #include <linux/gfs2_ondisk.h>
#include <linux/writeback.h>
#include "incore.h" #include "incore.h"
#include "glock.h" #include "glock.h"
...@@ -40,7 +41,9 @@ ...@@ -40,7 +41,9 @@
{(1UL << GLF_REPLY_PENDING), "r" }, \ {(1UL << GLF_REPLY_PENDING), "r" }, \
{(1UL << GLF_INITIAL), "I" }, \ {(1UL << GLF_INITIAL), "I" }, \
{(1UL << GLF_FROZEN), "F" }, \ {(1UL << GLF_FROZEN), "F" }, \
{(1UL << GLF_QUEUED), "q" }) {(1UL << GLF_QUEUED), "q" }, \
{(1UL << GLF_LRU), "L" }, \
{(1UL << GLF_OBJECT), "o" })
#ifndef NUMPTY #ifndef NUMPTY
#define NUMPTY #define NUMPTY
...@@ -94,7 +97,7 @@ TRACE_EVENT(gfs2_glock_state_change, ...@@ -94,7 +97,7 @@ TRACE_EVENT(gfs2_glock_state_change,
__entry->new_state = glock_trace_state(new_state); __entry->new_state = glock_trace_state(new_state);
__entry->tgt_state = glock_trace_state(gl->gl_target); __entry->tgt_state = glock_trace_state(gl->gl_target);
__entry->dmt_state = glock_trace_state(gl->gl_demote_state); __entry->dmt_state = glock_trace_state(gl->gl_demote_state);
__entry->flags = gl->gl_flags; __entry->flags = gl->gl_flags | (gl->gl_object ? (1UL<<GLF_OBJECT) : 0);
), ),
TP_printk("%u,%u glock %d:%lld state %s to %s tgt:%s dmt:%s flags:%s", TP_printk("%u,%u glock %d:%lld state %s to %s tgt:%s dmt:%s flags:%s",
...@@ -127,7 +130,7 @@ TRACE_EVENT(gfs2_glock_put, ...@@ -127,7 +130,7 @@ TRACE_EVENT(gfs2_glock_put,
__entry->gltype = gl->gl_name.ln_type; __entry->gltype = gl->gl_name.ln_type;
__entry->glnum = gl->gl_name.ln_number; __entry->glnum = gl->gl_name.ln_number;
__entry->cur_state = glock_trace_state(gl->gl_state); __entry->cur_state = glock_trace_state(gl->gl_state);
__entry->flags = gl->gl_flags; __entry->flags = gl->gl_flags | (gl->gl_object ? (1UL<<GLF_OBJECT) : 0);
), ),
TP_printk("%u,%u glock %d:%lld state %s => %s flags:%s", TP_printk("%u,%u glock %d:%lld state %s => %s flags:%s",
...@@ -161,7 +164,7 @@ TRACE_EVENT(gfs2_demote_rq, ...@@ -161,7 +164,7 @@ TRACE_EVENT(gfs2_demote_rq,
__entry->glnum = gl->gl_name.ln_number; __entry->glnum = gl->gl_name.ln_number;
__entry->cur_state = glock_trace_state(gl->gl_state); __entry->cur_state = glock_trace_state(gl->gl_state);
__entry->dmt_state = glock_trace_state(gl->gl_demote_state); __entry->dmt_state = glock_trace_state(gl->gl_demote_state);
__entry->flags = gl->gl_flags; __entry->flags = gl->gl_flags | (gl->gl_object ? (1UL<<GLF_OBJECT) : 0);
), ),
TP_printk("%u,%u glock %d:%lld demote %s to %s flags:%s", TP_printk("%u,%u glock %d:%lld demote %s to %s flags:%s",
...@@ -318,6 +321,33 @@ TRACE_EVENT(gfs2_log_blocks, ...@@ -318,6 +321,33 @@ TRACE_EVENT(gfs2_log_blocks,
MINOR(__entry->dev), __entry->blocks) MINOR(__entry->dev), __entry->blocks)
); );
/* Writing back the AIL */
TRACE_EVENT(gfs2_ail_flush,
TP_PROTO(const struct gfs2_sbd *sdp, const struct writeback_control *wbc, int start),
TP_ARGS(sdp, wbc, start),
TP_STRUCT__entry(
__field( dev_t, dev )
__field( int, start )
__field( int, sync_mode )
__field( long, nr_to_write )
),
TP_fast_assign(
__entry->dev = sdp->sd_vfs->s_dev;
__entry->start = start;
__entry->sync_mode = wbc->sync_mode;
__entry->nr_to_write = wbc->nr_to_write;
),
TP_printk("%u,%u ail flush %s %s %ld", MAJOR(__entry->dev),
MINOR(__entry->dev), __entry->start ? "start" : "end",
__entry->sync_mode == WB_SYNC_ALL ? "all" : "none",
__entry->nr_to_write)
);
/* Section 3 - bmap /* Section 3 - bmap
* *
* Objectives: * Objectives:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment