diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-21 14:42:59 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-09-21 14:42:59 -0700 |
commit | f7c3bf8fa7e5a8e45f4a8e82be6466157854b59b (patch) | |
tree | ace565b119da1c379f4873d38c205eac7740a6d3 /fs | |
parent | fbc246a12aac27f7b25a37f9398bb3bc552cec92 (diff) | |
parent | f0b444b349e33ae0d3dd93e25ca365482a5d17d4 (diff) | |
download | lwn-f7c3bf8fa7e5a8e45f4a8e82be6466157854b59b.tar.gz lwn-f7c3bf8fa7e5a8e45f4a8e82be6466157854b59b.zip |
Merge tag 'gfs2-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2
Pull gfs2 updates from Andreas Gruenbacher:
- Use asynchronous glocks and timeouts to recover from deadlocks during
rename and exchange: the lock ordering constraints the vfs uses are
not sufficient to prevent deadlocks across multiple nodes.
- Add support for IOMAP_ZERO and use iomap_zero_range to replace gfs2
specific code.
- Various other minor fixes and cleanups.
* tag 'gfs2-for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
gfs2: clear buf_in_tr when ending a transaction in sweep_bh_for_rgrps
gfs2: Improve mmap write vs. truncate consistency
gfs2: Use async glocks for rename
gfs2: create function gfs2_glock_update_hold_time
gfs2: separate holder for rgrps in gfs2_rename
gfs2: Delete an unnecessary check before brelse()
gfs2: Minor PAGE_SIZE arithmetic cleanups
gfs2: Fix recovery slot bumping
gfs2: Fix possible fs name overflows
gfs2: untangle the logic in gfs2_drevalidate
gfs2: Always mark inode dirty in fallocate
gfs2: Minor gfs2_alloc_inode cleanup
gfs2: implement gfs2_block_zero_range using iomap_zero_range
gfs2: Add support for IOMAP_ZERO
gfs2: gfs2_iomap_begin cleanup
Diffstat (limited to 'fs')
-rw-r--r-- | fs/gfs2/aops.c | 4 | ||||
-rw-r--r-- | fs/gfs2/bmap.c | 193 | ||||
-rw-r--r-- | fs/gfs2/dentry.c | 45 | ||||
-rw-r--r-- | fs/gfs2/dir.c | 3 | ||||
-rw-r--r-- | fs/gfs2/dir.h | 3 | ||||
-rw-r--r-- | fs/gfs2/file.c | 9 | ||||
-rw-r--r-- | fs/gfs2/glock.c | 115 | ||||
-rw-r--r-- | fs/gfs2/glock.h | 6 | ||||
-rw-r--r-- | fs/gfs2/incore.h | 1 | ||||
-rw-r--r-- | fs/gfs2/inode.c | 63 | ||||
-rw-r--r-- | fs/gfs2/lock_dlm.c | 8 | ||||
-rw-r--r-- | fs/gfs2/ops_fstype.c | 1 | ||||
-rw-r--r-- | fs/gfs2/quota.c | 2 | ||||
-rw-r--r-- | fs/gfs2/rgrp.c | 2 | ||||
-rw-r--r-- | fs/gfs2/super.c | 14 | ||||
-rw-r--r-- | fs/gfs2/util.c | 2 |
16 files changed, 264 insertions, 207 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index f42048cc5454..b9fe975d7625 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -243,7 +243,7 @@ static int gfs2_write_jdata_pagevec(struct address_space *mapping, { struct inode *inode = mapping->host; struct gfs2_sbd *sdp = GFS2_SB(inode); - unsigned nrblocks = nr_pages * (PAGE_SIZE/inode->i_sb->s_blocksize); + unsigned nrblocks = nr_pages * (PAGE_SIZE >> inode->i_blkbits); int i; int ret; @@ -552,7 +552,7 @@ int gfs2_internal_read(struct gfs2_inode *ip, char *buf, loff_t *pos, unsigned size) { struct address_space *mapping = ip->i_inode.i_mapping; - unsigned long index = *pos / PAGE_SIZE; + unsigned long index = *pos >> PAGE_SHIFT; unsigned offset = *pos & (PAGE_SIZE - 1); unsigned copied = 0; unsigned amt; diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 4f8b5fd6c81f..f63df54a08c6 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1065,54 +1065,38 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); - unsigned int data_blocks = 0, ind_blocks = 0, rblocks; - bool unstuff, alloc_required; + bool unstuff; int ret; - ret = gfs2_write_lock(inode); - if (ret) - return ret; - unstuff = gfs2_is_stuffed(ip) && pos + length > gfs2_max_stuffed_size(ip); - ret = gfs2_iomap_get(inode, pos, length, flags, iomap, mp); - if (ret) - goto out_unlock; - - alloc_required = unstuff || iomap->type == IOMAP_HOLE; + if (unstuff || iomap->type == IOMAP_HOLE) { + unsigned int data_blocks, ind_blocks; + struct gfs2_alloc_parms ap = {}; + unsigned int rblocks; + struct gfs2_trans *tr; - if (alloc_required || gfs2_is_jdata(ip)) gfs2_write_calc_reserv(ip, iomap->length, &data_blocks, &ind_blocks); - - if (alloc_required) { - struct gfs2_alloc_parms ap = { - .target = data_blocks + ind_blocks - }; - + ap.target = data_blocks + ind_blocks; ret = gfs2_quota_lock_check(ip, &ap); if (ret) - goto out_unlock; + return ret; ret = gfs2_inplace_reserve(ip, &ap); if (ret) goto out_qunlock; - } - rblocks = RES_DINODE + ind_blocks; - if (gfs2_is_jdata(ip)) - rblocks += data_blocks; - if (ind_blocks || data_blocks) - rblocks += RES_STATFS + RES_QUOTA; - if (inode == sdp->sd_rindex) - rblocks += 2 * RES_STATFS; - if (alloc_required) + rblocks = RES_DINODE + ind_blocks; + if (gfs2_is_jdata(ip)) + rblocks += data_blocks; + if (ind_blocks || data_blocks) + rblocks += RES_STATFS + RES_QUOTA; + if (inode == sdp->sd_rindex) + rblocks += 2 * RES_STATFS; rblocks += gfs2_rg_blocks(ip, data_blocks + ind_blocks); - if (unstuff || iomap->type == IOMAP_HOLE) { - struct gfs2_trans *tr; - ret = gfs2_trans_begin(sdp, rblocks, iomap->length >> inode->i_blkbits); if (ret) @@ -1153,16 +1137,17 @@ static int gfs2_iomap_begin_write(struct inode *inode, loff_t pos, out_trans_end: gfs2_trans_end(sdp); out_trans_fail: - if (alloc_required) - gfs2_inplace_release(ip); + gfs2_inplace_release(ip); out_qunlock: - if (alloc_required) - gfs2_quota_unlock(ip); -out_unlock: - gfs2_write_unlock(inode); + gfs2_quota_unlock(ip); return ret; } +static inline bool gfs2_iomap_need_write_lock(unsigned flags) +{ + return (flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT); +} + static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, unsigned flags, struct iomap *iomap) { @@ -1173,20 +1158,43 @@ static int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, iomap->flags |= IOMAP_F_BUFFER_HEAD; trace_gfs2_iomap_start(ip, pos, length, flags); - if ((flags & IOMAP_WRITE) && !(flags & IOMAP_DIRECT)) { - ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); - } else { - ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); + if (gfs2_iomap_need_write_lock(flags)) { + ret = gfs2_write_lock(inode); + if (ret) + goto out; + } - /* - * Silently fall back to buffered I/O for stuffed files or if - * we've hot a hole (see gfs2_file_direct_write). - */ - if ((flags & IOMAP_WRITE) && (flags & IOMAP_DIRECT) && - iomap->type != IOMAP_MAPPED) - ret = -ENOTBLK; + ret = gfs2_iomap_get(inode, pos, length, flags, iomap, &mp); + if (ret) + goto out_unlock; + + switch(flags & (IOMAP_WRITE | IOMAP_ZERO)) { + case IOMAP_WRITE: + if (flags & IOMAP_DIRECT) { + /* + * Silently fall back to buffered I/O for stuffed files + * or if we've got a hole (see gfs2_file_direct_write). + */ + if (iomap->type != IOMAP_MAPPED) + ret = -ENOTBLK; + goto out_unlock; + } + break; + case IOMAP_ZERO: + if (iomap->type == IOMAP_HOLE) + goto out_unlock; + break; + default: + goto out_unlock; } + + ret = gfs2_iomap_begin_write(inode, pos, length, flags, iomap, &mp); + +out_unlock: + if (ret && gfs2_iomap_need_write_lock(flags)) + gfs2_write_unlock(inode); release_metapath(&mp); +out: trace_gfs2_iomap_end(ip, iomap, ret); return ret; } @@ -1197,8 +1205,18 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); - if ((flags & (IOMAP_WRITE | IOMAP_DIRECT)) != IOMAP_WRITE) - goto out; + switch (flags & (IOMAP_WRITE | IOMAP_ZERO)) { + case IOMAP_WRITE: + if (flags & IOMAP_DIRECT) + return 0; + break; + case IOMAP_ZERO: + if (iomap->type == IOMAP_HOLE) + return 0; + break; + default: + return 0; + } if (!gfs2_is_stuffed(ip)) gfs2_ordered_add_inode(ip); @@ -1231,8 +1249,8 @@ static int gfs2_iomap_end(struct inode *inode, loff_t pos, loff_t length, set_bit(GLF_DIRTY, &ip->i_gl->gl_flags); out_unlock: - gfs2_write_unlock(inode); -out: + if (gfs2_iomap_need_write_lock(flags)) + gfs2_write_unlock(inode); return 0; } @@ -1330,76 +1348,10 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi return ret; } -/** - * gfs2_block_zero_range - Deal with zeroing out data - * - * This is partly borrowed from ext3. - */ static int gfs2_block_zero_range(struct inode *inode, loff_t from, unsigned int length) { - struct address_space *mapping = inode->i_mapping; - struct gfs2_inode *ip = GFS2_I(inode); - unsigned long index = from >> PAGE_SHIFT; - unsigned offset = from & (PAGE_SIZE-1); - unsigned blocksize, iblock, pos; - struct buffer_head *bh; - struct page *page; - int err; - - page = find_or_create_page(mapping, index, GFP_NOFS); - if (!page) - return 0; - - blocksize = inode->i_sb->s_blocksize; - iblock = index << (PAGE_SHIFT - inode->i_sb->s_blocksize_bits); - - if (!page_has_buffers(page)) - create_empty_buffers(page, blocksize, 0); - - /* Find the buffer that contains "offset" */ - bh = page_buffers(page); - pos = blocksize; - while (offset >= pos) { - bh = bh->b_this_page; - iblock++; - pos += blocksize; - } - - err = 0; - - if (!buffer_mapped(bh)) { - gfs2_block_map(inode, iblock, bh, 0); - /* unmapped? It's a hole - nothing to do */ - if (!buffer_mapped(bh)) - goto unlock; - } - - /* Ok, it's mapped. Make sure it's up-to-date */ - if (PageUptodate(page)) - set_buffer_uptodate(bh); - - if (!buffer_uptodate(bh)) { - err = -EIO; - ll_rw_block(REQ_OP_READ, 0, 1, &bh); - wait_on_buffer(bh); - /* Uhhuh. Read error. Complain and punt. */ - if (!buffer_uptodate(bh)) - goto unlock; - err = 0; - } - - if (gfs2_is_jdata(ip)) - gfs2_trans_add_data(ip->i_gl, bh); - else - gfs2_ordered_add_inode(ip); - - zero_user(page, offset, length); - mark_buffer_dirty(bh); -unlock: - unlock_page(page); - put_page(page); - return err; + return iomap_zero_range(inode, from, length, NULL, &gfs2_iomap_ops); } #define GFS2_JTRUNC_REVOKES 8192 @@ -1680,6 +1632,7 @@ out_unlock: brelse(dibh); up_write(&ip->i_rw_mutex); gfs2_trans_end(sdp); + buf_in_tr = false; } gfs2_glock_dq_uninit(rd_gh); cond_resched(); @@ -2187,7 +2140,7 @@ static int do_grow(struct inode *inode, u64 size) if (error) goto do_end_trans; - i_size_write(inode, size); + truncate_setsize(inode, size); ip->i_inode.i_mtime = ip->i_inode.i_ctime = current_time(&ip->i_inode); gfs2_trans_add_meta(ip->i_gl, dibh); gfs2_dinode_out(ip, dibh->b_data); diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c index a7bb76e9a82b..2e215e8c3c88 100644 --- a/fs/gfs2/dentry.c +++ b/fs/gfs2/dentry.c @@ -38,7 +38,7 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags) struct inode *inode; struct gfs2_holder d_gh; struct gfs2_inode *ip = NULL; - int error; + int error, valid = 0; int had_lock = 0; if (flags & LOOKUP_RCU) @@ -51,53 +51,30 @@ static int gfs2_drevalidate(struct dentry *dentry, unsigned int flags) if (inode) { if (is_bad_inode(inode)) - goto invalid; + goto out; ip = GFS2_I(inode); } - if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) - goto valid; + if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) { + valid = 1; + goto out; + } had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL); if (!had_lock) { error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); if (error) - goto fail; - } - - error = gfs2_dir_check(d_inode(parent), &dentry->d_name, ip); - switch (error) { - case 0: - if (!inode) - goto invalid_gunlock; - break; - case -ENOENT: - if (!inode) - goto valid_gunlock; - goto invalid_gunlock; - default: - goto fail_gunlock; + goto out; } -valid_gunlock: - if (!had_lock) - gfs2_glock_dq_uninit(&d_gh); -valid: - dput(parent); - return 1; + error = gfs2_dir_check(d_inode(parent), &dentry->d_name, ip); + valid = inode ? !error : (error == -ENOENT); -invalid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); -invalid: +out: dput(parent); - return 0; - -fail_gunlock: - gfs2_glock_dq_uninit(&d_gh); -fail: - dput(parent); - return 0; + return valid; } static int gfs2_dhash(const struct dentry *dentry, struct qstr *str) diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 6f35d19eec25..eb9c0578978f 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1463,8 +1463,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, struct dir_context *ctx, sort_offset : entries, copied); out_free: for(i = 0; i < leaf; i++) - if (larr[i]) - brelse(larr[i]); + brelse(larr[i]); kvfree(larr); out: return error; diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h index 0ac2dc8564df..5b76480c17c9 100644 --- a/fs/gfs2/dir.h +++ b/fs/gfs2/dir.h @@ -32,8 +32,7 @@ extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename, const struct gfs2_inode *ip, struct gfs2_diradd *da); static inline void gfs2_dir_no_add(struct gfs2_diradd *da) { - if (da->bh) - brelse(da->bh); + brelse(da->bh); da->bh = NULL; } extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 52fa1ef8400b..997b326247e2 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1049,7 +1049,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t rblocks += data_blocks ? data_blocks : 1; error = gfs2_trans_begin(sdp, rblocks, - PAGE_SIZE/sdp->sd_sb.sb_bsize); + PAGE_SIZE >> inode->i_blkbits); if (error) goto out_trans_fail; @@ -1065,11 +1065,10 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t gfs2_quota_unlock(ip); } - if (!(mode & FALLOC_FL_KEEP_SIZE) && (pos + count) > inode->i_size) { + if (!(mode & FALLOC_FL_KEEP_SIZE) && (pos + count) > inode->i_size) i_size_write(inode, pos + count); - file_update_time(file); - mark_inode_dirty(inode); - } + file_update_time(file); + mark_inode_dirty(inode); if ((file->f_flags & O_DSYNC) || IS_SYNC(file->f_mapping->host)) return vfs_fsync_range(file, pos, pos + count - 1, diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index e23fb8b7b020..0290a22ebccf 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -305,6 +305,11 @@ static void gfs2_holder_wake(struct gfs2_holder *gh) clear_bit(HIF_WAIT, &gh->gh_iflags); smp_mb__after_atomic(); wake_up_bit(&gh->gh_iflags, HIF_WAIT); + if (gh->gh_flags & GL_ASYNC) { + struct gfs2_sbd *sdp = gh->gh_gl->gl_name.ln_sbd; + + wake_up(&sdp->sd_async_glock_wait); + } } /** @@ -931,6 +936,17 @@ void gfs2_holder_uninit(struct gfs2_holder *gh) gh->gh_ip = 0; } +static void gfs2_glock_update_hold_time(struct gfs2_glock *gl, + unsigned long start_time) +{ + /* Have we waited longer that a second? */ + if (time_after(jiffies, start_time + HZ)) { + /* Lengthen the minimum hold time. */ + gl->gl_hold_time = min(gl->gl_hold_time + GL_GLOCK_HOLD_INCR, + GL_GLOCK_MAX_HOLD); + } +} + /** * gfs2_glock_wait - wait on a glock acquisition * @gh: the glock holder @@ -940,18 +956,99 @@ void gfs2_holder_uninit(struct gfs2_holder *gh) int gfs2_glock_wait(struct gfs2_holder *gh) { - unsigned long time1 = jiffies; + unsigned long start_time = jiffies; might_sleep(); wait_on_bit(&gh->gh_iflags, HIF_WAIT, TASK_UNINTERRUPTIBLE); - if (time_after(jiffies, time1 + HZ)) /* have we waited > a second? */ - /* Lengthen the minimum hold time. */ - gh->gh_gl->gl_hold_time = min(gh->gh_gl->gl_hold_time + - GL_GLOCK_HOLD_INCR, - GL_GLOCK_MAX_HOLD); + gfs2_glock_update_hold_time(gh->gh_gl, start_time); return gh->gh_error; } +static int glocks_pending(unsigned int num_gh, struct gfs2_holder *ghs) +{ + int i; + + for (i = 0; i < num_gh; i++) + if (test_bit(HIF_WAIT, &ghs[i].gh_iflags)) + return 1; + return 0; +} + +/** + * gfs2_glock_async_wait - wait on multiple asynchronous glock acquisitions + * @num_gh: the number of holders in the array + * @ghs: the glock holder array + * + * Returns: 0 on success, meaning all glocks have been granted and are held. + * -ESTALE if the request timed out, meaning all glocks were released, + * and the caller should retry the operation. + */ + +int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs) +{ + struct gfs2_sbd *sdp = ghs[0].gh_gl->gl_name.ln_sbd; + int i, ret = 0, timeout = 0; + unsigned long start_time = jiffies; + bool keep_waiting; + + might_sleep(); + /* + * Total up the (minimum hold time * 2) of all glocks and use that to + * determine the max amount of time we should wait. + */ + for (i = 0; i < num_gh; i++) + timeout += ghs[i].gh_gl->gl_hold_time << 1; + +wait_for_dlm: + if (!wait_event_timeout(sdp->sd_async_glock_wait, + !glocks_pending(num_gh, ghs), timeout)) + ret = -ESTALE; /* request timed out. */ + + /* + * If dlm granted all our requests, we need to adjust the glock + * minimum hold time values according to how long we waited. + * + * If our request timed out, we need to repeatedly release any held + * glocks we acquired thus far to allow dlm to acquire the remaining + * glocks without deadlocking. We cannot currently cancel outstanding + * glock acquisitions. + * + * The HIF_WAIT bit tells us which requests still need a response from + * dlm. + * + * If dlm sent us any errors, we return the first error we find. + */ + keep_waiting = false; + for (i = 0; i < num_gh; i++) { + /* Skip holders we have already dequeued below. */ + if (!gfs2_holder_queued(&ghs[i])) + continue; + /* Skip holders with a pending DLM response. */ + if (test_bit(HIF_WAIT, &ghs[i].gh_iflags)) { + keep_waiting = true; + continue; + } + + if (test_bit(HIF_HOLDER, &ghs[i].gh_iflags)) { + if (ret == -ESTALE) + gfs2_glock_dq(&ghs[i]); + else + gfs2_glock_update_hold_time(ghs[i].gh_gl, + start_time); + } + if (!ret) + ret = ghs[i].gh_error; + } + + if (keep_waiting) + goto wait_for_dlm; + + /* + * At this point, we've either acquired all locks or released them all. + */ + return ret; +} + /** * handle_callback - process a demote request * @gl: the glock @@ -1018,9 +1115,9 @@ __acquires(&gl->gl_lockref.lock) struct gfs2_holder *gh2; int try_futile = 0; - BUG_ON(gh->gh_owner_pid == NULL); + GLOCK_BUG_ON(gl, gh->gh_owner_pid == NULL); if (test_and_set_bit(HIF_WAIT, &gh->gh_iflags)) - BUG(); + GLOCK_BUG_ON(gl, true); if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) { if (test_bit(GLF_LOCK, &gl->gl_flags)) @@ -1788,8 +1885,8 @@ void gfs2_dump_glock(struct seq_file *seq, struct gfs2_glock *gl, bool fsid) unsigned long long dtime; const struct gfs2_holder *gh; char gflags_buf[32]; - char fs_id_buf[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2]; struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + char fs_id_buf[sizeof(sdp->sd_fsname) + 7]; memset(fs_id_buf, 0, sizeof(fs_id_buf)); if (fsid && sdp) /* safety precaution */ diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index e4e0bed5257c..b8adaf80e4c5 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -190,6 +190,7 @@ extern void gfs2_holder_uninit(struct gfs2_holder *gh); extern int gfs2_glock_nq(struct gfs2_holder *gh); extern int gfs2_glock_poll(struct gfs2_holder *gh); extern int gfs2_glock_wait(struct gfs2_holder *gh); +extern int gfs2_glock_async_wait(unsigned int num_gh, struct gfs2_holder *ghs); extern void gfs2_glock_dq(struct gfs2_holder *gh); extern void gfs2_glock_dq_wait(struct gfs2_holder *gh); extern void gfs2_glock_dq_uninit(struct gfs2_holder *gh); @@ -260,6 +261,11 @@ static inline bool gfs2_holder_initialized(struct gfs2_holder *gh) return gh->gh_gl; } +static inline bool gfs2_holder_queued(struct gfs2_holder *gh) +{ + return !list_empty(&gh->gh_list); +} + /** * glock_set_object - set the gl_object field of a glock * @gl: the glock diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 7a993d7c022e..6b450065b9d5 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -725,6 +725,7 @@ struct gfs2_sbd { struct gfs2_glock *sd_freeze_gl; struct work_struct sd_freeze_work; wait_queue_head_t sd_glock_wait; + wait_queue_head_t sd_async_glock_wait; atomic_t sd_glock_disposal; struct completion sd_locking_init; struct completion sd_wdack; diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 2e2a8a2fb51d..e1e18fb587eb 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -1348,7 +1348,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, struct gfs2_inode *ip = GFS2_I(d_inode(odentry)); struct gfs2_inode *nip = NULL; struct gfs2_sbd *sdp = GFS2_SB(odir); - struct gfs2_holder ghs[5], r_gh; + struct gfs2_holder ghs[4], r_gh, rd_gh; struct gfs2_rgrpd *nrgd; unsigned int num_gh; int dir_rename = 0; @@ -1357,6 +1357,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, int error; gfs2_holder_mark_uninitialized(&r_gh); + gfs2_holder_mark_uninitialized(&rd_gh); if (d_really_is_positive(ndentry)) { nip = GFS2_I(d_inode(ndentry)); if (ip == nip) @@ -1387,24 +1388,19 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, } num_gh = 1; - gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); + gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, ghs); if (odip != ndip) { - gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE,GL_ASYNC, + ghs + num_gh); num_gh++; } - gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, ghs + num_gh); num_gh++; if (nip) { - gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, + ghs + num_gh); num_gh++; - /* grab the resource lock for unlink flag twiddling - * this is the case of the target file already existing - * so we unlink before doing the rename - */ - nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr, 1); - if (nrgd) - gfs2_holder_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh++); } for (x = 0; x < num_gh; x++) { @@ -1412,6 +1408,25 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, if (error) goto out_gunlock; } + error = gfs2_glock_async_wait(num_gh, ghs); + if (error) + goto out_gunlock; + + if (nip) { + /* Grab the resource group glock for unlink flag twiddling. + * This is the case where the target dinode already exists + * so we unlink before doing the rename. + */ + nrgd = gfs2_blk2rgrpd(sdp, nip->i_no_addr, 1); + if (!nrgd) { + error = -ENOENT; + goto out_gunlock; + } + error = gfs2_glock_nq_init(nrgd->rd_gl, LM_ST_EXCLUSIVE, 0, + &rd_gh); + if (error) + goto out_gunlock; + } error = -ENOENT; if (ip->i_inode.i_nlink == 0) @@ -1541,8 +1556,12 @@ out_gunlock_q: gfs2_quota_unlock(ndip); out_gunlock: gfs2_dir_no_add(&da); + if (gfs2_holder_initialized(&rd_gh)) + gfs2_glock_dq_uninit(&rd_gh); + while (x--) { - gfs2_glock_dq(ghs + x); + if (gfs2_holder_queued(ghs + x)) + gfs2_glock_dq(ghs + x); gfs2_holder_uninit(ghs + x); } out_gunlock_r: @@ -1572,7 +1591,7 @@ static int gfs2_exchange(struct inode *odir, struct dentry *odentry, struct gfs2_inode *oip = GFS2_I(odentry->d_inode); struct gfs2_inode *nip = GFS2_I(ndentry->d_inode); struct gfs2_sbd *sdp = GFS2_SB(odir); - struct gfs2_holder ghs[5], r_gh; + struct gfs2_holder ghs[4], r_gh; unsigned int num_gh; unsigned int x; umode_t old_mode = oip->i_inode.i_mode; @@ -1606,15 +1625,16 @@ static int gfs2_exchange(struct inode *odir, struct dentry *odentry, } num_gh = 1; - gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, 0, ghs); + gfs2_holder_init(odip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, ghs); if (odip != ndip) { - gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + gfs2_holder_init(ndip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, + ghs + num_gh); num_gh++; } - gfs2_holder_init(oip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + gfs2_holder_init(oip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, ghs + num_gh); num_gh++; - gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, 0, ghs + num_gh); + gfs2_holder_init(nip->i_gl, LM_ST_EXCLUSIVE, GL_ASYNC, ghs + num_gh); num_gh++; for (x = 0; x < num_gh; x++) { @@ -1623,6 +1643,10 @@ static int gfs2_exchange(struct inode *odir, struct dentry *odentry, goto out_gunlock; } + error = gfs2_glock_async_wait(num_gh, ghs); + if (error) + goto out_gunlock; + error = -ENOENT; if (oip->i_inode.i_nlink == 0 || nip->i_inode.i_nlink == 0) goto out_gunlock; @@ -1683,7 +1707,8 @@ out_end_trans: gfs2_trans_end(sdp); out_gunlock: while (x--) { - gfs2_glock_dq(ghs + x); + if (gfs2_holder_queued(ghs + x)) + gfs2_glock_dq(ghs + x); gfs2_holder_uninit(ghs + x); } out_gunlock_r: diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 4361804646d8..7c7197343ee2 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -1035,12 +1035,12 @@ static int set_recover_size(struct gfs2_sbd *sdp, struct dlm_slot *slots, } old_size = ls->ls_recover_size; - - if (old_size >= max_jid + 1) + new_size = old_size; + while (new_size < max_jid + 1) + new_size += RECOVER_SIZE_INC; + if (new_size == old_size) return 0; - new_size = old_size + RECOVER_SIZE_INC; - submit = kcalloc(new_size, sizeof(uint32_t), GFP_NOFS); result = kcalloc(new_size, sizeof(uint32_t), GFP_NOFS); if (!submit || !result) { diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 4a8e5a7310f0..f3fd5cd9d43f 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -87,6 +87,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) gfs2_tune_init(&sdp->sd_tune); init_waitqueue_head(&sdp->sd_glock_wait); + init_waitqueue_head(&sdp->sd_async_glock_wait); atomic_set(&sdp->sd_glock_disposal, 0); init_completion(&sdp->sd_locking_init); init_completion(&sdp->sd_wdack); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 69c4b77f127b..7c016a082aa6 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -774,7 +774,7 @@ static int gfs2_write_disk_quota(struct gfs2_inode *ip, struct gfs2_quota *qp, nbytes = sizeof(struct gfs2_quota); pg_beg = loc >> PAGE_SHIFT; - pg_off = loc % PAGE_SIZE; + pg_off = offset_in_page(loc); /* If the quota straddles a page boundary, split the write in two */ if ((pg_off + nbytes) > PAGE_SIZE) { diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 49ac0a5e74ea..2466bb44a23c 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -2285,7 +2285,7 @@ void gfs2_rgrp_dump(struct seq_file *seq, struct gfs2_glock *gl, static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd) { struct gfs2_sbd *sdp = rgd->rd_sbd; - char fs_id_buf[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2]; + char fs_id_buf[sizeof(sdp->sd_fsname) + 7]; fs_warn(sdp, "rgrp %llu has an error, marking it readonly until umount\n", (unsigned long long)rgd->rd_addr); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 0acc5834f653..644c70ae09f7 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1722,13 +1722,13 @@ static struct inode *gfs2_alloc_inode(struct super_block *sb) struct gfs2_inode *ip; ip = kmem_cache_alloc(gfs2_inode_cachep, GFP_KERNEL); - if (ip) { - ip->i_flags = 0; - ip->i_gl = NULL; - memset(&ip->i_res, 0, sizeof(ip->i_res)); - RB_CLEAR_NODE(&ip->i_res.rs_node); - ip->i_rahead = 0; - } + if (!ip) + return NULL; + ip->i_flags = 0; + ip->i_gl = NULL; + memset(&ip->i_res, 0, sizeof(ip->i_res)); + RB_CLEAR_NODE(&ip->i_res.rs_node); + ip->i_rahead = 0; return &ip->i_inode; } diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 83f6c582773a..c45159133d8e 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -178,7 +178,7 @@ int gfs2_consist_rgrpd_i(struct gfs2_rgrpd *rgd, int cluster_wide, const char *function, char *file, unsigned int line) { struct gfs2_sbd *sdp = rgd->rd_sbd; - char fs_id_buf[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2]; + char fs_id_buf[sizeof(sdp->sd_fsname) + 7]; int rv; sprintf(fs_id_buf, "fsid=%s: ", sdp->sd_fsname); |