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

Commit 3cc3f710 authored by Steven Whitehouse's avatar Steven Whitehouse
Browse files

[GFS2] Use ->page_mkwrite() for mmap()



This cleans up the mmap() code path for GFS2 by implementing the
page_mkwrite function for GFS2. We are thus able to use the
generic filemap_fault function for our ->fault() implementation.

This now means that shared writable mappings will be much more
efficiently shared across the cluster if there is a reasonable
proportion of read activity (the greater proportion, the better).

As a side effect, it also reduces the size of the code, removes
special cases from readpage and readpages, and makes the code
path easier to follow.

Signed-off-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
parent 51ff87bd
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -2,7 +2,7 @@ obj-$(CONFIG_GFS2_FS) += gfs2.o
gfs2-y := acl.o bmap.o daemon.o dir.o eaops.o eattr.o glock.o \
gfs2-y := acl.o bmap.o daemon.o dir.o eaops.o eattr.o glock.o \
	glops.o inode.o lm.o log.o lops.o locking.o main.o meta_io.o \
	glops.o inode.o lm.o log.o lops.o locking.o main.o meta_io.o \
	mount.o ops_address.o ops_dentry.o ops_export.o ops_file.o \
	mount.o ops_address.o ops_dentry.o ops_export.o ops_file.o \
	ops_fstype.o ops_inode.o ops_super.o ops_vm.o quota.o \
	ops_fstype.o ops_inode.o ops_super.o quota.o \
	recovery.o rgrp.o super.o sys.o trans.o util.o
	recovery.o rgrp.o super.o sys.o trans.o util.o


obj-$(CONFIG_GFS2_FS_LOCKING_NOLOCK) += locking/nolock/
obj-$(CONFIG_GFS2_FS_LOCKING_NOLOCK) += locking/nolock/
+1 −8
Original line number Original line Diff line number Diff line
@@ -86,15 +86,10 @@ static void gfs2_pte_inval(struct gfs2_glock *gl)
	if (!ip || !S_ISREG(inode->i_mode))
	if (!ip || !S_ISREG(inode->i_mode))
		return;
		return;


	if (!test_bit(GIF_PAGED, &ip->i_flags))
		return;

	unmap_shared_mapping_range(inode->i_mapping, 0, 0);
	unmap_shared_mapping_range(inode->i_mapping, 0, 0);

	if (test_bit(GIF_SW_PAGED, &ip->i_flags))
	if (test_bit(GIF_SW_PAGED, &ip->i_flags))
		set_bit(GLF_DIRTY, &gl->gl_flags);
		set_bit(GLF_DIRTY, &gl->gl_flags);


	clear_bit(GIF_SW_PAGED, &ip->i_flags);
}
}


/**
/**
@@ -234,10 +229,8 @@ static void inode_go_inval(struct gfs2_glock *gl, int flags)
			set_bit(GIF_INVALID, &ip->i_flags);
			set_bit(GIF_INVALID, &ip->i_flags);
	}
	}


	if (ip && S_ISREG(ip->i_inode.i_mode)) {
	if (ip && S_ISREG(ip->i_inode.i_mode))
		truncate_inode_pages(ip->i_inode.i_mapping, 0);
		truncate_inode_pages(ip->i_inode.i_mapping, 0);
		clear_bit(GIF_PAGED, &ip->i_flags);
	}
}
}


/**
/**
+0 −8
Original line number Original line Diff line number Diff line
@@ -241,7 +241,6 @@ struct gfs2_alloc {
enum {
enum {
	GIF_INVALID		= 0,
	GIF_INVALID		= 0,
	GIF_QD_LOCKED		= 1,
	GIF_QD_LOCKED		= 1,
	GIF_PAGED		= 2,
	GIF_SW_PAGED		= 3,
	GIF_SW_PAGED		= 3,
};
};


@@ -289,19 +288,12 @@ static inline struct gfs2_inode *GFS2_I(struct inode *inode)
	return container_of(inode, struct gfs2_inode, i_inode);
	return container_of(inode, struct gfs2_inode, i_inode);
}
}


/* To be removed? */
static inline struct gfs2_sbd *GFS2_SB(struct inode *inode)
static inline struct gfs2_sbd *GFS2_SB(struct inode *inode)
{
{
	return inode->i_sb->s_fs_info;
	return inode->i_sb->s_fs_info;
}
}


enum {
	GFF_DID_DIRECT_ALLOC	= 0,
	GFF_EXLOCK = 1,
};

