Commit 26064ea4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'gfs2-4.16.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2

Pull GFS2 updates from Bob Peterson:
 "We've got 30 patches for this merge window. These generally fall into
  five categories:

   - code cleanups

   - patches related to adding PUNCH_HOLE support to GFS2

   - support for new fields in resource group headers

   - a few bug fixes

   - support for new fields in journal log headers. These new fields,
     which were previously unused, are designed to make it easier to
     track down file system corruption, and allow fsck.gfs2 to make more
     intelligent decisions when finding and fixing file system
     corruption.

  Details:

   - Two patches from Abhi Das, to trim the ordered writes list, which
     used to grow uncontrollably until unmount.

   - Several patches from Andreas Gruenbacher: remove an unused
     parameter from function gfs2_write_jdata_pagevec, remove a
     pointless BUG_ON, clean up an error patch in trunc_start, remove
     some unused parameters from truncate, make gfs2_journaled_truncate
     more efficient, clean up the support functions for truncate, fix
     metadata read-ahead for truncate to make it faster, fix up the
     non-recursive truncate code, rework and rename
     gfs2_block_truncate_page, generalize the non-recursive truncate
     code so it can take a range of values for punch_hole support,
     introduce new PUNCH_HOLE support that take advantage of the
     previous patches, add fallocate support with PUNCH_HOLE, fix some
     typos in the comments, add the function gfs2_max_stuffed_size to
     replace a piece of code that was needlessly repeated throughout
     GFS2, a minor cleanup to function gfs2_page_add_databufs, get rid
     of function gfs2_log_header_in in preparation for the new log
     header fields, and also fix up some missing newlines in kernel
     messages.

   - Andy Price added a new field to resource groups to indicate where
     the next one should be, to allow fsck.gfs2 to make better repairs.
     He also added new rindex fields for consistency checking, and added
     a crc field to resource group headers for consistency checking.

   - I reduced redundancy in functions common to freeing dinodes, and
     when writing log headers between the journalling code and journal
     recovery code. Also added new fields to journal log headers based
     on a prototype from Steve Whitehouse, and log the source of journal
     log headers so we can better track down journal corruption. Minor
     comment typo fix and a fix for a BUG in an unlink error path.

   - Steve Whitehouse contributed a patch to fix an incorrect use of the
     gfs2_blk2rgrpd function.

   - Tetsuo Handa contributed a patch that fixes incorrect error
     handling in function init_gfs2_fs"

* tag 'gfs2-4.16.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2: (30 commits)
  gfs2: Add a few missing newlines in messages
  gfs2: Remove inode from ordered write list in gfs2_write_inode()
  GFS2: Don't try to end a non-existent transaction in unlink
  GFS2: Fix minor comment typo
  GFS2: Log the reason for log flushes in every log header
  GFS2: Introduce new gfs2_log_header_v2
  gfs2: Get rid of gfs2_log_header_in
  gfs2: Minor gfs2_page_add_databufs cleanup
  gfs2: Add gfs2_max_stuffed_size
  gfs2: Typo fixes
  gfs2: Implement fallocate(FALLOC_FL_PUNCH_HOLE)
  gfs2: Turn trunc_dealloc into punch_hole
  gfs2: Generalize truncate code
  Turn gfs2_block_truncate_page into gfs2_block_zero_range
  gfs2: Improve non-recursive delete algorithm
  gfs2: Fix metadata read-ahead during truncate
  gfs2: Clean up {lookup,fillup}_metapath
  gfs2: Remove minor gfs2_journaled_truncate inefficiencies
  gfs2: truncate: Remove unnecessary oldsize parameters
  gfs2: Clean up trunc_start error path
  ...
