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

Commit debf798b authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://oss.sgi.com:8090/oss/git/xfs-2.6: (71 commits)
  [XFS] Sync up one/two other minor changes missed in previous merges.
  [XFS] Reenable the noikeep (delete inode cluster space) option by default.
  [XFS] Check that a page has dirty buffers before finding it acceptable for
  [XFS] Fixup naming inconsistencies found by Pekka Enberg and one from Jan
  [XFS] Explain the race closed by the addition of vn_iowait() to the start
  [XFS] Fixing the error caused by the conflict between DIO Write's
  [XFS] Fixing KDB's xrwtrc command, also added the current process id into
  [XFS] Fix compiler warning from xfs_file_compat_invis_ioctl prototype. 
  [XFS] remove bogus INT_GET for u8 variables in xfs_dir_leaf.c 
  [XFS] endianess annotations for xfs_da_node_hdr_t 
  [XFS] endianess annotations for xfs_da_node_entry_t 
  [XFS] store xfs_attr_inactive_list_t in native endian 
  [XFS] store xfs_attr_sf_sort in native endian 
  [XFS] endianess annotations for xfs_attr_shortform_t 
  [XFS] endianess annotations for xfs_attr_leaf_name_remote_t 
  [XFS] endianess annotations for xfs_attr_leaf_name_local_t 
  [XFS] endianess annotations for xfs_attr_leaf_entry_t 
  [XFS] endianess annotations for xfs_attr_leaf_hdr_t 
  [XFS] remove bogus INT_GET on u8 variables in xfs_dir2_block.c 
  [XFS] endianess annotations for xfs_da_blkinfo_t 
  ...
parents b0e6e962 4d74f423
Loading
Loading
Loading
Loading
+13 −27
Original line number Diff line number Diff line
#
# Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
# Copyright (c) 2000-2005 Silicon Graphics, Inc.
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it would be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# This program is distributed in the hope that it would be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# Further, this software is distributed without any warranty that it is
# free of the rightful claim of any third person regarding infringement
# or the like.  Any license provided herein, whether implied or
# otherwise, applies only to this software file.  Patent licenses, if
# any, provided herein do not apply to combinations of this program with
# other software, or any other product whatsoever.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write the Free Software Foundation, Inc., 59
# Temple Place - Suite 330, Boston MA 02111-1307, USA.
#
# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
# Mountain View, CA  94043, or:
#
# http://www.sgi.com
#
# For further information regarding this notice, see:
#
# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
# You should have received a copy of the GNU General Public License
# along with this program; if not, write the Free Software Foundation,
# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

EXTRA_CFLAGS +=	 -Ifs/xfs -Ifs/xfs/linux-2.6 -funsigned-char
@@ -36,7 +22,7 @@ XFS_LINUX := linux-2.6

ifeq ($(CONFIG_XFS_DEBUG),y)
	EXTRA_CFLAGS += -g -DSTATIC="" -DDEBUG
	EXTRA_CFLAGS += -DPAGEBUF_LOCK_TRACKING
	EXTRA_CFLAGS += -DXFS_BUF_LOCK_TRACKING
endif
ifeq ($(CONFIG_XFS_TRACE),y)
	EXTRA_CFLAGS += -DXFS_ALLOC_TRACE
@@ -50,7 +36,7 @@ ifeq ($(CONFIG_XFS_TRACE),y)
	EXTRA_CFLAGS += -DXFS_ILOCK_TRACE
	EXTRA_CFLAGS += -DXFS_LOG_TRACE
	EXTRA_CFLAGS += -DXFS_RW_TRACE
	EXTRA_CFLAGS += -DPAGEBUF_TRACE
	EXTRA_CFLAGS += -DXFS_BUF_TRACE
	EXTRA_CFLAGS += -DXFS_VNODE_TRACE
endif

+55 −36
Original line number Diff line number Diff line
@@ -23,17 +23,8 @@
#include <linux/mm.h>

/*
 * memory management routines
 * Process flags handling
 */
