📄 inode.c
字号:
/* * 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 + -