parents c9cc8d01 af38816e
...@@ -3,6 +3,8 @@ config GFS2_FS ...@@ -3,6 +3,8 @@ config GFS2_FS
depends on (64BIT || LBDAF) depends on (64BIT || LBDAF)
select FS_POSIX_ACL select FS_POSIX_ACL
select CRC32 select CRC32
select CRYPTO
select CRYPTO_CRC32C
select QUOTACTL select QUOTACTL
select FS_IOMAP select FS_IOMAP
help help
......
...@@ -39,18 +39,21 @@ ...@@ -39,18 +39,21 @@
static void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page, static void gfs2_page_add_databufs(struct gfs2_inode *ip, struct page *page,
unsigned int from, unsigned int to) unsigned int from, unsigned int len)
{ {
struct buffer_head *head = page_buffers(page); struct buffer_head *head = page_buffers(page);
unsigned int bsize = head->b_size; unsigned int bsize = head->b_size;
struct buffer_head *bh; struct buffer_head *bh;
unsigned int to = from + len;
unsigned int start, end; unsigned int start, end;
for (bh = head, start = 0; bh != head || !start; for (bh = head, start = 0; bh != head || !start;
bh = bh->b_this_page, start = end) { bh = bh->b_this_page, start = end) {
end = start + bsize; end = start + bsize;
if (end <= from || start >= to) if (end <= from)
continue; continue;
if (start >= to)
break;
if (gfs2_is_jdata(ip)) if (gfs2_is_jdata(ip))
set_buffer_uptodate(bh); set_buffer_uptodate(bh);
gfs2_trans_add_data(ip->i_gl, bh); gfs2_trans_add_data(ip->i_gl, bh);
...@@ -189,7 +192,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w ...@@ -189,7 +192,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w
create_empty_buffers(page, inode->i_sb->s_blocksize, create_empty_buffers(page, inode->i_sb->s_blocksize,
BIT(BH_Dirty)|BIT(BH_Uptodate)); BIT(BH_Dirty)|BIT(BH_Uptodate));
} }
gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize-1); gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize);
} }
return gfs2_write_full_page(page, gfs2_get_block_noalloc, wbc); return gfs2_write_full_page(page, gfs2_get_block_noalloc, wbc);
} }
...@@ -255,7 +258,6 @@ static int gfs2_writepages(struct address_space *mapping, ...@@ -255,7 +258,6 @@ static int gfs2_writepages(struct address_space *mapping,
* @wbc: The writeback control * @wbc: The writeback control
* @pvec: The vector of pages * @pvec: The vector of pages
* @nr_pages: The number of pages to write * @nr_pages: The number of pages to write
* @end: End position
* @done_index: Page index * @done_index: Page index
* *
* Returns: non-zero if loop should terminate, zero otherwise * Returns: non-zero if loop should terminate, zero otherwise
...@@ -264,7 +266,7 @@ static int gfs2_writepages(struct address_space *mapping, ...@@ -264,7 +266,7 @@ static int gfs2_writepages(struct address_space *mapping,
static int gfs2_write_jdata_pagevec(struct address_space *mapping, static int gfs2_write_jdata_pagevec(struct address_space *mapping,
struct writeback_control *wbc, struct writeback_control *wbc,
struct pagevec *pvec, struct pagevec *pvec,
int nr_pages, pgoff_t end, int nr_pages,
pgoff_t *done_index) pgoff_t *done_index)
{ {
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
...@@ -402,7 +404,7 @@ static int gfs2_write_cache_jdata(struct address_space *mapping, ...@@ -402,7 +404,7 @@ static int gfs2_write_cache_jdata(struct address_space *mapping,
if (nr_pages == 0) if (nr_pages == 0)
break; break;
ret = gfs2_write_jdata_pagevec(mapping, wbc, &pvec, nr_pages, end, &done_index); ret = gfs2_write_jdata_pagevec(mapping, wbc, &pvec, nr_pages, &done_index);
if (ret) if (ret)
done = 1; done = 1;
if (ret > 0) if (ret > 0)
...@@ -446,7 +448,8 @@ static int gfs2_jdata_writepages(struct address_space *mapping, ...@@ -446,7 +448,8 @@ static int gfs2_jdata_writepages(struct address_space *mapping,
ret = gfs2_write_cache_jdata(mapping, wbc); ret = gfs2_write_cache_jdata(mapping, wbc);
if (ret == 0 && wbc->sync_mode == WB_SYNC_ALL) { if (ret == 0 && wbc->sync_mode == WB_SYNC_ALL) {
gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); gfs2_log_flush(sdp, ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_JDATA_WPAGES);
ret = gfs2_write_cache_jdata(mapping, wbc); ret = gfs2_write_cache_jdata(mapping, wbc);
} }
return ret; return ret;
...@@ -483,8 +486,8 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) ...@@ -483,8 +486,8 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page)
return error; return error;
kaddr = kmap_atomic(page); kaddr = kmap_atomic(page);
if (dsize > (dibh->b_size - sizeof(struct gfs2_dinode))) if (dsize > gfs2_max_stuffed_size(ip))
dsize = (dibh->b_size - sizeof(struct gfs2_dinode)); dsize = gfs2_max_stuffed_size(ip);
memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize); memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize);
memset(kaddr + dsize, 0, PAGE_SIZE - dsize); memset(kaddr + dsize, 0, PAGE_SIZE - dsize);
kunmap_atomic(kaddr); kunmap_atomic(kaddr);
...@@ -501,10 +504,9 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page) ...@@ -501,10 +504,9 @@ static int stuffed_readpage(struct gfs2_inode *ip, struct page *page)
* @file: The file to read a page for * @file: The file to read a page for
* @page: The page to read * @page: The page to read
* *
* This is the core of gfs2's readpage. Its used by the internal file * This is the core of gfs2's readpage. It's used by the internal file
* reading code as in that case we already hold the glock. Also its * reading code as in that case we already hold the glock. Also it's
* called by gfs2_readpage() once the required lock has been granted. * called by gfs2_readpage() once the required lock has been granted.
*
*/ */
static int __gfs2_readpage(void *file, struct page *page) static int __gfs2_readpage(void *file, struct page *page)
...@@ -725,7 +727,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, ...@@ -725,7 +727,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
if (gfs2_is_stuffed(ip)) { if (gfs2_is_stuffed(ip)) {
error = 0; error = 0;
if (pos + len > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) { if (pos + len > gfs2_max_stuffed_size(ip)) {
error = gfs2_unstuff_dinode(ip, page); error = gfs2_unstuff_dinode(ip, page);
if (error == 0) if (error == 0)
goto prepare_write; goto prepare_write;
...@@ -832,7 +834,8 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh, ...@@ -832,7 +834,8 @@ static int gfs2_stuffed_write_end(struct inode *inode, struct buffer_head *dibh,
void *kaddr; void *kaddr;
unsigned char *buf = dibh->b_data + sizeof(struct gfs2_dinode); unsigned char *buf = dibh->b_data + sizeof(struct gfs2_dinode);
BUG_ON((pos + len) > (dibh->b_size - sizeof(struct gfs2_dinode))); BUG_ON(pos + len > gfs2_max_stuffed_size(ip));
kaddr = kmap_atomic(page); kaddr = kmap_atomic(page);
memcpy(buf + pos, kaddr + pos, copied); memcpy(buf + pos, kaddr + pos, copied);
flush_dcache_page(page); flush_dcache_page(page);
...@@ -890,8 +893,6 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping, ...@@ -890,8 +893,6 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping,
struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_sbd *sdp = GFS2_SB(inode);
struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode); struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
struct buffer_head *dibh; struct buffer_head *dibh;
unsigned int from = pos & (PAGE_SIZE - 1);
unsigned int to = from + len;
int ret; int ret;
struct gfs2_trans *tr = current->journal_info; struct gfs2_trans *tr = current->journal_info;
BUG_ON(!tr); BUG_ON(!tr);
...@@ -909,7 +910,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping, ...@@ -909,7 +910,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping,
return gfs2_stuffed_write_end(inode, dibh, pos, len, copied, page); return gfs2_stuffed_write_end(inode, dibh, pos, len, copied, page);
if (!gfs2_is_writeback(ip)) if (!gfs2_is_writeback(ip))
gfs2_page_add_databufs(ip, page, from, to); gfs2_page_add_databufs(ip, page, pos & ~PAGE_MASK, len);
ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata);
if (tr->tr_num_buf_new) if (tr->tr_num_buf_new)
......
...@@ -69,8 +69,8 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh, ...@@ -69,8 +69,8 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
void *kaddr = kmap(page); void *kaddr = kmap(page);
u64 dsize = i_size_read(inode); u64 dsize = i_size_read(inode);
if (dsize > (dibh->b_size - sizeof(struct gfs2_dinode))) if (dsize > gfs2_max_stuffed_size(ip))
dsize = dibh->b_size - sizeof(struct gfs2_dinode); dsize = gfs2_max_stuffed_size(ip);
memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize); memcpy(kaddr, dibh->b_data + sizeof(struct gfs2_dinode), dsize);
memset(kaddr + dsize, 0, PAGE_SIZE - dsize); memset(kaddr + dsize, 0, PAGE_SIZE - dsize);
...@@ -279,14 +279,13 @@ static inline __be64 *metapointer(unsigned int height, const struct metapath *mp ...@@ -279,14 +279,13 @@ static inline __be64 *metapointer(unsigned int height, const struct metapath *mp
return p + mp->mp_list[height]; return p + mp->mp_list[height];
} }
static void gfs2_metapath_ra(struct gfs2_glock *gl, static void gfs2_metapath_ra(struct gfs2_glock *gl, __be64 *start, __be64 *end)
const struct buffer_head *bh, const __be64 *pos)
{ {
struct buffer_head *rabh;
const __be64 *endp = (const __be64 *)(bh->b_data + bh->b_size);
const __be64 *t; const __be64 *t;
for (t = pos; t < endp; t++) { for (t = start; t < end; t++) {
struct buffer_head *rabh;
if (!*t) if (!*t)
continue; continue;
...@@ -305,21 +304,22 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl, ...@@ -305,21 +304,22 @@ static void gfs2_metapath_ra(struct gfs2_glock *gl,
} }
} }
/** static int __fillup_metapath(struct gfs2_inode *ip, struct metapath *mp,
* lookup_mp_height - helper function for lookup_metapath unsigned int x, unsigned int h)
* @ip: the inode
* @mp: the metapath
* @h: the height which needs looking up
*/
static int lookup_mp_height(struct gfs2_inode *ip, struct metapath *mp, int h)
{ {
__be64 *ptr = metapointer(h, mp); for (; x < h; x++) {
__be64 *ptr = metapointer(x, mp);
u64 dblock = be64_to_cpu(*ptr); u64 dblock = be64_to_cpu(*ptr);
int ret;
if (!dblock) if (!dblock)
return h + 1; break;
ret = gfs2_meta_indirect_buffer(ip, x + 1, dblock, &mp->mp_bh[x + 1]);
return gfs2_meta_indirect_buffer(ip, h + 1, dblock, &mp->mp_bh[h + 1]); if (ret)
return ret;
}
mp->mp_aheight = x + 1;
return 0;
} }
/** /**
...@@ -336,25 +336,12 @@ static int lookup_mp_height(struct gfs2_inode *ip, struct metapath *mp, int h) ...@@ -336,25 +336,12 @@ static int lookup_mp_height(struct gfs2_inode *ip, struct metapath *mp, int h)
* at which it found the unallocated block. Blocks which are found are * at which it found the unallocated block. Blocks which are found are
* added to the mp->mp_bh[] list. * added to the mp->mp_bh[] list.
* *
* Returns: error or height of metadata tree * Returns: error
*/ */
static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp) static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp)
{ {
unsigned int end_of_metadata = ip->i_height - 1; return __fillup_metapath(ip, mp, 0, ip->i_height - 1);
unsigned int x;
int ret;
for (x = 0; x < end_of_metadata; x++) {
ret = lookup_mp_height(ip, mp, x);
if (ret)
goto out;
}
ret = ip->i_height;
out:
mp->mp_aheight = ret;
return ret;
} }
/** /**
...@@ -365,25 +352,25 @@ static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp) ...@@ -365,25 +352,25 @@ static int lookup_metapath(struct gfs2_inode *ip, struct metapath *mp)
* *
* Similar to lookup_metapath, but does lookups for a range of heights * Similar to lookup_metapath, but does lookups for a range of heights
* *
* Returns: error or height of metadata tree * Returns: error or the number of buffers filled
*/ */
static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h) static int fillup_metapath(struct gfs2_inode *ip, struct metapath *mp, int h)
{ {
unsigned int start_h = h - 1; unsigned int x = 0;
int ret; int ret;
if (h) { if (h) {
/* find the first buffer we need to look up. */ /* find the first buffer we need to look up. */
while (start_h > 0 && mp->mp_bh[start_h] == NULL) for (x = h - 1; x > 0; x--) {
start_h--; if (mp->mp_bh[x])
for (; start_h < h; start_h++) { break;
ret = lookup_mp_height(ip, mp, start_h);
if (ret)
return ret;
} }
} }
return ip->i_height; ret = __fillup_metapath(ip, mp, x, h);
if (ret)
return ret;
return mp->mp_aheight - x - 1;
} }
static inline void release_metapath(struct metapath *mp) static inline void release_metapath(struct metapath *mp)
...@@ -474,13 +461,6 @@ enum alloc_state { ...@@ -474,13 +461,6 @@ enum alloc_state {
/* ALLOC_UNSTUFF = 3, TBD and rather complicated */ /* ALLOC_UNSTUFF = 3, TBD and rather complicated */
}; };
static inline unsigned int hptrs(struct gfs2_sbd *sdp, const unsigned int hgt)
{
if (hgt)
return sdp->sd_inptrs;
return sdp->sd_diptrs;
}
/** /**
* gfs2_bmap_alloc - Build a metadata tree of the requested height * gfs2_bmap_alloc - Build a metadata tree of the requested height
* @inode: The GFS2 inode * @inode: The GFS2 inode
...@@ -788,7 +768,7 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, ...@@ -788,7 +768,7 @@ int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
goto do_alloc; goto do_alloc;
ret = lookup_metapath(ip, &mp); ret = lookup_metapath(ip, &mp);
if (ret < 0) if (ret)
goto out_release; goto out_release;
if (mp.mp_aheight != ip->i_height) if (mp.mp_aheight != ip->i_height)
...@@ -913,17 +893,18 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi ...@@ -913,17 +893,18 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi
} }
/** /**
* gfs2_block_truncate_page - Deal with zeroing out data for truncate * gfs2_block_zero_range - Deal with zeroing out data
* *
* This is partly borrowed from ext3. * This is partly borrowed from ext3.
*/ */
static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from) static int gfs2_block_zero_range(struct inode *inode, loff_t from,
unsigned int length)
{ {
struct inode *inode = mapping->host; struct address_space *mapping = inode->i_mapping;
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
unsigned long index = from >> PAGE_SHIFT; unsigned long index = from >> PAGE_SHIFT;
unsigned offset = from & (PAGE_SIZE-1); unsigned offset = from & (PAGE_SIZE-1);
unsigned blocksize, iblock, length, pos; unsigned blocksize, iblock, pos;
struct buffer_head *bh; struct buffer_head *bh;
struct page *page; struct page *page;
int err; int err;
...@@ -933,7 +914,6 @@ static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from) ...@@ -933,7 +914,6 @@ static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from)
return 0; return 0;
blocksize = inode->i_sb->s_blocksize; blocksize = inode->i_sb->s_blocksize;
length = blocksize - (offset & (blocksize - 1));
iblock = index << (PAGE_SHIFT - inode->i_sb->s_blocksize_bits); iblock = index << (PAGE_SHIFT - inode->i_sb->s_blocksize_bits);
if (!page_has_buffers(page)) if (!page_has_buffers(page))
...@@ -1003,11 +983,24 @@ static int gfs2_journaled_truncate(struct inode *inode, u64 oldsize, u64 newsize ...@@ -1003,11 +983,24 @@ static int gfs2_journaled_truncate(struct inode *inode, u64 oldsize, u64 newsize
int error; int error;
while (oldsize != newsize) { while (oldsize != newsize) {
struct gfs2_trans *tr;
unsigned int offs;
chunk = oldsize - newsize; chunk = oldsize - newsize;
if (chunk > max_chunk) if (chunk > max_chunk)
chunk = max_chunk; chunk = max_chunk;
offs = oldsize & ~PAGE_MASK;
if (offs && chunk > PAGE_SIZE)
chunk = offs + ((chunk - offs) & PAGE_MASK);
truncate_pagecache(inode, oldsize - chunk); truncate_pagecache(inode, oldsize - chunk);
oldsize -= chunk; oldsize -= chunk;
tr = current->journal_info;
if (!test_bit(TR_TOUCHED, &tr->tr_flags))
continue;
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
error = gfs2_trans_begin(sdp, RES_DINODE, GFS2_JTRUNC_REVOKES); error = gfs2_trans_begin(sdp, RES_DINODE, GFS2_JTRUNC_REVOKES);
if (error) if (error)
...@@ -1017,13 +1010,13 @@ static int gfs2_journaled_truncate(struct inode *inode, u64 oldsize, u64 newsize ...@@ -1017,13 +1010,13 @@ static int gfs2_journaled_truncate(struct inode *inode, u64 oldsize, u64 newsize
return 0; return 0;
} }
static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) static int trunc_start(struct inode *inode, u64 newsize)
{ {
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode); struct gfs2_sbd *sdp = GFS2_SB(inode);
struct address_space *mapping = inode->i_mapping; struct buffer_head *dibh = NULL;
struct buffer_head *dibh;
int journaled = gfs2_is_jdata(ip); int journaled = gfs2_is_jdata(ip);
u64 oldsize = inode->i_size;
int error; int error;
if (journaled) if (journaled)
...@@ -1042,10 +1035,13 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) ...@@ -1042,10 +1035,13 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
if (gfs2_is_stuffed(ip)) { if (gfs2_is_stuffed(ip)) {
gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize); gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize);
} else { } else {
if (newsize & (u64)(sdp->sd_sb.sb_bsize - 1)) { unsigned int blocksize = i_blocksize(inode);
error = gfs2_block_truncate_page(mapping, newsize); unsigned int offs = newsize & (blocksize - 1);
if (offs) {
error = gfs2_block_zero_range(inode, newsize,
blocksize - offs);
if (error) if (error)
goto out_brelse; goto out;
} }
ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG; ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG;
} }
...@@ -1059,14 +1055,9 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) ...@@ -1059,14 +1055,9 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
else else
truncate_pagecache(inode, newsize); truncate_pagecache(inode, newsize);
if (error) {
brelse(dibh);
return error;
}
out_brelse:
brelse(dibh);
out: out:
brelse(dibh);
if (current->journal_info)
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
return error; return error;
} }
...@@ -1075,10 +1066,11 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) ...@@ -1075,10 +1066,11 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
* sweep_bh_for_rgrps - find an rgrp in a meta buffer and free blocks therein * sweep_bh_for_rgrps - find an rgrp in a meta buffer and free blocks therein
* @ip: inode * @ip: inode
* @rg_gh: holder of resource group glock * @rg_gh: holder of resource group glock
* @mp: current metapath fully populated with buffers * @bh: buffer head to sweep
* @start: starting point in bh
* @end: end point in bh
* @meta: true if bh points to metadata (rather than data)
* @btotal: place to keep count of total blocks freed * @btotal: place to keep count of total blocks freed
* @hgt: height we're processing
* @first: true if this is the first call to this function for this height
* *
* We sweep a metadata buffer (provided by the metapath) for blocks we need to * We sweep a metadata buffer (provided by the metapath) for blocks we need to
* free, and free them all. However, we do it one rgrp at a time. If this * free, and free them all. However, we do it one rgrp at a time. If this
...@@ -1093,47 +1085,46 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize) ...@@ -1093,47 +1085,46 @@ static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
* *btotal has the total number of blocks freed * *btotal has the total number of blocks freed
*/ */
static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
const struct metapath *mp, u32 *btotal, int hgt, struct buffer_head *bh, __be64 *start, __be64 *end,
bool preserve1) bool meta, u32 *btotal)
{ {
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct gfs2_rgrpd *rgd; struct gfs2_rgrpd *rgd;
struct gfs2_trans *tr; struct gfs2_trans *tr;
struct buffer_head *bh = mp->mp_bh[hgt]; __be64 *p;
__be64 *top, *bottom, *p;
int blks_outside_rgrp; int blks_outside_rgrp;
u64 bn, bstart, isize_blks; u64 bn, bstart, isize_blks;
s64 blen; /* needs to be s64 or gfs2_add_inode_blocks breaks */ s64 blen; /* needs to be s64 or gfs2_add_inode_blocks breaks */
int meta = ((hgt != ip->i_height - 1) ? 1 : 0);
int ret = 0; int ret = 0;
bool buf_in_tr = false; /* buffer was added to transaction */ bool buf_in_tr = false; /* buffer was added to transaction */
if (gfs2_metatype_check(sdp, bh,
(hgt ? GFS2_METATYPE_IN : GFS2_METATYPE_DI)))
return -EIO;
more_rgrps: more_rgrps:
rgd = NULL;
if (gfs2_holder_initialized(rd_gh)) {
rgd = gfs2_glock2rgrp(rd_gh->gh_gl);
gfs2_assert_withdraw(sdp,
gfs2_glock_is_locked_by_me(rd_gh->gh_gl));
}
blks_outside_rgrp = 0; blks_outside_rgrp = 0;
bstart = 0; bstart = 0;
blen = 0; blen = 0;
top = metapointer(hgt, mp); /* first ptr from metapath */
/* If we're keeping some data at the truncation point, we've got to
preserve the metadata tree by adding 1 to the starting metapath. */
if (preserve1)
top++;
bottom = (__be64 *)(bh->b_data + bh->b_size);
for (p = top; p < bottom; p++) { for (p = start; p < end; p++) {
if (!*p) if (!*p)
continue; continue;
bn = be64_to_cpu(*p); bn = be64_to_cpu(*p);
if (gfs2_holder_initialized(rd_gh)) {
rgd = gfs2_glock2rgrp(rd_gh->gh_gl); if (rgd) {
gfs2_assert_withdraw(sdp, if (!rgrp_contains_block(rgd, bn)) {
gfs2_glock_is_locked_by_me(rd_gh->gh_gl)); blks_outside_rgrp++;
continue;
}
} else { } else {
rgd = gfs2_blk2rgrpd(sdp, bn, false); rgd = gfs2_blk2rgrpd(sdp, bn, true);
if (unlikely(!rgd)) {
ret = -EIO;
goto out;
}
ret = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE, ret = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_EXCLUSIVE,
0, rd_gh); 0, rd_gh);
if (ret) if (ret)
...@@ -1145,11 +1136,6 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, ...@@ -1145,11 +1136,6 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
gfs2_rs_deltree(&ip->i_res); gfs2_rs_deltree(&ip->i_res);
} }
if (!rgrp_contains_block(rgd, bn)) {
blks_outside_rgrp++;
continue;
}
/* The size of our transactions will be unknown until we /* The size of our transactions will be unknown until we
actually process all the metadata blocks that relate to actually process all the metadata blocks that relate to
the rgrp. So we estimate. We know it can't be more than the rgrp. So we estimate. We know it can't be more than
...@@ -1168,7 +1154,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, ...@@ -1168,7 +1154,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
jblocks_rqsted += isize_blks; jblocks_rqsted += isize_blks;
revokes = jblocks_rqsted; revokes = jblocks_rqsted;
if (meta) if (meta)
revokes += hptrs(sdp, hgt); revokes += end - start;
else if (ip->i_depth) else if (ip->i_depth)
revokes += sdp->sd_inptrs; revokes += sdp->sd_inptrs;
ret = gfs2_trans_begin(sdp, jblocks_rqsted, revokes); ret = gfs2_trans_begin(sdp, jblocks_rqsted, revokes);
...@@ -1226,7 +1212,11 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, ...@@ -1226,7 +1212,11 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
outside the rgrp we just processed, outside the rgrp we just processed,
do it all over again. */ do it all over again. */
if (current->journal_info) { if (current->journal_info) {
struct buffer_head *dibh = mp->mp_bh[0]; struct buffer_head *dibh;
ret = gfs2_meta_inode_buffer(ip, &dibh);
if (ret)
goto out;
/* Every transaction boundary, we rewrite the dinode /* Every transaction boundary, we rewrite the dinode
to keep its di_blocks current in case of failure. */ to keep its di_blocks current in case of failure. */
...@@ -1234,6 +1224,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, ...@@ -1234,6 +1224,7 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
current_time(&ip->i_inode); current_time(&ip->i_inode);
gfs2_trans_add_meta(ip->i_gl, dibh); gfs2_trans_add_meta(ip->i_gl, dibh);
gfs2_dinode_out(ip, dibh->b_data); gfs2_dinode_out(ip, dibh->b_data);
brelse(dibh);
up_write(&ip->i_rw_mutex); up_write(&ip->i_rw_mutex);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
} }
...@@ -1245,38 +1236,48 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh, ...@@ -1245,38 +1236,48 @@ static int sweep_bh_for_rgrps(struct gfs2_inode *ip, struct gfs2_holder *rd_gh,
return ret; return ret;
} }
static bool mp_eq_to_hgt(struct metapath *mp, __u16 *list, unsigned int h)
{
if (memcmp(mp->mp_list, list, h * sizeof(mp->mp_list[0])))
return false;
return true;
}
/** /**
* find_nonnull_ptr - find a non-null pointer given a metapath and height * find_nonnull_ptr - find a non-null pointer given a metapath and height
* assumes the metapath is valid (with buffers) out to height h
* @mp: starting metapath * @mp: starting metapath
* @h: desired height to search * @h: desired height to search
* *
* Assumes the metapath is valid (with buffers) out to height h.
* Returns: true if a non-null pointer was found in the metapath buffer * Returns: true if a non-null pointer was found in the metapath buffer
* false if all remaining pointers are NULL in the buffer * false if all remaining pointers are NULL in the buffer
*/ */
static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp, static bool find_nonnull_ptr(struct gfs2_sbd *sdp, struct metapath *mp,
unsigned int h) unsigned int h,
__u16 *end_list, unsigned int end_aligned)
{ {
__be64 *ptr; struct buffer_head *bh = mp->mp_bh[h];
unsigned int ptrs = hptrs(sdp, h) - 1; __be64 *first, *ptr, *end;
first = metaptr1(h, mp);
ptr = first + mp->mp_list[h];
end = (__be64 *)(bh->b_data + bh->b_size);
if (end_list && mp_eq_to_hgt(mp, end_list, h)) {
bool keep_end = h < end_aligned;
end = first + end_list[h] + keep_end;
}
while (true) { while (ptr < end) {
ptr = metapointer(h, mp);
if (*ptr) { /* if we have a non-null pointer */ if (*ptr) { /* if we have a non-null pointer */
/* Now zero the metapath after the current height. */ mp->mp_list[h] = ptr - first;
h++; h++;
if (h < GFS2_MAX_META_HEIGHT) if (h < GFS2_MAX_META_HEIGHT)
memset(&mp->mp_list[h], 0, mp->mp_list[h] = 0;
(GFS2_MAX_META_HEIGHT - h) *
sizeof(mp->mp_list[0]));
return true; return true;
} }
ptr++;
if (mp->mp_list[h] < ptrs)
mp->mp_list[h]++;
else
return false; /* no more pointers in this buffer */
} }
return false;
} }
enum dealloc_states { enum dealloc_states {
...@@ -1286,49 +1287,126 @@ enum dealloc_states { ...@@ -1286,49 +1287,126 @@ enum dealloc_states {
DEALLOC_DONE = 3, /* process complete */ DEALLOC_DONE = 3, /* process complete */
}; };
static bool mp_eq_to_hgt(struct metapath *mp, __u16 *nbof, unsigned int h) static inline void
metapointer_range(struct metapath *mp, int height,
__u16 *start_list, unsigned int start_aligned,
__u16 *end_list, unsigned int end_aligned,
__be64 **start, __be64 **end)
{ {
if (memcmp(mp->mp_list, nbof, h * sizeof(mp->mp_list[0]))) struct buffer_head *bh = mp->mp_bh[height];
__be64 *first;
first = metaptr1(height, mp);
*start = first;
if (mp_eq_to_hgt(mp, start_list, height)) {
bool keep_start = height < start_aligned;
*start = first + start_list[height] + keep_start;
}
*end = (__be64 *)(bh->b_data + bh->b_size);
if (end_list && mp_eq_to_hgt(mp, end_list, height)) {
bool keep_end = height < end_aligned;
*end = first + end_list[height] + keep_end;
}
}
static inline bool walk_done(struct gfs2_sbd *sdp,
struct metapath *mp, int height,
__u16 *end_list, unsigned int end_aligned)
{
__u16 end;
if (end_list) {
bool keep_end = height < end_aligned;
if (!mp_eq_to_hgt(mp, end_list, height))
return false; return false;
return true; end = end_list[height] + keep_end;
} else
end = (height > 0) ? sdp->sd_inptrs : sdp->sd_diptrs;
return mp->mp_list[height] >= end;
} }
/** /**
* trunc_dealloc - truncate a file down to a desired size * punch_hole - deallocate blocks in a file
* @ip: inode to truncate * @ip: inode to truncate
* @newsize: The desired size of the file * @offset: the start of the hole
* @length: the size of the hole (or 0 for truncate)
* *
* This function truncates a file to newsize. It works from the * Punch a hole into a file or truncate a file at a given position. This
* bottom up, and from the right to the left. In other words, it strips off * function operates in whole blocks (@offset and @length are rounded
* the highest layer (data) before stripping any of the metadata. Doing it * accordingly); partially filled blocks must be cleared otherwise.
* this way is best in case the operation is interrupted by power failure, etc. *
* The dinode is rewritten in every transaction to guarantee integrity. * This function works from the bottom up, and from the right to the left. In
* other words, it strips off the highest layer (data) before stripping any of
* the metadata. Doing it this way is best in case the operation is interrupted
* by power failure, etc. The dinode is rewritten in every transaction to
* guarantee integrity.
*/ */
static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) static int punch_hole(struct gfs2_inode *ip, u64 offset, u64 length)
{ {
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct metapath mp; struct metapath mp = {};
struct buffer_head *dibh, *bh; struct buffer_head *dibh, *bh;
struct gfs2_holder rd_gh; struct gfs2_holder rd_gh;
u64 lblock; unsigned int bsize_shift = sdp->sd_sb.sb_bsize_shift;
__u16 nbof[GFS2_MAX_META_HEIGHT]; /* new beginning of truncation */ u64 lblock = (offset + (1 << bsize_shift) - 1) >> bsize_shift;
__u16 start_list[GFS2_MAX_META_HEIGHT];
__u16 __end_list[GFS2_MAX_META_HEIGHT], *end_list = NULL;
unsigned int start_aligned, uninitialized_var(end_aligned);
unsigned int strip_h = ip->i_height - 1; unsigned int strip_h = ip->i_height - 1;
u32 btotal = 0; u32 btotal = 0;
int ret, state; int ret, state;
int mp_h; /* metapath buffers are read in to this height */ int mp_h; /* metapath buffers are read in to this height */
sector_t last_ra = 0;
u64 prev_bnr = 0; u64 prev_bnr = 0;
bool preserve1; /* need to preserve the first meta pointer? */ __be64 *start, *end;
if (!newsize) /*
lblock = 0; * The start position of the hole is defined by lblock, start_list, and
else * start_aligned. The end position of the hole is defined by lend,
lblock = (newsize - 1) >> sdp->sd_sb.sb_bsize_shift; * end_list, and end_aligned.
*
* start_aligned and end_aligned define down to which height the start
* and end positions are aligned to the metadata tree (i.e., the
* position is a multiple of the metadata granularity at the height
* above). This determines at which heights additional meta pointers
* needs to be preserved for the remaining data.
*/
if (length) {
u64 maxsize = sdp->sd_heightsize[ip->i_height];
u64 end_offset = offset + length;
u64 lend;
/*
* Clip the end at the maximum file size for the given height:
* that's how far the metadata goes; files bigger than that
* will have additional layers of indirection.
*/
if (end_offset > maxsize)
end_offset = maxsize;
lend = end_offset >> bsize_shift;
if (lblock >= lend)
return 0;
find_metapath(sdp, lend, &mp, ip->i_height);
end_list = __end_list;
memcpy(end_list, mp.mp_list, sizeof(mp.mp_list));
for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) {
if (end_list[mp_h])
break;
}
end_aligned = mp_h;
}
memset(&mp, 0, sizeof(mp));
find_metapath(sdp, lblock, &mp, ip->i_height); find_metapath(sdp, lblock, &mp, ip->i_height);
memcpy(start_list, mp.mp_list, sizeof(start_list));
memcpy(&nbof, &mp.mp_list, sizeof(nbof)); for (mp_h = ip->i_height - 1; mp_h > 0; mp_h--) {
if (start_list[mp_h])
break;
}
start_aligned = mp_h;
ret = gfs2_meta_inode_buffer(ip, &dibh); ret = gfs2_meta_inode_buffer(ip, &dibh);
if (ret) if (ret)
...@@ -1336,7 +1414,17 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) ...@@ -1336,7 +1414,17 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
mp.mp_bh[0] = dibh; mp.mp_bh[0] = dibh;
ret = lookup_metapath(ip, &mp); ret = lookup_metapath(ip, &mp);
if (ret == ip->i_height) if (ret)
goto out_metapath;
/* issue read-ahead on metadata */
for (mp_h = 0; mp_h < mp.mp_aheight - 1; mp_h++) {
metapointer_range(&mp, mp_h, start_list, start_aligned,
end_list, end_aligned, &start, &end);
gfs2_metapath_ra(ip->i_gl, start, end);
}
if (mp.mp_aheight == ip->i_height)
state = DEALLOC_MP_FULL; /* We have a complete metapath */ state = DEALLOC_MP_FULL; /* We have a complete metapath */
else else
state = DEALLOC_FILL_MP; /* deal with partial metapath */ state = DEALLOC_FILL_MP; /* deal with partial metapath */
...@@ -1357,20 +1445,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) ...@@ -1357,20 +1445,6 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
/* Truncate a full metapath at the given strip height. /* Truncate a full metapath at the given strip height.
* Note that strip_h == mp_h in order to be in this state. */ * Note that strip_h == mp_h in order to be in this state. */
case DEALLOC_MP_FULL: case DEALLOC_MP_FULL:
if (mp_h > 0) { /* issue read-ahead on metadata */
__be64 *top;
bh = mp.mp_bh[mp_h - 1];
if (bh->b_blocknr != last_ra) {
last_ra = bh->b_blocknr;
top = metaptr1(mp_h - 1, &mp);
gfs2_metapath_ra(ip->i_gl, bh, top);
}
}
/* If we're truncating to a non-zero size and the mp is
at the beginning of file for the strip height, we
need to preserve the first metadata pointer. */
preserve1 = (newsize && mp_eq_to_hgt(&mp, nbof, mp_h));
bh = mp.mp_bh[mp_h]; bh = mp.mp_bh[mp_h];
gfs2_assert_withdraw(sdp, bh); gfs2_assert_withdraw(sdp, bh);
if (gfs2_assert_withdraw(sdp, if (gfs2_assert_withdraw(sdp,
...@@ -1382,8 +1456,28 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) ...@@ -1382,8 +1456,28 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
prev_bnr, ip->i_height, strip_h, mp_h); prev_bnr, ip->i_height, strip_h, mp_h);
} }
prev_bnr = bh->b_blocknr; prev_bnr = bh->b_blocknr;
ret = sweep_bh_for_rgrps(ip, &rd_gh, &mp, &btotal,
mp_h, preserve1); if (gfs2_metatype_check(sdp, bh,
(mp_h ? GFS2_METATYPE_IN :
GFS2_METATYPE_DI))) {
ret = -EIO;
goto out;
}
/*
* Below, passing end_aligned as 0 gives us the
* metapointer range excluding the end point: the end
* point is the first metapath we must not deallocate!
*/
metapointer_range(&mp, mp_h, start_list, start_aligned,
end_list, 0 /* end_aligned */,
&start, &end);
ret = sweep_bh_for_rgrps(ip, &rd_gh, mp.mp_bh[mp_h],
start, end,
mp_h != ip->i_height - 1,
&btotal);
/* If we hit an error or just swept dinode buffer, /* If we hit an error or just swept dinode buffer,
just exit. */ just exit. */
if (ret || !mp_h) { if (ret || !mp_h) {
...@@ -1407,20 +1501,20 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) ...@@ -1407,20 +1501,20 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
stripping the previous level of metadata. */ stripping the previous level of metadata. */
if (mp_h == 0) { if (mp_h == 0) {
strip_h--; strip_h--;
memcpy(&mp.mp_list, &nbof, sizeof(nbof)); memcpy(mp.mp_list, start_list, sizeof(start_list));
mp_h = strip_h; mp_h = strip_h;
state = DEALLOC_FILL_MP; state = DEALLOC_FILL_MP;
break; break;
} }
mp.mp_list[mp_h] = 0; mp.mp_list[mp_h] = 0;
mp_h--; /* search one metadata height down */ mp_h--; /* search one metadata height down */
if (mp.mp_list[mp_h] >= hptrs(sdp, mp_h) - 1)
break; /* loop around in the same state */
mp.mp_list[mp_h]++; mp.mp_list[mp_h]++;
if (walk_done(sdp, &mp, mp_h, end_list, end_aligned))
break;
/* Here we've found a part of the metapath that is not /* Here we've found a part of the metapath that is not
* allocated. We need to search at that height for the * allocated. We need to search at that height for the
* next non-null pointer. */ * next non-null pointer. */
if (find_nonnull_ptr(sdp, &mp, mp_h)) { if (find_nonnull_ptr(sdp, &mp, mp_h, end_list, end_aligned)) {
state = DEALLOC_FILL_MP; state = DEALLOC_FILL_MP;
mp_h++; mp_h++;
} }
...@@ -1435,18 +1529,29 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize) ...@@ -1435,18 +1529,29 @@ static int trunc_dealloc(struct gfs2_inode *ip, u64 newsize)
if (ret < 0) if (ret < 0)
goto out; goto out;
/* issue read-ahead on metadata */
if (mp.mp_aheight > 1) {
for (; ret > 1; ret--) {
metapointer_range(&mp, mp.mp_aheight - ret,
start_list, start_aligned,
end_list, end_aligned,
&start, &end);
gfs2_metapath_ra(ip->i_gl, start, end);
}
}
/* If buffers found for the entire strip height */ /* If buffers found for the entire strip height */
if ((ret == ip->i_height) && (mp_h == strip_h)) { if (mp.mp_aheight - 1 == strip_h) {
state = DEALLOC_MP_FULL; state = DEALLOC_MP_FULL;
break; break;
} }
if (ret < ip->i_height) /* We have a partial height */ if (mp.mp_aheight < ip->i_height) /* We have a partial height */
mp_h = ret - 1; mp_h = mp.mp_aheight - 1;
/* If we find a non-null block pointer, crawl a bit /* If we find a non-null block pointer, crawl a bit
higher up in the metapath and try again, otherwise higher up in the metapath and try again, otherwise
we need to look lower for a new starting point. */ we need to look lower for a new starting point. */
if (find_nonnull_ptr(sdp, &mp, mp_h)) if (find_nonnull_ptr(sdp, &mp, mp_h, end_list, end_aligned))
mp_h++; mp_h++;
else else
state = DEALLOC_MP_LOWER; state = DEALLOC_MP_LOWER;
...@@ -1524,7 +1629,6 @@ static int trunc_end(struct gfs2_inode *ip) ...@@ -1524,7 +1629,6 @@ static int trunc_end(struct gfs2_inode *ip)
/** /**
* do_shrink - make a file smaller * do_shrink - make a file smaller
* @inode: the inode * @inode: the inode
* @oldsize: the current inode size
* @newsize: the size to make the file * @newsize: the size to make the file
* *
* Called with an exclusive lock on @inode. The @size must * Called with an exclusive lock on @inode. The @size must
...@@ -1533,18 +1637,18 @@ static int trunc_end(struct gfs2_inode *ip) ...@@ -1533,18 +1637,18 @@ static int trunc_end(struct gfs2_inode *ip)
* Returns: errno * Returns: errno
*/ */
static int do_shrink(struct inode *inode, u64 oldsize, u64 newsize) static int do_shrink(struct inode *inode, u64 newsize)
{ {
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
int error; int error;
error = trunc_start(inode, oldsize, newsize); error = trunc_start(inode, newsize);
if (error < 0) if (error < 0)
return error; return error;
if (gfs2_is_stuffed(ip)) if (gfs2_is_stuffed(ip))
return 0; return 0;
error = trunc_dealloc(ip, newsize); error = punch_hole(ip, newsize, 0);
if (error == 0) if (error == 0)
error = trunc_end(ip); error = trunc_end(ip);
...@@ -1553,10 +1657,9 @@ static int do_shrink(struct inode *inode, u64 oldsize, u64 newsize) ...@@ -1553,10 +1657,9 @@ static int do_shrink(struct inode *inode, u64 oldsize, u64 newsize)
void gfs2_trim_blocks(struct inode *inode) void gfs2_trim_blocks(struct inode *inode)
{ {
u64 size = inode->i_size;
int ret; int ret;
ret = do_shrink(inode, size, size); ret = do_shrink(inode, inode->i_size);
WARN_ON(ret != 0); WARN_ON(ret != 0);
} }
...@@ -1589,8 +1692,7 @@ static int do_grow(struct inode *inode, u64 size) ...@@ -1589,8 +1692,7 @@ static int do_grow(struct inode *inode, u64 size)
int error; int error;
int unstuff = 0; int unstuff = 0;
if (gfs2_is_stuffed(ip) && if (gfs2_is_stuffed(ip) && size > gfs2_max_stuffed_size(ip)) {
(size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) {
error = gfs2_quota_lock_check(ip, &ap); error = gfs2_quota_lock_check(ip, &ap);
if (error) if (error)
return error; return error;
...@@ -1650,7 +1752,6 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize) ...@@ -1650,7 +1752,6 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)
{ {
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
int ret; int ret;
u64 oldsize;
BUG_ON(!S_ISREG(inode->i_mode)); BUG_ON(!S_ISREG(inode->i_mode));
...@@ -1664,13 +1765,12 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize) ...@@ -1664,13 +1765,12 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)
if (ret) if (ret)
goto out; goto out;
oldsize = inode->i_size; if (newsize >= inode->i_size) {
if (newsize >= oldsize) {
ret = do_grow(inode, newsize); ret = do_grow(inode, newsize);
goto out; goto out;
} }
ret = do_shrink(inode, oldsize, newsize); ret = do_shrink(inode, newsize);
out: out:
gfs2_rsqa_delete(ip, NULL); gfs2_rsqa_delete(ip, NULL);
return ret; return ret;
...@@ -1679,7 +1779,7 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize) ...@@ -1679,7 +1779,7 @@ int gfs2_setattr_size(struct inode *inode, u64 newsize)
int gfs2_truncatei_resume(struct gfs2_inode *ip) int gfs2_truncatei_resume(struct gfs2_inode *ip)
{ {
int error; int error;
error = trunc_dealloc(ip, i_size_read(&ip->i_inode)); error = punch_hole(ip, i_size_read(&ip->i_inode), 0);
if (!error) if (!error)
error = trunc_end(ip); error = trunc_end(ip);
return error; return error;
...@@ -1687,7 +1787,7 @@ int gfs2_truncatei_resume(struct gfs2_inode *ip) ...@@ -1687,7 +1787,7 @@ int gfs2_truncatei_resume(struct gfs2_inode *ip)
int gfs2_file_dealloc(struct gfs2_inode *ip) int gfs2_file_dealloc(struct gfs2_inode *ip)
{ {
return trunc_dealloc(ip, 0); return punch_hole(ip, 0, 0);
} }
/** /**
...@@ -1827,8 +1927,7 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, ...@@ -1827,8 +1927,7 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
return 0; return 0;
if (gfs2_is_stuffed(ip)) { if (gfs2_is_stuffed(ip)) {
if (offset + len > if (offset + len > gfs2_max_stuffed_size(ip))
sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode))
return 1; return 1;
return 0; return 0;
} }
...@@ -1855,3 +1954,123 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, ...@@ -1855,3 +1954,123 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
return 0; return 0;
} }
static int stuffed_zero_range(struct inode *inode, loff_t offset, loff_t length)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct buffer_head *dibh;
int error;
if (offset >= inode->i_size)
return 0;
if (offset + length > inode->i_size)
length = inode->i_size - offset;
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
return error;
gfs2_trans_add_meta(ip->i_gl, dibh);
memset(dibh->b_data + sizeof(struct gfs2_dinode) + offset, 0,
length);
brelse(dibh);
return 0;
}
static int gfs2_journaled_truncate_range(struct inode *inode, loff_t offset,
loff_t length)
{
struct gfs2_sbd *sdp = GFS2_SB(inode);
loff_t max_chunk = GFS2_JTRUNC_REVOKES * sdp->sd_vfs->s_blocksize;
int error;
while (length) {
struct gfs2_trans *tr;
loff_t chunk;
unsigned int offs;
chunk = length;
if (chunk > max_chunk)
chunk = max_chunk;
offs = offset & ~PAGE_MASK;
if (offs && chunk > PAGE_SIZE)
chunk = offs + ((chunk - offs) & PAGE_MASK);
truncate_pagecache_range(inode, offset, chunk);
offset += chunk;
length -= chunk;
tr = current->journal_info;
if (!test_bit(TR_TOUCHED, &tr->tr_flags))
continue;
gfs2_trans_end(sdp);
error = gfs2_trans_begin(sdp, RES_DINODE, GFS2_JTRUNC_REVOKES);
if (error)
return error;
}
return 0;
}
int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length)
{
struct inode *inode = file_inode(file);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
int error;
if (gfs2_is_jdata(ip))
error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_JDATA,
GFS2_JTRUNC_REVOKES);
else
error = gfs2_trans_begin(sdp, RES_DINODE, 0);
if (error)
return error;
if (gfs2_is_stuffed(ip)) {
error = stuffed_zero_range(inode, offset, length);
if (error)
goto out;
} else {
unsigned int start_off, end_off, blocksize;
blocksize = i_blocksize(inode);
start_off = offset & (blocksize - 1);
end_off = (offset + length) & (blocksize - 1);
if (start_off) {
unsigned int len = length;
if (length > blocksize - start_off)
len = blocksize - start_off;
error = gfs2_block_zero_range(inode, offset, len);
if (error)
goto out;
if (start_off + length < blocksize)
end_off = 0;
}
if (end_off) {
error = gfs2_block_zero_range(inode,
offset + length - end_off, end_off);
if (error)
goto out;
}
}
if (gfs2_is_jdata(ip)) {
BUG_ON(!current->journal_info);
gfs2_journaled_truncate_range(inode, offset, length);
} else
truncate_pagecache_range(inode, offset, offset + length - 1);
file_update_time(file);
mark_inode_dirty(inode);
if (current->journal_info)
gfs2_trans_end(sdp);
if (!gfs2_is_stuffed(ip))
error = punch_hole(ip, offset, length);
out:
if (current->journal_info)
gfs2_trans_end(sdp);
return error;
}
...@@ -61,5 +61,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, ...@@ -61,5 +61,6 @@ extern int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset,
unsigned int len); unsigned int len);
extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd); extern int gfs2_map_journal_extents(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd);
extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd); extern void gfs2_free_journal_extents(struct gfs2_jdesc *jd);
extern int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length);
#endif /* __BMAP_DOT_H__ */ #endif /* __BMAP_DOT_H__ */
...@@ -170,8 +170,7 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf, ...@@ -170,8 +170,7 @@ static int gfs2_dir_write_data(struct gfs2_inode *ip, const char *buf,
if (!size) if (!size)
return 0; return 0;
if (gfs2_is_stuffed(ip) && if (gfs2_is_stuffed(ip) && offset + size <= gfs2_max_stuffed_size(ip))
offset + size <= sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode))
return gfs2_dir_write_stuffed(ip, buf, (unsigned int)offset, return gfs2_dir_write_stuffed(ip, buf, (unsigned int)offset,
size); size);
......
...@@ -246,7 +246,9 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask) ...@@ -246,7 +246,9 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
} }
if ((flags ^ new_flags) & GFS2_DIF_JDATA) { if ((flags ^ new_flags) & GFS2_DIF_JDATA) {
if (new_flags & GFS2_DIF_JDATA) if (new_flags & GFS2_DIF_JDATA)
gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); gfs2_log_flush(sdp, ip->i_gl,
GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_SET_FLAGS);
error = filemap_fdatawrite(inode->i_mapping); error = filemap_fdatawrite(inode->i_mapping);
if (error) if (error)
goto out; goto out;
...@@ -924,7 +926,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le ...@@ -924,7 +926,7 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
struct gfs2_holder gh; struct gfs2_holder gh;
int ret; int ret;
if (mode & ~FALLOC_FL_KEEP_SIZE) if (mode & ~(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* fallocate is needed by gfs2_grow to reserve space in the rindex */ /* fallocate is needed by gfs2_grow to reserve space in the rindex */
if (gfs2_is_jdata(ip) && inode != sdp->sd_rindex) if (gfs2_is_jdata(ip) && inode != sdp->sd_rindex)
...@@ -948,13 +950,18 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le ...@@ -948,13 +950,18 @@ static long gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t le
if (ret) if (ret)
goto out_unlock; goto out_unlock;
if (mode & FALLOC_FL_PUNCH_HOLE) {
ret = __gfs2_punch_hole(file, offset, len);
} else {
ret = gfs2_rsqa_alloc(ip); ret = gfs2_rsqa_alloc(ip);
if (ret) if (ret)
goto out_putw; goto out_putw;
ret = __gfs2_fallocate(file, mode, offset, len); ret = __gfs2_fallocate(file, mode, offset, len);
if (ret) if (ret)
gfs2_rs_deltree(&ip->i_res); gfs2_rs_deltree(&ip->i_res);
}
out_putw: out_putw:
put_write_access(inode); put_write_access(inode);
......
...@@ -107,7 +107,8 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl) ...@@ -107,7 +107,8 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
__gfs2_ail_flush(gl, 0, tr.tr_revokes); __gfs2_ail_flush(gl, 0, tr.tr_revokes);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_AIL_EMPTY_GL);
} }
void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
...@@ -128,7 +129,8 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) ...@@ -128,7 +129,8 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
return; return;
__gfs2_ail_flush(gl, fsync, max_revokes); __gfs2_ail_flush(gl, fsync, max_revokes);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_AIL_FLUSH);
} }
/** /**
...@@ -157,7 +159,8 @@ static void rgrp_go_sync(struct gfs2_glock *gl) ...@@ -157,7 +159,8 @@ static void rgrp_go_sync(struct gfs2_glock *gl)
return; return;
GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
gfs2_log_flush(sdp, gl, NORMAL_FLUSH); gfs2_log_flush(sdp, gl, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_RGRP_GO_SYNC);
filemap_fdatawrite_range(mapping, gl->gl_vm.start, gl->gl_vm.end); filemap_fdatawrite_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
error = filemap_fdatawait_range(mapping, gl->gl_vm.start, gl->gl_vm.end); error = filemap_fdatawait_range(mapping, gl->gl_vm.start, gl->gl_vm.end);
mapping_set_error(mapping, error); mapping_set_error(mapping, error);
...@@ -252,7 +255,8 @@ static void inode_go_sync(struct gfs2_glock *gl) ...@@ -252,7 +255,8 @@ static void inode_go_sync(struct gfs2_glock *gl)
GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE); GLOCK_BUG_ON(gl, gl->gl_state != LM_ST_EXCLUSIVE);
gfs2_log_flush(gl->gl_name.ln_sbd, gl, NORMAL_FLUSH); gfs2_log_flush(gl->gl_name.ln_sbd, gl, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_INODE_GO_SYNC);
filemap_fdatawrite(metamapping); filemap_fdatawrite(metamapping);
if (isreg) { if (isreg) {
struct address_space *mapping = ip->i_inode.i_mapping; struct address_space *mapping = ip->i_inode.i_mapping;
...@@ -303,7 +307,9 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags) ...@@ -303,7 +307,9 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags)
} }
if (ip == GFS2_I(gl->gl_name.ln_sbd->sd_rindex)) { if (ip == GFS2_I(gl->gl_name.ln_sbd->sd_rindex)) {
gfs2_log_flush(gl->gl_name.ln_sbd, NULL, NORMAL_FLUSH); gfs2_log_flush(gl->gl_name.ln_sbd, NULL,
GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_INODE_GO_INVAL);
gl->gl_name.ln_sbd->sd_rindex_uptodate = 0; gl->gl_name.ln_sbd->sd_rindex_uptodate = 0;
} }
if (ip && S_ISREG(ip->i_inode.i_mode)) if (ip && S_ISREG(ip->i_inode.i_mode))
...@@ -495,7 +501,8 @@ static void freeze_go_sync(struct gfs2_glock *gl) ...@@ -495,7 +501,8 @@ static void freeze_go_sync(struct gfs2_glock *gl)
gfs2_assert_withdraw(sdp, 0); gfs2_assert_withdraw(sdp, 0);
} }
queue_work(gfs2_freeze_wq, &sdp->sd_freeze_work); queue_work(gfs2_freeze_wq, &sdp->sd_freeze_work);
gfs2_log_flush(sdp, NULL, FREEZE_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE |
GFS2_LFC_FREEZE_GO_SYNC);
} }
} }
......
...@@ -44,7 +44,6 @@ struct gfs2_log_header_host { ...@@ -44,7 +44,6 @@ struct gfs2_log_header_host {
u32 lh_flags; /* GFS2_LOG_HEAD_... */ u32 lh_flags; /* GFS2_LOG_HEAD_... */
u32 lh_tail; /* Block number of log tail */ u32 lh_tail; /* Block number of log tail */
u32 lh_blkno; u32 lh_blkno;
u32 lh_hash;
}; };
/* /*
...@@ -861,5 +860,10 @@ static inline void gfs2_sbstats_inc(const struct gfs2_glock *gl, int which) ...@@ -861,5 +860,10 @@ static inline void gfs2_sbstats_inc(const struct gfs2_glock *gl, int which)
extern struct gfs2_rgrpd *gfs2_glock2rgrp(struct gfs2_glock *gl); extern struct gfs2_rgrpd *gfs2_glock2rgrp(struct gfs2_glock *gl);
static inline unsigned gfs2_max_stuffed_size(const struct gfs2_inode *ip)
{
return GFS2_SB(&ip->i_inode)->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
}
#endif /* __INCORE_DOT_H__ */ #endif /* __INCORE_DOT_H__ */
...@@ -1152,12 +1152,11 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1152,12 +1152,11 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0); error = gfs2_trans_begin(sdp, 2*RES_DINODE + 3*RES_LEAF + RES_RG_BIT, 0);
if (error) if (error)
goto out_end_trans; goto out_gunlock;
error = gfs2_unlink_inode(dip, dentry); error = gfs2_unlink_inode(dip, dentry);
out_end_trans:
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
out_gunlock: out_gunlock:
gfs2_glock_dq(ghs + 2); gfs2_glock_dq(ghs + 2);
out_rgrp: out_rgrp:
...@@ -1184,11 +1183,10 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1184,11 +1183,10 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
static int gfs2_symlink(struct inode *dir, struct dentry *dentry, static int gfs2_symlink(struct inode *dir, struct dentry *dentry,
const char *symname) const char *symname)
{ {
struct gfs2_sbd *sdp = GFS2_SB(dir);
unsigned int size; unsigned int size;
size = strlen(symname); size = strlen(symname);
if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode) - 1) if (size >= gfs2_max_stuffed_size(GFS2_I(dir)))
return -ENAMETOOLONG; return -ENAMETOOLONG;
return gfs2_create_inode(dir, dentry, NULL, S_IFLNK | S_IRWXUGO, 0, symname, size, 0, NULL); return gfs2_create_inode(dir, dentry, NULL, S_IFLNK | S_IRWXUGO, 0, symname, size, 0, NULL);
...@@ -1205,8 +1203,7 @@ static int gfs2_symlink(struct inode *dir, struct dentry *dentry, ...@@ -1205,8 +1203,7 @@ static int gfs2_symlink(struct inode *dir, struct dentry *dentry,
static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) static int gfs2_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{ {
struct gfs2_sbd *sdp = GFS2_SB(dir); unsigned dsize = gfs2_max_stuffed_size(GFS2_I(dir));
unsigned dsize = sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode);
return gfs2_create_inode(dir, dentry, NULL, S_IFDIR | mode, 0, NULL, dsize, 0, NULL); return gfs2_create_inode(dir, dentry, NULL, S_IFDIR | mode, 0, NULL, dsize, 0, NULL);
} }
......
...@@ -1091,7 +1091,7 @@ static void gdlm_recover_slot(void *arg, struct dlm_slot *slot) ...@@ -1091,7 +1091,7 @@ static void gdlm_recover_slot(void *arg, struct dlm_slot *slot)
spin_lock(&ls->ls_recover_spin); spin_lock(&ls->ls_recover_spin);
if (ls->ls_recover_size < jid + 1) { if (ls->ls_recover_size < jid + 1) {
fs_err(sdp, "recover_slot jid %d gen %u short size %d", fs_err(sdp, "recover_slot jid %d gen %u short size %d\n",
jid, ls->ls_recover_block, ls->ls_recover_size); jid, ls->ls_recover_block, ls->ls_recover_size);
spin_unlock(&ls->ls_recover_spin); spin_unlock(&ls->ls_recover_spin);
return; return;
...@@ -1153,7 +1153,7 @@ static void gdlm_recovery_result(struct gfs2_sbd *sdp, unsigned int jid, ...@@ -1153,7 +1153,7 @@ static void gdlm_recovery_result(struct gfs2_sbd *sdp, unsigned int jid,
return; return;
} }
if (ls->ls_recover_size < jid + 1) { if (ls->ls_recover_size < jid + 1) {
fs_err(sdp, "recovery_result jid %d short size %d", fs_err(sdp, "recovery_result jid %d short size %d\n",
jid, ls->ls_recover_size); jid, ls->ls_recover_size);
spin_unlock(&ls->ls_recover_spin); spin_unlock(&ls->ls_recover_spin);
return; return;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h> #include <linux/gfs2_ondisk.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/crc32c.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
...@@ -538,9 +539,12 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp) ...@@ -538,9 +539,12 @@ static void gfs2_ordered_write(struct gfs2_sbd *sdp)
list_sort(NULL, &sdp->sd_log_le_ordered, &ip_cmp); list_sort(NULL, &sdp->sd_log_le_ordered, &ip_cmp);
while (!list_empty(&sdp->sd_log_le_ordered)) { while (!list_empty(&sdp->sd_log_le_ordered)) {
ip = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_inode, i_ordered); ip = list_entry(sdp->sd_log_le_ordered.next, struct gfs2_inode, i_ordered);
list_move(&ip->i_ordered, &written); if (ip->i_inode.i_mapping->nrpages == 0) {
if (ip->i_inode.i_mapping->nrpages == 0) test_and_clear_bit(GIF_ORDERED, &ip->i_flags);
list_del(&ip->i_ordered);
continue; continue;
}
list_move(&ip->i_ordered, &written);
spin_unlock(&sdp->sd_ordered_lock); spin_unlock(&sdp->sd_ordered_lock);
filemap_fdatawrite(ip->i_inode.i_mapping); filemap_fdatawrite(ip->i_inode.i_mapping);
spin_lock(&sdp->sd_ordered_lock); spin_lock(&sdp->sd_ordered_lock);
...@@ -648,49 +652,102 @@ void gfs2_write_revokes(struct gfs2_sbd *sdp) ...@@ -648,49 +652,102 @@ void gfs2_write_revokes(struct gfs2_sbd *sdp)
} }
/** /**
* log_write_header - Get and initialize a journal header buffer * write_log_header - Write a journal log header buffer at sd_log_flush_head
* @sdp: The GFS2 superblock * @sdp: The GFS2 superblock
* @jd: journal descriptor of the journal to which we are writing
* @seq: sequence number
* @tail: tail of the log
* @flags: log header flags GFS2_LOG_HEAD_*
* @op_flags: flags to pass to the bio
* *
* Returns: the initialized log buffer descriptor * Returns: the initialized log buffer descriptor
*/ */
static void log_write_header(struct gfs2_sbd *sdp, u32 flags) void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
u64 seq, u32 tail, u32 flags, int op_flags)
{ {
struct gfs2_log_header *lh; struct gfs2_log_header *lh;
unsigned int tail; u32 hash, crc;
u32 hash;
int op_flags = REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC;
struct page *page = mempool_alloc(gfs2_page_pool, GFP_NOIO); struct page *page = mempool_alloc(gfs2_page_pool, GFP_NOIO);
enum gfs2_freeze_state state = atomic_read(&sdp->sd_freeze_state); struct gfs2_statfs_change_host *l_sc = &sdp->sd_statfs_local;
struct timespec64 tv;
struct super_block *sb = sdp->sd_vfs;
u64 addr;
lh = page_address(page); lh = page_address(page);
clear_page(lh); clear_page(lh);
gfs2_assert_withdraw(sdp, (state != SFS_FROZEN));
tail = current_tail(sdp);
lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC); lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH); lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH);
lh->lh_header.__pad0 = cpu_to_be64(0); lh->lh_header.__pad0 = cpu_to_be64(0);
lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH); lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH);
lh->lh_header.mh_jid = cpu_to_be32(sdp->sd_jdesc->jd_jid); lh->lh_header.mh_jid = cpu_to_be32(sdp->sd_jdesc->jd_jid);
lh->lh_sequence = cpu_to_be64(sdp->sd_log_sequence++); lh->lh_sequence = cpu_to_be64(seq);
lh->lh_flags = cpu_to_be32(flags); lh->lh_flags = cpu_to_be32(flags);
lh->lh_tail = cpu_to_be32(tail); lh->lh_tail = cpu_to_be32(tail);
lh->lh_blkno = cpu_to_be32(sdp->sd_log_flush_head); lh->lh_blkno = cpu_to_be32(sdp->sd_log_flush_head);
hash = gfs2_disk_hash(page_address(page), sizeof(struct gfs2_log_header)); hash = ~crc32(~0, lh, LH_V1_SIZE);
lh->lh_hash = cpu_to_be32(hash); lh->lh_hash = cpu_to_be32(hash);
tv = current_kernel_time64();
lh->lh_nsec = cpu_to_be32(tv.tv_nsec);
lh->lh_sec = cpu_to_be64(tv.tv_sec);
addr = gfs2_log_bmap(sdp);
lh->lh_addr = cpu_to_be64(addr);
lh->lh_jinode = cpu_to_be64(GFS2_I(jd->jd_inode)->i_no_addr);
/* We may only write local statfs, quota, etc., when writing to our
own journal. The values are left 0 when recovering a journal
different from our own. */
if (!(flags & GFS2_LOG_HEAD_RECOVERY)) {
lh->lh_statfs_addr =
cpu_to_be64(GFS2_I(sdp->sd_sc_inode)->i_no_addr);
lh->lh_quota_addr =
cpu_to_be64(GFS2_I(sdp->sd_qc_inode)->i_no_addr);
spin_lock(&sdp->sd_statfs_spin);
lh->lh_local_total = cpu_to_be64(l_sc->sc_total);
lh->lh_local_free = cpu_to_be64(l_sc->sc_free);
lh->lh_local_dinodes = cpu_to_be64(l_sc->sc_dinodes);
spin_unlock(&sdp->sd_statfs_spin);
}
BUILD_BUG_ON(offsetof(struct gfs2_log_header, lh_crc) != LH_V1_SIZE);
crc = crc32c(~0, (void *)lh + LH_V1_SIZE + 4,
sb->s_blocksize - LH_V1_SIZE - 4);
lh->lh_crc = cpu_to_be32(crc);
gfs2_log_write(sdp, page, sb->s_blocksize, 0, addr);
gfs2_log_flush_bio(sdp, REQ_OP_WRITE, op_flags);
log_flush_wait(sdp);
}
/**
* log_write_header - Get and initialize a journal header buffer
* @sdp: The GFS2 superblock
* @flags: The log header flags, including log header origin
*
* Returns: the initialized log buffer descriptor
*/
static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
{
unsigned int tail;
int op_flags = REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC;
enum gfs2_freeze_state state = atomic_read(&sdp->sd_freeze_state);
gfs2_assert_withdraw(sdp, (state != SFS_FROZEN));
tail = current_tail(sdp);
if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) { if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) {
gfs2_ordered_wait(sdp); gfs2_ordered_wait(sdp);
log_flush_wait(sdp); log_flush_wait(sdp);
op_flags = REQ_SYNC | REQ_META | REQ_PRIO; op_flags = REQ_SYNC | REQ_META | REQ_PRIO;
} }
sdp->sd_log_idle = (tail == sdp->sd_log_flush_head); sdp->sd_log_idle = (tail == sdp->sd_log_flush_head);
gfs2_log_write_page(sdp, page); gfs2_write_log_header(sdp, sdp->sd_jdesc, sdp->sd_log_sequence++, tail,
gfs2_log_flush_bio(sdp, REQ_OP_WRITE, op_flags); flags, op_flags);
log_flush_wait(sdp);
if (sdp->sd_log_tail != tail) if (sdp->sd_log_tail != tail)
log_pull_tail(sdp, tail); log_pull_tail(sdp, tail);
...@@ -700,11 +757,11 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags) ...@@ -700,11 +757,11 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
* gfs2_log_flush - flush incore transaction(s) * gfs2_log_flush - flush incore transaction(s)
* @sdp: the filesystem * @sdp: the filesystem
* @gl: The glock structure to flush. If NULL, flush the whole incore log * @gl: The glock structure to flush. If NULL, flush the whole incore log
* @flags: The log header flags: GFS2_LOG_HEAD_FLUSH_* and debug flags
* *
*/ */
void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
enum gfs2_flush_type type)
{ {
struct gfs2_trans *tr; struct gfs2_trans *tr;
enum gfs2_freeze_state state = atomic_read(&sdp->sd_freeze_state); enum gfs2_freeze_state state = atomic_read(&sdp->sd_freeze_state);
...@@ -716,9 +773,9 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, ...@@ -716,9 +773,9 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
up_write(&sdp->sd_log_flush_lock); up_write(&sdp->sd_log_flush_lock);
return; return;
} }
trace_gfs2_log_flush(sdp, 1); trace_gfs2_log_flush(sdp, 1, flags);
if (type == SHUTDOWN_FLUSH) if (flags & GFS2_LOG_HEAD_FLUSH_SHUTDOWN)
clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); clear_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
sdp->sd_log_flush_head = sdp->sd_log_head; sdp->sd_log_flush_head = sdp->sd_log_head;
...@@ -743,11 +800,11 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, ...@@ -743,11 +800,11 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
if (sdp->sd_log_head != sdp->sd_log_flush_head) { if (sdp->sd_log_head != sdp->sd_log_flush_head) {
log_flush_wait(sdp); log_flush_wait(sdp);
log_write_header(sdp, 0); log_write_header(sdp, flags);
} else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){ } else if (sdp->sd_log_tail != current_tail(sdp) && !sdp->sd_log_idle){
atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */ atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
trace_gfs2_log_blocks(sdp, -1); trace_gfs2_log_blocks(sdp, -1);
log_write_header(sdp, 0); log_write_header(sdp, flags);
} }
lops_after_commit(sdp, tr); lops_after_commit(sdp, tr);
...@@ -764,7 +821,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, ...@@ -764,7 +821,7 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
gfs2_log_unlock(sdp); gfs2_log_unlock(sdp);
if (type != NORMAL_FLUSH) { if (!(flags & GFS2_LOG_HEAD_FLUSH_NORMAL)) {
if (!sdp->sd_log_idle) { if (!sdp->sd_log_idle) {
for (;;) { for (;;) {
gfs2_ail1_start(sdp); gfs2_ail1_start(sdp);
...@@ -774,16 +831,17 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, ...@@ -774,16 +831,17 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
} }
atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */ atomic_dec(&sdp->sd_log_blks_free); /* Adjust for unreserved buffer */
trace_gfs2_log_blocks(sdp, -1); trace_gfs2_log_blocks(sdp, -1);
log_write_header(sdp, 0); log_write_header(sdp, flags);
sdp->sd_log_head = sdp->sd_log_flush_head; sdp->sd_log_head = sdp->sd_log_flush_head;
} }
if (type == SHUTDOWN_FLUSH || type == FREEZE_FLUSH) if (flags & (GFS2_LOG_HEAD_FLUSH_SHUTDOWN |
GFS2_LOG_HEAD_FLUSH_FREEZE))
gfs2_log_shutdown(sdp); gfs2_log_shutdown(sdp);
if (type == FREEZE_FLUSH) if (flags & GFS2_LOG_HEAD_FLUSH_FREEZE)
atomic_set(&sdp->sd_freeze_state, SFS_FROZEN); atomic_set(&sdp->sd_freeze_state, SFS_FROZEN);
} }
trace_gfs2_log_flush(sdp, 0); trace_gfs2_log_flush(sdp, 0, flags);
up_write(&sdp->sd_log_flush_lock); up_write(&sdp->sd_log_flush_lock);
kfree(tr); kfree(tr);
...@@ -879,7 +937,7 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp) ...@@ -879,7 +937,7 @@ void gfs2_log_shutdown(struct gfs2_sbd *sdp)
sdp->sd_log_flush_head = sdp->sd_log_head; sdp->sd_log_flush_head = sdp->sd_log_head;
log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT); log_write_header(sdp, GFS2_LOG_HEAD_UNMOUNT | GFS2_LFC_SHUTDOWN);
gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail); gfs2_assert_warn(sdp, sdp->sd_log_head == sdp->sd_log_tail);
gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list)); gfs2_assert_warn(sdp, list_empty(&sdp->sd_ail2_list));
...@@ -935,7 +993,8 @@ int gfs2_logd(void *data) ...@@ -935,7 +993,8 @@ int gfs2_logd(void *data)
did_flush = false; did_flush = false;
if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
gfs2_ail1_empty(sdp); gfs2_ail1_empty(sdp);
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_LOGD_JFLUSH_REQD);
did_flush = true; did_flush = true;
} }
...@@ -943,7 +1002,8 @@ int gfs2_logd(void *data) ...@@ -943,7 +1002,8 @@ int gfs2_logd(void *data)
gfs2_ail1_start(sdp); gfs2_ail1_start(sdp);
gfs2_ail1_wait(sdp); gfs2_ail1_wait(sdp);
gfs2_ail1_empty(sdp); gfs2_ail1_empty(sdp);
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_LOGD_AIL_FLUSH_REQD);
did_flush = true; did_flush = true;
} }
......
...@@ -65,14 +65,10 @@ extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct, ...@@ -65,14 +65,10 @@ extern unsigned int gfs2_struct2blk(struct gfs2_sbd *sdp, unsigned int nstruct,
extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks); extern void gfs2_log_release(struct gfs2_sbd *sdp, unsigned int blks);
extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks); extern int gfs2_log_reserve(struct gfs2_sbd *sdp, unsigned int blks);
enum gfs2_flush_type { extern void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd,
NORMAL_FLUSH = 0, u64 seq, u32 tail, u32 flags, int op_flags);
SYNC_FLUSH,
SHUTDOWN_FLUSH,
FREEZE_FLUSH
};
extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl,
enum gfs2_flush_type type); u32 type);
extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd); extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc); extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/list_sort.h> #include <linux/list_sort.h>
#include "dir.h"
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
#include "inode.h" #include "inode.h"
...@@ -138,7 +139,7 @@ static void gfs2_log_incr_head(struct gfs2_sbd *sdp) ...@@ -138,7 +139,7 @@ static void gfs2_log_incr_head(struct gfs2_sbd *sdp)
sdp->sd_log_flush_head = 0; sdp->sd_log_flush_head = 0;
} }
static u64 gfs2_log_bmap(struct gfs2_sbd *sdp) u64 gfs2_log_bmap(struct gfs2_sbd *sdp)
{ {
unsigned int lbn = sdp->sd_log_flush_head; unsigned int lbn = sdp->sd_log_flush_head;
struct gfs2_journal_extent *je; struct gfs2_journal_extent *je;
...@@ -161,7 +162,7 @@ static u64 gfs2_log_bmap(struct gfs2_sbd *sdp) ...@@ -161,7 +162,7 @@ static u64 gfs2_log_bmap(struct gfs2_sbd *sdp)
* @bvec: The bio_vec * @bvec: The bio_vec
* @error: The i/o status * @error: The i/o status
* *
* This finds the relavent buffers and unlocks then and sets the * This finds the relevant buffers and unlocks them and sets the
* error flag according to the status of the i/o request. This is * error flag according to the status of the i/o request. This is
* used when the log is writing data which has an in-place version * used when the log is writing data which has an in-place version
* that is pinned in the pagecache. * that is pinned in the pagecache.
...@@ -306,23 +307,22 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno) ...@@ -306,23 +307,22 @@ static struct bio *gfs2_log_get_bio(struct gfs2_sbd *sdp, u64 blkno)
return gfs2_log_alloc_bio(sdp, blkno); return gfs2_log_alloc_bio(sdp, blkno);
} }
/** /**
* gfs2_log_write - write to log * gfs2_log_write - write to log
* @sdp: the filesystem * @sdp: the filesystem
* @page: the page to write * @page: the page to write
* @size: the size of the data to write * @size: the size of the data to write
* @offset: the offset within the page * @offset: the offset within the page
* @blkno: block number of the log entry
* *
* Try and add the page segment to the current bio. If that fails, * Try and add the page segment to the current bio. If that fails,
* submit the current bio to the device and create a new one, and * submit the current bio to the device and create a new one, and
* then add the page segment to that. * then add the page segment to that.
*/ */
static void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page, void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
unsigned size, unsigned offset) unsigned size, unsigned offset, u64 blkno)
{ {
u64 blkno = gfs2_log_bmap(sdp);
struct bio *bio; struct bio *bio;
int ret; int ret;
...@@ -348,7 +348,8 @@ static void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page, ...@@ -348,7 +348,8 @@ static void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh) static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh)
{ {
gfs2_log_write(sdp, bh->b_page, bh->b_size, bh_offset(bh)); gfs2_log_write(sdp, bh->b_page, bh->b_size, bh_offset(bh),
gfs2_log_bmap(sdp));
} }
/** /**
...@@ -365,7 +366,8 @@ static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh) ...@@ -365,7 +366,8 @@ static void gfs2_log_write_bh(struct gfs2_sbd *sdp, struct buffer_head *bh)
void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page) void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page)
{ {
struct super_block *sb = sdp->sd_vfs; struct super_block *sb = sdp->sd_vfs;
gfs2_log_write(sdp, page, sb->s_blocksize, 0); gfs2_log_write(sdp, page, sb->s_blocksize, 0,
gfs2_log_bmap(sdp));
} }
static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type, static struct page *gfs2_get_log_desc(struct gfs2_sbd *sdp, u32 ld_type,
......
...@@ -26,6 +26,9 @@ extern const struct gfs2_log_operations gfs2_revoke_lops; ...@@ -26,6 +26,9 @@ extern const struct gfs2_log_operations gfs2_revoke_lops;
extern const struct gfs2_log_operations gfs2_databuf_lops; extern const struct gfs2_log_operations gfs2_databuf_lops;
extern const struct gfs2_log_operations *gfs2_log_ops[]; extern const struct gfs2_log_operations *gfs2_log_ops[];
extern u64 gfs2_log_bmap(struct gfs2_sbd *sdp);
extern void gfs2_log_write(struct gfs2_sbd *sdp, struct page *page,
unsigned size, unsigned offset, u64 blkno);
extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page); extern void gfs2_log_write_page(struct gfs2_sbd *sdp, struct page *page);
extern void gfs2_log_flush_bio(struct gfs2_sbd *sdp, int op, int op_flags); extern void gfs2_log_flush_bio(struct gfs2_sbd *sdp, int op, int op_flags);
extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh); extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
......
...@@ -93,7 +93,7 @@ static int __init init_gfs2_fs(void) ...@@ -93,7 +93,7 @@ static int __init init_gfs2_fs(void)
error = gfs2_glock_init(); error = gfs2_glock_init();
if (error) if (error)
goto fail; goto fail_glock;
error = -ENOMEM; error = -ENOMEM;
gfs2_glock_cachep = kmem_cache_create("gfs2_glock", gfs2_glock_cachep = kmem_cache_create("gfs2_glock",
...@@ -101,7 +101,7 @@ static int __init init_gfs2_fs(void) ...@@ -101,7 +101,7 @@ static int __init init_gfs2_fs(void)
0, 0, 0, 0,
gfs2_init_glock_once); gfs2_init_glock_once);
if (!gfs2_glock_cachep) if (!gfs2_glock_cachep)
goto fail; goto fail_cachep1;
gfs2_glock_aspace_cachep = kmem_cache_create("gfs2_glock(aspace)", gfs2_glock_aspace_cachep = kmem_cache_create("gfs2_glock(aspace)",
sizeof(struct gfs2_glock) + sizeof(struct gfs2_glock) +
...@@ -109,7 +109,7 @@ static int __init init_gfs2_fs(void) ...@@ -109,7 +109,7 @@ static int __init init_gfs2_fs(void)
0, 0, gfs2_init_gl_aspace_once); 0, 0, gfs2_init_gl_aspace_once);
if (!gfs2_glock_aspace_cachep) if (!gfs2_glock_aspace_cachep)
goto fail; goto fail_cachep2;
gfs2_inode_cachep = kmem_cache_create("gfs2_inode", gfs2_inode_cachep = kmem_cache_create("gfs2_inode",
sizeof(struct gfs2_inode), sizeof(struct gfs2_inode),
...@@ -118,107 +118,105 @@ static int __init init_gfs2_fs(void) ...@@ -118,107 +118,105 @@ static int __init init_gfs2_fs(void)
SLAB_ACCOUNT, SLAB_ACCOUNT,
gfs2_init_inode_once); gfs2_init_inode_once);
if (!gfs2_inode_cachep) if (!gfs2_inode_cachep)
goto fail; goto fail_cachep3;
gfs2_bufdata_cachep = kmem_cache_create("gfs2_bufdata", gfs2_bufdata_cachep = kmem_cache_create("gfs2_bufdata",
sizeof(struct gfs2_bufdata), sizeof(struct gfs2_bufdata),
0, 0, NULL); 0, 0, NULL);
if (!gfs2_bufdata_cachep) if (!gfs2_bufdata_cachep)
goto fail; goto fail_cachep4;
gfs2_rgrpd_cachep = kmem_cache_create("gfs2_rgrpd", gfs2_rgrpd_cachep = kmem_cache_create("gfs2_rgrpd",
sizeof(struct gfs2_rgrpd), sizeof(struct gfs2_rgrpd),
0, 0, NULL); 0, 0, NULL);
if (!gfs2_rgrpd_cachep) if (!gfs2_rgrpd_cachep)
goto fail; goto fail_cachep5;
gfs2_quotad_cachep = kmem_cache_create("gfs2_quotad", gfs2_quotad_cachep = kmem_cache_create("gfs2_quotad",
sizeof(struct gfs2_quota_data), sizeof(struct gfs2_quota_data),
0, 0, NULL); 0, 0, NULL);
if (!gfs2_quotad_cachep) if (!gfs2_quotad_cachep)
goto fail; goto fail_cachep6;
gfs2_qadata_cachep = kmem_cache_create("gfs2_qadata", gfs2_qadata_cachep = kmem_cache_create("gfs2_qadata",
sizeof(struct gfs2_qadata), sizeof(struct gfs2_qadata),
0, 0, NULL); 0, 0, NULL);
if (!gfs2_qadata_cachep) if (!gfs2_qadata_cachep)
goto fail; goto fail_cachep7;
error = register_shrinker(&gfs2_qd_shrinker); error = register_shrinker(&gfs2_qd_shrinker);
if (error) if (error)
goto fail; goto fail_shrinker;
error = register_filesystem(&gfs2_fs_type); error = register_filesystem(&gfs2_fs_type);
if (error) if (error)
goto fail; goto fail_fs1;
error = register_filesystem(&gfs2meta_fs_type); error = register_filesystem(&gfs2meta_fs_type);
if (error) if (error)
goto fail_unregister; goto fail_fs2;
error = -ENOMEM; error = -ENOMEM;
gfs_recovery_wq = alloc_workqueue("gfs_recovery", gfs_recovery_wq = alloc_workqueue("gfs_recovery",
WQ_MEM_RECLAIM | WQ_FREEZABLE, 0); WQ_MEM_RECLAIM | WQ_FREEZABLE, 0);
if (!gfs_recovery_wq) if (!gfs_recovery_wq)
goto fail_wq; goto fail_wq1;
gfs2_control_wq = alloc_workqueue("gfs2_control", gfs2_control_wq = alloc_workqueue("gfs2_control",
WQ_UNBOUND | WQ_FREEZABLE, 0); WQ_UNBOUND | WQ_FREEZABLE, 0);
if (!gfs2_control_wq) if (!gfs2_control_wq)
goto fail_recovery; goto fail_wq2;
gfs2_freeze_wq = alloc_workqueue("freeze_workqueue", 0, 0); gfs2_freeze_wq = alloc_workqueue("freeze_workqueue", 0, 0);
if (!gfs2_freeze_wq) if (!gfs2_freeze_wq)
goto fail_control; goto fail_wq3;
gfs2_page_pool = mempool_create_page_pool(64, 0); gfs2_page_pool = mempool_create_page_pool(64, 0);
if (!gfs2_page_pool) if (!gfs2_page_pool)
goto fail_freeze; goto fail_mempool;
gfs2_register_debugfs(); error = gfs2_register_debugfs();
if (error)
goto fail_debugfs;
pr_info("GFS2 installed\n"); pr_info("GFS2 installed\n");
return 0; return 0;
fail_freeze: fail_debugfs:
mempool_destroy(gfs2_page_pool);
fail_mempool:
destroy_workqueue(gfs2_freeze_wq); destroy_workqueue(gfs2_freeze_wq);
fail_control: fail_wq3:
destroy_workqueue(gfs2_control_wq); destroy_workqueue(gfs2_control_wq);
fail_recovery: fail_wq2:
destroy_workqueue(gfs_recovery_wq); destroy_workqueue(gfs_recovery_wq);
fail_wq: fail_wq1:
unregister_filesystem(&gfs2meta_fs_type); unregister_filesystem(&gfs2meta_fs_type);
fail_unregister: fail_fs2:
unregister_filesystem(&gfs2_fs_type); unregister_filesystem(&gfs2_fs_type);
fail: fail_fs1:
list_lru_destroy(&gfs2_qd_lru);
fail_lru:
unregister_shrinker(&gfs2_qd_shrinker); unregister_shrinker(&gfs2_qd_shrinker);
gfs2_glock_exit(); fail_shrinker:
if (gfs2_qadata_cachep)
kmem_cache_destroy(gfs2_qadata_cachep); kmem_cache_destroy(gfs2_qadata_cachep);
fail_cachep7:
if (gfs2_quotad_cachep)
kmem_cache_destroy(gfs2_quotad_cachep); kmem_cache_destroy(gfs2_quotad_cachep);
fail_cachep6:
if (gfs2_rgrpd_cachep)
kmem_cache_destroy(gfs2_rgrpd_cachep); kmem_cache_destroy(gfs2_rgrpd_cachep);
fail_cachep5:
if (gfs2_bufdata_cachep)
kmem_cache_destroy(gfs2_bufdata_cachep); kmem_cache_destroy(gfs2_bufdata_cachep);
fail_cachep4:
if (gfs2_inode_cachep)
kmem_cache_destroy(gfs2_inode_cachep); kmem_cache_destroy(gfs2_inode_cachep);
fail_cachep3:
if (gfs2_glock_aspace_cachep)
kmem_cache_destroy(gfs2_glock_aspace_cachep); kmem_cache_destroy(gfs2_glock_aspace_cachep);
fail_cachep2:
if (gfs2_glock_cachep)
kmem_cache_destroy(gfs2_glock_cachep); kmem_cache_destroy(gfs2_glock_cachep);
fail_cachep1:
gfs2_glock_exit();
fail_glock:
list_lru_destroy(&gfs2_qd_lru);
fail_lru:
gfs2_sys_uninit(); gfs2_sys_uninit();
return error; return error;
} }
......
...@@ -1382,7 +1382,7 @@ static void gfs2_kill_sb(struct super_block *sb) ...@@ -1382,7 +1382,7 @@ static void gfs2_kill_sb(struct super_block *sb)
return; return;
} }
gfs2_log_flush(sdp, NULL, SYNC_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_SYNC | GFS2_LFC_KILL_SB);
dput(sdp->sd_root_dir); dput(sdp->sd_root_dir);
dput(sdp->sd_master_dir); dput(sdp->sd_master_dir);
sdp->sd_root_dir = NULL; sdp->sd_root_dir = NULL;
......
...@@ -955,7 +955,8 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) ...@@ -955,7 +955,8 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
gfs2_glock_dq_uninit(&ghs[qx]); gfs2_glock_dq_uninit(&ghs[qx]);
inode_unlock(&ip->i_inode); inode_unlock(&ip->i_inode);
kfree(ghs); kfree(ghs);
gfs2_log_flush(ip->i_gl->gl_name.ln_sbd, ip->i_gl, NORMAL_FLUSH); gfs2_log_flush(ip->i_gl->gl_name.ln_sbd, ip->i_gl,
GFS2_LOG_HEAD_FLUSH_NORMAL | GFS2_LFC_DO_SYNC);
return error; return error;
} }
......
...@@ -14,12 +14,14 @@ ...@@ -14,12 +14,14 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/gfs2_ondisk.h> #include <linux/gfs2_ondisk.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/crc32c.h>
#include "gfs2.h" #include "gfs2.h"
#include "incore.h" #include "incore.h"
#include "bmap.h" #include "bmap.h"
#include "glock.h" #include "glock.h"
#include "glops.h" #include "glops.h"
#include "log.h"
#include "lops.h" #include "lops.h"
#include "meta_io.h" #include "meta_io.h"
#include "recovery.h" #include "recovery.h"
...@@ -117,22 +119,6 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd) ...@@ -117,22 +119,6 @@ void gfs2_revoke_clean(struct gfs2_jdesc *jd)
} }
} }
static int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf)
{
const struct gfs2_log_header *str = buf;
if (str->lh_header.mh_magic != cpu_to_be32(GFS2_MAGIC) ||
str->lh_header.mh_type != cpu_to_be32(GFS2_METATYPE_LH))
return 1;
lh->lh_sequence = be64_to_cpu(str->lh_sequence);
lh->lh_flags = be32_to_cpu(str->lh_flags);
lh->lh_tail = be32_to_cpu(str->lh_tail);
lh->lh_blkno = be32_to_cpu(str->lh_blkno);
lh->lh_hash = be32_to_cpu(str->lh_hash);
return 0;
}
/** /**
* get_log_header - read the log header for a given segment * get_log_header - read the log header for a given segment
* @jd: the journal * @jd: the journal
...@@ -150,29 +136,37 @@ static int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf) ...@@ -150,29 +136,37 @@ static int gfs2_log_header_in(struct gfs2_log_header_host *lh, const void *buf)
static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk, static int get_log_header(struct gfs2_jdesc *jd, unsigned int blk,
struct gfs2_log_header_host *head) struct gfs2_log_header_host *head)
{ {
struct gfs2_log_header *lh;
struct buffer_head *bh; struct buffer_head *bh;
struct gfs2_log_header_host uninitialized_var(lh); u32 hash, crc;
const u32 nothing = 0;
u32 hash;
int error; int error;
error = gfs2_replay_read_block(jd, blk, &bh); error = gfs2_replay_read_block(jd, blk, &bh);
if (error) if (error)
return error; return error;
lh = (void *)bh->b_data;
hash = crc32_le((u32)~0, bh->b_data, sizeof(struct gfs2_log_header) - hash = crc32(~0, lh, LH_V1_SIZE - 4);
sizeof(u32)); hash = ~crc32_le_shift(hash, 4); /* assume lh_hash is zero */
hash = crc32_le(hash, (unsigned char const *)&nothing, sizeof(nothing));
hash ^= (u32)~0;
error = gfs2_log_header_in(&lh, bh->b_data);
brelse(bh);
if (error || lh.lh_blkno != blk || lh.lh_hash != hash) crc = crc32c(~0, (void *)lh + LH_V1_SIZE + 4,
return 1; bh->b_size - LH_V1_SIZE - 4);
*head = lh; error = lh->lh_header.mh_magic != cpu_to_be32(GFS2_MAGIC) ||
lh->lh_header.mh_type != cpu_to_be32(GFS2_METATYPE_LH) ||
be32_to_cpu(lh->lh_blkno) != blk ||
be32_to_cpu(lh->lh_hash) != hash ||
(lh->lh_crc != 0 && be32_to_cpu(lh->lh_crc) != crc);
return 0; brelse(bh);
if (!error) {
head->lh_sequence = be64_to_cpu(lh->lh_sequence);
head->lh_flags = be32_to_cpu(lh->lh_flags);
head->lh_tail = be32_to_cpu(lh->lh_tail);
head->lh_blkno = be32_to_cpu(lh->lh_blkno);
}
return error;
} }
/** /**
...@@ -370,62 +364,22 @@ static int foreach_descriptor(struct gfs2_jdesc *jd, unsigned int start, ...@@ -370,62 +364,22 @@ static int foreach_descriptor(struct gfs2_jdesc *jd, unsigned int start,
/** /**
* clean_journal - mark a dirty journal as being clean * clean_journal - mark a dirty journal as being clean
* @sdp: the filesystem
* @jd: the journal * @jd: the journal
* @gl: the journal's glock
* @head: the head journal to start from * @head: the head journal to start from
* *
* Returns: errno * Returns: errno
*/ */
static int clean_journal(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head) static void clean_journal(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head)
{ {
struct gfs2_inode *ip = GFS2_I(jd->jd_inode);
struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode);
unsigned int lblock;
struct gfs2_log_header *lh;
u32 hash;
struct buffer_head *bh;
int error;
struct buffer_head bh_map = { .b_state = 0, .b_blocknr = 0 };
lblock = head->lh_blkno;
gfs2_replay_incr_blk(jd, &lblock);
bh_map.b_size = 1 << ip->i_inode.i_blkbits;
error = gfs2_block_map(&ip->i_inode, lblock, &bh_map, 0);
if (error)
return error;
if (!bh_map.b_blocknr) {
gfs2_consist_inode(ip);
return -EIO;
}
bh = sb_getblk(sdp->sd_vfs, bh_map.b_blocknr);
lock_buffer(bh);
memset(bh->b_data, 0, bh->b_size);
set_buffer_uptodate(bh);
clear_buffer_dirty(bh);
unlock_buffer(bh);
lh = (struct gfs2_log_header *)bh->b_data;
memset(lh, 0, sizeof(struct gfs2_log_header));
lh->lh_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
lh->lh_header.mh_type = cpu_to_be32(GFS2_METATYPE_LH);
lh->lh_header.__pad0 = cpu_to_be64(0);
lh->lh_header.mh_format = cpu_to_be32(GFS2_FORMAT_LH);
lh->lh_header.mh_jid = cpu_to_be32(sdp->sd_jdesc->jd_jid);
lh->lh_sequence = cpu_to_be64(head->lh_sequence + 1);
lh->lh_flags = cpu_to_be32(GFS2_LOG_HEAD_UNMOUNT);
lh->lh_blkno = cpu_to_be32(lblock);
hash = gfs2_disk_hash((const char *)lh, sizeof(struct gfs2_log_header));
lh->lh_hash = cpu_to_be32(hash);
set_buffer_dirty(bh);
if (sync_dirty_buffer(bh))
gfs2_io_error_bh(sdp, bh);
brelse(bh);
return error; sdp->sd_log_flush_head = head->lh_blkno;
gfs2_replay_incr_blk(jd, &sdp->sd_log_flush_head);
gfs2_write_log_header(sdp, jd, head->lh_sequence + 1, 0,
GFS2_LOG_HEAD_UNMOUNT | GFS2_LOG_HEAD_RECOVERY,
REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC);
} }
...@@ -552,9 +506,7 @@ void gfs2_recover_func(struct work_struct *work) ...@@ -552,9 +506,7 @@ void gfs2_recover_func(struct work_struct *work)
goto fail_gunlock_thaw; goto fail_gunlock_thaw;
} }
error = clean_journal(jd, &head); clean_journal(jd, &head);
if (error)
goto fail_gunlock_thaw;
gfs2_glock_dq_uninit(&thaw_gh); gfs2_glock_dq_uninit(&thaw_gh);
t = DIV_ROUND_UP(jiffies - t, HZ); t = DIV_ROUND_UP(jiffies - t, HZ);
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "log.h" #include "log.h"
#include "inode.h" #include "inode.h"
#include "trace_gfs2.h" #include "trace_gfs2.h"
#include "dir.h"
#define BFITNOENT ((u32)~0) #define BFITNOENT ((u32)~0)
#define NO_BLOCK ((u64)~0) #define NO_BLOCK ((u64)~0)
...@@ -489,6 +490,13 @@ void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd) ...@@ -489,6 +490,13 @@ void gfs2_rgrp_verify(struct gfs2_rgrpd *rgd)
* @blk: The data block number * @blk: The data block number
* @exact: True if this needs to be an exact match * @exact: True if this needs to be an exact match
* *
* The @exact argument should be set to true by most callers. The exception
* is when we need to match blocks which are not represented by the rgrp
* bitmap, but which are part of the rgrp (i.e. padding blocks) which are
* there for alignment purposes. Another way of looking at it is that @exact
* matches only valid data/metadata blocks, but with @exact false, it will
* match any block within the extent of the rgrp.
*
* Returns: The resource group, or NULL if not found * Returns: The resource group, or NULL if not found
*/ */
...@@ -1040,17 +1048,30 @@ static void gfs2_rgrp_in(struct gfs2_rgrpd *rgd, const void *buf) ...@@ -1040,17 +1048,30 @@ static void gfs2_rgrp_in(struct gfs2_rgrpd *rgd, const void *buf)
rgd->rd_free = be32_to_cpu(str->rg_free); rgd->rd_free = be32_to_cpu(str->rg_free);
rgd->rd_dinodes = be32_to_cpu(str->rg_dinodes); rgd->rd_dinodes = be32_to_cpu(str->rg_dinodes);
rgd->rd_igeneration = be64_to_cpu(str->rg_igeneration); rgd->rd_igeneration = be64_to_cpu(str->rg_igeneration);
/* rd_data0, rd_data and rd_bitbytes already set from rindex */
} }
static void gfs2_rgrp_out(struct gfs2_rgrpd *rgd, void *buf) static void gfs2_rgrp_out(struct gfs2_rgrpd *rgd, void *buf)
{ {
struct gfs2_rgrpd *next = gfs2_rgrpd_get_next(rgd);
struct gfs2_rgrp *str = buf; struct gfs2_rgrp *str = buf;
u32 crc;
str->rg_flags = cpu_to_be32(rgd->rd_flags & ~GFS2_RDF_MASK); str->rg_flags = cpu_to_be32(rgd->rd_flags & ~GFS2_RDF_MASK);
str->rg_free = cpu_to_be32(rgd->rd_free); str->rg_free = cpu_to_be32(rgd->rd_free);
str->rg_dinodes = cpu_to_be32(rgd->rd_dinodes); str->rg_dinodes = cpu_to_be32(rgd->rd_dinodes);
str->__pad = cpu_to_be32(0); if (next == NULL)
str->rg_skip = 0;
else if (next->rd_addr > rgd->rd_addr)
str->rg_skip = cpu_to_be32(next->rd_addr - rgd->rd_addr);
str->rg_igeneration = cpu_to_be64(rgd->rd_igeneration); str->rg_igeneration = cpu_to_be64(rgd->rd_igeneration);
str->rg_data0 = cpu_to_be64(rgd->rd_data0);
str->rg_data = cpu_to_be32(rgd->rd_data);
str->rg_bitbytes = cpu_to_be32(rgd->rd_bitbytes);
str->rg_crc = 0;
crc = gfs2_disk_hash(buf, sizeof(struct gfs2_rgrp));
str->rg_crc = cpu_to_be32(crc);
memset(&str->rg_reserved, 0, sizeof(str->rg_reserved)); memset(&str->rg_reserved, 0, sizeof(str->rg_reserved));
} }
...@@ -1318,7 +1339,7 @@ int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset, ...@@ -1318,7 +1339,7 @@ int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
fail: fail:
if (sdp->sd_args.ar_discard) if (sdp->sd_args.ar_discard)
fs_warn(sdp, "error %d on discard request, turning discards off for this filesystem", rv); fs_warn(sdp, "error %d on discard request, turning discards off for this filesystem\n", rv);
sdp->sd_args.ar_discard = 0; sdp->sd_args.ar_discard = 0;
return -EIO; return -EIO;
} }
...@@ -2072,7 +2093,8 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, struct gfs2_alloc_parms *ap) ...@@ -2072,7 +2093,8 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, struct gfs2_alloc_parms *ap)
} }
/* Flushing the log may release space */ /* Flushing the log may release space */
if (loops == 2) if (loops == 2)
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_INPLACE_RESERVE);
} }
return -ENOSPC; return -ENOSPC;
...@@ -2453,12 +2475,12 @@ void gfs2_unlink_di(struct inode *inode) ...@@ -2453,12 +2475,12 @@ void gfs2_unlink_di(struct inode *inode)
update_rgrp_lvb_unlinked(rgd, 1); update_rgrp_lvb_unlinked(rgd, 1);
} }
static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
{ {
struct gfs2_sbd *sdp = rgd->rd_sbd; struct gfs2_sbd *sdp = rgd->rd_sbd;
struct gfs2_rgrpd *tmp_rgd; struct gfs2_rgrpd *tmp_rgd;
tmp_rgd = rgblk_free(sdp, blkno, 1, GFS2_BLKST_FREE); tmp_rgd = rgblk_free(sdp, ip->i_no_addr, 1, GFS2_BLKST_FREE);
if (!tmp_rgd) if (!tmp_rgd)
return; return;
gfs2_assert_withdraw(sdp, rgd == tmp_rgd); gfs2_assert_withdraw(sdp, rgd == tmp_rgd);
...@@ -2474,12 +2496,6 @@ static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno) ...@@ -2474,12 +2496,6 @@ static void gfs2_free_uninit_di(struct gfs2_rgrpd *rgd, u64 blkno)
update_rgrp_lvb_unlinked(rgd, -1); update_rgrp_lvb_unlinked(rgd, -1);
gfs2_statfs_change(sdp, 0, +1, -1); gfs2_statfs_change(sdp, 0, +1, -1);
}
void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
{
gfs2_free_uninit_di(rgd, ip->i_no_addr);
trace_gfs2_block_alloc(ip, rgd, ip->i_no_addr, 1, GFS2_BLKST_FREE); trace_gfs2_block_alloc(ip, rgd, ip->i_no_addr, 1, GFS2_BLKST_FREE);
gfs2_quota_change(ip, -1, ip->i_inode.i_uid, ip->i_inode.i_gid); gfs2_quota_change(ip, -1, ip->i_inode.i_uid, ip->i_inode.i_gid);
gfs2_meta_wipe(ip, ip->i_no_addr, 1); gfs2_meta_wipe(ip, ip->i_no_addr, 1);
......
...@@ -757,7 +757,9 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -757,7 +757,9 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
bool flush_all = (wbc->sync_mode == WB_SYNC_ALL || gfs2_is_jdata(ip)); bool flush_all = (wbc->sync_mode == WB_SYNC_ALL || gfs2_is_jdata(ip));
if (flush_all) if (flush_all)
gfs2_log_flush(GFS2_SB(inode), ip->i_gl, NORMAL_FLUSH); gfs2_log_flush(GFS2_SB(inode), ip->i_gl,
GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_WRITE_INODE);
if (bdi->wb.dirty_exceeded) if (bdi->wb.dirty_exceeded)
gfs2_ail1_flush(sdp, wbc); gfs2_ail1_flush(sdp, wbc);
else else
...@@ -766,6 +768,12 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc) ...@@ -766,6 +768,12 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
ret = filemap_fdatawait(metamapping); ret = filemap_fdatawait(metamapping);
if (ret) if (ret)
mark_inode_dirty_sync(inode); mark_inode_dirty_sync(inode);
else {
spin_lock(&inode->i_lock);
if (!(inode->i_flags & I_DIRTY))
gfs2_ordered_del_inode(ip);
spin_unlock(&inode->i_lock);
}
return ret; return ret;
} }
...@@ -853,7 +861,8 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp) ...@@ -853,7 +861,8 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
gfs2_quota_sync(sdp->sd_vfs, 0); gfs2_quota_sync(sdp->sd_vfs, 0);
gfs2_statfs_sync(sdp->sd_vfs, 0); gfs2_statfs_sync(sdp->sd_vfs, 0);
gfs2_log_flush(sdp, NULL, SHUTDOWN_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_SHUTDOWN |
GFS2_LFC_MAKE_FS_RO);
wait_event(sdp->sd_reserving_log_wait, atomic_read(&sdp->sd_reserving_log) == 0); wait_event(sdp->sd_reserving_log_wait, atomic_read(&sdp->sd_reserving_log) == 0);
gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks); gfs2_assert_warn(sdp, atomic_read(&sdp->sd_log_blks_free) == sdp->sd_jdesc->jd_blocks);
...@@ -946,7 +955,8 @@ static int gfs2_sync_fs(struct super_block *sb, int wait) ...@@ -946,7 +955,8 @@ static int gfs2_sync_fs(struct super_block *sb, int wait)
gfs2_quota_sync(sb, -1); gfs2_quota_sync(sb, -1);
if (wait) if (wait)
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_SYNC_FS);
return sdp->sd_log_error; return sdp->sd_log_error;
} }
...@@ -1650,7 +1660,8 @@ static void gfs2_evict_inode(struct inode *inode) ...@@ -1650,7 +1660,8 @@ static void gfs2_evict_inode(struct inode *inode)
goto out_unlock; goto out_unlock;
out_truncate: out_truncate:
gfs2_log_flush(sdp, ip->i_gl, NORMAL_FLUSH); gfs2_log_flush(sdp, ip->i_gl, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_EVICT_INODE);
metamapping = gfs2_glock2aspace(ip->i_gl); metamapping = gfs2_glock2aspace(ip->i_gl);
if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) { if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) {
filemap_fdatawrite(metamapping); filemap_fdatawrite(metamapping);
......
...@@ -112,7 +112,7 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len) ...@@ -112,7 +112,7 @@ static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
} }
if (error) { if (error) {
fs_warn(sdp, "freeze %d error %d", n, error); fs_warn(sdp, "freeze %d error %d\n", n, error);
return error; return error;
} }
...@@ -679,7 +679,7 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp) ...@@ -679,7 +679,7 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
sysfs_remove_group(&sdp->sd_kobj, &tune_group); sysfs_remove_group(&sdp->sd_kobj, &tune_group);
fail_reg: fail_reg:
free_percpu(sdp->sd_lkstats); free_percpu(sdp->sd_lkstats);
fs_err(sdp, "error %d adding sysfs files", error); fs_err(sdp, "error %d adding sysfs files\n", error);
if (sysfs_frees_sdp) if (sysfs_frees_sdp)
kobject_put(&sdp->sd_kobj); kobject_put(&sdp->sd_kobj);
else else
......
...@@ -353,26 +353,29 @@ TRACE_EVENT(gfs2_pin, ...@@ -353,26 +353,29 @@ TRACE_EVENT(gfs2_pin,
/* Flushing the log */ /* Flushing the log */
TRACE_EVENT(gfs2_log_flush, TRACE_EVENT(gfs2_log_flush,
TP_PROTO(const struct gfs2_sbd *sdp, int start), TP_PROTO(const struct gfs2_sbd *sdp, int start, u32 flags),
TP_ARGS(sdp, start), TP_ARGS(sdp, start, flags),
TP_STRUCT__entry( TP_STRUCT__entry(
__field( dev_t, dev ) __field( dev_t, dev )
__field( int, start ) __field( int, start )
__field( u64, log_seq ) __field( u64, log_seq )
__field( u32, flags )
), ),
TP_fast_assign( TP_fast_assign(
__entry->dev = sdp->sd_vfs->s_dev; __entry->dev = sdp->sd_vfs->s_dev;
__entry->start = start; __entry->start = start;
__entry->log_seq = sdp->sd_log_sequence; __entry->log_seq = sdp->sd_log_sequence;
__entry->flags = flags;
), ),
TP_printk("%u,%u log flush %s %llu", TP_printk("%u,%u log flush %s %llu %llx",
MAJOR(__entry->dev), MINOR(__entry->dev), MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->start ? "start" : "end", __entry->start ? "start" : "end",
(unsigned long long)__entry->log_seq) (unsigned long long)__entry->log_seq,
(unsigned long long)__entry->flags)
); );
/* Reserving/releasing blocks in the log */ /* Reserving/releasing blocks in the log */
......
...@@ -92,7 +92,6 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) ...@@ -92,7 +92,6 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
s64 nbuf; s64 nbuf;
int alloced = test_bit(TR_ALLOCED, &tr->tr_flags); int alloced = test_bit(TR_ALLOCED, &tr->tr_flags);
BUG_ON(!tr);
current->journal_info = NULL; current->journal_info = NULL;
if (!test_bit(TR_TOUCHED, &tr->tr_flags)) { if (!test_bit(TR_TOUCHED, &tr->tr_flags)) {
...@@ -118,7 +117,8 @@ void gfs2_trans_end(struct gfs2_sbd *sdp) ...@@ -118,7 +117,8 @@ void gfs2_trans_end(struct gfs2_sbd *sdp)
up_read(&sdp->sd_log_flush_lock); up_read(&sdp->sd_log_flush_lock);
if (sdp->sd_vfs->s_flags & SB_SYNCHRONOUS) if (sdp->sd_vfs->s_flags & SB_SYNCHRONOUS)
gfs2_log_flush(sdp, NULL, NORMAL_FLUSH); gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_NORMAL |
GFS2_LFC_TRANS_END);
if (alloced) if (alloced)
sb_end_intwrite(sdp->sd_vfs); sb_end_intwrite(sdp->sd_vfs);
} }
......
...@@ -187,10 +187,19 @@ struct gfs2_rgrp { ...@@ -187,10 +187,19 @@ struct gfs2_rgrp {
__be32 rg_flags; __be32 rg_flags;
__be32 rg_free; __be32 rg_free;
__be32 rg_dinodes; __be32 rg_dinodes;
union {
__be32 __pad; __be32 __pad;
__be32 rg_skip; /* Distance to the next rgrp in fs blocks */
};
__be64 rg_igeneration; __be64 rg_igeneration;
/* The following 3 fields are duplicated from gfs2_rindex to reduce
__u8 rg_reserved[80]; /* Several fields from gfs1 now reserved */ reliance on the rindex */
__be64 rg_data0; /* First data location */
__be32 rg_data; /* Number of data blocks in rgrp */
__be32 rg_bitbytes; /* Number of bytes in data bitmaps */
__be32 rg_crc; /* crc32 of the structure with this field 0 */
__u8 rg_reserved[60]; /* Several fields from gfs1 now reserved */
}; };
/* /*
...@@ -395,6 +404,35 @@ struct gfs2_ea_header { ...@@ -395,6 +404,35 @@ struct gfs2_ea_header {
*/ */
#define GFS2_LOG_HEAD_UNMOUNT 0x00000001 /* log is clean */ #define GFS2_LOG_HEAD_UNMOUNT 0x00000001 /* log is clean */
#define GFS2_LOG_HEAD_FLUSH_NORMAL 0x00000002 /* normal log flush */
#define GFS2_LOG_HEAD_FLUSH_SYNC 0x00000004 /* Sync log flush */
#define GFS2_LOG_HEAD_FLUSH_SHUTDOWN 0x00000008 /* Shutdown log flush */
#define GFS2_LOG_HEAD_FLUSH_FREEZE 0x00000010 /* Freeze flush */
#define GFS2_LOG_HEAD_RECOVERY 0x00000020 /* Journal recovery */
#define GFS2_LOG_HEAD_USERSPACE 0x80000000 /* Written by gfs2-utils */
/* Log flush callers */
#define GFS2_LFC_SHUTDOWN 0x00000100
#define GFS2_LFC_JDATA_WPAGES 0x00000200
#define GFS2_LFC_SET_FLAGS 0x00000400
#define GFS2_LFC_AIL_EMPTY_GL 0x00000800
#define GFS2_LFC_AIL_FLUSH 0x00001000
#define GFS2_LFC_RGRP_GO_SYNC 0x00002000
#define GFS2_LFC_INODE_GO_SYNC 0x00004000
#define GFS2_LFC_INODE_GO_INVAL 0x00008000
#define GFS2_LFC_FREEZE_GO_SYNC 0x00010000
#define GFS2_LFC_KILL_SB 0x00020000
#define GFS2_LFC_DO_SYNC 0x00040000
#define GFS2_LFC_INPLACE_RESERVE 0x00080000
#define GFS2_LFC_WRITE_INODE 0x00100000
#define GFS2_LFC_MAKE_FS_RO 0x00200000
#define GFS2_LFC_SYNC_FS 0x00400000
#define GFS2_LFC_EVICT_INODE 0x00800000
#define GFS2_LFC_TRANS_END 0x01000000
#define GFS2_LFC_LOGD_JFLUSH_REQD 0x02000000
#define GFS2_LFC_LOGD_AIL_FLUSH_REQD 0x04000000
#define LH_V1_SIZE (offsetofend(struct gfs2_log_header, lh_hash))
struct gfs2_log_header { struct gfs2_log_header {
struct gfs2_meta_header lh_header; struct gfs2_meta_header lh_header;
...@@ -403,7 +441,21 @@ struct gfs2_log_header { ...@@ -403,7 +441,21 @@ struct gfs2_log_header {
__be32 lh_flags; /* GFS2_LOG_HEAD_... */ __be32 lh_flags; /* GFS2_LOG_HEAD_... */
__be32 lh_tail; /* Block number of log tail */ __be32 lh_tail; /* Block number of log tail */
__be32 lh_blkno; __be32 lh_blkno;
__be32 lh_hash; __be32 lh_hash; /* crc up to here with this field 0 */
/* Version 2 additional fields start here */
__be32 lh_crc; /* crc32c from lh_nsec to end of block */
__be32 lh_nsec; /* Nanoseconds of timestamp */
__be64 lh_sec; /* Seconds of timestamp */
__be64 lh_addr; /* Block addr of this log header (absolute) */
__be64 lh_jinode; /* Journal inode number */
__be64 lh_statfs_addr; /* Local statfs inode number */
__be64 lh_quota_addr; /* Local quota change inode number */
/* Statfs local changes (i.e. diff from global statfs) */
__be64 lh_local_total;
__be64 lh_local_free;
__be64 lh_local_dinodes;
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment