Skip to content

Commit

Permalink
Merge tag 'gfs2-merge-window' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/git/steve/gfs2-3.0-nmw

Pull GFS2 updates from Steven Whitehouse:
 "The main topics this time are allocation, in the form of Bob's
  improvements when searching resource groups and several updates to
  quotas which should increase scalability.  The quota changes follow on
  from those in the last merge window, and there will likely be further
  work to come in this area in due course.

  There are also a few patches which help to improve efficiency of
  adding entries into directories, and clean up some of that code.

  One on-disk change is included this time, which is to write some
  additional information which should be useful to fsck and also
  potentially for debugging.

  Other than that, its just a few small random bug fixes and clean ups"

* tag 'gfs2-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-3.0-nmw: (24 commits)
  GFS2: revert "GFS2: d_splice_alias() can't return error"
  GFS2: Small cleanup
  GFS2: Don't use ENOBUFS when ENOMEM is the correct error code
  GFS2: Fix kbuild test robot reported warning
  GFS2: Move quota bitmap operations under their own lock
  GFS2: Clean up quota slot allocation
  GFS2: Only run logd and quota when mounted read/write
  GFS2: Use RCU/hlist_bl based hash for quotas
  GFS2: No need to invalidate pages for a dio read
  GFS2: Add initialization for address space in super block
  GFS2: Add hints to directory leaf blocks
  GFS2: For exhash conversion, only one block is needed
  GFS2: Increase i_writecount during gfs2_setattr_chown
  GFS2: Remember directory insert point
  GFS2: Consolidate transaction blocks calculation for dir add
  GFS2: Add directory addition info structure
  GFS2: Use only a single address space for rgrps
  GFS2: Use range based functions for rgrp sync/invalidation
  GFS2: Remove test which is always true
  GFS2: Remove gfs2_quota_change_host structure
  ...
  • Loading branch information
Linus Torvalds committed Jan 22, 2014
2 parents 03d11a0 + d57b9c9 commit 2182c81
Show file tree
Hide file tree
Showing 18 changed files with 550 additions and 359 deletions.
23 changes: 8 additions & 15 deletions fs/gfs2/aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1032,8 +1032,9 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
unmap_shared_mapping_range(ip->i_inode.i_mapping, offset, len);
rv = filemap_write_and_wait_range(mapping, lstart, end);
if (rv)
return rv;
truncate_inode_pages_range(mapping, lstart, end);
goto out;
if (rw == WRITE)
truncate_inode_pages_range(mapping, lstart, end);
}

rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
Expand Down Expand Up @@ -1080,30 +1081,22 @@ int gfs2_releasepage(struct page *page, gfp_t gfp_mask)
bh = bh->b_this_page;
} while(bh != head);
spin_unlock(&sdp->sd_ail_lock);
gfs2_log_unlock(sdp);

head = bh = page_buffers(page);
do {
gfs2_log_lock(sdp);
bd = bh->b_private;
if (bd) {
gfs2_assert_warn(sdp, bd->bd_bh == bh);
if (!list_empty(&bd->bd_list)) {
if (!buffer_pinned(bh))
list_del_init(&bd->bd_list);
else
bd = NULL;
}
if (bd)
bd->bd_bh = NULL;
if (!list_empty(&bd->bd_list))
list_del_init(&bd->bd_list);
bd->bd_bh = NULL;
bh->b_private = NULL;
}
gfs2_log_unlock(sdp);
if (bd)
kmem_cache_free(gfs2_bufdata_cachep, bd);
}

bh = bh->b_this_page;
} while (bh != head);
gfs2_log_unlock(sdp);

return try_to_free_buffers(page);

Expand Down
90 changes: 74 additions & 16 deletions fs/gfs2/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
struct gfs2_leaf *leaf;
struct gfs2_dirent *dent;
struct qstr name = { .name = "" };
struct timespec tv = CURRENT_TIME;

