⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 inode.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	/*	 * Next look up the indirect map to count the totoal number of	 * direct blocks to allocate for this branch.	 */	count = ext4_blks_to_allocate(partial, indirect_blks,					maxblocks, blocks_to_boundary);	/*	 * Block out ext4_truncate while we alter the tree	 */	err = ext4_alloc_branch(handle, inode, indirect_blks, &count, goal,				offsets + (partial - chain), partial);	/*	 * The ext4_splice_branch call will free and forget any buffers	 * on the new chain if there is a failure, but that risks using	 * up transaction credits, especially for bitmaps where the	 * credits cannot be returned.  Can we handle this somehow?  We	 * may need to return -EAGAIN upwards in the worst case.  --sct	 */	if (!err)		err = ext4_splice_branch(handle, inode, iblock,					partial, indirect_blks, count);	/*	 * i_disksize growing is protected by truncate_mutex.  Don't forget to	 * protect it if you're about to implement concurrent	 * ext4_get_block() -bzzz	*/	if (!err && extend_disksize && inode->i_size > ei->i_disksize)		ei->i_disksize = inode->i_size;	mutex_unlock(&ei->truncate_mutex);	if (err)		goto cleanup;	set_buffer_new(bh_result);got_it:	map_bh(bh_result, inode->i_sb, le32_to_cpu(chain[depth-1].key));	if (count > blocks_to_boundary)		set_buffer_boundary(bh_result);	err = count;	/* Clean up and exit */	partial = chain + depth - 1;	/* the whole chain */cleanup:	while (partial > chain) {		BUFFER_TRACE(partial->bh, "call brelse");		brelse(partial->bh);		partial--;	}	BUFFER_TRACE(bh_result, "returned");out:	return err;}#define DIO_CREDITS (EXT4_RESERVE_TRANS_BLOCKS + 32)static int ext4_get_block(struct inode *inode, sector_t iblock,			struct buffer_head *bh_result, int create){	handle_t *handle = ext4_journal_current_handle();	int ret = 0;	unsigned max_blocks = bh_result->b_size >> inode->i_blkbits;	if (!create)		goto get_block;		/* A read */	if (max_blocks == 1)		goto get_block;		/* A single block get */	if (handle->h_transaction->t_state == T_LOCKED) {		/*		 * Huge direct-io writes can hold off commits for long		 * periods of time.  Let this commit run.		 */		ext4_journal_stop(handle);		handle = ext4_journal_start(inode, DIO_CREDITS);		if (IS_ERR(handle))			ret = PTR_ERR(handle);		goto get_block;	}	if (handle->h_buffer_credits <= EXT4_RESERVE_TRANS_BLOCKS) {		/*		 * Getting low on buffer credits...		 */		ret = ext4_journal_extend(handle, DIO_CREDITS);		if (ret > 0) {			/*			 * Couldn't extend the transaction.  Start a new one.			 */			ret = ext4_journal_restart(handle, DIO_CREDITS);		}	}get_block:	if (ret == 0) {		ret = ext4_get_blocks_wrap(handle, inode, iblock,					max_blocks, bh_result, create, 0);		if (ret > 0) {			bh_result->b_size = (ret << inode->i_blkbits);			ret = 0;		}	}	return ret;}/* * `handle' can be NULL if create is zero */struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,				long block, int create, int *errp){	struct buffer_head dummy;	int fatal = 0, err;	J_ASSERT(handle != NULL || create == 0);	dummy.b_state = 0;	dummy.b_blocknr = -1000;	buffer_trace_init(&dummy.b_history);	err = ext4_get_blocks_wrap(handle, inode, block, 1,					&dummy, create, 1);	/*	 * ext4_get_blocks_handle() returns number of blocks	 * mapped. 0 in case of a HOLE.	 */	if (err > 0) {		if (err > 1)			WARN_ON(1);		err = 0;	}	*errp = err;	if (!err && buffer_mapped(&dummy)) {		struct buffer_head *bh;		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);		if (!bh) {			*errp = -EIO;			goto err;		}		if (buffer_new(&dummy)) {			J_ASSERT(create != 0);			J_ASSERT(handle != NULL);			/*			 * Now that we do not always journal data, we should			 * keep in mind whether this should always journal the			 * new buffer as metadata.  For now, regular file			 * writes use ext4_get_block instead, so it's not a			 * problem.			 */			lock_buffer(bh);			BUFFER_TRACE(bh, "call get_create_access");			fatal = ext4_journal_get_create_access(handle, bh);			if (!fatal && !buffer_uptodate(bh)) {				memset(bh->b_data,0,inode->i_sb->s_blocksize);				set_buffer_uptodate(bh);			}			unlock_buffer(bh);			BUFFER_TRACE(bh, "call ext4_journal_dirty_metadata");			err = ext4_journal_dirty_metadata(handle, bh);			if (!fatal)				fatal = err;		} else {			BUFFER_TRACE(bh, "not a new buffer");		}		if (fatal) {			*errp = fatal;			brelse(bh);			bh = NULL;		}		return bh;	}err:	return NULL;}struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,			       int block, int create, int *err){	struct buffer_head * bh;	bh = ext4_getblk(handle, inode, block, create, err);	if (!bh)		return bh;	if (buffer_uptodate(bh))		return bh;	ll_rw_block(READ_META, 1, &bh);	wait_on_buffer(bh);	if (buffer_uptodate(bh))		return bh;	put_bh(bh);	*err = -EIO;	return NULL;}static int walk_page_buffers(	handle_t *handle,				struct buffer_head *head,				unsigned from,				unsigned to,				int *partial,				int (*fn)(	handle_t *handle,						struct buffer_head *bh)){	struct buffer_head *bh;	unsigned block_start, block_end;	unsigned blocksize = head->b_size;	int err, ret = 0;	struct buffer_head *next;	for (	bh = head, block_start = 0;		ret == 0 && (bh != head || !block_start);		block_start = block_end, bh = next)	{		next = bh->b_this_page;		block_end = block_start + blocksize;		if (block_end <= from || block_start >= to) {			if (partial && !buffer_uptodate(bh))				*partial = 1;			continue;		}		err = (*fn)(handle, bh);		if (!ret)			ret = err;	}	return ret;}/* * To preserve ordering, it is essential that the hole instantiation and * the data write be encapsulated in a single transaction.  We cannot * close off a transaction and start a new one between the ext4_get_block() * and the commit_write().  So doing the jbd2_journal_start at the start of * prepare_write() is the right place. * * Also, this function can nest inside ext4_writepage() -> * block_write_full_page(). In that case, we *know* that ext4_writepage() * has generated enough buffer credits to do the whole page.  So we won't * block on the journal in that case, which is good, because the caller may * be PF_MEMALLOC. * * By accident, ext4 can be reentered when a transaction is open via * quota file writes.  If we were to commit the transaction while thus * reentered, there can be a deadlock - we would be holding a quota * lock, and the commit would never complete if another thread had a * transaction open and was blocking on the quota lock - a ranking * violation. * * So what we do is to rely on the fact that jbd2_journal_stop/journal_start * will _not_ run commit under these circumstances because handle->h_ref * is elevated.  We'll still have enough credits for the tiny quotafile * write. */static int do_journal_get_write_access(handle_t *handle,					struct buffer_head *bh){	if (!buffer_mapped(bh) || buffer_freed(bh))		return 0;	return ext4_journal_get_write_access(handle, bh);}static int ext4_write_begin(struct file *file, struct address_space *mapping,				loff_t pos, unsigned len, unsigned flags,				struct page **pagep, void **fsdata){ 	struct inode *inode = mapping->host;	int ret, needed_blocks = ext4_writepage_trans_blocks(inode);	handle_t *handle;	int retries = 0; 	struct page *page; 	pgoff_t index; 	unsigned from, to; 	index = pos >> PAGE_CACHE_SHIFT; 	from = pos & (PAGE_CACHE_SIZE - 1); 	to = from + len;retry: 	page = __grab_cache_page(mapping, index); 	if (!page) 		return -ENOMEM; 	*pagep = page;  	handle = ext4_journal_start(inode, needed_blocks);  	if (IS_ERR(handle)) { 		unlock_page(page); 		page_cache_release(page);  		ret = PTR_ERR(handle);  		goto out;	}	ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata,							ext4_get_block);	if (!ret && ext4_should_journal_data(inode)) {		ret = walk_page_buffers(handle, page_buffers(page),				from, to, NULL, do_journal_get_write_access);	}	if (ret) {		ext4_journal_stop(handle); 		unlock_page(page); 		page_cache_release(page);	}	if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))		goto retry;out:	return ret;}int ext4_journal_dirty_data(handle_t *handle, struct buffer_head *bh){	int err = jbd2_journal_dirty_data(handle, bh);	if (err)		ext4_journal_abort_handle(__FUNCTION__, __FUNCTION__,						bh, handle, err);	return err;}/* For write_end() in data=journal mode */static int write_end_fn(handle_t *handle, struct buffer_head *bh){	if (!buffer_mapped(bh) || buffer_freed(bh))		return 0;	set_buffer_uptodate(bh);	return ext4_journal_dirty_metadata(handle, bh);}/* * Generic write_end handler for ordered and writeback ext4 journal modes. * We can't use generic_write_end, because that unlocks the page and we need to * unlock the page after ext4_journal_stop, but ext4_journal_stop must run * after block_write_end. */static int ext4_generic_write_end(struct file *file,				struct address_space *mapping,				loff_t pos, unsigned len, unsigned copied,				struct page *page, void *fsdata){	struct inode *inode = file->f_mapping->host;	copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);	if (pos+copied > inode->i_size) {		i_size_write(inode, pos+copied);		mark_inode_dirty(inode);	}	return copied;}/* * We need to pick up the new inode size which generic_commit_write gave us * `file' can be NULL - eg, when called from page_symlink(). * * ext4 never places buffers on inode->i_mapping->private_list.  metadata * buffers are managed internally. */static int ext4_ordered_write_end(struct file *file,				struct address_space *mapping,				loff_t pos, unsigned len, unsigned copied,				struct page *page, void *fsdata){	handle_t *handle = ext4_journal_current_handle();	struct inode *inode = file->f_mapping->host;	unsigned from, to;	int ret = 0, ret2;	from = pos & (PAGE_CACHE_SIZE - 1);	to = from + len;	ret = walk_page_buffers(handle, page_buffers(page),		from, to, NULL, ext4_journal_dirty_data);	if (ret == 0) {		/*		 * generic_write_end() will run mark_inode_dirty() if i_size		 * changes.  So let's piggyback the i_disksize mark_inode_dirty		 * into that.		 */		loff_t new_i_size;		new_i_size = pos + copied;		if (new_i_size > EXT4_I(inode)->i_disksize)			EXT4_I(inode)->i_disksize = new_i_size;		copied = ext4_generic_write_end(file, mapping, pos, len, copied,							page, fsdata);		if (copied < 0)			ret = copied;	}	ret2 = ext4_journal_stop(handle);	if (!ret)		ret = ret2;	unlock_page(page);	page_cache_release(page);	return ret ? ret : copied;}static int ext4_writeback_write_end(struct file *file,				struct address_space *mapping,				loff_t pos, unsigned len, unsigned copied,				struct page *page, void *fsdata){	handle_t *handle = ext4_journal_current_handle();	struct inode *inode = file->f_mapping->host;	int ret = 0, ret2;	loff_t new_i_size;	new_i_size = pos + copied;	if (new_i_size > EXT4_I(inode)->i_disksize)		EXT4_I(inode)->i_disksize = new_i_size;	copied = ext4_generic_write_end(file, mapping, pos, len, copied,							page, fsdata);	if (copied < 0)		ret = copied;	ret2 = ext4_journal_stop(handle);	if (!ret)		ret = ret2;	unlock_page(page);	page_cache_release(page);	return ret ? ret : copied;}static int ext4_journalled_write_end(struct file *file,				struct address_space *mapping,				loff_t pos, unsigned len, unsigned copied,				struct page *page, void *fsdata){	handle_t *handle = ext4_journal_current_handle();	struct inode *inode = mapping->host;	int ret = 0, ret2;	int partial = 0;	unsigned from, to;	from = pos & (PAGE_CACHE_SIZE - 1);	to = from + len;	if (copied < len) {		if (!PageUptodate(page))			copied = 0;		page_zero_new_buffers(page, from+copied, to);	}

⌨️ 快捷键说明

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