#define KM_SLEEP	0x0001u
#define KM_NOSLEEP	0x0002u
#define KM_NOFS		0x0004u
#define KM_MAYFAIL	0x0008u

#define	kmem_zone	kmem_cache
#define kmem_zone_t	struct kmem_cache

typedef unsigned long xfs_pflags_t;

#define PFLAGS_TEST_NOIO()              (current->flags & PF_NOIO)
#define PFLAGS_TEST_FSTRANS()           (current->flags & PF_FSTRANS)
@@ -67,74 +58,102 @@ typedef unsigned long xfs_pflags_t;
	*(NSTATEP) = *(OSTATEP);	\
} while (0)

static __inline gfp_t kmem_flags_convert(unsigned int __nocast flags)
/*
 * General memory allocation interfaces
 */

#define KM_SLEEP	0x0001u
#define KM_NOSLEEP	0x0002u
#define KM_NOFS		0x0004u
#define KM_MAYFAIL	0x0008u

/*
 * We use a special process flag to avoid recursive callbacks into
 * the filesystem during transactions.  We will also issue our own
 * warnings, so we explicitly skip any generic ones (silly of us).
 */
static inline gfp_t
kmem_flags_convert(unsigned int __nocast flags)
{
	gfp_t	lflags = __GFP_NOWARN;	/* we'll report problems, if need be */
	gfp_t	lflags;

#ifdef DEBUG
	if (unlikely(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL))) {
		printk(KERN_WARNING
		    "XFS: memory allocation with wrong flags (%x)\n", flags);
		BUG();
	}
#endif
	BUG_ON(flags & ~(KM_SLEEP|KM_NOSLEEP|KM_NOFS|KM_MAYFAIL));

	if (flags & KM_NOSLEEP) {
		lflags |= GFP_ATOMIC;
		lflags = GFP_ATOMIC | __GFP_NOWARN;
	} else {
		lflags |= GFP_KERNEL;

		/* avoid recusive callbacks to filesystem during transactions */
		lflags = GFP_KERNEL | __GFP_NOWARN;
		if (PFLAGS_TEST_FSTRANS() || (flags & KM_NOFS))
			lflags &= ~__GFP_FS;
	}

	return lflags;
}

static __inline kmem_zone_t *
extern void *kmem_alloc(size_t, unsigned int __nocast);
extern void *kmem_realloc(void *, size_t, size_t, unsigned int __nocast);
extern void *kmem_zalloc(size_t, unsigned int __nocast);
extern void  kmem_free(void *, size_t);

/*
 * Zone interfaces
 */

#define KM_ZONE_HWALIGN	SLAB_HWCACHE_ALIGN
#define KM_ZONE_RECLAIM	SLAB_RECLAIM_ACCOUNT
#define KM_ZONE_SPREAD	0

#define kmem_zone	kmem_cache
#define kmem_zone_t	struct kmem_cache

static inline kmem_zone_t *
kmem_zone_init(int size, char *zone_name)
{
	return kmem_cache_create(zone_name, size, 0, 0, NULL, NULL);
}

static __inline void
static inline kmem_zone_t *
kmem_zone_init_flags(int size, char *zone_name, unsigned long flags,
		     void (*construct)(void *, kmem_zone_t *, unsigned long))
{
	return kmem_cache_create(zone_name, size, 0, flags, construct, NULL);
}

static inline void
kmem_zone_free(kmem_zone_t *zone, void *ptr)
{
	kmem_cache_free(zone, ptr);
}

static __inline void
static inline void
kmem_zone_destroy(kmem_zone_t *zone)
{
	if (zone && kmem_cache_destroy(zone))
		BUG();
}

extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast);
extern void *kmem_zone_alloc(kmem_zone_t *, unsigned int __nocast);
extern void *kmem_zone_zalloc(kmem_zone_t *, unsigned int __nocast);