error = gfs2_alloc_blocks(ip, &bn, &n, 0, NULL);
if (error)
Expand All @@ -850,7 +851,11 @@ static struct gfs2_leaf *new_leaf(struct inode *inode, struct buffer_head **pbh,
leaf->lf_entries = 0;
leaf->lf_dirent_format = cpu_to_be32(GFS2_FORMAT_DE);
leaf->lf_next = 0;
memset(leaf->lf_reserved, 0, sizeof(leaf->lf_reserved));
leaf->lf_inode = cpu_to_be64(ip->i_no_addr);
leaf->lf_dist = cpu_to_be32(1);
leaf->lf_nsec = cpu_to_be32(tv.tv_nsec);
leaf->lf_sec = cpu_to_be64(tv.tv_sec);
memset(leaf->lf_reserved2, 0, sizeof(leaf->lf_reserved2));
dent = (struct gfs2_dirent *)(leaf+1);
gfs2_qstr2dirent(&name, bh->b_size - sizeof(struct gfs2_leaf), dent);
*pbh = bh;
Expand Down Expand Up @@ -1612,11 +1617,31 @@ int gfs2_dir_check(struct inode *dir, const struct qstr *name,
return ret;
}

/**
* dir_new_leaf - Add a new leaf onto hash chain
* @inode: The directory
* @name: The name we are adding
*
* This adds a new dir leaf onto an existing leaf when there is not
* enough space to add a new dir entry. This is a last resort after
* we've expanded the hash table to max size and also split existing
* leaf blocks, so it will only occur for very large directories.
*
* The dist parameter is set to 1 for leaf blocks directly attached
* to the hash table, 2 for one layer of indirection, 3 for two layers
* etc. We are thus able to tell the difference between an old leaf
* with dist set to zero (i.e. "don't know") and a new one where we
* set this information for debug/fsck purposes.
*
* Returns: 0 on success, or -ve on error
*/

static int dir_new_leaf(struct inode *inode, const struct qstr *name)
{
struct buffer_head *bh, *obh;
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_leaf *leaf, *oleaf;
u32 dist = 1;
int error;
u32 index;
u64 bn;
Expand All @@ -1626,6 +1651,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
if (error)
return error;
do {
dist++;
oleaf = (struct gfs2_leaf *)obh->b_data;
bn = be64_to_cpu(oleaf->lf_next);
if (!bn)
Expand All @@ -1643,6 +1669,7 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)
brelse(obh);
return -ENOSPC;
}
leaf->lf_dist = cpu_to_be32(dist);
oleaf->lf_next = cpu_to_be64(bh->b_blocknr);
brelse(bh);
brelse(obh);
Expand All @@ -1659,39 +1686,53 @@ static int dir_new_leaf(struct inode *inode, const struct qstr *name)

/**
* gfs2_dir_add - Add new filename into directory
* @dip: The GFS2 inode
* @filename: The new name
* @inode: The inode number of the entry
* @type: The type of the entry
* @inode: The directory inode
* @name: The new name
* @nip: The GFS2 inode to be linked in to the directory
* @da: The directory addition info
*
* If the call to gfs2_diradd_alloc_required resulted in there being
* no need to allocate any new directory blocks, then it will contain
* a pointer to the directory entry and the bh in which it resides. We
* can use that without having to repeat the search. If there was no
* free space, then we must now create more space.
*
* Returns: 0 on success, error code on failure
*/

int gfs2_dir_add(struct inode *inode, const struct qstr *name,
const struct gfs2_inode *nip)
const struct gfs2_inode *nip, struct gfs2_diradd *da)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct buffer_head *bh;
struct gfs2_dirent *dent;
struct buffer_head *bh = da->bh;
struct gfs2_dirent *dent = da->dent;
struct timespec tv;
struct gfs2_leaf *leaf;
int error;