struct gfs2_file {
struct gfs2_file {
	unsigned long f_flags;		/* GFF_... */
	struct mutex f_fl_mutex;
	struct mutex f_fl_mutex;
	struct gfs2_holder f_fl_gh;
	struct gfs2_holder f_fl_gh;
};
};
+8 −37
Original line number Original line Diff line number Diff line
@@ -265,9 +265,7 @@ static int __gfs2_readpage(void *file, struct page *page)
 * @file: The file to read
 * @file: The file to read
 * @page: The page of the file
 * @page: The page of the file
 *
 *
 * This deals with the locking required. If the GFF_EXLOCK flags is set
 * This deals with the locking required. We use a trylock in order to
 * then we already hold the glock (due to page fault) and thus we call
 * __gfs2_readpage() directly. Otherwise we use a trylock in order to
 * avoid the page lock / glock ordering problems returning AOP_TRUNCATED_PAGE
 * avoid the page lock / glock ordering problems returning AOP_TRUNCATED_PAGE
 * in the event that we are unable to get the lock.
 * in the event that we are unable to get the lock.
 */
 */
@@ -278,12 +276,6 @@ static int gfs2_readpage(struct file *file, struct page *page)
	struct gfs2_holder gh;
	struct gfs2_holder gh;
	int error;
	int error;


	if (file) {
		struct gfs2_file *gf = file->private_data;
		if (test_bit(GFF_EXLOCK, &gf->f_flags))
			return __gfs2_readpage(file, page);
	}

	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME|LM_FLAG_TRY_1CB, &gh);
	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME|LM_FLAG_TRY_1CB, &gh);
	error = gfs2_glock_nq_atime(&gh);
	error = gfs2_glock_nq_atime(&gh);
	if (unlikely(error)) {
	if (unlikely(error)) {
@@ -354,9 +346,8 @@ int gfs2_internal_read(struct gfs2_inode *ip, struct file_ra_state *ra_state,
 * 2. We don't handle stuffed files here we let readpage do the honours.
 * 2. We don't handle stuffed files here we let readpage do the honours.
 * 3. mpage_readpages() does most of the heavy lifting in the common case.
 * 3. mpage_readpages() does most of the heavy lifting in the common case.
 * 4. gfs2_get_block() is relied upon to set BH_Boundary in the right places.
 * 4. gfs2_get_block() is relied upon to set BH_Boundary in the right places.
 * 5. We use LM_FLAG_TRY_1CB here, effectively we then have lock-ahead as
 *    well as read-ahead.
 */
 */

static int gfs2_readpages(struct file *file, struct address_space *mapping,
static int gfs2_readpages(struct file *file, struct address_space *mapping,
			  struct list_head *pages, unsigned nr_pages)
			  struct list_head *pages, unsigned nr_pages)
{
{
@@ -364,40 +355,20 @@ static int gfs2_readpages(struct file *file, struct address_space *mapping,
	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 gfs2_holder gh;
	struct gfs2_holder gh;
	int ret = 0;
	int ret;
	int do_unlock = 0;


	if (file) {
	gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, &gh);
		struct gfs2_file *gf = file->private_data;
		if (test_bit(GFF_EXLOCK, &gf->f_flags))
			goto skip_lock;
	}
	gfs2_holder_init(ip->i_gl, LM_ST_SHARED,
			 LM_FLAG_TRY_1CB|GL_ATIME, &gh);
	do_unlock = 1;
	ret = gfs2_glock_nq_atime(&gh);
	ret = gfs2_glock_nq_atime(&gh);
	if (ret == GLR_TRYFAILED)
		goto out_noerror;
	if (unlikely(ret))
	if (unlikely(ret))
		goto out_unlock;
		goto out_uninit;
skip_lock:
	if (!gfs2_is_stuffed(ip))
	if (!gfs2_is_stuffed(ip))
		ret = mpage_readpages(mapping, pages, nr_pages, gfs2_get_block);
		ret = mpage_readpages(mapping, pages, nr_pages, gfs2_get_block);

	gfs2_glock_dq(&gh);
	if (do_unlock) {
out_uninit:
		gfs2_glock_dq_m(1, &gh);
	gfs2_holder_uninit(&gh);
	gfs2_holder_uninit(&gh);
	}
out:
	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
	if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
		ret = -EIO;
		ret = -EIO;
	return ret;
	return ret;
out_noerror:
	ret = 0;
out_unlock:
	if (do_unlock)
		gfs2_holder_uninit(&gh);
	goto out;
}
}


/**
/**
+121 −10
Original line number Original line Diff line number Diff line
@@ -33,7 +33,6 @@
#include "lm.h"
#include "lm.h"
#include "log.h"
#include "log.h"
#include "meta_io.h"
#include "meta_io.h"
#include "ops_vm.h"
#include "quota.h"
#include "quota.h"
#include "rgrp.h"
#include "rgrp.h"
#include "trans.h"
#include "trans.h"
@@ -169,7 +168,7 @@ static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
	if (put_user(fsflags, ptr))
	if (put_user(fsflags, ptr))
		error = -EFAULT;
		error = -EFAULT;


	gfs2_glock_dq_m(1, &gh);
	gfs2_glock_dq(&gh);
	gfs2_holder_uninit(&gh);
	gfs2_holder_uninit(&gh);
	return error;
	return error;
}
}
@@ -293,6 +292,125 @@ static long gfs2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
	return -ENOTTY;
	return -ENOTTY;
}
}


/**
 * gfs2_allocate_page_backing - Use bmap to allocate blocks
 * @page: The (locked) page to allocate backing for
 *
 * We try to allocate all the blocks required for the page in
 * one go. This might fail for various reasons, so we keep
 * trying until all the blocks to back this page are allocated.
 * If some of the blocks are already allocated, thats ok too.
 */

static int gfs2_allocate_page_backing(struct page *page)
{
	struct inode *inode = page->mapping->host;
	struct buffer_head bh;
	unsigned long size = PAGE_CACHE_SIZE;
	u64 lblock = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);

	do {
		bh.b_state = 0;
		bh.b_size = size;
		gfs2_block_map(inode, lblock, 1, &bh);
		if (!buffer_mapped(&bh))
			return -EIO;
		size -= bh.b_size;
		lblock += (bh.b_size >> inode->i_blkbits);
	} while(size > 0);
	return 0;
}

/**
 * gfs2_page_mkwrite - Make a shared, mmap()ed, page writable
 * @vma: The virtual memory area
 * @page: The page which is about to become writable
 *
 * When the page becomes writable, we need to ensure that we have
 * blocks allocated on disk to back that page.
 */

static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct page *page)
{
	struct inode *inode = vma->vm_file->f_path.dentry->d_inode;
	struct gfs2_inode *ip = GFS2_I(inode);
	struct gfs2_sbd *sdp = GFS2_SB(inode);
	unsigned long last_index;
	u64 pos = page->index << (PAGE_CACHE_SIZE - inode->i_blkbits);
	unsigned int data_blocks, ind_blocks, rblocks;
	int alloc_required = 0;
	struct gfs2_holder gh;
	struct gfs2_alloc *al;
	int ret;

	gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, GL_ATIME, &gh);
	ret = gfs2_glock_nq_atime(&gh);
	if (ret)
		goto out;

	set_bit(GIF_SW_PAGED, &ip->i_flags);
	gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks);
	ret = gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE, &alloc_required);
	if (ret || !alloc_required)
		goto out_unlock;

	ip->i_alloc.al_requested = 0;
	al = gfs2_alloc_get(ip);
	ret = gfs2_quota_lock(ip, NO_QUOTA_CHANGE, NO_QUOTA_CHANGE);
	if (ret)
		goto out_alloc_put;
	ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid);
	if (ret)
		goto out_quota_unlock;
	al->al_requested = data_blocks + ind_blocks;
	ret = gfs2_inplace_reserve(ip);
	if (ret)
		goto out_quota_unlock;

	rblocks = RES_DINODE + ind_blocks;
	if (gfs2_is_jdata(ip))
		rblocks += data_blocks ? data_blocks : 1;
	if (ind_blocks || data_blocks)
		rblocks += RES_STATFS + RES_QUOTA;
	ret = gfs2_trans_begin(sdp, rblocks, 0);
	if (ret)
		goto out_trans_fail;

	lock_page(page);
	ret = -EINVAL;
	last_index = ip->i_inode.i_size >> PAGE_CACHE_SHIFT;
	if (page->index > last_index)
		goto out_unlock_page;
	if (!PageUptodate(page) || page->mapping != ip->i_inode.i_mapping)
		goto out_unlock_page;
	if (gfs2_is_stuffed(ip)) {
		ret = gfs2_unstuff_dinode(ip, page);
		if (ret)
			goto out_unlock_page;
	}
	ret = gfs2_allocate_page_backing(page);

