diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2018-11-11 06:15:21 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-11-16 12:35:09 -0500 |
commit | c26b5aa8ef0d46035060fded475e6ab957b9f69f (patch) | |
tree | 469f981b657c1bc9ed005d96c97b8fb86b2fcb25 /fs/gfs2/bmap.c | |
parent | e7445ceddfc220c1aede6d42758a5acb8844e9c3 (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.c | 40 |
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; |
899 | unlock: | 896 | unlock: |
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 | ||
905 | do_alloc: | 900 | do_alloc: |
@@ -980,9 +975,9 @@ static void gfs2_iomap_journaled_page_done(struct inode *inode, loff_t pos, | |||
980 | 975 | ||
981 | static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, | 976 | static 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: | |||
1069 | out_qunlock: | 1061 | out_qunlock: |
1070 | if (alloc_required) | 1062 | if (alloc_required) |
1071 | gfs2_quota_unlock(ip); | 1063 | gfs2_quota_unlock(ip); |
1072 | out_release: | 1064 | out_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 | } |