summaryrefslogtreecommitdiffstats
path: root/fs/gfs2/bmap.c
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2018-11-11 06:15:21 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-11-16 12:35:09 -0500
commitc26b5aa8ef0d46035060fded475e6ab957b9f69f (patch)
tree469f981b657c1bc9ed005d96c97b8fb86b2fcb25 /fs/gfs2/bmap.c
parente7445ceddfc220c1aede6d42758a5acb8844e9c3 (diff)
gfs2: Fix iomap buffer head reference counting bug
GFS2 passes the inode buffer head (dibh) from gfs2_iomap_begin to gfs2_iomap_end in iomap->private. It sets that private pointer in gfs2_iomap_get. Users of gfs2_iomap_get other than gfs2_iomap_begin would have to release iomap->private, but this isn't done correctly, leading to a leak of buffer head references. To fix this, move the code for setting iomap->private from gfs2_iomap_get to gfs2_iomap_begin. Fixes: 64bc06bb32 ("gfs2: iomap buffered write support") Cc: stable@vger.kernel.org # v4.19+ Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/gfs2/bmap.c')
-rw-r--r--fs/gfs2/bmap.c40
1 files changed, 17 insertions, 23 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 38d88fcb6988..0d643306c255 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -826,7 +826,7 @@ static int gfs2_iomap_get(struct inode *inode, loff_t pos, loff_t length,
826 ret = gfs2_meta_inode_buffer(ip, &dibh); 826 ret = gfs2_meta_inode_buffer(ip, &dibh);
827 if (ret) 827 if (ret)
828 goto unlock; 828 goto unlock;
829 iomap->private = dibh; 829 mp->mp_bh[0] = dibh;
830 830
831 if (gfs2_is_stuffed(ip)) { 831 if (gfs2_is_stuffed(ip)) {
832 if (flags & IOMAP_WRITE) { 832 if (flags & IOMAP_WRITE) {
@@ -863,9 +863,6 @@ unstuff:
863 len = lblock_stop - lblock + 1; 863 len = lblock_stop - lblock + 1;
864 iomap->length = len << inode->i_blkbits; 864 iomap->length = len << inode->i_blkbits;
865 865
866 get_bh(dibh);
867 mp->mp_bh[0] = dibh;
868
869 height = ip->i_height; 866 height = ip->i_height;
870 while ((lblock + 1) * sdp->sd_sb.sb_bsize > sdp->sd_heightsize[height]) 867 while ((lblock + 1) * sdp->sd_sb.sb_bsize > sdp->sd_heightsize[height])
871 height++; 868 height++;
@@ -898,8 +895,6 @@ out:
898 iomap->bdev = inode->i_sb->s_bdev; 895 iomap->bdev = inode->i_sb->s_bdev;
899unlock: 896unlock:
900 up_read(&ip->i_rw_mutex); 897 up_read(&ip->i_rw_mutex);
901 if (ret && dibh)
902 brelse(dibh);
903 return ret; 898 return ret;
904 899
905do_alloc: 900do_alloc:
@@ -980,9 +975,9 @@ static void gfs2_iomap_journaled_page_done(struct inode *inode, loff_t pos,
980 975
981static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, 976static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
982 loff_t length, unsigned flags, 977 loff_t length, unsigned flags,
983 struct iomap *iomap) 978 struct iomap *iomap,
979 struct metapath *mp)
984{ 980{
985 struct metapath mp = { .mp_aheight = 1, };
986 struct gfs2_inode *ip = GFS2_I(inode); 981 struct gfs2_inode *ip = GFS2_I(inode);
987 struct gfs2_sbd *sdp = GFS2_SB(inode); 982 struct gfs2_sbd *sdp = GFS2_SB(inode);
988 unsigned int data_blocks = 0, ind_blocks = 0, rblocks; 983 unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
@@ -996,9 +991,9 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
996 unstuff = gfs2_is_stuffed(ip) && 991 unstuff = gfs2_is_stuffed(ip) &&
997 pos + length > gfs2_max_stuffed_size(ip); 992 pos + length > gfs2_max_stuffed_size(ip);
998 993
999 ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); 994 ret = gfs2_iomap_get(inode, pos, length, flags, iomap, mp);
1000 if (ret) 995 if (ret)
1001 goto out_release; 996 goto out_unlock;
1002 997
1003 alloc_required = unstuff || iomap->type == IOMAP_HOLE; 998 alloc_required = unstuff || iomap->type == IOMAP_HOLE;
1004 999
@@ -1013,7 +1008,7 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
1013 1008
1014 ret = gfs2_quota_lock_check(ip, &ap); 1009 ret = gfs2_quota_lock_check(ip, &ap);
1015 if (ret) 1010 if (ret)
1016 goto out_release; 1011 goto out_unlock;
1017 1012
1018 ret = gfs2_inplace_reserve(ip, &ap); 1013 ret = gfs2_inplace_reserve(ip, &ap);
1019 if (ret) 1014 if (ret)
@@ -1038,17 +1033,15 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
1038 ret = gfs2_unstuff_dinode(ip, NULL); 1033 ret = gfs2_unstuff_dinode(ip, NULL);
1039 if (ret) 1034 if (ret)
1040 goto out_trans_end; 1035 goto out_trans_end;
1041 release_metapath(&mp); 1036 release_metapath(mp);
1042 brelse(iomap->private);
1043 iomap->private = NULL;
1044 ret = gfs2_iomap_get(inode, iomap->offset, iomap->length, 1037 ret = gfs2_iomap_get(inode, iomap->offset, iomap->length,
1045 flags, iomap, &mp); 1038 flags, iomap, mp);
1046 if (ret) 1039 if (ret)
1047 goto out_trans_end; 1040 goto out_trans_end;
1048 } 1041 }
1049 1042
1050 if (iomap->type == IOMAP_HOLE) { 1043 if (iomap->type == IOMAP_HOLE) {
1051 ret = gfs2_iomap_alloc(inode, iomap, flags, &mp); 1044 ret = gfs2_iomap_alloc(inode, iomap, flags, mp);
1052 if (ret) { 1045 if (ret) {
1053 gfs2_trans_end(sdp); 1046 gfs2_trans_end(sdp);
1054 gfs2_inplace_release(ip); 1047 gfs2_inplace_release(ip);
@@ -1056,7 +1049,6 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos,
1056 goto out_qunlock; 1049 goto out_qunlock;
1057 } 1050 }
1058 } 1051 }
1059 release_metapath(&mp);
1060 if (gfs2_is_jdata(ip)) 1052 if (gfs2_is_jdata(ip))
1061 iomap->page_done = gfs2_iomap_journaled_page_done; 1053 iomap->page_done = gfs2_iomap_journaled_page_done;
1062 return 0; 1054 return 0;
@@ -1069,10 +1061,7 @@ out_trans_fail:
1069out_qunlock: 1061out_qunlock:
1070 if (alloc_required) 1062 if (alloc_required)
1071 gfs2_quota_unlock(ip); 1063 gfs2_quota_unlock(ip);
1072out_release: 1064out_unlock:
1073 if (iomap->private)
1074 brelse(iomap->private);
1075 release_metapath(&mp);
1076 gfs2_write_unlock(inode); 1065 gfs2_write_unlock(inode);
1077 return ret; 1066 return ret;
1078} 1067}
@@ -1088,10 +1077,10 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
1088 1077
1089 trace_gfs2_iomap_start(ip, pos, length, flags); 1078 trace_gfs2_iomap_start(ip, pos, length, flags);
1090 if ((flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT)) { 1079 if ((flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT)) {
1091 ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap); 1080 ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp);
1092 } else { 1081 } else {
1093 ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); 1082 ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp);
1094 release_metapath(&mp); 1083
1095 /* 1084 /*
1096 * Silently fall back to buffered I/O for stuffed files or if 1085 * Silently fall back to buffered I/O for stuffed files or if
1097 * we've hot a hole (see gfs2_file_direct_write). 1086 * we've hot a hole (see gfs2_file_direct_write).
@@ -1100,6 +1089,11 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
1100 iomap->type != IOMAP_MAPPED) 1089 iomap->type != IOMAP_MAPPED)
1101 ret = -ENOTBLK; 1090 ret = -ENOTBLK;
1102 } 1091 }
1092 if (!ret) {
1093 get_bh(mp.mp_bh[0]);
1094 iomap->private = mp.mp_bh[0];
1095 }
1096 release_metapath(&mp);
1103 trace_gfs2_iomap_end(ip, iomap, ret); 1097 trace_gfs2_iomap_end(ip, iomap, ret);
1104 return ret; 1098 return ret;
1105} 1099}