extern void *kmem_alloc(size_t, unsigned int __nocast);
extern void *kmem_realloc(void *, size_t, size_t, unsigned int __nocast);
extern void *kmem_zalloc(size_t, unsigned int __nocast);
extern void  kmem_free(void *, size_t);
/*
 * Low memory cache shrinkers
 */

typedef struct shrinker *kmem_shaker_t;
typedef int (*kmem_shake_func_t)(int, gfp_t);

static __inline kmem_shaker_t
static inline kmem_shaker_t
kmem_shake_register(kmem_shake_func_t sfunc)
{
	return set_shrinker(DEFAULT_SEEKS, sfunc);
}

static __inline void
static inline void
kmem_shake_deregister(kmem_shaker_t shrinker)
{
	remove_shrinker(shrinker);
}

static __inline int
static inline int
kmem_shake_allow(gfp_t gfp_mask)
{
	return (gfp_mask & __GFP_WAIT);
+242 −242
Original line number Diff line number Diff line
@@ -43,7 +43,29 @@
#include <linux/pagevec.h>
#include <linux/writeback.h>

STATIC void xfs_count_page_state(struct page *, int *, int *, int *);
STATIC void
xfs_count_page_state(
	struct page		*page,
	int			*delalloc,
	int			*unmapped,
	int			*unwritten)
{
	struct buffer_head	*bh, *head;

	*delalloc = *unmapped = *unwritten = 0;

	bh = head = page_buffers(page);
	do {
		if (buffer_uptodate(bh) && !buffer_mapped(bh))
			(*unmapped) = 1;
		else if (buffer_unwritten(bh) && !buffer_delay(bh))
			clear_buffer_unwritten(bh);
		else if (buffer_unwritten(bh))
			(*unwritten) = 1;
		else if (buffer_delay(bh))
			(*delalloc) = 1;
	} while ((bh = bh->b_this_page) != head);
}

#if defined(XFS_RW_TRACE)
void
@@ -54,7 +76,7 @@ xfs_page_trace(
	int		mask)
{
	xfs_inode_t	*ip;
	vnode_t		*vp = LINVFS_GET_VP(inode);
	vnode_t		*vp = vn_from_inode(inode);
	loff_t		isize = i_size_read(inode);
	loff_t		offset = page_offset(page);
	int		delalloc = -1, unmapped = -1, unwritten = -1;
@@ -81,7 +103,7 @@ xfs_page_trace(
		(void *)((unsigned long)delalloc),
		(void *)((unsigned long)unmapped),
		(void *)((unsigned long)unwritten),
		(void *)NULL,
		(void *)((unsigned long)current_pid()),
		(void *)NULL);
}
#else
@@ -192,7 +214,7 @@ xfs_alloc_ioend(
	ioend->io_uptodate = 1; /* cleared if any I/O fails */
	ioend->io_list = NULL;
	ioend->io_type = type;
	ioend->io_vnode = LINVFS_GET_VP(inode);
	ioend->io_vnode = vn_from_inode(inode);
	ioend->io_buffer_head = NULL;
	ioend->io_buffer_tail = NULL;
	atomic_inc(&ioend->io_vnode->v_iocount);
@@ -217,7 +239,7 @@ xfs_map_blocks(
	xfs_iomap_t		*mapp,
	int			flags)
{
	vnode_t			*vp = LINVFS_GET_VP(inode);
	vnode_t			*vp = vn_from_inode(inode);
	int			error, nmaps = 1;

	VOP_BMAP(vp, offset, count, flags, mapp, &nmaps, error);
@@ -461,6 +483,26 @@ xfs_add_to_ioend(
	ioend->io_size += bh->b_size;
}

STATIC void
xfs_map_buffer(
	struct buffer_head	*bh,
	xfs_iomap_t		*mp,
	xfs_off_t		offset,
	uint			block_bits)
{
	sector_t		bn;

	ASSERT(mp->iomap_bn != IOMAP_DADDR_NULL);

	bn = (mp->iomap_bn >> (block_bits - BBSHIFT)) +
	      ((offset - mp->iomap_offset) >> block_bits);

	ASSERT(bn || (mp->iomap_flags & IOMAP_REALTIME));

	bh->b_blocknr = bn;
	set_buffer_mapped(bh);
}

STATIC void
xfs_map_at_offset(
	struct buffer_head	*bh,
@@ -468,22 +510,11 @@ xfs_map_at_offset(
	int			block_bits,
	xfs_iomap_t		*iomapp)
{
	xfs_daddr_t		bn;
	int			sector_shift;

	ASSERT(!(iomapp->iomap_flags & IOMAP_HOLE));
	ASSERT(!(iomapp->iomap_flags & IOMAP_DELAY));
	ASSERT(iomapp->iomap_bn != IOMAP_DADDR_NULL);

	sector_shift = block_bits - BBSHIFT;
	bn = (iomapp->iomap_bn >> sector_shift) +
	      ((offset - iomapp->iomap_offset) >> block_bits);

	ASSERT(bn || (iomapp->iomap_flags & IOMAP_REALTIME));
	ASSERT((bn << sector_shift) >= iomapp->iomap_bn);

	lock_buffer(bh);
	bh->b_blocknr = bn;
	xfs_map_buffer(bh, iomapp, offset, block_bits);
	bh->b_bdev = iomapp->iomap_target->bt_bdev;
	set_buffer_mapped(bh);
	clear_buffer_delay(bh);
@@ -616,7 +647,7 @@ xfs_is_delayed_page(
				acceptable = (type == IOMAP_UNWRITTEN);
			else if (buffer_delay(bh))
				acceptable = (type == IOMAP_DELAY);
			else if (buffer_mapped(bh))
			else if (buffer_dirty(bh) && buffer_mapped(bh))
				acceptable = (type == 0);
			else
				break;
@@ -1040,8 +1071,159 @@ error:
	return err;
}

/*
 * writepage: Called from one of two places:
 *
 * 1. we are flushing a delalloc buffer head.
 *
 * 2. we are writing out a dirty page. Typically the page dirty
 *    state is cleared before we get here. In this case is it
 *    conceivable we have no buffer heads.
 *
 * For delalloc space on the page we need to allocate space and
 * flush it. For unmapped buffer heads on the page we should
 * allocate space if the page is uptodate. For any other dirty
 * buffer heads on the page we should flush them.
 *
 * If we detect that a transaction would be required to flush
 * the page, we have to check the process flags first, if we
 * are already in a transaction or disk I/O during allocations
 * is off, we need to fail the writepage and redirty the page.
 */

STATIC int
xfs_vm_writepage(
	struct page		*page,
	struct writeback_control *wbc)
{
	int			error;
	int			need_trans;
	int			delalloc, unmapped, unwritten;
	struct inode		*inode = page->mapping->host;

	xfs_page_trace(XFS_WRITEPAGE_ENTER, inode, page, 0);

	/*
	 * We need a transaction if:
	 *  1. There are delalloc buffers on the page
	 *  2. The page is uptodate and we have unmapped buffers
	 *  3. The page is uptodate and we have no buffers
	 *  4. There are unwritten buffers on the page
	 */

	if (!page_has_buffers(page)) {
		unmapped = 1;
		need_trans = 1;
	} else {
		xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
		if (!PageUptodate(page))
			unmapped = 0;
		need_trans = delalloc + unmapped + unwritten;
	}

	/*
	 * If we need a transaction and the process flags say
	 * we are already in a transaction, or no IO is allowed
	 * then mark the page dirty again and leave the page
	 * as is.
	 */
	if (PFLAGS_TEST_FSTRANS() && need_trans)
		goto out_fail;

	/*
	 * Delay hooking up buffer heads until we have
	 * made our go/no-go decision.
	 */
	if (!page_has_buffers(page))
		create_empty_buffers(page, 1 << inode->i_blkbits, 0);

	/*
	 * Convert delayed allocate, unwritten or unmapped space
	 * to real space and flush out to disk.
	 */
	error = xfs_page_state_convert(inode, page, wbc, 1, unmapped);
	if (error == -EAGAIN)
		goto out_fail;
	if (unlikely(error < 0))
		goto out_unlock;

	return 0;

out_fail:
	redirty_page_for_writepage(wbc, page);
	unlock_page(page);
	return 0;
out_unlock:
	unlock_page(page);
	return error;
}

/*
 * Called to move a page into cleanable state - and from there
 * to be released. Possibly the page is already clean. We always
 * have buffer heads in this call.
 *
 * Returns 0 if the page is ok to release, 1 otherwise.
 *
 * Possible scenarios are:
 *
 * 1. We are being called to release a page which has been written
 *    to via regular I/O. buffer heads will be dirty and possibly
 *    delalloc. If no delalloc buffer heads in this case then we
 *    can just return zero.
 *
 * 2. We are called to release a page which has been written via
 *    mmap, all we need to do is ensure there is no delalloc
 *    state in the buffer heads, if not we can let the caller
 *    free them and we should come back later via writepage.
 */
STATIC int
__linvfs_get_block(
xfs_vm_releasepage(
	struct page		*page,
	gfp_t			gfp_mask)
{
	struct inode		*inode = page->mapping->host;
	int			dirty, delalloc, unmapped, unwritten;
	struct writeback_control wbc = {
		.sync_mode = WB_SYNC_ALL,
		.nr_to_write = 1,
	};

	xfs_page_trace(XFS_RELEASEPAGE_ENTER, inode, page, gfp_mask);

	if (!page_has_buffers(page))
		return 0;

	xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
	if (!delalloc && !unwritten)
		goto free_buffers;

	if (!(gfp_mask & __GFP_FS))
		return 0;

	/* If we are already inside a transaction or the thread cannot
	 * do I/O, we cannot release this page.
	 */
	if (PFLAGS_TEST_FSTRANS())
		return 0;

	/*
	 * Convert delalloc space to real space, do not flush the
	 * data out to disk, that will be done by the caller.
	 * Never need to allocate space here - we will always
	 * come back to writepage in that case.
	 */
	dirty = xfs_page_state_convert(inode, page, &wbc, 0, 0);
	if (dirty == 0 && !unwritten)
		goto free_buffers;
	return 0;

free_buffers:
	return try_to_free_buffers(page);
}

STATIC int
__xfs_get_block(
	struct inode		*inode,
	sector_t		iblock,
	unsigned long		blocks,
@@ -1050,7 +1232,7 @@ __linvfs_get_block(
	int			direct,
	bmapi_flags_t		flags)
{
	vnode_t			*vp = LINVFS_GET_VP(inode);
	vnode_t			*vp = vn_from_inode(inode);
	xfs_iomap_t		iomap;
	xfs_off_t		offset;
	ssize_t			size;
@@ -1073,21 +1255,13 @@ __linvfs_get_block(
		return 0;

	if (iomap.iomap_bn != IOMAP_DADDR_NULL) {
		xfs_daddr_t	bn;
		xfs_off_t	delta;

		/* For unwritten extents do not report a disk address on
		/*
		 * For unwritten extents do not report a disk address on
		 * the read case (treat as if we're reading into a hole).
		 */
		if (create || !(iomap.iomap_flags & IOMAP_UNWRITTEN)) {
			delta = offset - iomap.iomap_offset;
			delta >>= inode->i_blkbits;

			bn = iomap.iomap_bn >> (inode->i_blkbits - BBSHIFT);
			bn += delta;
			BUG_ON(!bn && !(iomap.iomap_flags & IOMAP_REALTIME));
			bh_result->b_blocknr = bn;
			set_buffer_mapped(bh_result);
			xfs_map_buffer(bh_result, &iomap, offset,
				       inode->i_blkbits);
		}
		if (create && (iomap.iomap_flags & IOMAP_UNWRITTEN)) {
			if (direct)
@@ -1130,30 +1304,30 @@ __linvfs_get_block(
}

int
linvfs_get_block(
xfs_get_block(
	struct inode		*inode,
	sector_t		iblock,
	struct buffer_head	*bh_result,
	int			create)
{
	return __linvfs_get_block(inode, iblock, 0, bh_result,
	return __xfs_get_block(inode, iblock, 0, bh_result,
					create, 0, BMAPI_WRITE);
}

STATIC int
linvfs_get_blocks_direct(
xfs_get_blocks_direct(
	struct inode		*inode,
	sector_t		iblock,
	unsigned long		max_blocks,
	struct buffer_head	*bh_result,
	int			create)
{
	return __linvfs_get_block(inode, iblock, max_blocks, bh_result,
	return __xfs_get_block(inode, iblock, max_blocks, bh_result,
					create, 1, BMAPI_WRITE|BMAPI_DIRECT);
}

STATIC void
linvfs_end_io_direct(
xfs_end_io_direct(
	struct kiocb	*iocb,
	loff_t		offset,
	ssize_t		size,
@@ -1191,7 +1365,7 @@ linvfs_end_io_direct(
}

STATIC ssize_t
linvfs_direct_IO(
xfs_vm_direct_IO(
	int			rw,
	struct kiocb		*iocb,
	const struct iovec	*iov,
@@ -1200,7 +1374,7 @@ linvfs_direct_IO(
{
	struct file	*file = iocb->ki_filp;
	struct inode	*inode = file->f_mapping->host;
	vnode_t		*vp = LINVFS_GET_VP(inode);
	vnode_t		*vp = vn_from_inode(inode);
	xfs_iomap_t	iomap;
	int		maps = 1;
	int		error;
@@ -1215,164 +1389,61 @@ linvfs_direct_IO(
	ret = blockdev_direct_IO_own_locking(rw, iocb, inode,
		iomap.iomap_target->bt_bdev,
		iov, offset, nr_segs,
		linvfs_get_blocks_direct,
		linvfs_end_io_direct);
		xfs_get_blocks_direct,
		xfs_end_io_direct);

	if (unlikely(ret <= 0 && iocb->private))
		xfs_destroy_ioend(iocb->private);
	return ret;
}

STATIC int
xfs_vm_prepare_write(
	struct file		*file,
	struct page		*page,
	unsigned int		from,
	unsigned int		to)
{
	return block_prepare_write(page, from, to, xfs_get_block);
}

STATIC sector_t
linvfs_bmap(
xfs_vm_bmap(
	struct address_space	*mapping,
	sector_t		block)
{
	struct inode		*inode = (struct inode *)mapping->host;
	vnode_t			*vp = LINVFS_GET_VP(inode);
	vnode_t			*vp = vn_from_inode(inode);
	int			error;

	vn_trace_entry(vp, "linvfs_bmap", (inst_t *)__return_address);
	vn_trace_entry(vp, __FUNCTION__, (inst_t *)__return_address);

	VOP_RWLOCK(vp, VRWLOCK_READ);
	VOP_FLUSH_PAGES(vp, (xfs_off_t)0, -1, 0, FI_REMAPF, error);
	VOP_RWUNLOCK(vp, VRWLOCK_READ);
	return generic_block_bmap(mapping, block, linvfs_get_block);
	return generic_block_bmap(mapping, block, xfs_get_block);
}

STATIC int
linvfs_readpage(
xfs_vm_readpage(
	struct file		*unused,
	struct page		*page)
{
	return mpage_readpage(page, linvfs_get_block);
	return mpage_readpage(page, xfs_get_block);
}

STATIC int
linvfs_readpages(
xfs_vm_readpages(
	struct file		*unused,
	struct address_space	*mapping,
	struct list_head	*pages,
	unsigned		nr_pages)
{
	return mpage_readpages(mapping, pages, nr_pages, linvfs_get_block);
}

STATIC void
xfs_count_page_state(
	struct page		*page,
	int			*delalloc,
	int			*unmapped,
	int			*unwritten)
{
	struct buffer_head	*bh, *head;

	*delalloc = *unmapped = *unwritten = 0;

	bh = head = page_buffers(page);
	do {
		if (buffer_uptodate(bh) && !buffer_mapped(bh))
			(*unmapped) = 1;
		else if (buffer_unwritten(bh) && !buffer_delay(bh))
			clear_buffer_unwritten(bh);
		else if (buffer_unwritten(bh))
			(*unwritten) = 1;
		else if (buffer_delay(bh))
			(*delalloc) = 1;
	} while ((bh = bh->b_this_page) != head);
	return mpage_readpages(mapping, pages, nr_pages, xfs_get_block);
}


/*
 * writepage: Called from one of two places:
 *
 * 1. we are flushing a delalloc buffer head.
 *
 * 2. we are writing out a dirty page. Typically the page dirty
 *    state is cleared before we get here. In this case is it
 *    conceivable we have no buffer heads.
 *
 * For delalloc space on the page we need to allocate space and
 * flush it. For unmapped buffer heads on the page we should
 * allocate space if the page is uptodate. For any other dirty
 * buffer heads on the page we should flush them.
 *
 * If we detect that a transaction would be required to flush
 * the page, we have to check the process flags first, if we
 * are already in a transaction or disk I/O during allocations
 * is off, we need to fail the writepage and redirty the page.
 */

STATIC int
linvfs_writepage(
	struct page		*page,
	struct writeback_control *wbc)
{
	int			error;
	int			need_trans;
	int			delalloc, unmapped, unwritten;
	struct inode		*inode = page->mapping->host;

	xfs_page_trace(XFS_WRITEPAGE_ENTER, inode, page, 0);

	/*
	 * We need a transaction if:
	 *  1. There are delalloc buffers on the page
	 *  2. The page is uptodate and we have unmapped buffers
	 *  3. The page is uptodate and we have no buffers
	 *  4. There are unwritten buffers on the page
	 */

	if (!page_has_buffers(page)) {
		unmapped = 1;
		need_trans = 1;
	} else {
		xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
		if (!PageUptodate(page))
			unmapped = 0;
		need_trans = delalloc + unmapped + unwritten;
	}

	/*
	 * If we need a transaction and the process flags say
	 * we are already in a transaction, or no IO is allowed
	 * then mark the page dirty again and leave the page
	 * as is.
	 */
	if (PFLAGS_TEST_FSTRANS() && need_trans)
		goto out_fail;

	/*
	 * Delay hooking up buffer heads until we have
	 * made our go/no-go decision.
	 */
	if (!page_has_buffers(page))
		create_empty_buffers(page, 1 << inode->i_blkbits, 0);

	/*
	 * Convert delayed allocate, unwritten or unmapped space
	 * to real space and flush out to disk.
	 */
	error = xfs_page_state_convert(inode, page, wbc, 1, unmapped);
	if (error == -EAGAIN)
		goto out_fail;
	if (unlikely(error < 0))
		goto out_unlock;

	return 0;

out_fail:
	redirty_page_for_writepage(wbc, page);
	unlock_page(page);
	return 0;
out_unlock:
	unlock_page(page);
	return error;
}

STATIC int
linvfs_invalidate_page(
xfs_vm_invalidatepage(
	struct page		*page,
	unsigned long		offset)
{
@@ -1381,87 +1452,16 @@ linvfs_invalidate_page(
	return block_invalidatepage(page, offset);
}

/*
 * Called to move a page into cleanable state - and from there
 * to be released. Possibly the page is already clean. We always
 * have buffer heads in this call.
 *
 * Returns 0 if the page is ok to release, 1 otherwise.
 *
 * Possible scenarios are:
 *
 * 1. We are being called to release a page which has been written
 *    to via regular I/O. buffer heads will be dirty and possibly
 *    delalloc. If no delalloc buffer heads in this case then we
 *    can just return zero.
 *
 * 2. We are called to release a page which has been written via
 *    mmap, all we need to do is ensure there is no delalloc
 *    state in the buffer heads, if not we can let the caller
 *    free them and we should come back later via writepage.
 */
STATIC int
linvfs_release_page(
	struct page		*page,
	gfp_t			gfp_mask)
{
	struct inode		*inode = page->mapping->host;
	int			dirty, delalloc, unmapped, unwritten;
	struct writeback_control wbc = {
		.sync_mode = WB_SYNC_ALL,
		.nr_to_write = 1,
	};

	xfs_page_trace(XFS_RELEASEPAGE_ENTER, inode, page, gfp_mask);

	xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);
	if (!delalloc && !unwritten)
		goto free_buffers;

	if (!(gfp_mask & __GFP_FS))
		return 0;

	/* If we are already inside a transaction or the thread cannot
	 * do I/O, we cannot release this page.
	 */
	if (PFLAGS_TEST_FSTRANS())
		return 0;

	/*
	 * Convert delalloc space to real space, do not flush the
	 * data out to disk, that will be done by the caller.
	 * Never need to allocate space here - we will always
	 * come back to writepage in that case.
	 */
	dirty = xfs_page_state_convert(inode, page, &wbc, 0, 0);
	if (dirty == 0 && !unwritten)
		goto free_buffers;
	return 0;

free_buffers:
	return try_to_free_buffers(page);
}

STATIC int
linvfs_prepare_write(
	struct file		*file,
	struct page		*page,
	unsigned int		from,
	unsigned int		to)
{
	return block_prepare_write(page, from, to, linvfs_get_block);
}

struct address_space_operations linvfs_aops = {
	.readpage		= linvfs_readpage,
	.readpages		= linvfs_readpages,
	.writepage		= linvfs_writepage,
struct address_space_operations xfs_address_space_operations = {
	.readpage		= xfs_vm_readpage,
	.readpages		= xfs_vm_readpages,
	.writepage		= xfs_vm_writepage,
	.sync_page		= block_sync_page,
	.releasepage		= linvfs_release_page,
	.invalidatepage		= linvfs_invalidate_page,
	.prepare_write		= linvfs_prepare_write,
	.releasepage		= xfs_vm_releasepage,
	.invalidatepage		= xfs_vm_invalidatepage,
	.prepare_write		= xfs_vm_prepare_write,
	.commit_write		= generic_commit_write,
	.bmap			= linvfs_bmap,
	.direct_IO		= linvfs_direct_IO,
	.bmap			= xfs_vm_bmap,
	.direct_IO		= xfs_vm_direct_IO,
	.migratepage		= buffer_migrate_page,
};
+2 −2
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ typedef struct xfs_ioend {
	struct work_struct	io_work;	/* xfsdatad work queue */
} xfs_ioend_t;

extern struct address_space_operations linvfs_aops;
extern int linvfs_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern struct address_space_operations xfs_address_space_operations;
extern int xfs_get_block(struct inode *, sector_t, struct buffer_head *, int);

#endif /* __XFS_IOPS_H__ */
+3 −4
Original line number Diff line number Diff line
@@ -1806,13 +1806,12 @@ xfs_flush_buftarg(
int __init
xfs_buf_init(void)
{
	int		error = -ENOMEM;

#ifdef XFS_BUF_TRACE
	xfs_buf_trace_buf = ktrace_alloc(XFS_BUF_TRACE_SIZE, KM_SLEEP);
#endif

	xfs_buf_zone = kmem_zone_init(sizeof(xfs_buf_t), "xfs_buf");
	xfs_buf_zone = kmem_zone_init_flags(sizeof(xfs_buf_t), "xfs_buf",
						KM_ZONE_HWALIGN, NULL);
	if (!xfs_buf_zone)
		goto out_free_trace_buf;

@@ -1840,7 +1839,7 @@ xfs_buf_init(void)
#ifdef XFS_BUF_TRACE
	ktrace_free(xfs_buf_trace_buf);
#endif
	return error;
	return -ENOMEM;
}

void
Loading