aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2014-01-08 07:14:57 -0500
committerSteven Whitehouse <swhiteho@redhat.com>2014-01-08 07:14:57 -0500
commit01bcb0dedbcd77a5ebb4f2dfd7d3196e3ad5cea5 (patch)
treeef18400f34911ac88061334060ffb0c6f93f0fec
parent22b5a6c0c0cd5eb524d31c949d113c6683e37ec9 (diff)
GFS2: Add hints to directory leaf blocks
This patch adds four new fields to directory leaf blocks. The intent is not to use them in the kernel itself, although perhaps we may be able to use them as hints at some later date, but instead to provide more information for debug/fsck use. One new field adds a pointer to the inode to which the leaf belongs. This can be useful if the pointer to the leaf block has become corrupt, as it will allow us to know which inode this block should be associated with. This field is set when the leaf is created and never changed over its lifetime. The second field is a "distance from the hash table" field. The meaning is as follows: 0 = An old leaf in which this value has not been set 1 = This leaf is pointed to directly from the hash table 2+ = This leaf is part of a chain, pointed to by another leaf block, the value gives the position in the chain. The third and fourth fields combine to give a time stamp of the most recent directory insertion or deletion from this leaf block. The time stamp is not updated when a new leaf block is chained from the current one. The code is currently written such that the timestamp on the dir inode will match that of the leaf block for the most recent insertion/deletion. For backwards compatibility, any of these new fields which is zero should be considered to be "unknown". Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
-rw-r--r--fs/gfs2/dir.c40
-rw-r--r--include/uapi/linux/gfs2_ondisk.h11
2 files changed, 47 insertions, 4 deletions
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index b2e5ebfb4bb1..fa32655449c8 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -834,6 +834,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
834 struct gfs2_leaf *leaf; 834 struct gfs2_leaf *leaf;
835 struct gfs2_dirent *dent; 835 struct gfs2_dirent *dent;
836 struct qstr name = { .name = "" }; 836 struct qstr name = { .name = "" };
837 struct timespec tv = CURRENT_TIME;
837 838
838 error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL); 839 error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);
839 if (error) 840 if (error)
@@ -850,7 +851,11 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
850 leaf->lf_entries = 0; 851 leaf->lf_entries = 0;
851 leaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE); 852 leaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
852 leaf->lf_next = 0; 853 leaf->lf_next = 0;
853 memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved)); 854 leaf->lf_inode = cpu_to_be64(ip->i_no_addr);
855 leaf->lf_dist = cpu_to_be32(1);
856 leaf->lf_nsec = cpu_to_be32(tv.tv_nsec);
857 leaf->lf_sec = cpu_to_be64(tv.tv_sec);
858 memset(leaf->lf_reserved2, 0, sizeof(leaf->lf_reserved2));
854 dent = (struct gfs2_dirent *)(leaf+1); 859 dent = (struct gfs2_dirent *)(leaf+1);
855 gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent); 860 gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent);
856 *pbh = bh; 861 *pbh = bh;
@@ -1612,11 +1617,31 @@ out:
1612 return ret; 1617 return ret;
1613} 1618}
1614 1619
1620/**
1621 * dir_new_leaf - Add a new leaf onto hash chain
1622 * @inode: The directory
1623 * @name: The name we are adding
1624 *
1625 * This adds a new dir leaf onto an existing leaf when there is not
1626 * enough space to add a new dir entry. This is a last resort after
1627 * we've expanded the hash table to max size and also split existing
1628 * leaf blocks, so it will only occur for very large directories.
1629 *
1630 * The dist parameter is set to 1 for leaf blocks directly attached
1631 * to the hash table, 2 for one layer of indirection, 3 for two layers
1632 * etc. We are thus able to tell the difference between an old leaf
1633 * with dist set to zero (i.e. "don't know") and a new one where we
1634 * set this information for debug/fsck purposes.
1635 *
1636 * Returns: 0 on success, or -ve on error
1637 */
1638
1615static int dir_new_leaf(struct inode *inode, const struct qstr *name) 1639static int dir_new_leaf(struct inode *inode, const struct qstr *name)
1616{ 1640{
1617 struct buffer_head *bh, *obh; 1641 struct buffer_head *bh, *obh;
1618 struct gfs2_inode *ip = GFS2_I(inode); 1642 struct gfs2_inode *ip = GFS2_I(inode);
1619 struct gfs2_leaf *leaf, *oleaf; 1643 struct gfs2_leaf *leaf, *oleaf;
1644 u32 dist = 1;
1620 int error; 1645 int error;
1621 u32 index; 1646 u32 index;
1622 u64 bn; 1647 u64 bn;
@@ -1626,6 +1651,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
1626 if (error) 1651 if (error)
1627 return error; 1652 return error;
1628 do { 1653 do {
1654 dist++;
1629 oleaf = (struct gfs2_leaf *)obh->b_data; 1655 oleaf = (struct gfs2_leaf *)obh->b_data;
1630 bn = be64_to_cpu(oleaf->lf_next); 1656 bn = be64_to_cpu(oleaf->lf_next);
1631 if (!bn) 1657 if (!bn)
@@ -1643,6 +1669,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
1643 brelse(obh); 1669 brelse(obh);
1644 return -ENOSPC; 1670 return -ENOSPC;
1645 } 1671 }
1672 leaf->lf_dist = cpu_to_be32(dist);
1646 oleaf->lf_next = cpu_to_be64(bh->b_blocknr); 1673 oleaf->lf_next = cpu_to_be64(bh->b_blocknr);
1647 brelse(bh); 1674 brelse(bh);
1648 brelse(obh); 1675 brelse(obh);
@@ -1679,6 +1706,7 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
1679 struct gfs2_inode *ip = GFS2_I(inode); 1706 struct gfs2_inode *ip = GFS2_I(inode);
1680 struct buffer_head *bh = da->bh; 1707 struct buffer_head *bh = da->bh;
1681 struct gfs2_dirent *dent = da->dent; 1708 struct gfs2_dirent *dent = da->dent;
1709 struct timespec tv;
1682 struct gfs2_leaf *leaf; 1710 struct gfs2_leaf *leaf;
1683 int error; 1711 int error;
1684 1712
@@ -1693,15 +1721,18 @@ int gfs2_dir_add(struct inode *inode, const struct qstr *name,
1693 dent = gfs2_init_dirent(inode, dent, name, bh); 1721 dent = gfs2_init_dirent(inode, dent, name, bh);
1694 gfs2_inum_out(nip, dent); 1722 gfs2_inum_out(nip, dent);
1695 dent->de_type = cpu_to_be16(IF2DT(nip->i_inode.i_mode)); 1723 dent->de_type = cpu_to_be16(IF2DT(nip->i_inode.i_mode));
1724 tv = CURRENT_TIME;
1696 if (ip->i_diskflags & GFS2_DIF_EXHASH) { 1725 if (ip->i_diskflags & GFS2_DIF_EXHASH) {
1697 leaf = (struct gfs2_leaf *)bh->b_data; 1726 leaf = (struct gfs2_leaf *)bh->b_data;
1698 be16_add_cpu(&leaf->lf_entries, 1); 1727 be16_add_cpu(&leaf->lf_entries, 1);
1728 leaf->lf_nsec = cpu_to_be32(tv.tv_nsec);
1729 leaf->lf_sec = cpu_to_be64(tv.tv_sec);
1699 } 1730 }
1700 da->dent = NULL; 1731 da->dent = NULL;
1701 da->bh = NULL; 1732 da->bh = NULL;
1702 brelse(bh); 1733 brelse(bh);
1703 ip->i_entries++; 1734 ip->i_entries++;
1704 ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; 1735 ip->i_inode.i_mtime = ip->i_inode.i_ctime = tv;
1705 if (S_ISDIR(nip->i_inode.i_mode)) 1736 if (S_ISDIR(nip->i_inode.i_mode))
1706 inc_nlink(&ip->i_inode); 1737 inc_nlink(&ip->i_inode);
1707 mark_inode_dirty(inode); 1738 mark_inode_dirty(inode);
@@ -1752,6 +1783,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
1752 const struct qstr *name = &dentry->d_name; 1783 const struct qstr *name = &dentry->d_name;
1753 struct gfs2_dirent *dent, *prev = NULL; 1784 struct gfs2_dirent *dent, *prev = NULL;
1754 struct buffer_head *bh; 1785 struct buffer_head *bh;
1786 struct timespec tv = CURRENT_TIME;
1755 1787
1756 /* Returns _either_ the entry (if its first in block) or the 1788 /* Returns _either_ the entry (if its first in block) or the
1757 previous entry otherwise */ 1789 previous entry otherwise */
@@ -1777,13 +1809,15 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
1777 if (!entries) 1809 if (!entries)
1778 gfs2_consist_inode(dip); 1810 gfs2_consist_inode(dip);
1779 leaf->lf_entries = cpu_to_be16(--entries); 1811 leaf->lf_entries = cpu_to_be16(--entries);
1812 leaf->lf_nsec = cpu_to_be32(tv.tv_nsec);
1813 leaf->lf_sec = cpu_to_be64(tv.tv_sec);
1780 } 1814 }
1781 brelse(bh); 1815 brelse(bh);
1782 1816
1783 if (!dip->i_entries) 1817 if (!dip->i_entries)
1784 gfs2_consist_inode(dip); 1818 gfs2_consist_inode(dip);
1785 dip->i_entries--; 1819 dip->i_entries--;
1786 dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME; 1820 dip->i_inode.i_mtime = dip->i_inode.i_ctime = tv;
1787 if (S_ISDIR(dentry->d_inode->i_mode)) 1821 if (S_ISDIR(dentry->d_inode->i_mode))
1788 drop_nlink(&dip->i_inode); 1822 drop_nlink(&dip->i_inode);
1789 mark_inode_dirty(&dip->i_inode); 1823 mark_inode_dirty(&dip->i_inode);
diff --git a/include/uapi/linux/gfs2_ondisk.h b/include/uapi/linux/gfs2_ondisk.h
index b2de1f9a88d6..0f24c07aed51 100644
--- a/include/uapi/linux/gfs2_ondisk.h
+++ b/include/uapi/linux/gfs2_ondisk.h
@@ -319,7 +319,16 @@ struct gfs2_leaf {
319 __be32 lf_dirent_format; /* Format of the dirents */ 319 __be32 lf_dirent_format; /* Format of the dirents */
320 __be64 lf_next; /* Next leaf, if overflow */ 320 __be64 lf_next; /* Next leaf, if overflow */
321 321
322 __u8 lf_reserved[64]; 322 union {
323 __u8 lf_reserved[64];
324 struct {
325 __be64 lf_inode; /* Dir inode number */
326 __be32 lf_dist; /* Dist from inode on chain */
327 __be32 lf_nsec; /* Last ins/del usecs */
328 __be64 lf_sec; /* Last ins/del in secs */
329 __u8 lf_reserved2[40];
330 };
331 };
323}; 332};
324 333
325/* 334/*