Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e8830d88 authored by Bob Peterson's avatar Bob Peterson Committed by Steven Whitehouse
Browse files

GFS2: Fall back to vmalloc if kmalloc fails for dir hash tables



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: default avatarBob Peterson <rpeterso@redhat.com>
Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent 2b3dcf35
Loading
Loading
Loading
Loading
+33 −10
Original line number Diff line number Diff line
@@ -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) {
		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);
	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;
@@ -385,6 +394,9 @@ void gfs2_dir_hash_inval(struct gfs2_inode *ip)
{
	__be64 *hc = ip->i_hash_cache;
	ip->i_hash_cache = NULL;
	if (is_vmalloc_addr(hc))
		vfree(hc);
	else
		kfree(hc);
}

@@ -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;

@@ -1145,6 +1160,9 @@ static int dir_double_exhash(struct gfs2_inode *dip)
	gfs2_dinode_out(dip, dibh->b_data);
	brelse(dibh);
out_kfree:
	if (is_vmalloc_addr(hc2))
		vfree(hc2);
	else
		kfree(hc2);
	return error;
}
@@ -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;

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