aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2017-12-14 11:11:03 -0500
committerAndreas Gruenbacher <agruenba@redhat.com>2018-01-18 15:15:58 -0500
commit4e56a6411fbce6f859566e17298114c2434391a4 (patch)
tree9619a913d0176f33b2dae824439a94e74cf50693
parent10d2cf94c23d48ef1b141084216e7580011e4790 (diff)
gfs2: Implement fallocate(FALLOC_FL_PUNCH_HOLE)
Implement the top-level bits of punching a hole into a file. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/gfs2/bmap.c122
-rw-r--r--fs/gfs2/bmap.h1
-rw-r--r--fs/gfs2/file.c19
3 files changed, 134 insertions, 8 deletions
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index f6dbd2f400cc..2ec11981e694 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -1351,7 +1351,7 @@ static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length)
1351 u64 lblock = (offset + (1 << bsize_shift) - 1) >> bsize_shift; 1351 u64 lblock = (offset + (1 << bsize_shift) - 1) >> bsize_shift;
1352 __u16 start_list[GFS2_MAX_META_HEIGHT]; 1352 __u16 start_list[GFS2_MAX_META_HEIGHT];
1353 __u16 __end_list[GFS2_MAX_META_HEIGHT], *end_list = NULL; 1353 __u16 __end_list[GFS2_MAX_META_HEIGHT], *end_list = NULL;
1354 unsigned int start_aligned, end_aligned; 1354 unsigned int start_aligned, uninitialized_var(end_aligned);
1355 unsigned int strip_h = ip->i_height - 1; 1355 unsigned int strip_h = ip->i_height - 1;
1356 u32 btotal = 0; 1356 u32 btotal = 0;
1357 int ret, state; 1357 int ret, state;
@@ -1956,3 +1956,123 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
1956 return 0; 1956 return 0;
1957} 1957}
1958 1958
1959static int stuffed_zero_range(struct inode *inode, loff_t offset, loff_t length)
1960{
1961 struct gfs2_inode *ip = GFS2_I(inode);
1962 struct buffer_head *dibh;
1963 int error;
1964
1965 if (offset >= inode->i_size)
1966 return 0;
1967 if (offset + length > inode->i_size)
1968 length = inode->i_size - offset;
1969
1970 error = gfs2_meta_inode_buffer(ip, &dibh);
1971 if (error)
1972 return error;
1973 gfs2_trans_add_meta(ip->i_gl, dibh);
1974 memset(dibh->b_data + sizeof(struct gfs2_dinode) + offset, 0,
1975 length);
1976 brelse(dibh);
1977 return 0;
1978}
1979
1980static int gfs2_journaled_truncate_range(struct inode *inode, loff_t offset,
1981 loff_t length)
1982{
1983 struct gfs2_sbd *sdp = GFS2_SB(inode);
1984 loff_t max_chunk = GFS2_JTRUNC_REVOKES * sdp->sd_vfs->s_blocksize;
1985 int error;
1986
1987 while (length) {
1988 struct gfs2_trans *tr;
1989 loff_t chunk;
1990 unsigned int offs;
1991
1992 chunk = length;
1993 if (chunk > max_chunk)
1994 chunk = max_chunk;
1995
1996 offs = offset & ~PAGE_MASK;
1997 if (offs && chunk > PAGE_SIZE)
1998 chunk = offs + ((chunk - offs) & PAGE_MASK);
1999
2000 truncate_pagecache_range(inode, offset, chunk);
2001 offset += chunk;
2002 length -= chunk;
2003
2004 tr = current->journal_info;
2005 if (!test_bit(TR_TOUCHED, &tr->tr_flags))
2006 continue;
2007
2008 gfs2_trans_end(sdp);
2009 error = gfs2_trans_begin(sdp, RES_DINODE, GFS2_JTRUNC_REVOKES);
2010 if (error)
2011 return error;
2012 }
2013 return 0;
2014}
2015
2016int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length)
2017{
2018 struct inode *inode = file_inode(file);
2019 struct gfs2_inode *ip = GFS2_I(inode);
2020 struct gfs2_sbd *sdp = GFS2_SB(inode);
2021 int error;
2022
2023 if (gfs2_is_jdata(ip))
2024 error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_JDATA,
2025 GFS2_JTRUNC_REVOKES);
2026 else
2027 error = gfs2_trans_begin(sdp, RES_DINODE, 0);
2028 if (error)
2029 return error;
2030
2031 if (gfs2_is_stuffed(ip)) {
2032 error = stuffed_zero_range(inode, offset, length);
2033 if (error)
2034 goto out;
2035 } else {
2036 unsigned int start_off, end_off, blocksize;
2037
2038 blocksize = i_blocksize(inode);
2039 start_off = offset & (blocksize - 1);
2040 end_off = (offset + length) & (blocksize - 1);
2041 if (start_off) {
2042 unsigned int len = length;
2043 if (length > blocksize - start_off)
2044 len = blocksize - start_off;
2045 error = gfs2_block_zero_range(inode, offset, len);
2046 if (error)
2047 goto out;
2048 if (start_off + length < blocksize)
2049 end_off = 0;
2050 }
2051 if (end_off) {
2052 error = gfs2_block_zero_range(inode,
2053 offset + length - end_off, end_off);
2054 if (error)
2055 goto out;
2056 }
2057 }
2058
2059 if (gfs2_is_jdata(ip)) {
2060 BUG_ON(!current->journal_info);
2061 gfs2_journaled_truncate_range(inode, offset, length);
2062 } else
2063 truncate_pagecache_range(inode, offset, offset + length - 1);
2064
2065 file_update_time(file);
2066 mark_inode_dirty(inode);
2067
2068 if (current->journal_info)
2069 gfs2_trans_end(sdp);
2070
2071 if (!gfs2_is_stuffed(ip))
2072 error = punch_hole(ip, offset, length);
2073
2074out:
2075 if (current->journal_info)
2076 gfs2_trans_end(sdp);
2077 return error;
2078}
diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h
index 443cc182cf18..c3402fe00653 100644
--- a/fs/gfs2/bmap.h
+++ b/fs/gfs2/bmap.h
@@ -61,5 +61,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
61 unsigned int len); 61 unsigned int len);
62extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd); 62extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd);
63extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd); 63extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd);
64extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length);
64 65
65#endif /* __BMAP_DOT_H__ */ 66#endif /* __BMAP_DOT_H__ */
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 58705ef8643a..bd60dc682676 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -924,7 +924,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
924 struct gfs2_holder gh; 924 struct gfs2_holder gh;
925 int ret; 925 int ret;
926 926
927 if (mode & ~FALLOC_FL_KEEP_SIZE) 927 if (mode & ~(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE))
928 return -EOPNOTSUPP; 928 return -EOPNOTSUPP;
929 /* fallocate is needed by gfs2_grow to reserve space in the rindex */ 929 /* fallocate is needed by gfs2_grow to reserve space in the rindex */
930 if (gfs2_is_jdata(ip) && inode != sdp->sd_rindex) 930 if (gfs2_is_jdata(ip) && inode != sdp->sd_rindex)
@@ -948,13 +948,18 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
948 if (ret) 948 if (ret)
949 goto out_unlock; 949 goto out_unlock;
950 950
951 ret = gfs2_rsqa_alloc(ip); 951 if (mode & FALLOC_FL_PUNCH_HOLE) {
952 if (ret) 952 ret = __gfs2_punch_hole(file, offset, len);
953 goto out_putw; 953 } else {
954 ret = gfs2_rsqa_alloc(ip);
955 if (ret)
956 goto out_putw;
954 957
955 ret = __gfs2_fallocate(file, mode, offset, len); 958 ret = __gfs2_fallocate(file, mode, offset, len);
956 if (ret) 959
957 gfs2_rs_deltree(&ip->i_res); 960 if (ret)
961 gfs2_rs_deltree(&ip->i_res);
962 }
958 963
959out_putw: 964out_putw:
960 put_write_access(inode); 965 put_write_access(inode);