diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2017-12-14 11:11:03 -0500 |
---|---|---|
committer | Andreas Gruenbacher <agruenba@redhat.com> | 2018-01-18 15:15:58 -0500 |
commit | 4e56a6411fbce6f859566e17298114c2434391a4 (patch) | |
tree | 9619a913d0176f33b2dae824439a94e74cf50693 | |
parent | 10d2cf94c23d48ef1b141084216e7580011e4790 (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.c | 122 | ||||
-rw-r--r-- | fs/gfs2/bmap.h | 1 | ||||
-rw-r--r-- | fs/gfs2/file.c | 19 |
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 | ||
1959 | static 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 | |||
1980 | static 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 | |||
2016 | int __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 | |||
2074 | out: | ||
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); |
62 | extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd); | 62 | extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd); |
63 | extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd); | 63 | extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd); |
64 | extern 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 | ||
959 | out_putw: | 964 | out_putw: |
960 | put_write_access(inode); | 965 | put_write_access(inode); |