From f4a3ae9308e34bcd704325a08879b2c1cfb74686 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 19 Nov 2014 12:27:11 -0600 Subject: GFS2: Use average srttb value in congestion calculations This patch changes function gfs2_rgrp_congested so that it uses an average srttb (smoothed round trip time for blocking rgrp glocks) rather than the CPU-specific value. If we use the CPU-specific value it can incorrectly report no contention when there really is contention due to the glock processing occurring on a different CPU. Signed-off-by: Bob Peterson Acked-by: Steven Whitehouse --- fs/gfs2/rgrp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 6af2396a317c..f39eedc1af76 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1850,14 +1850,19 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops) const struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_lkstats *st; s64 r_dcount, l_dcount; - s64 r_srttb, l_srttb; + s64 l_srttb, a_srttb = 0; s64 srttb_diff; s64 sqr_diff; s64 var; + int cpu; preempt_disable(); + for_each_present_cpu(cpu) { + st = &per_cpu_ptr(sdp->sd_lkstats, cpu)->lkstats[LM_TYPE_RGRP]; + a_srttb += st->stats[GFS2_LKS_SRTTB]; + } st = &this_cpu_ptr(sdp->sd_lkstats)->lkstats[LM_TYPE_RGRP]; - r_srttb = st->stats[GFS2_LKS_SRTTB]; + a_srttb /= num_present_cpus(); r_dcount = st->stats[GFS2_LKS_DCOUNT]; var = st->stats[GFS2_LKS_SRTTVARB] + gl->gl_stats.stats[GFS2_LKS_SRTTVARB]; @@ -1866,10 +1871,10 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops) l_srttb = gl->gl_stats.stats[GFS2_LKS_SRTTB]; l_dcount = gl->gl_stats.stats[GFS2_LKS_DCOUNT]; - if ((l_dcount < 1) || (r_dcount < 1) || (r_srttb == 0)) + if ((l_dcount < 1) || (r_dcount < 1) || (a_srttb == 0)) return false; - srttb_diff = r_srttb - l_srttb; + srttb_diff = a_srttb - l_srttb; sqr_diff = srttb_diff * srttb_diff; var *= 2; -- cgit v1.2.3 From 0166b197c2ed2327bb7761ded8cba2cfd371a425 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 22 Apr 2015 11:24:12 -0500 Subject: GFS2: Average in only non-zero round-trip times for congestion stats This patch changes function gfs2_rgrp_congested so that it only factors in non-zero values into its average round trip time. If the round-trip time is zero for a particular cpu, that cpu has obviously never dealt with bouncing the resource group in question, so factoring in a zero value will only skew the numbers. It also fixes a compile error on some arches related to division. Signed-off-by: Bob Peterson Acked-by: Steven Whitehouse --- fs/gfs2/rgrp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index f39eedc1af76..cb270651c613 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1854,15 +1854,19 @@ static bool gfs2_rgrp_congested(const struct gfs2_rgrpd *rgd, int loops) s64 srttb_diff; s64 sqr_diff; s64 var; - int cpu; + int cpu, nonzero = 0; preempt_disable(); for_each_present_cpu(cpu) { st = &per_cpu_ptr(sdp->sd_lkstats, cpu)->lkstats[LM_TYPE_RGRP]; - a_srttb += st->stats[GFS2_LKS_SRTTB]; + if (st->stats[GFS2_LKS_SRTTB]) { + a_srttb += st->stats[GFS2_LKS_SRTTB]; + nonzero++; + } } st = &this_cpu_ptr(sdp->sd_lkstats)->lkstats[LM_TYPE_RGRP]; - a_srttb /= num_present_cpus(); + if (nonzero) + do_div(a_srttb, nonzero); r_dcount = st->stats[GFS2_LKS_DCOUNT]; var = st->stats[GFS2_LKS_SRTTVARB] + gl->gl_stats.stats[GFS2_LKS_SRTTVARB]; -- cgit v1.2.3 From 086cc672e1cb600b9c17688a4aa44560db858c03 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Fri, 1 May 2015 09:36:00 -0500 Subject: GFS2: mark the journal idle to fix ro mounts When gfs2 was mounted read-only and then unmounted, it was writing a header block to the journal in the syncing gfs2_log_flush() call from kill_sb(). This is because the journal was not being marked as idle until the first log header was written out, and on a read-only mount there never was a log header written out. Since the journal was not marked idle, gfs2_log_flush() was writing out a header lock to make sure it was empty during the sync. Not only did this cause IO to a read-only filesystem, but the journalling isn't completely initialized on read-only mounts, and so gfs2 was writing out the wrong sequence number in the log header. Now, the journal is marked idle on mount, and gfs2_log_flush() won't write out anything until there starts being transactions to flush. Signed-off-by: Benjamin Marzinski Signed-off-by: Bob Peterson Acked-by: Steven Whitehouse --- fs/gfs2/ops_fstype.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/gfs2') diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index efc8e254787c..e4fdeccd50cd 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -756,6 +756,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) } } + sdp->sd_log_idle = 1; set_bit(SDF_JOURNAL_CHECKED, &sdp->sd_flags); gfs2_glock_dq_uninit(&ji_gh); jindex = 0; -- cgit v1.2.3 From 86fbca4923f956dae31247e68dc73ffdfd6e5cb0 Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Fri, 1 May 2015 12:54:38 -0500 Subject: GFS2: inode.c: indent with TABs, not spaces Follow the same style used for the other functions in the same file. Signed-off-by: Antonio Ospite Signed-off-by: Bob Peterson --- fs/gfs2/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 08bc84d7e768..2cd385d151b5 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1227,8 +1227,8 @@ static int gfs2_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, */ static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry, - struct file *file, unsigned flags, - umode_t mode, int *opened) + struct file *file, unsigned flags, + umode_t mode, int *opened) { struct dentry *d; bool excl = !!(flags & O_EXCL); -- cgit v1.2.3 From 959b6717175713259664950f3bba2418b038f69a Mon Sep 17 00:00:00 2001 From: Abhi Das Date: Tue, 5 May 2015 11:26:04 -0500 Subject: gfs2: handle NULL rgd in set_rgrp_preferences The function set_rgrp_preferences() does not handle the (rarely returned) NULL value from gfs2_rgrpd_get_next() and this patch fixes that. The fs image in question is only 150MB in size which allows for only 1 rgrp to be created. The in-memory rb tree has only 1 node and when gfs2_rgrpd_get_next() is called on this sole rgrp, it returns NULL. (Default behavior is to wrap around the rb tree and return the first node to give the illusion of a circular linked list. In the case of only 1 rgrp, we can't have gfs2_rgrpd_get_next() return the same rgrp (first, last, next all point to the same rgrp)... that would cause unintended consequences and infinite loops.) Signed-off-by: Abhi Das Signed-off-by: Bob Peterson --- fs/gfs2/rgrp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index cb270651c613..900e515bbdf6 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -978,10 +978,10 @@ static void set_rgrp_preferences(struct gfs2_sbd *sdp) rgd->rd_flags |= GFS2_RDF_PREFERRED; for (i = 0; i < sdp->sd_journals; i++) { rgd = gfs2_rgrpd_get_next(rgd); - if (rgd == first) + if (!rgd || rgd == first) break; } - } while (rgd != first); + } while (rgd && rgd != first); } /** -- cgit v1.2.3 From a63b7bbc2175901d79fa36ba734499655c077f0d Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Tue, 5 May 2015 12:12:19 -0500 Subject: GFS2: add support for rename2 and RENAME_EXCHANGE gfs2 now uses the rename2 directory iop, and supports the RENAME_EXCHANGE flag (as well as RENAME_NOREPLACE, which the vfs takes care of). Signed-off-by: Benjamin Marzinski Signed-off-by: Bob Peterson --- fs/gfs2/inode.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 189 insertions(+), 16 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 2cd385d151b5..4d809eb71b48 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1306,6 +1306,35 @@ static int gfs2_ok_to_move(struct gfs2_inode *this, struct gfs2_inode *to) return error; } +/** + * update_moved_ino - Update an inode that's being moved + * @ip: The inode being moved + * @ndip: The parent directory of the new filename + * @dir_rename: True of ip is a directory + * + * Returns: errno + */ + +static int update_moved_ino(struct gfs2_inode *ip, struct gfs2_inode *ndip, + int dir_rename) +{ + int error; + struct buffer_head *dibh; + + if (dir_rename) + return gfs2_dir_mvino(ip, &gfs2_qdotdot, ndip, DT_DIR); + + error = gfs2_meta_inode_buffer(ip, &dibh); + if (error) + return error; + ip->i_inode.i_ctime = CURRENT_TIME; + gfs2_trans_add_meta(ip->i_gl, dibh); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; +} + + /** * gfs2_rename - Rename a file * @odir: Parent directory of old file name @@ -1354,7 +1383,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (S_ISDIR(ip->i_inode.i_mode)) { dir_rename = 1; - /* don't move a dirctory into it's subdir */ + /* don't move a directory into its subdir */ error = gfs2_ok_to_move(ip, ndip); if (error) goto out_gunlock_r; @@ -1494,20 +1523,9 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (nip) error = gfs2_unlink_inode(ndip, ndentry); - 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_meta(ip->i_gl, dibh); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); - } + error = update_moved_ino(ip, ndip, dir_rename); + if (error) + goto out_end_trans; error = gfs2_dir_del(odip, odentry); if (error) @@ -1538,6 +1556,161 @@ out: return error; } +/** + * gfs2_exchange - exchange two files + * @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 + * @flags: The rename flags + * + * Returns: errno + */ + +static int gfs2_exchange(struct inode *odir, struct dentry *odentry, + struct inode *ndir, struct dentry *ndentry, + unsigned int flags) +{ + struct gfs2_inode *odip = GFS2_I(odir); + struct gfs2_inode *ndip = GFS2_I(ndir); + struct gfs2_inode *oip = GFS2_I(odentry->d_inode); + struct gfs2_inode *nip = GFS2_I(ndentry->d_inode); + struct gfs2_sbd *sdp = GFS2_SB(odir); + struct gfs2_holder ghs[5], r_gh = { .gh_gl = NULL, }; + unsigned int num_gh; + unsigned int x; + umode_t old_mode = oip->i_inode.i_mode; + umode_t new_mode = nip->i_inode.i_mode; + int error; + + error = gfs2_rindex_update(sdp); + 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(old_mode)) { + /* don't move a directory into its subdir */ + error = gfs2_ok_to_move(oip, ndip); + if (error) + goto out_gunlock_r; + } + + if (S_ISDIR(new_mode)) { + /* don't move a directory into its subdir */ + error = gfs2_ok_to_move(nip, odip); + 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(oip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + num_gh++; + + gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + num_gh++; + + for (x = 0; x < num_gh; x++) { + error = gfs2_glock_nq(ghs + x); + if (error) + goto out_gunlock; + } + + error = -ENOENT; + if (oip->i_inode.i_nlink == 0 || nip->i_inode.i_nlink == 0) + goto out_gunlock; + + error = gfs2_unlink_ok(odip, &odentry->d_name, oip); + if (error) + goto out_gunlock; + error = gfs2_unlink_ok(ndip, &ndentry->d_name, nip); + if (error) + goto out_gunlock; + + if (S_ISDIR(old_mode)) { + error = gfs2_permission(odentry->d_inode, MAY_WRITE); + if (error) + goto out_gunlock; + } + if (S_ISDIR(new_mode)) { + error = gfs2_permission(ndentry->d_inode, MAY_WRITE); + if (error) + goto out_gunlock; + } + error = gfs2_trans_begin(sdp, 4 * RES_DINODE + 4 * RES_LEAF, 0); + if (error) + goto out_gunlock; + + error = update_moved_ino(oip, ndip, S_ISDIR(old_mode)); + if (error) + goto out_end_trans; + + error = update_moved_ino(nip, odip, S_ISDIR(new_mode)); + if (error) + goto out_end_trans; + + error = gfs2_dir_mvino(ndip, &ndentry->d_name, oip, + IF2DT(old_mode)); + if (error) + goto out_end_trans; + + error = gfs2_dir_mvino(odip, &odentry->d_name, nip, + IF2DT(new_mode)); + if (error) + goto out_end_trans; + + if (odip != ndip) { + if (S_ISDIR(new_mode) && !S_ISDIR(old_mode)) { + inc_nlink(&odip->i_inode); + drop_nlink(&ndip->i_inode); + } else if (S_ISDIR(old_mode) && !S_ISDIR(new_mode)) { + inc_nlink(&ndip->i_inode); + drop_nlink(&odip->i_inode); + } + } + mark_inode_dirty(&ndip->i_inode); + if (odip != ndip) + mark_inode_dirty(&odip->i_inode); + +out_end_trans: + gfs2_trans_end(sdp); +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: + return error; +} + +static int gfs2_rename2(struct inode *odir, struct dentry *odentry, + struct inode *ndir, struct dentry *ndentry, + unsigned int flags) +{ + flags &= ~RENAME_NOREPLACE; + + if (flags & ~RENAME_EXCHANGE) + return -EINVAL; + + if (flags & RENAME_EXCHANGE) + return gfs2_exchange(odir, odentry, ndir, ndentry, flags); + + return gfs2_rename(odir, odentry, ndir, ndentry); +} + /** * gfs2_follow_link - Follow a symbolic link * @dentry: The dentry of the link @@ -1943,7 +2116,7 @@ const struct inode_operations gfs2_dir_iops = { .mkdir = gfs2_mkdir, .rmdir = gfs2_unlink, .mknod = gfs2_mknod, - .rename = gfs2_rename, + .rename2 = gfs2_rename2, .permission = gfs2_permission, .setattr = gfs2_setattr, .getattr = gfs2_getattr, -- cgit v1.2.3 From 01e64ee40ad741037352d1d6202eaa432f833eb4 Mon Sep 17 00:00:00 2001 From: Benjamin Marzinski Date: Tue, 5 May 2015 12:25:48 -0500 Subject: GFS2: make sure S_NOSEC flag isn't overwritten At the end of gfs2_set_inode_flags inode->i_flags is set to flags, so we should be modifying flags instead of inode->i_flags, so it isn't overwritten. Signed-off-by: Benjamin Marzinski Signed-off-by: Bob Peterson --- fs/gfs2/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 8ec43ab5babf..c706c6df4898 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -180,7 +180,7 @@ void gfs2_set_inode_flags(struct inode *inode) flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_NOSEC); if ((ip->i_eattr == 0) && !is_sxid(inode->i_mode)) - inode->i_flags |= S_NOSEC; + flags |= S_NOSEC; if (ip->i_diskflags & GFS2_DIF_IMMUTABLE) flags |= S_IMMUTABLE; if (ip->i_diskflags & GFS2_DIF_APPENDONLY) -- cgit v1.2.3 From e50ead480fac63ede9e0b656cd29c1820f7af9de Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 5 May 2015 13:23:22 -0500 Subject: gfs2: convert simple_str to kstr -Remove obsolete simple_str functions. -Return error code when kstr failed. -This patch also calls functions corresponding to destination type. Thanks to Alexey Dobriyan for suggesting improvements in block_store() and wdack_store() Signed-off-by: Fabian Frederick Signed-off-by: Bob Peterson --- fs/gfs2/sys.c | 66 +++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 18 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index ae8e8811f0e8..c9ff1cf7d4f3 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -101,8 +101,11 @@ static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf) static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { - int error; - int n = simple_strtol(buf, NULL, 0); + int error, n; + + error = kstrtoint(buf, 0, &n); + if (error) + return error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -134,10 +137,16 @@ static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf) static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + int error, val; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (simple_strtol(buf, NULL, 0) != 1) + error = kstrtoint(buf, 0, &val); + if (error) + return error; + + if (val != 1) return -EINVAL; gfs2_lm_withdraw(sdp, "withdrawing from cluster at user's request\n"); @@ -148,10 +157,16 @@ static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len) static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + int error, val; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (simple_strtol(buf, NULL, 0) != 1) + error = kstrtoint(buf, 0, &val); + if (error) + return error; + + if (val != 1) return -EINVAL; gfs2_statfs_sync(sdp->sd_vfs, 0); @@ -161,10 +176,16 @@ static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf, static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { + int error, val; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (simple_strtol(buf, NULL, 0) != 1) + error = kstrtoint(buf, 0, &val); + if (error) + return error; + + if (val != 1) return -EINVAL; gfs2_quota_sync(sdp->sd_vfs, 0); @@ -181,7 +202,9 @@ static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - id = simple_strtoul(buf, NULL, 0); + error = kstrtou32(buf, 0, &id); + if (error) + return error; qid = make_kqid(current_user_ns(), USRQUOTA, id); if (!qid_valid(qid)) @@ -201,7 +224,9 @@ static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - id = simple_strtoul(buf, NULL, 0); + error = kstrtou32(buf, 0, &id); + if (error) + return error; qid = make_kqid(current_user_ns(), GRPQUOTA, id); if (!qid_valid(qid)) @@ -324,10 +349,11 @@ static ssize_t block_show(struct gfs2_sbd *sdp, char *buf) static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { struct lm_lockstruct *ls = &sdp->sd_lockstruct; - ssize_t ret = len; - int val; + int ret, val; - val = simple_strtol(buf, NULL, 0); + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; if (val == 1) set_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags); @@ -336,9 +362,9 @@ static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len) smp_mb__after_atomic(); gfs2_glock_thaw(sdp); } else { - ret = -EINVAL; + return -EINVAL; } - return ret; + return len; } static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf) @@ -350,17 +376,18 @@ static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf) static ssize_t wdack_store(struct gfs2_sbd *sdp, const char *buf, size_t len) { - ssize_t ret = len; - int val; + int ret, val; - val = simple_strtol(buf, NULL, 0); + ret = kstrtoint(buf, 0, &val); + if (ret) + return ret; if ((val == 1) && !strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm")) complete(&sdp->sd_wdack); else - ret = -EINVAL; - return ret; + return -EINVAL; + return len; } static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf) @@ -553,11 +580,14 @@ static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field, { struct gfs2_tune *gt = &sdp->sd_tune; unsigned int x; + int error; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - x = simple_strtoul(buf, NULL, 0); + error = kstrtouint(buf, 0, &x); + if (error) + return error; if (check_zero && !x) return -EINVAL; -- cgit v1.2.3 From 1272574bf94874a644ea82fad321034c15b157ac Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 5 May 2015 13:29:54 -0500 Subject: gfs2: kerneldoc warning fixes Fixes the following kernel-doc warnings: Warning(fs/gfs2/aops.c:180): No description found for parameter 'wbc' Warning(fs/gfs2/aops.c:236): No description found for parameter 'end' Warning(fs/gfs2/aops.c:236): No description found for parameter 'done_index' Warning(fs/gfs2/aops.c:236): Excess function parameter 'writepage' description in 'gfs2_write_jdata_pagevec' Warning(fs/gfs2/aops.c:346): Excess function parameter 'writepage' description in 'gfs2_write_cache_jdata' Warning(fs/gfs2/aops.c:346): Excess function parameter 'data' description in 'gfs2_write_cache_jdata' Warning(fs/gfs2/aops.c:605): No description found for parameter 'file' Warning(fs/gfs2/aops.c:605): No description found for parameter 'mapping' Warning(fs/gfs2/aops.c:605): No description found for parameter 'pages' Warning(fs/gfs2/aops.c:605): No description found for parameter 'nr_pages' Warning(fs/gfs2/aops.c:870): No description found for parameter 'copied' Signed-off-by: Fabian Frederick Signed-off-by: Bob Peterson --- fs/gfs2/aops.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index a6e6990aea39..7c5a96c0b9f1 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -171,6 +171,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w /** * gfs2_jdata_writepage - Write complete page * @page: Page to write + * @wbc: The writeback control * * Returns: errno * @@ -221,9 +222,10 @@ static int gfs2_writepages(struct address_space *mapping, * gfs2_write_jdata_pagevec - Write back a pagevec's worth of pages * @mapping: The mapping * @wbc: The writeback control - * @writepage: The writepage function to call for each page * @pvec: The vector of pages * @nr_pages: The number of pages to write + * @end: End position + * @done_index: Page index * * Returns: non-zero if loop should terminate, zero otherwise */ @@ -333,8 +335,6 @@ continue_unlock: * gfs2_write_cache_jdata - Like write_cache_pages but different * @mapping: The mapping to write * @wbc: The writeback control - * @writepage: The writepage function to call - * @data: The data to pass to writepage * * The reason that we use our own function here is that we need to * start transactions before we grab page locks. This allows us @@ -588,6 +588,10 @@ int gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos, /** * gfs2_readpages - Read a bunch of pages at once + * @file: The file to read from + * @mapping: Address space info + * @pages: List of pages to read + * @nr_pages: Number of pages to read * * Some notes: * 1. This is only for readahead, so we can simply ignore any things @@ -853,7 +857,7 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh, * @mapping: The address space to write to * @pos: The file position * @len: The length of the data - * @copied: + * @copied: How much was actually copied by the VFS * @page: The page that has been written * @fsdata: The fsdata (unused in GFS2) * -- cgit v1.2.3 From a3e3213676d8208328ef9f79936f94e583c0ec90 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 18 May 2015 15:23:03 -0500 Subject: gfs2: fix shadow warning in gfs2_rbm_find() bi was already declared and initialized globally in gfs2_rbm_find() Signed-off-by: Fabian Frederick Signed-off-by: Bob Peterson --- fs/gfs2/rgrp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 900e515bbdf6..cd53d6e9e44e 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1711,10 +1711,8 @@ static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 *minext, return ret; bitmap_full: /* Mark bitmap as full and fall through */ - if ((state == GFS2_BLKST_FREE) && initial_offset == 0) { - struct gfs2_bitmap *bi = rbm_bi(rbm); + if ((state == GFS2_BLKST_FREE) && initial_offset == 0) set_bit(GBF_FULL, &bi->bi_flags); - } next_bitmap: /* Find next bitmap in the rgrp */ rbm->offset = 0; -- cgit v1.2.3 From 39a725803beeffeb319261df1ab145ef3c1ea0a1 Mon Sep 17 00:00:00 2001 From: Abhi Das Date: Tue, 2 Jun 2015 11:02:24 -0500 Subject: gfs2: fix quota updates on block boundaries For smaller block sizes (512B, 1K, 2K), some quotas straddle block boundaries such that the usage value is on one block and the rest of the quota is on the previous block. In such cases, the value does not get updated correctly. This patch fixes that by addressing the boundary conditions correctly. This patch also adds a (s64) cast that was missing in a call to gfs2_quota_change() in inode.c Signed-off-by: Abhi Das Signed-off-by: Bob Peterson --- fs/gfs2/inode.c | 2 +- fs/gfs2/quota.c | 197 +++++++++++++++++++++++++++++++++----------------------- 2 files changed, 119 insertions(+), 80 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 4d809eb71b48..a088e54090f5 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1889,7 +1889,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { - gfs2_quota_change(ip, -ap.target, ouid, ogid); + gfs2_quota_change(ip, -(s64)ap.target, ouid, ogid); gfs2_quota_change(ip, ap.target, nuid, ngid); } diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 5c27e48aa76f..7c2003177884 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -652,6 +652,112 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) mutex_unlock(&sdp->sd_quota_mutex); } +static int gfs2_write_buf_to_page(struct gfs2_inode *ip, unsigned long index, + unsigned off, void *buf, unsigned bytes) +{ + struct inode *inode = &ip->i_inode; + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct address_space *mapping = inode->i_mapping; + struct page *page; + struct buffer_head *bh; + void *kaddr; + u64 blk; + unsigned bsize = sdp->sd_sb.sb_bsize, bnum = 0, boff = 0; + unsigned to_write = bytes, pg_off = off; + int done = 0; + + blk = index << (PAGE_CACHE_SHIFT - sdp->sd_sb.sb_bsize_shift); + boff = off % bsize; + + page = find_or_create_page(mapping, index, GFP_NOFS); + if (!page) + return -ENOMEM; + if (!page_has_buffers(page)) + create_empty_buffers(page, bsize, 0); + + bh = page_buffers(page); + while (!done) { + /* Find the beginning block within the page */ + if (pg_off >= ((bnum * bsize) + bsize)) { + bh = bh->b_this_page; + bnum++; + blk++; + continue; + } + if (!buffer_mapped(bh)) { + gfs2_block_map(inode, blk, bh, 1); + if (!buffer_mapped(bh)) + goto unlock_out; + /* If it's a newly allocated disk block, zero it */ + if (buffer_new(bh)) + zero_user(page, bnum * bsize, bh->b_size); + } + if (PageUptodate(page)) + set_buffer_uptodate(bh); + if (!buffer_uptodate(bh)) { + ll_rw_block(READ | REQ_META, 1, &bh); + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) + goto unlock_out; + } + gfs2_trans_add_data(ip->i_gl, bh); + + /* If we need to write to the next block as well */ + if (to_write > (bsize - boff)) { + pg_off += (bsize - boff); + to_write -= (bsize - boff); + boff = pg_off % bsize; + continue; + } + done = 1; + } + + /* Write to the page, now that we have setup the buffer(s) */ + kaddr = kmap_atomic(page); + memcpy(kaddr + off, buf, bytes); + flush_dcache_page(page); + kunmap_atomic(kaddr); + unlock_page(page); + page_cache_release(page); + + return 0; + +unlock_out: + unlock_page(page); + page_cache_release(page); + return -EIO; +} + +static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, + loff_t loc) +{ + unsigned long pg_beg; + unsigned pg_off, nbytes, overflow = 0; + int pg_oflow = 0, error; + void *ptr; + + nbytes = sizeof(struct gfs2_quota); + + pg_beg = loc >> PAGE_CACHE_SHIFT; + pg_off = loc % PAGE_CACHE_SIZE; + + /* If the quota straddles a page boundary, split the write in two */ + if ((pg_off + nbytes) > PAGE_CACHE_SIZE) { + pg_oflow = 1; + overflow = (pg_off + nbytes) - PAGE_CACHE_SIZE; + } + + ptr = qp; + error = gfs2_write_buf_to_page(ip, pg_beg, pg_off, ptr, + nbytes - overflow); + /* If there's an overflow, write the remaining bytes to the next page */ + if (!error && pg_oflow) + error = gfs2_write_buf_to_page(ip, pg_beg + 1, 0, + ptr + nbytes - overflow, + overflow); + return error; +} + /** * gfs2_adjust_quota - adjust record of current block usage * @ip: The quota inode @@ -672,15 +778,8 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, { struct inode *inode = &ip->i_inode; struct gfs2_sbd *sdp = GFS2_SB(inode); - struct address_space *mapping = inode->i_mapping; - unsigned long index = loc >> PAGE_CACHE_SHIFT; - unsigned offset = loc & (PAGE_CACHE_SIZE - 1); - unsigned blocksize, iblock, pos; - struct buffer_head *bh; - struct page *page; - void *kaddr, *ptr; struct gfs2_quota q; - int err, nbytes; + int err; u64 size; if (gfs2_is_stuffed(ip)) { @@ -694,8 +793,11 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, if (err < 0) return err; + loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */ err = -EIO; be64_add_cpu(&q.qu_value, change); + if (be64_to_cpu(q.qu_value) < 0) + q.qu_value = 0; /* Never go negative on quota usage */ qd->qd_qb.qb_value = q.qu_value; if (fdq) { if (fdq->d_fieldmask & QC_SPC_SOFT) { @@ -712,79 +814,16 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, } } - /* Write the quota into the quota file on disk */ - ptr = &q; - nbytes = sizeof(struct gfs2_quota); -get_a_page: - page = find_or_create_page(mapping, index, GFP_NOFS); - if (!page) - return -ENOMEM; - - blocksize = inode->i_sb->s_blocksize; - iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - - if (!page_has_buffers(page)) - create_empty_buffers(page, blocksize, 0); - - bh = page_buffers(page); - pos = blocksize; - while (offset >= pos) { - bh = bh->b_this_page; - iblock++; - pos += blocksize; - } - - if (!buffer_mapped(bh)) { - gfs2_block_map(inode, iblock, bh, 1); - if (!buffer_mapped(bh)) - goto unlock_out; - /* If it's a newly allocated disk block for quota, zero it */ - if (buffer_new(bh)) - zero_user(page, pos - blocksize, bh->b_size); - } - - if (PageUptodate(page)) - set_buffer_uptodate(bh); - - if (!buffer_uptodate(bh)) { - ll_rw_block(READ | REQ_META, 1, &bh); - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) - goto unlock_out; - } - - gfs2_trans_add_data(ip->i_gl, bh); - - kaddr = kmap_atomic(page); - if (offset + sizeof(struct gfs2_quota) > PAGE_CACHE_SIZE) - nbytes = PAGE_CACHE_SIZE - offset; - memcpy(kaddr + offset, ptr, nbytes); - flush_dcache_page(page); - kunmap_atomic(kaddr); - unlock_page(page); - page_cache_release(page); - - /* If quota straddles page boundary, we need to update the rest of the - * quota at the beginning of the next page */ - if ((offset + sizeof(struct gfs2_quota)) > PAGE_CACHE_SIZE) { - ptr = ptr + nbytes; - nbytes = sizeof(struct gfs2_quota) - nbytes; - offset = 0; - index++; - goto get_a_page; + err = gfs2_write_disk_quota(ip, &q, loc); + if (!err) { + size = loc + sizeof(struct gfs2_quota); + if (size > inode->i_size) + i_size_write(inode, size); + inode->i_mtime = inode->i_atime = CURRENT_TIME; + mark_inode_dirty(inode); + set_bit(QDF_REFRESH, &qd->qd_flags); } - size = loc + sizeof(struct gfs2_quota); - if (size > inode->i_size) - i_size_write(inode, size); - inode->i_mtime = inode->i_atime = CURRENT_TIME; - mark_inode_dirty(inode); - set_bit(QDF_REFRESH, &qd->qd_flags); - return 0; - -unlock_out: - unlock_page(page); - page_cache_release(page); return err; } -- cgit v1.2.3 From 9cde2898d04595c3bf24828822b3ea7acff181dd Mon Sep 17 00:00:00 2001 From: Abhi Das Date: Tue, 2 Jun 2015 11:03:04 -0500 Subject: gfs2: limit quota log messages This patch makes the quota subsystem only report once that a particular user/group has exceeded their allotted quota. Previously, it was possible for a program to continuously try exceeding quota (despite receiving EDQUOT) and in turn trigger gfs2 to issue a kernel log message about quota exceed. In theory, this could get out of hand and flood the log and the filesystem hosting the log files. Signed-off-by: Abhi Das Signed-off-by: Bob Peterson --- fs/gfs2/incore.h | 1 + fs/gfs2/quota.c | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 58b75abf6ab2..304a22315448 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -432,6 +432,7 @@ enum { QDF_CHANGE = 1, QDF_LOCKED = 2, QDF_REFRESH = 3, + QDF_QMSG_QUIET = 4, }; struct gfs2_quota_data { diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 7c2003177884..dcd598aa2691 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -649,6 +649,8 @@ static void do_qc(struct gfs2_quota_data *qd, s64 change) slot_hold(qd); } + if (change < 0) /* Reset quiet flag if we freed some blocks */ + clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); mutex_unlock(&sdp->sd_quota_mutex); } @@ -1187,10 +1189,13 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, /* If no min_target specified or we don't meet * min_target, return -EDQUOT */ if (!ap->min_target || ap->min_target > ap->allowed) { - print_message(qd, "exceeded"); - quota_send_warning(qd->qd_id, - sdp->sd_vfs->s_dev, - QUOTA_NL_BHARDWARN); + if (!test_and_set_bit(QDF_QMSG_QUIET, + &qd->qd_flags)) { + print_message(qd, "exceeded"); + quota_send_warning(qd->qd_id, + sdp->sd_vfs->s_dev, + QUOTA_NL_BHARDWARN); + } error = -EDQUOT; break; } @@ -1685,6 +1690,8 @@ static int gfs2_set_dqblk(struct super_block *sb, struct kqid qid, /* Apply changes */ error = gfs2_adjust_quota(ip, offset, 0, qd, fdq); + if (!error) + clear_bit(QDF_QMSG_QUIET, &qd->qd_flags); gfs2_trans_end(sdp); out_release: -- cgit v1.2.3 From 1bdf45352e1897ab02632a4ed3648db8cc183f71 Mon Sep 17 00:00:00 2001 From: Abhi Das Date: Mon, 8 Jun 2015 11:20:50 -0500 Subject: gfs2: s64 cast for negative quota value One-line fix to cast quota value to s64 before comparison. By default the quantity is treated as u64. Signed-off-by: Abhi Das Signed-off-by: Bob Peterson Acked-by: Steven Whitehouse --- fs/gfs2/quota.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index dcd598aa2691..c2607a26be89 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -798,7 +798,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, loc -= sizeof(q); /* gfs2_internal_read would've advanced the loc ptr */ err = -EIO; be64_add_cpu(&q.qu_value, change); - if (be64_to_cpu(q.qu_value) < 0) + if (((s64)be64_to_cpu(q.qu_value)) < 0) q.qu_value = 0; /* Never go negative on quota usage */ qd->qd_qb.qb_value = q.qu_value; if (fdq) { -- cgit v1.2.3 From 86066914edff2316cbed63aac8a87d5001441a16 Mon Sep 17 00:00:00 2001 From: Abhi Das Date: Tue, 9 Jun 2015 09:16:46 -0500 Subject: gfs2: Don't support fallocate on jdata files We cannot provide an efficient implementation due to the headers on the data blocks, so there doesn't seem much point in having it. Signed-off-by: Abhi Das Signed-off-by: Bob Peterson --- fs/gfs2/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index c706c6df4898..8252115551a6 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -917,7 +917,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le struct gfs2_holder gh; int ret; - if (mode & ~FALLOC_FL_KEEP_SIZE) + if ((mode & ~FALLOC_FL_KEEP_SIZE) || gfs2_is_jdata(ip)) return -EOPNOTSUPP; mutex_lock(&inode->i_mutex); -- cgit v1.2.3 From e7ccaf5fe1590667b3fa2f8df5c5ec9ba0dc5b85 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 12 Jun 2015 13:15:54 -0500 Subject: GFS2: Don't add all glocks to the lru The glocks used for resource groups often come and go hundreds of thousands of times per second. Adding them to the lru list just adds unnecessary contention for the lru_lock spin_lock, especially considering we're almost certainly going to re-use the glock and take it back off the lru microseconds later. We never want the glock shrinker to cull them anyway. This patch adds a new bit in the glops that determines which glock types get put onto the lru list and which ones don't. Signed-off-by: Bob Peterson Acked-by: Steven Whitehouse --- fs/gfs2/glock.c | 3 ++- fs/gfs2/glops.c | 6 ++++-- fs/gfs2/incore.h | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 0fa8062f85a7..a38e38f7b6fc 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1076,7 +1076,8 @@ void gfs2_glock_dq(struct gfs2_holder *gh) !test_bit(GLF_DEMOTE, &gl->gl_flags)) fast_path = 1; } - if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl)) + if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl) && + (glops->go_flags & GLOF_LRU)) gfs2_glock_add_to_lru(gl); trace_gfs2_glock_queue(gh, 0); diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index fe91951c3361..1249b2bb2830 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -561,7 +561,7 @@ const struct gfs2_glock_operations gfs2_inode_glops = { .go_lock = inode_go_lock, .go_dump = inode_go_dump, .go_type = LM_TYPE_INODE, - .go_flags = GLOF_ASPACE, + .go_flags = GLOF_ASPACE | GLOF_LRU, }; const struct gfs2_glock_operations gfs2_rgrp_glops = { @@ -584,10 +584,12 @@ const struct gfs2_glock_operations gfs2_freeze_glops = { const struct gfs2_glock_operations gfs2_iopen_glops = { .go_type = LM_TYPE_IOPEN, .go_callback = iopen_go_callback, + .go_flags = GLOF_LRU, }; const struct gfs2_glock_operations gfs2_flock_glops = { .go_type = LM_TYPE_FLOCK, + .go_flags = GLOF_LRU, }; const struct gfs2_glock_operations gfs2_nondisk_glops = { @@ -596,7 +598,7 @@ const struct gfs2_glock_operations gfs2_nondisk_glops = { const struct gfs2_glock_operations gfs2_quota_glops = { .go_type = LM_TYPE_QUOTA, - .go_flags = GLOF_LVB, + .go_flags = GLOF_LVB | GLOF_LRU, }; const struct gfs2_glock_operations gfs2_journal_glops = { diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 304a22315448..a1ec7c20e498 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -225,6 +225,7 @@ struct gfs2_glock_operations { const unsigned long go_flags; #define GLOF_ASPACE 1 #define GLOF_LVB 2 +#define GLOF_LRU 4 }; enum { -- cgit v1.2.3 From 39b0f1e9290880a6c905f639e7db6b646e302a4f Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Fri, 5 Jun 2015 08:38:57 -0500 Subject: GFS2: Don't brelse rgrp buffer_heads every allocation This patch allows the block allocation code to retain the buffers for the resource groups so they don't need to be re-read from buffer cache with every request. This is a performance improvement that's especially noticeable when resource groups are very large. For example, with 2GB resource groups and 4K blocks, there can be 33 blocks for every resource group. This patch allows those 33 buffers to be kept around and not read in and thrown away with every operation. The buffers are released when the resource group is either synced or invalidated. Signed-off-by: Bob Peterson Reviewed-by: Steven Whitehouse Reviewed-by: Benjamin Marzinski --- fs/gfs2/glops.c | 14 +++++++++++--- fs/gfs2/rgrp.c | 23 +++++++++++++++++++---- fs/gfs2/rgrp.h | 1 + 3 files changed, 31 insertions(+), 7 deletions(-) (limited to 'fs/gfs2') diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c index 1249b2bb2830..fa3fa5e94553 100644 --- a/fs/gfs2/glops.c +++ b/fs/gfs2/glops.c @@ -144,6 +144,12 @@ static void rgrp_go_sync(struct gfs2_glock *gl) struct gfs2_rgrpd *rgd; int error; + spin_lock(&gl->gl_spin); + rgd = gl->gl_object; + if (rgd) + gfs2_rgrp_brelse(rgd); + spin_unlock(&gl->gl_spin); + if (!test_and_clear_bit(GLF_DIRTY, &gl->gl_flags)) return; GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); @@ -175,15 +181,17 @@ static void rgrp_go_inval(struct gfs2_glock *gl, int flags) { struct gfs2_sbd *sdp = gl->gl_sbd; struct address_space *mapping = &sdp->sd_aspace; + struct gfs2_rgrpd *rgd = gl->gl_object; + + if (rgd) + gfs2_rgrp_brelse(rgd); WARN_ON_ONCE(!(flags & DIO_METADATA)); gfs2_assert_withdraw(sdp, !atomic_read(&gl->gl_ail_count)); truncate_inode_pages_range(mapping, gl->gl_vm.start, gl->gl_vm.end); - if (gl->gl_object) { - struct gfs2_rgrpd *rgd = (struct gfs2_rgrpd *)gl->gl_object; + if (rgd) rgd->rd_flags &= ~GFS2_RDF_UPTODATE; - } } /** diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index cd53d6e9e44e..c6c62321dfd6 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -1244,14 +1244,13 @@ int gfs2_rgrp_go_lock(struct gfs2_holder *gh) } /** - * gfs2_rgrp_go_unlock - Release RG bitmaps read in with gfs2_rgrp_bh_get() - * @gh: The glock holder for the resource group + * gfs2_rgrp_brelse - Release RG bitmaps read in with gfs2_rgrp_bh_get() + * @rgd: The resource group * */ -void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) +void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd) { - struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; int x, length = rgd->rd_length; for (x = 0; x < length; x++) { @@ -1264,6 +1263,22 @@ void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) } +/** + * gfs2_rgrp_go_unlock - Unlock a rgrp glock + * @gh: The glock holder for the resource group + * + */ + +void gfs2_rgrp_go_unlock(struct gfs2_holder *gh) +{ + struct gfs2_rgrpd *rgd = gh->gh_gl->gl_object; + int demote_requested = test_bit(GLF_DEMOTE, &gh->gh_gl->gl_flags) | + test_bit(GLF_PENDING_DEMOTE, &gh->gh_gl->gl_flags); + + if (rgd && demote_requested) + gfs2_rgrp_brelse(rgd); +} + int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset, struct buffer_head *bh, const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed) diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h index 68972ecfbb01..c0ab33fa3eed 100644 --- a/fs/gfs2/rgrp.h +++ b/fs/gfs2/rgrp.h @@ -36,6 +36,7 @@ extern void gfs2_clear_rgrpd(struct gfs2_sbd *sdp); extern int gfs2_rindex_update(struct gfs2_sbd *sdp); extern void gfs2_free_clones(struct gfs2_rgrpd *rgd); extern int gfs2_rgrp_go_lock(struct gfs2_holder *gh); +extern void gfs2_rgrp_brelse(struct gfs2_rgrpd *rgd); extern void gfs2_rgrp_go_unlock(struct gfs2_holder *gh); extern struct gfs2_alloc *gfs2_alloc_get(struct gfs2_inode *ip); -- cgit v1.2.3