diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index f4c48395208a0be24e14ebf99a2d688d71bc176d..22ca3b5ddaea06c30322dc3084b7af9ac3e10ae5 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -38,15 +38,17 @@ #include "util.h" /** - * inode_attr_in - Copy attributes from the dinode into the VFS inode + * gfs2_inode_attr_in - Copy attributes from the dinode into the VFS inode * @ip: The GFS2 inode (with embedded disk inode data) * @inode: The Linux VFS inode * */ -static void inode_attr_in(struct gfs2_inode *ip, struct inode *inode) +void gfs2_inode_attr_in(struct gfs2_inode *ip) { - inode->i_ino = ip->i_num.no_formal_ino; + struct inode *inode = &ip->i_inode; + + inode->i_ino = ip->i_num.no_addr; switch (ip->i_di.di_mode & S_IFMT) { case S_IFBLK: @@ -84,18 +86,6 @@ static void inode_attr_in(struct gfs2_inode *ip, struct inode *inode) inode->i_flags &= ~S_APPEND; } -/** - * gfs2_inode_attr_in - Copy attributes from the dinode into the VFS inode - * @ip: The GFS2 inode (with embedded disk inode data) - * - */ - -void gfs2_inode_attr_in(struct gfs2_inode *ip) -{ - struct inode *inode = &ip->i_inode; - inode_attr_in(ip, inode); -} - /** * gfs2_inode_attr_out - Copy attributes from VFS inode into the dinode * @ip: The GFS2 inode @@ -621,7 +611,8 @@ static void munge_mode_uid_gid(struct gfs2_inode *dip, unsigned int *mode, *gid = current->fsgid; } -static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum *inum) +static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum *inum, + u64 *generation) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); int error; @@ -637,14 +628,14 @@ static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum *inum) if (error) goto out_ipreserv; - inum->no_addr = gfs2_alloc_di(dip); + inum->no_addr = gfs2_alloc_di(dip, generation); gfs2_trans_end(sdp); - out_ipreserv: +out_ipreserv: gfs2_inplace_release(dip); - out: +out: gfs2_alloc_put(dip); return error; @@ -662,8 +653,9 @@ static int alloc_dinode(struct gfs2_inode *dip, struct gfs2_inum *inum) */ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, - struct gfs2_inum *inum, unsigned int mode, - unsigned int uid, unsigned int gid) + const struct gfs2_inum *inum, unsigned int mode, + unsigned int uid, unsigned int gid, + const u64 *generation) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_dinode *di; @@ -686,7 +678,7 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, di->di_atime = di->di_mtime = di->di_ctime = cpu_to_be64(get_seconds()); di->di_major = di->di_minor = cpu_to_be32(0); di->di_goal_meta = di->di_goal_data = cpu_to_be64(inum->no_addr); - di->__pad[0] = di->__pad[1] = 0; + di->di_generation = cpu_to_be64(*generation); di->di_flags = cpu_to_be32(0); if (S_ISREG(mode)) { @@ -717,7 +709,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, - unsigned int mode, struct gfs2_inum *inum) + unsigned int mode, const struct gfs2_inum *inum, + const u64 *generation) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); unsigned int uid, gid; @@ -738,7 +731,7 @@ static int make_dinode(struct gfs2_inode *dip, struct gfs2_glock *gl, if (error) goto out_quota; - init_dinode(dip, gl, inum, mode, uid, gid); + init_dinode(dip, gl, inum, mode, uid, gid, generation); gfs2_quota_change(dip, +1, uid, gid); gfs2_trans_end(sdp); @@ -844,6 +837,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_inum inum; int error; + u64 generation; if (!name->len || name->len > GFS2_FNAMESIZE) return ERR_PTR(-ENAMETOOLONG); @@ -861,7 +855,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, if (error) goto fail_gunlock; - error = alloc_dinode(dip, &inum); + error = alloc_dinode(dip, &inum, &generation); if (error) goto fail_gunlock; @@ -893,7 +887,7 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name, goto fail_gunlock; } - error = make_dinode(dip, ghs[1].gh_gl, mode, &inum); + error = make_dinode(dip, ghs[1].gh_gl, mode, &inum, &generation); if (error) goto fail_gunlock2; diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index ddcd4dc1081d53437a79a146fb243d594df025c6..cad44fd70d67b832d165637d9c47705d42dda6f3 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -31,6 +31,7 @@ #include "rgrp.h" #include "trans.h" #include "util.h" +#include "ops_address.h" #define buffer_busy(bh) \ ((bh)->b_state & ((1ul << BH_Dirty) | (1ul << BH_Lock) | (1ul << BH_Pinned))) @@ -50,118 +51,9 @@ static int gfs2_aspace_writepage(struct page *page, return block_write_full_page(page, aspace_get_block, wbc); } -/** - * stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out. - * @bh: the buffer we're stuck on - * - */ - -static void stuck_releasepage(struct buffer_head *bh) -{ - struct inode *inode = bh->b_page->mapping->host; - struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; - struct gfs2_bufdata *bd = bh->b_private; - struct gfs2_glock *gl; - - fs_warn(sdp, "stuck in gfs2_releasepage() %p\n", inode); - fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", - (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); - fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); - fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); - - if (!bd) - return; - - gl = bd->bd_gl; - - fs_warn(sdp, "gl = (%u, %llu)\n", - gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); - - fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", - (list_empty(&bd->bd_list_tr)) ? "no" : "yes", - (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); - - if (gl->gl_ops == &gfs2_inode_glops) { - struct gfs2_inode *ip = gl->gl_object; - unsigned int x; - - if (!ip) - return; - - fs_warn(sdp, "ip = %llu %llu\n", - (unsigned long long)ip->i_num.no_formal_ino, - (unsigned long long)ip->i_num.no_addr); - - for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) - fs_warn(sdp, "ip->i_cache[%u] = %s\n", - x, (ip->i_cache[x]) ? "!NULL" : "NULL"); - } -} - -/** - * gfs2_aspace_releasepage - free the metadata associated with a page - * @page: the page that's being released - * @gfp_mask: passed from Linux VFS, ignored by us - * - * Call try_to_free_buffers() if the buffers in this page can be - * released. - * - * Returns: 0 - */ - -static int gfs2_aspace_releasepage(struct page *page, gfp_t gfp_mask) -{ - struct inode *aspace = page->mapping->host; - struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; - struct buffer_head *bh, *head; - struct gfs2_bufdata *bd; - unsigned long t; - - if (!page_has_buffers(page)) - goto out; - - head = bh = page_buffers(page); - do { - t = jiffies; - - while (atomic_read(&bh->b_count)) { - if (atomic_read(&aspace->i_writecount)) { - if (time_after_eq(jiffies, t + - gfs2_tune_get(sdp, gt_stall_secs) * HZ)) { - stuck_releasepage(bh); - t = jiffies; - } - - yield(); - continue; - } - - return 0; - } - - gfs2_assert_warn(sdp, !buffer_pinned(bh)); - - bd = bh->b_private; - if (bd) { - gfs2_assert_warn(sdp, bd->bd_bh == bh); - gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); - gfs2_assert_warn(sdp, list_empty(&bd->bd_le.le_list)); - gfs2_assert_warn(sdp, !bd->bd_ail); - kmem_cache_free(gfs2_bufdata_cachep, bd); - bh->b_private = NULL; - } - - bh = bh->b_this_page; - } - while (bh != head); - - out: - return try_to_free_buffers(page); -} - static const struct address_space_operations aspace_aops = { .writepage = gfs2_aspace_writepage, - .releasepage = gfs2_aspace_releasepage, + .releasepage = gfs2_releasepage, }; /** diff --git a/fs/gfs2/ondisk.c b/fs/gfs2/ondisk.c index 09154ad7b2706b5c65d1810d7462df314b3b8d6f..39c7f0345fc6cdd2683932fc134abea45c5404d9 100644 --- a/fs/gfs2/ondisk.c +++ b/fs/gfs2/ondisk.c @@ -128,6 +128,7 @@ void gfs2_rgrp_in(struct gfs2_rgrp *rg, char *buf) rg->rg_flags = be32_to_cpu(str->rg_flags); rg->rg_free = be32_to_cpu(str->rg_free); rg->rg_dinodes = be32_to_cpu(str->rg_dinodes); + rg->rg_igeneration = be64_to_cpu(str->rg_igeneration); } void gfs2_rgrp_out(struct gfs2_rgrp *rg, char *buf) @@ -138,7 +139,8 @@ void gfs2_rgrp_out(struct gfs2_rgrp *rg, char *buf) str->rg_flags = cpu_to_be32(rg->rg_flags); str->rg_free = cpu_to_be32(rg->rg_free); str->rg_dinodes = cpu_to_be32(rg->rg_dinodes); - + str->__pad = cpu_to_be32(0); + str->rg_igeneration = cpu_to_be64(rg->rg_igeneration); memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); } @@ -172,6 +174,7 @@ void gfs2_dinode_in(struct gfs2_dinode *di, char *buf) di->di_goal_meta = be64_to_cpu(str->di_goal_meta); di->di_goal_data = be64_to_cpu(str->di_goal_data); + di->di_generation = be64_to_cpu(str->di_generation); di->di_flags = be32_to_cpu(str->di_flags); di->di_payload_format = be32_to_cpu(str->di_payload_format); @@ -205,6 +208,7 @@ void gfs2_dinode_out(struct gfs2_dinode *di, char *buf) str->di_goal_meta = cpu_to_be64(di->di_goal_meta); str->di_goal_data = cpu_to_be64(di->di_goal_data); + str->di_generation = cpu_to_be64(di->di_generation); str->di_flags = cpu_to_be32(di->di_flags); str->di_payload_format = cpu_to_be32(di->di_payload_format); diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c index 2c4ec5cf21ffebac2d30a4f36ec295d58645d4bc..031270ad55e2208768cf27cff5e5f40e924bccf7 100644 --- a/fs/gfs2/ops_address.c +++ b/fs/gfs2/ops_address.c @@ -33,6 +33,7 @@ #include "rgrp.h" #include "ops_file.h" #include "util.h" +#include "glops.h" /** * gfs2_get_block - Fills in a buffer head with details about a block @@ -659,6 +660,115 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, return ret; } +/** + * stuck_releasepage - We're stuck in gfs2_releasepage(). Print stuff out. + * @bh: the buffer we're stuck on + * + */ + +static void stuck_releasepage(struct buffer_head *bh) +{ + struct inode *inode = bh->b_page->mapping->host; + struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; + struct gfs2_bufdata *bd = bh->b_private; + struct gfs2_glock *gl; + + fs_warn(sdp, "stuck in gfs2_releasepage() %p\n", inode); + fs_warn(sdp, "blkno = %llu, bh->b_count = %d\n", + (unsigned long long)bh->b_blocknr, atomic_read(&bh->b_count)); + fs_warn(sdp, "pinned = %u\n", buffer_pinned(bh)); + fs_warn(sdp, "bh->b_private = %s\n", (bd) ? "!NULL" : "NULL"); + + if (!bd) + return; + + gl = bd->bd_gl; + + fs_warn(sdp, "gl = (%u, %llu)\n", + gl->gl_name.ln_type, (unsigned long long)gl->gl_name.ln_number); + + fs_warn(sdp, "bd_list_tr = %s, bd_le.le_list = %s\n", + (list_empty(&bd->bd_list_tr)) ? "no" : "yes", + (list_empty(&bd->bd_le.le_list)) ? "no" : "yes"); + + if (gl->gl_ops == &gfs2_inode_glops) { + struct gfs2_inode *ip = gl->gl_object; + unsigned int x; + + if (!ip) + return; + + fs_warn(sdp, "ip = %llu %llu\n", + (unsigned long long)ip->i_num.no_formal_ino, + (unsigned long long)ip->i_num.no_addr); + + for (x = 0; x < GFS2_MAX_META_HEIGHT; x++) + fs_warn(sdp, "ip->i_cache[%u] = %s\n", + x, (ip->i_cache[x]) ? "!NULL" : "NULL"); + } +} + +/** + * gfs2_aspace_releasepage - free the metadata associated with a page + * @page: the page that's being released + * @gfp_mask: passed from Linux VFS, ignored by us + * + * Call try_to_free_buffers() if the buffers in this page can be + * released. + * + * Returns: 0 + */ + +int gfs2_releasepage(struct page *page, gfp_t gfp_mask) +{ + struct inode *aspace = page->mapping->host; + struct gfs2_sbd *sdp = aspace->i_sb->s_fs_info; + struct buffer_head *bh, *head; + struct gfs2_bufdata *bd; + unsigned long t; + + if (!page_has_buffers(page)) + goto out; + + head = bh = page_buffers(page); + do { + t = jiffies; + + while (atomic_read(&bh->b_count)) { + if (atomic_read(&aspace->i_writecount)) { + if (time_after_eq(jiffies, t + + gfs2_tune_get(sdp, gt_stall_secs) * HZ)) { + stuck_releasepage(bh); + t = jiffies; + } + + yield(); + continue; + } + + return 0; + } + + gfs2_assert_warn(sdp, !buffer_pinned(bh)); + + bd = bh->b_private; + if (bd) { + gfs2_assert_warn(sdp, bd->bd_bh == bh); + gfs2_assert_warn(sdp, list_empty(&bd->bd_list_tr)); + gfs2_assert_warn(sdp, list_empty(&bd->bd_le.le_list)); + gfs2_assert_warn(sdp, !bd->bd_ail); + kmem_cache_free(gfs2_bufdata_cachep, bd); + bh->b_private = NULL; + } + + bh = bh->b_this_page; + } + while (bh != head); + + out: + return try_to_free_buffers(page); +} + const struct address_space_operations gfs2_file_aops = { .writepage = gfs2_writepage, .readpage = gfs2_readpage, @@ -668,6 +778,7 @@ const struct address_space_operations gfs2_file_aops = { .commit_write = gfs2_commit_write, .bmap = gfs2_bmap, .invalidatepage = gfs2_invalidatepage, + .releasepage = gfs2_releasepage, .direct_IO = gfs2_direct_IO, }; diff --git a/fs/gfs2/ops_address.h b/fs/gfs2/ops_address.h index a7ef3bf36f3ebcfc327719d9fa844a7e0fe68c3e..dfc3dda6de11118cc67ba4c8d4f8b1bd61780d31 100644 --- a/fs/gfs2/ops_address.h +++ b/fs/gfs2/ops_address.h @@ -13,5 +13,6 @@ extern const struct address_space_operations gfs2_file_aops; extern int gfs2_get_block(struct inode *inode, sector_t lblock, struct buffer_head *bh_result, int create); +extern int gfs2_releasepage(struct page *page, gfp_t gfp_mask); #endif /* __OPS_ADDRESS_DOT_H__ */ diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index c8e2c98700a7155a93743e626ba3e8c2c7960192..26f1d3249b0fce0803a77a67337d27f1128d8dd2 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -333,7 +333,7 @@ static int filldir_reg_func(void *opaque, const char *name, unsigned int length, int error; error = fdr->fdr_filldir(fdr->fdr_opaque, name, length, offset, - inum->no_formal_ino, type); + inum->no_addr, type); if (error) return 1; diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 14c1f88bfb5d3e025e52ae8b27b2906d5c087fdc..65eea0b88bf7d858b54fef6095f12cdb5073e830 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1205,7 +1205,7 @@ static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, uint64_t bstart, * Returns: the allocated block */ -uint64_t gfs2_alloc_data(struct gfs2_inode *ip) +u64 gfs2_alloc_data(struct gfs2_inode *ip) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_alloc *al = &ip->i_alloc; @@ -1249,7 +1249,7 @@ uint64_t gfs2_alloc_data(struct gfs2_inode *ip) * Returns: the allocated block */ -uint64_t gfs2_alloc_meta(struct gfs2_inode *ip) +u64 gfs2_alloc_meta(struct gfs2_inode *ip) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_alloc *al = &ip->i_alloc; @@ -1294,13 +1294,13 @@ uint64_t gfs2_alloc_meta(struct gfs2_inode *ip) * Returns: the block allocated */ -uint64_t gfs2_alloc_di(struct gfs2_inode *dip) +u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation) { struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode); struct gfs2_alloc *al = &dip->i_alloc; struct gfs2_rgrpd *rgd = al->al_rgd; - uint32_t blk; - uint64_t block; + u32 blk; + u64 block; blk = rgblk_search(rgd, rgd->rd_last_alloc_meta, GFS2_BLKST_FREE, GFS2_BLKST_DINODE); @@ -1312,7 +1312,7 @@ uint64_t gfs2_alloc_di(struct gfs2_inode *dip) gfs2_assert_withdraw(sdp, rgd->rd_rg.rg_free); rgd->rd_rg.rg_free--; rgd->rd_rg.rg_dinodes++; - + *generation = rgd->rd_rg.rg_igeneration++; gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1); gfs2_rgrp_out(&rgd->rd_rg, rgd->rd_bits[0].bi_bh->b_data); diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h index 9c42d2252ddd2fcb780f3438962b89f5e91745ec..14600944d1843a62a64a002d07d4f2227882ff8f 100644 --- a/fs/gfs2/rgrp.h +++ b/fs/gfs2/rgrp.h @@ -37,9 +37,9 @@ void gfs2_inplace_release(struct gfs2_inode *ip); unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, uint64_t block); -uint64_t gfs2_alloc_data(struct gfs2_inode *ip); -uint64_t gfs2_alloc_meta(struct gfs2_inode *ip); -uint64_t gfs2_alloc_di(struct gfs2_inode *ip); +u64 gfs2_alloc_data(struct gfs2_inode *ip); +u64 gfs2_alloc_meta(struct gfs2_inode *ip); +u64 gfs2_alloc_di(struct gfs2_inode *ip, u64 *generation); void gfs2_free_data(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen); void gfs2_free_meta(struct gfs2_inode *ip, uint64_t bstart, uint32_t blen); diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h index 1181da8319394e447496a999f2251965d7372c25..3ebd8743ce8c40deb3dc3bb1d462b4368f5f5eb1 100644 --- a/include/linux/gfs2_ondisk.h +++ b/include/linux/gfs2_ondisk.h @@ -170,8 +170,10 @@ struct gfs2_rgrp { __be32 rg_flags; __be32 rg_free; __be32 rg_dinodes; + __be32 __pad; + __be64 rg_igeneration; - __u8 rg_reserved[92]; /* Several fields from gfs1 now reserved */ + __u8 rg_reserved[80]; /* Several fields from gfs1 now reserved */ }; /* @@ -248,7 +250,7 @@ struct gfs2_dinode { */ __be64 di_goal_meta; /* rgrp to alloc from next */ __be64 di_goal_data; /* data block goal */ - __u32 __pad[2]; + __be64 di_generation; /* generation number for NFS */ __be32 di_flags; /* GFS2_DIF_... */ __be32 di_payload_format; /* GFS2_FORMAT_... */