while(1) {
dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space,
&bh);
if (da->bh == NULL) {
dent = gfs2_dirent_search(inode, name,
gfs2_dirent_find_space, &bh);
}
if (dent) {
if (IS_ERR(dent))
return PTR_ERR(dent);
dent = gfs2_init_dirent(inode, dent, name, bh);
gfs2_inum_out(nip, dent);
dent->de_type = cpu_to_be16(IF2DT(nip->i_inode.i_mode));
tv = CURRENT_TIME;
if (ip->i_diskflags & GFS2_DIF_EXHASH) {
leaf = (struct gfs2_leaf *)bh->b_data;
be16_add_cpu(&leaf->lf_entries, 1);
leaf->lf_nsec = cpu_to_be32(tv.tv_nsec);
leaf->lf_sec = cpu_to_be64(tv.tv_sec);
}
da->dent = NULL;
da->bh = NULL;
brelse(bh);
ip->i_entries++;
ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
ip->i_inode.i_mtime = ip->i_inode.i_ctime = tv;
if (S_ISDIR(nip->i_inode.i_mode))
inc_nlink(&ip->i_inode);
mark_inode_dirty(inode);
Expand Down Expand Up @@ -1742,6 +1783,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
const struct qstr *name = &dentry->d_name;
struct gfs2_dirent *dent, *prev = NULL;
struct buffer_head *bh;
struct timespec tv = CURRENT_TIME;

/* Returns _either_ the entry (if its first in block) or the
previous entry otherwise */
Expand All @@ -1767,13 +1809,15 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
if (!entries)
gfs2_consist_inode(dip);
leaf->lf_entries = cpu_to_be16(--entries);
leaf->lf_nsec = cpu_to_be32(tv.tv_nsec);
leaf->lf_sec = cpu_to_be64(tv.tv_sec);
}
brelse(bh);

if (!dip->i_entries)
gfs2_consist_inode(dip);
dip->i_entries--;
dip->i_inode.i_mtime = dip->i_inode.i_ctime = CURRENT_TIME;
dip->i_inode.i_mtime = dip->i_inode.i_ctime = tv;
if (S_ISDIR(dentry->d_inode->i_mode))
drop_nlink(&dip->i_inode);
mark_inode_dirty(&dip->i_inode);
Expand Down Expand Up @@ -2017,22 +2061,36 @@ int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip)
* gfs2_diradd_alloc_required - find if adding entry will require an allocation
* @ip: the file being written to
* @filname: the filename that's going to be added
* @da: The structure to return dir alloc info
*
* Returns: 1 if alloc required, 0 if not, -ve on error
* Returns: 0 if ok, -ve on error
*/

int gfs2_diradd_alloc_required(struct inode *inode, const struct qstr *name)
int gfs2_diradd_alloc_required(struct inode *inode, const struct qstr *name,
struct gfs2_diradd *da)
{
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_sbd *sdp = GFS2_SB(inode);
const unsigned int extra = sizeof(struct gfs2_dinode) - sizeof(struct gfs2_leaf);
struct gfs2_dirent *dent;
struct buffer_head *bh;

da->nr_blocks = 0;
da->bh = NULL;
da->dent = NULL;

dent = gfs2_dirent_search(inode, name, gfs2_dirent_find_space, &bh);
if (!dent) {
return 1;
da->nr_blocks = sdp->sd_max_dirres;
if (!(ip->i_diskflags & GFS2_DIF_EXHASH) &&
(GFS2_DIRENT_SIZE(name->len) < extra))
da->nr_blocks = 1;
return 0;
}
if (IS_ERR(dent))
return PTR_ERR(dent);
brelse(bh);
da->bh = bh;
da->dent = dent;
return 0;
}

19 changes: 17 additions & 2 deletions fs/gfs2/dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@
struct inode;
struct gfs2_inode;
struct gfs2_inum;
struct buffer_head;
struct gfs2_dirent;

struct gfs2_diradd {
unsigned nr_blocks;
struct gfs2_dirent *dent;
struct buffer_head *bh;
};

extern struct inode *gfs2_dir_search(struct inode *dir,
const struct qstr *filename,
bool fail_on_exist);
extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename,
const struct gfs2_inode *ip);
extern int gfs2_dir_add(struct inode *inode, const struct qstr *filename,
const struct gfs2_inode *ip);
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);
da->bh = NULL;
}
extern int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry);
extern int gfs2_dir_read(struct inode *inode, struct dir_context *ctx,
struct file_ra_state *f_ra);
Expand All @@ -33,7 +47,8 @@ extern int gfs2_dir_mvino(struct gfs2_inode *dip, const struct qstr *filename,
extern int gfs2_dir_exhash_dealloc(struct gfs2_inode *dip);

extern int gfs2_diradd_alloc_required(struct inode *dir,
const struct qstr *filename);
const struct qstr *filename,
struct gfs2_diradd *da);
extern int gfs2_dir_get_new_buffer(struct gfs2_inode *ip, u64 block,
struct buffer_head **bhp);
extern void gfs2_dir_hash_inval(struct gfs2_inode *ip);
Expand Down
29 changes: 10 additions & 19 deletions fs/gfs2/glock.c
Original file line number Diff line number Diff line change
Expand Up @@ -1552,13 +1552,11 @@ void gfs2_glock_thaw(struct gfs2_sbd *sdp)
glock_hash_walk(thaw_glock, sdp);
}

