xfs_aops.c

来自「linux-2.4.29操作系统的源码」· C语言 代码 · 共 1,300 行 · 第 1/3 页

C
1,300
字号
	ssize_t			size;	loff_t			offset = (loff_t)iblock << inode->i_blkbits;	size = 1 << inode->i_blkbits;	VOP_BMAP(vp, offset, size,		create ? flags : BMAPI_READ, &iomap, &retpbbm, error);	if (error)		return -error;	if (retpbbm == 0)		return 0;	if (iomap.iomap_bn != IOMAP_DADDR_NULL) {		xfs_daddr_t		bn;		loff_t			delta;		/* 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);		}		if (create && (iomap.iomap_flags & IOMAP_UNWRITTEN)) {			set_buffer_unwritten(bh_result);			set_buffer_delay(bh_result);		}	}	/* If this is a realtime file, data might be on a new device */	bh_result->b_dev = iomap.iomap_target->pbr_kdev;	/* If we previously allocated a block out beyond eof and	 * we are now coming back to use it then we will need to	 * flag it as new even if it has a disk address.	 */	if (create &&	    ((!buffer_mapped(bh_result) && !buffer_uptodate(bh_result)) ||	     (offset >= i_size_read(inode)))) {		set_buffer_new(bh_result);	}	if (iomap.iomap_flags & IOMAP_DELAY) {		BUG_ON(direct);		if (create) {			set_buffer_mapped(bh_result);			set_buffer_uptodate(bh_result);		}		set_buffer_delay(bh_result);	}	return 0;}intlinvfs_get_block(	struct inode		*inode,	long			iblock,	struct buffer_head	*bh_result,	int			create){	return linvfs_get_block_core(inode, iblock, bh_result,					create, 0, BMAPI_WRITE);}STATIC intlinvfs_get_block_direct(	struct inode		*inode,	long			iblock,	struct buffer_head	*bh_result,	int			create){	return linvfs_get_block_core(inode, iblock, bh_result,					create, 1, BMAPI_WRITE|BMAPI_DIRECT);}STATIC intlinvfs_bmap(	struct address_space	*mapping,	long			block){	struct inode		*inode = (struct inode *)mapping->host;	vnode_t			*vp = LINVFS_GET_VP(inode);	int			error;	vn_trace_entry(vp, "linvfs_bmap", (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_direct);}STATIC intlinvfs_readpage(	struct file		*unused,	struct page		*page){	return block_read_full_page(page, linvfs_get_block);}STATIC voidxfs_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);}/* * 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 intlinvfs_writepage(	struct page		*page){	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() || PFLAGS_TEST_NOIO()) && 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, inode->i_dev, 1 << inode->i_blkbits);	/*	 * Convert delayed allocate, unwritten or unmapped space	 * to real space and flush out to disk.	 */	if (need_trans)		PFLAGS_SET_NOIO();	error = xfs_page_state_convert(inode, page, 1, unmapped);	if (need_trans)		PFLAGS_CLEAR_NOIO();	if (error == -EAGAIN)		goto out_fail;	if (unlikely(error < 0)) {		unlock_page(page);		return error;	}	return 0;out_fail:	SetPageDirty(page);	unlock_page(page);	return 0;}/* * 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 intlinvfs_release_page(	struct page		*page,	int			gfp_mask){	struct inode		*inode = page->mapping->host;	int			dirty, delalloc, unmapped, unwritten;	xfs_page_trace(XFS_RELEASEPAGE_ENTER, inode, page, gfp_mask);	xfs_count_page_state(page, &delalloc, &unmapped, &unwritten);	if (!delalloc && !unwritten)		return 1;	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() || PFLAGS_TEST_NOIO())		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, 0, 0);	return (dirty == 0 && !unwritten) ? 1 : 0;}STATIC intlinvfs_prepare_write(	struct file		*file,	struct page		*page,	unsigned int		from,	unsigned int		to){	return block_prepare_write(page, from, to, linvfs_get_block);}/* * Initiate I/O on a kiobuf of user memory */STATIC intlinvfs_direct_IO(	int			rw,	struct inode		*inode,	struct kiobuf		*iobuf,	unsigned long		blocknr,	int			blocksize){	struct page		**maplist;	size_t			page_offset;	xfs_buf_t		*pb;	xfs_iomap_t		iomap;	int			niomap, error = 0;	int			pb_flags, map_flags, pg_index = 0;	size_t			length, total;	loff_t			offset, map_size;	size_t			size;	vnode_t			*vp = LINVFS_GET_VP(inode);	/* Note - although the iomap could have a 64-bit size,	 * kiobuf->length is only an int, so the min(map_size, length)	 * test will keep us from overflowing the pagebuf size_t size.	 */	total = length = iobuf->length;	offset = blocknr;	offset <<= inode->i_blkbits;	maplist = iobuf->maplist;	page_offset = iobuf->offset;	map_flags = (rw ? BMAPI_WRITE : BMAPI_READ) | BMAPI_DIRECT;	pb_flags = (rw ? PBF_WRITE : PBF_READ) | PBF_FORCEIO | PBF_DIRECTIO;	while (length) {		niomap = 1;		VOP_BMAP(vp, offset, length, map_flags, &iomap, &niomap, error);		if (unlikely(!error && niomap &&				(iomap.iomap_flags & IOMAP_DELAY))) {#ifdef DEBUG			printk(	"XFS: %s - direct IO (%lld:%ld) into a delayed allocate extent?\n",				__FUNCTION__, (long long)offset, (long)length);			xfs_stack_trace();#endif			VOP_BMAP(vp, offset, length, BMAPI_ALLOCATE,					&iomap, &niomap, error);		}		if (error)			break;		if (rw == WRITE)			VMODIFY(vp);		BUG_ON(niomap != 1);		BUG_ON(iomap.iomap_flags & IOMAP_DELAY);		map_size = iomap.iomap_bsize - iomap.iomap_delta;		size = (size_t)min(map_size, (loff_t)length);		if ((iomap.iomap_flags & IOMAP_HOLE) ||		    ((iomap.iomap_flags & IOMAP_UNWRITTEN) && rw == READ)) {			size_t	zero_len = size;			if (rw == WRITE)				break;			/* Need to zero it all */			while (zero_len) {				struct page	*page;				size_t		pg_len;				pg_len = min((size_t)						(PAGE_CACHE_SIZE - page_offset),						zero_len);				page = maplist[pg_index];				memset(kmap(page) + page_offset, 0, pg_len);				flush_dcache_page(page);				kunmap(page);				zero_len -= pg_len;				if ((pg_len + page_offset) == PAGE_CACHE_SIZE) {					pg_index++;					page_offset = 0;				} else {					page_offset = (page_offset + pg_len) &							~PAGE_CACHE_MASK;				}			}		} else {			int	pg_count;			pg_count = (size + page_offset + PAGE_CACHE_SIZE - 1)					>> PAGE_CACHE_SHIFT;			if ((pb = pagebuf_lookup(iomap.iomap_target, offset,						size, pb_flags)) == NULL) {				error = ENOMEM;				break;			}			/* Need to hook up pagebuf to kiobuf pages */			pb->pb_pages = &maplist[pg_index];			pb->pb_offset = page_offset;			pb->pb_page_count = pg_count;			pb->pb_bn = iomap.iomap_bn + (iomap.iomap_delta >> BBSHIFT);			error = pagebuf_iostart(pb, pb_flags);			if (!error && (iomap.iomap_flags & IOMAP_UNWRITTEN)) {				VOP_BMAP(vp, XFS_BUF_OFFSET(pb),					XFS_BUF_SIZE(pb),					BMAPI_UNWRITTEN, NULL, NULL, error);			}			pagebuf_rele(pb);			if (error)				break;			page_offset = (page_offset + size) & ~PAGE_CACHE_MASK;			if (page_offset)				pg_count--;			pg_index += pg_count;		}		offset += size;		length -= size;	}	if (error)		return -error;	return (int)(total - length);}struct address_space_operations linvfs_aops = {	.readpage		= linvfs_readpage,	.writepage		= linvfs_writepage,	.sync_page		= block_sync_page,	.releasepage		= linvfs_release_page,	.prepare_write		= linvfs_prepare_write,	.commit_write		= generic_commit_write,	.bmap			= linvfs_bmap,	.direct_IO		= linvfs_direct_IO,};

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?