Skip to content

Commit

Permalink
GFS2: Fall back to vmalloc if kmalloc fails for dir hash tables
Browse files Browse the repository at this point in the history
This version has one more correction: the vmalloc calls are replaced
by __vmalloc calls to preserve the GFP_NOFS flag.

When GFS2's directory management code allocates buffers for a
directory hash table, if it can't get the memory it needs, it
currently gives a bad return code. Rather than giving an error,
this patch allows it to use virtual memory rather than kernel
memory for the hash table. This should make it possible for
directories to function properly, even when kernel memory becomes
very fragmented.

Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
  • Loading branch information
Bob Peterson authored and Steven Whitehouse committed Jun 3, 2013
1 parent 2b3dcf3 commit e8830d8
Showing 1 changed file with 33 additions and 10 deletions.
43 changes: 33 additions & 10 deletions fs/gfs2/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -354,22 +354,31 @@ static __be64 *gfs2_dir_get_hash_table(struct gfs2_inode *ip)
return ERR_PTR(-EIO);
}

hc = kmalloc(hsize, GFP_NOFS);
ret = -ENOMEM;
hc = kmalloc(hsize, GFP_NOFS | __GFP_NOWARN);
if (hc == NULL)
hc = __vmalloc(hsize, GFP_NOFS, PAGE_KERNEL);

if (hc == NULL)
return ERR_PTR(-ENOMEM);

ret = gfs2_dir_read_data(ip, hc, hsize);
if (ret < 0) {
kfree(hc);
if (is_vmalloc_addr(hc))
vfree(hc);
else
kfree(hc);
return ERR_PTR(ret);
}

spin_lock(&inode->i_lock);
if (ip->i_hash_cache)
kfree(hc);
else
if (ip->i_hash_cache) {
if (is_vmalloc_addr(hc))
vfree(hc);
else
kfree(hc);
} else {
ip->i_hash_cache = hc;
}
spin_unlock(&inode->i_lock);

return ip->i_hash_cache;
Expand All @@ -385,7 +394,10 @@ void gfs2_dir_hash_inval(struct gfs2_inode *ip)
{
__be64 *hc = ip->i_hash_cache;
ip->i_hash_cache = NULL;
kfree(hc);
if (is_vmalloc_addr(hc))
vfree(hc);
else
kfree(hc);
}

static inline int gfs2_dirent_sentinel(const struct gfs2_dirent *dent)
Expand Down Expand Up @@ -1113,7 +1125,10 @@ static int dir_double_exhash(struct gfs2_inode *dip)
if (IS_ERR(hc))
return PTR_ERR(hc);

h = hc2 = kmalloc(hsize_bytes * 2, GFP_NOFS);
h = hc2 = kmalloc(hsize_bytes * 2, GFP_NOFS | __GFP_NOWARN);
if (hc2 == NULL)
hc2 = __vmalloc(hsize_bytes * 2, GFP_NOFS, PAGE_KERNEL);

if (!hc2)
return -ENOMEM;

Expand Down Expand Up @@ -1145,7 +1160,10 @@ static int dir_double_exhash(struct gfs2_inode *dip)
gfs2_dinode_out(dip, dibh->b_data);
brelse(dibh);
out_kfree:
kfree(hc2);
if (is_vmalloc_addr(hc2))
vfree(hc2);
else
kfree(hc2);
return error;
}

Expand Down Expand Up @@ -1846,6 +1864,8 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
memset(&rlist, 0, sizeof(struct gfs2_rgrp_list));

ht = kzalloc(size, GFP_NOFS);
if (ht == NULL)
ht = vzalloc(size);
if (!ht)
return -ENOMEM;

Expand Down Expand Up @@ -1933,7 +1953,10 @@ static int leaf_dealloc(struct gfs2_inode *dip, u32 index, u32 len,
gfs2_rlist_free(&rlist);
gfs2_quota_unhold(dip);
out:
kfree(ht);
if (is_vmalloc_addr(ht))
vfree(ht);
else
kfree(ht);
return error;
}

Expand Down

0 comments on commit e8830d8

Please sign in to comment.