static int dump_glock(struct seq_file *seq, struct gfs2_glock *gl)
static void dump_glock(struct seq_file *seq, struct gfs2_glock *gl)
{
int ret;
spin_lock(&gl->gl_spin);
ret = gfs2_dump_glock(seq, gl);
gfs2_dump_glock(seq, gl);
spin_unlock(&gl->gl_spin);
return ret;
}

static void dump_glock_func(struct gfs2_glock *gl)
Expand Down Expand Up @@ -1647,10 +1645,9 @@ static const char *hflags2str(char *buf, unsigned flags, unsigned long iflags)
* @seq: the seq_file struct
* @gh: the glock holder
*
* Returns: 0 on success, -ENOBUFS when we run out of space
*/

static int dump_holder(struct seq_file *seq, const struct gfs2_holder *gh)
static void dump_holder(struct seq_file *seq, const struct gfs2_holder *gh)
{
struct task_struct *gh_owner = NULL;
char flags_buf[32];
Expand All @@ -1666,7 +1663,6 @@ static int dump_holder(struct seq_file *seq, const struct gfs2_holder *gh)
gh_owner ? gh_owner->comm : "(ended)",
(void *)gh->gh_ip);
rcu_read_unlock();
return 0;
}

static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
Expand Down Expand Up @@ -1721,16 +1717,14 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
* example. The field's are n = number (id of the object), f = flags,
* t = type, s = state, r = refcount, e = error, p = pid.
*
* Returns: 0 on success, -ENOBUFS when we run out of space
*/

int gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl)
void gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl)
{
const struct gfs2_glock_operations *glops = gl->gl_ops;
unsigned long long dtime;
const struct gfs2_holder *gh;
char gflags_buf[32];
int error = 0;

dtime = jiffies - gl->gl_demote_time;
dtime *= 1000000/HZ; /* demote time in uSec */
Expand All @@ -1747,15 +1741,11 @@ int gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl)
atomic_read(&gl->gl_revokes),
(int)gl->gl_lockref.count, gl->gl_hold_time);

list_for_each_entry(gh, &gl->gl_holders, gh_list) {
error = dump_holder(seq, gh);
if (error)
goto out;
}
list_for_each_entry(gh, &gl->gl_holders, gh_list)
dump_holder(seq, gh);

if (gl->gl_state != LM_ST_UNLOCKED && glops->go_dump)
error = glops->go_dump(seq, gl);
out:
return error;
glops->go_dump(seq, gl);
}

static int gfs2_glstats_seq_show(struct seq_file *seq, void *iter_ptr)
Expand Down Expand Up @@ -1953,7 +1943,8 @@ static void gfs2_glock_seq_stop(struct seq_file *seq, void *iter_ptr)

static int gfs2_glock_seq_show(struct seq_file *seq, void *iter_ptr)
{
return dump_glock(seq, iter_ptr);
dump_glock(seq, iter_ptr);
return 0;
}

static void *gfs2_sbstats_seq_start(struct seq_file *seq, loff_t *pos)
Expand Down
2 changes: 1 addition & 1 deletion fs/gfs2/glock.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ extern int gfs2_glock_nq_num(struct gfs2_sbd *sdp, u64 number,
struct gfs2_holder *gh);
extern int gfs2_glock_nq_m(unsigned int num_gh, struct gfs2_holder *ghs);
extern void gfs2_glock_dq_m(unsigned int num_gh, struct gfs2_holder *ghs);
extern int gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl);
extern void gfs2_dump_glock(struct seq_file *seq, const struct gfs2_glock *gl);
#define GLOCK_BUG_ON(gl,x) do { if (unlikely(x)) { gfs2_dump_glock(NULL, gl); BUG(); } } while(0)
extern __printf(2, 3)
void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...);
Expand Down
Loading

0 comments on commit 2182c81

Please sign in to comment.