out_unlock_page:
	unlock_page(page);
	gfs2_trans_end(sdp);
out_trans_fail:
	gfs2_inplace_release(ip);
out_quota_unlock:
	gfs2_quota_unlock(ip);
out_alloc_put:
	gfs2_alloc_put(ip);
out_unlock:
	gfs2_glock_dq(&gh);
out:
	gfs2_holder_uninit(&gh);
	return ret;
}

static struct vm_operations_struct gfs2_vm_ops = {
	.fault = filemap_fault,
	.page_mkwrite = gfs2_page_mkwrite,
};



/**
/**
 * gfs2_mmap -
 * gfs2_mmap -
@@ -315,14 +433,7 @@ static int gfs2_mmap(struct file *file, struct vm_area_struct *vma)
		return error;
		return error;
	}
	}


	/* This is VM_MAYWRITE instead of VM_WRITE because a call
	vma->vm_ops = &gfs2_vm_ops;
	   to mprotect() can turn on VM_WRITE later. */

	if ((vma->vm_flags & (VM_MAYSHARE | VM_MAYWRITE)) ==
	    (VM_MAYSHARE | VM_MAYWRITE))
		vma->vm_ops = &gfs2_vm_ops_sharewrite;
	else
		vma->vm_ops = &gfs2_vm_ops_private;


	gfs2_glock_dq_uninit(&i_gh);
	gfs2_glock_dq_uninit(&i_gh);


Loading