staticinlineint ext4_begin_ordered_truncate(struct inode *inode,
loff_t new_size)
{
trace_ext4_begin_ordered_truncate(inode, new_size); /* * If jinode is zero, then we never opened the file for * writing, so there's no need to call * jbd2_journal_begin_ordered_truncate() since there's no * outstanding writes we need to flush.
*/ if (!EXT4_I(inode)->jinode) return 0; return jbd2_journal_begin_ordered_truncate(EXT4_JOURNAL(inode),
EXT4_I(inode)->jinode,
new_size);
}
/* * Test whether an inode is a fast symlink. * A fast symlink has its symlink data stored in ext4_inode_info->i_data.
*/ int ext4_inode_is_fast_symlink(struct inode *inode)
{ if (!ext4_has_feature_ea_inode(inode->i_sb)) { int ea_blocks = EXT4_I(inode)->i_file_acl ?
EXT4_CLUSTER_SIZE(inode->i_sb) >> 9 : 0;
/* * Called at the last iput() if i_nlink is zero.
*/ void ext4_evict_inode(struct inode *inode)
{
handle_t *handle; int err; /* * Credits for final inode cleanup and freeing: * sb + inode (ext4_orphan_del()), block bitmap, group descriptor * (xattr block freeing), bitmap, group descriptor (inode freeing)
*/ int extra_credits = 6; struct ext4_xattr_inode_array *ea_inode_array = NULL; bool freeze_protected = false;
trace_ext4_evict_inode(inode);
dax_break_layout_final(inode);
if (EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL)
ext4_evict_ea_inode(inode); if (inode->i_nlink) {
truncate_inode_pages_final(&inode->i_data);
goto no_delete;
}
if (is_bad_inode(inode)) goto no_delete;
dquot_initialize(inode);
if (ext4_should_order_data(inode))
ext4_begin_ordered_truncate(inode, 0);
truncate_inode_pages_final(&inode->i_data);
/* * For inodes with journalled data, transaction commit could have * dirtied the inode. And for inodes with dioread_nolock, unwritten * extents converting worker could merge extents and also have dirtied * the inode. Flush worker is ignoring it because of I_FREEING flag but * we still need to remove the inode from the writeback lists.
*/ if (!list_empty_careful(&inode->i_io_list))
inode_io_list_del(inode);
/* * Protect us against freezing - iput() caller didn't have to have any * protection against it. When we are in a running transaction though, * we are already protected against freezing and we cannot grab further * protection due to lock ordering constraints.
*/ if (!ext4_journal_current_handle()) {
sb_start_intwrite(inode->i_sb);
freeze_protected = true;
}
if (!IS_NOQUOTA(inode))
extra_credits += EXT4_MAXQUOTAS_DEL_BLOCKS(inode->i_sb);
/* * Block bitmap, group descriptor, and inode are accounted in both * ext4_blocks_for_truncate() and extra_credits. So subtract 3.
*/
handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE,
ext4_blocks_for_truncate(inode) + extra_credits - 3); if (IS_ERR(handle)) {
ext4_std_error(inode->i_sb, PTR_ERR(handle)); /* * If we're going to skip the normal cleanup, we still need to * make sure that the in-core orphan linked list is properly * cleaned up.
*/
ext4_orphan_del(NULL, inode); if (freeze_protected)
sb_end_intwrite(inode->i_sb); goto no_delete;
}
if (IS_SYNC(inode))
ext4_handle_sync(handle);
/* * Set inode->i_size to 0 before calling ext4_truncate(). We need * special handling of symlinks here because i_size is used to * determine whether ext4_inode_info->i_data contains symlink data or * block mappings. Setting i_size to 0 will remove its fast symlink * status. Erase i_data so that it becomes a valid empty block map.
*/ if (ext4_inode_is_fast_symlink(inode))
memset(EXT4_I(inode)->i_data, 0, sizeof(EXT4_I(inode)->i_data));
inode->i_size = 0;
err = ext4_mark_inode_dirty(handle, inode); if (err) {
ext4_warning(inode->i_sb, "couldn't mark inode dirty (err %d)", err); goto stop_handle;
} if (inode->i_blocks) {
err = ext4_truncate(inode); if (err) {
ext4_error_err(inode->i_sb, -err, "couldn't truncate inode %lu (err %d)",
inode->i_ino, err); goto stop_handle;
}
}
/* * Kill off the orphan record which ext4_truncate created. * AKPM: I think this can be inside the above `if'. * Note that ext4_orphan_del() has to be able to cope with the * deletion of a non-existent orphan - this is because we don't * know if ext4_truncate() actually created an orphan record. * (Well, we could do this if we need to, but heck - it works)
*/
ext4_orphan_del(handle, inode);
EXT4_I(inode)->i_dtime = (__u32)ktime_get_real_seconds();
/* * One subtle ordering requirement: if anything has gone wrong * (transaction abort, IO errors, whatever), then we can still * do these next steps (the fs will already have been marked as * having errors), but we can't free the inode if the mark_dirty * fails.
*/ if (ext4_mark_inode_dirty(handle, inode)) /* If that failed, just do the required in-core inode clear. */
ext4_clear_inode(inode); else
ext4_free_inode(handle, inode);
ext4_journal_stop(handle); if (freeze_protected)
sb_end_intwrite(inode->i_sb);
ext4_xattr_inode_array_free(ea_inode_array); return;
no_delete: /* * Check out some where else accidentally dirty the evicting inode, * which may probably cause inode use-after-free issues later.
*/
WARN_ON_ONCE(!list_empty_careful(&inode->i_io_list));
if (!list_empty(&EXT4_I(inode)->i_fc_list))
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM, NULL);
ext4_clear_inode(inode); /* We must guarantee clearing of inode... */
}
/* * Called with i_data_sem down, which is important since we can call * ext4_discard_preallocations() from here.
*/ void ext4_da_update_reserve_space(struct inode *inode, int used, int quota_claim)
{ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode);
spin_lock(&ei->i_block_reservation_lock);
trace_ext4_da_update_reserve_space(inode, used, quota_claim); if (unlikely(used > ei->i_reserved_data_blocks)) {
ext4_warning(inode->i_sb, "%s: ino %lu, used %d " "with only %d reserved data blocks",
__func__, inode->i_ino, used,
ei->i_reserved_data_blocks);
WARN_ON(1);
used = ei->i_reserved_data_blocks;
}
/* Update quota subsystem for data blocks */ if (quota_claim)
dquot_claim_block(inode, EXT4_C2B(sbi, used)); else { /* * We did fallocate with an offset that is already delayed * allocated. So on delayed allocated writeback we should * not re-claim the quota for fallocated blocks.
*/
dquot_release_reservation_block(inode, EXT4_C2B(sbi, used));
}
/* * If we have done all the pending block allocations and if * there aren't any writers on the inode, we can discard the * inode's preallocations.
*/ if ((ei->i_reserved_data_blocks == 0) &&
!inode_is_open_for_write(inode))
ext4_discard_preallocations(inode);
}
int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk,
ext4_lblk_t len)
{ int ret;
if (IS_ENCRYPTED(inode) && S_ISREG(inode->i_mode)) return fscrypt_zeroout_range(inode, lblk, pblk, len);
ret = sb_issue_zeroout(inode->i_sb, pblk, len, GFP_NOFS); if (ret > 0)
ret = 0;
return ret;
}
/* * For generic regular files, when updating the extent tree, Ext4 should * hold the i_rwsem and invalidate_lock exclusively. This ensures * exclusion against concurrent page faults, as well as reads and writes.
*/ #ifdef CONFIG_EXT4_DEBUG void ext4_check_map_extents_env(struct inode *inode)
{ if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY) return;
#ifdef ES_AGGRESSIVE_TEST staticvoid ext4_map_blocks_es_recheck(handle_t *handle, struct inode *inode, struct ext4_map_blocks *es_map, struct ext4_map_blocks *map, int flags)
{ int retval;
map->m_flags = 0; /* * There is a race window that the result is not the same. * e.g. xfstests #223 when dioread_nolock enables. The reason * is that we lookup a block mapping in extent status tree with * out taking i_data_sem. So at the time the unwritten extent * could be converted.
*/
down_read(&EXT4_I(inode)->i_data_sem); if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
retval = ext4_ext_map_blocks(handle, inode, map, 0);
} else {
retval = ext4_ind_map_blocks(handle, inode, map, 0);
}
up_read((&EXT4_I(inode)->i_data_sem));
/* * We don't check m_len because extent will be collpased in status * tree. So the m_len might not equal.
*/ if (es_map->m_lblk != map->m_lblk ||
es_map->m_flags != map->m_flags ||
es_map->m_pblk != map->m_pblk) {
printk("ES cache assertion failed for inode: %lu " "es_cached ex [%d/%d/%llu/%x] != " "found ex [%d/%d/%llu/%x] retval %d flags %x\n",
inode->i_ino, es_map->m_lblk, es_map->m_len,
es_map->m_pblk, es_map->m_flags, map->m_lblk,
map->m_len, map->m_pblk, map->m_flags,
retval, flags);
}
} #endif/* ES_AGGRESSIVE_TEST */
/* * If map2 is contiguous with map, then let's insert it as a single * extent in es cache and return the combined length of both the maps.
*/ if (map->m_pblk + map->m_len == map2.m_pblk &&
status == status2) {
ext4_es_insert_extent(inode, map->m_lblk,
map->m_len + map2.m_len, map->m_pblk,
status, false);
map->m_len += map2.m_len;
} else {
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
map->m_pblk, status, false);
}
return map->m_len;
}
staticint ext4_map_query_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags)
{ unsignedint status; int retval; unsignedint orig_mlen = map->m_len;
if (unlikely(retval != map->m_len)) {
ext4_warning(inode->i_sb, "ES len assertion failed for inode " "%lu: retval %d != map->m_len %d",
inode->i_ino, retval, map->m_len);
WARN_ON(1);
}
/* * No need to query next in leaf: * - if returned extent is not last in leaf or * - if the last in leaf is the full requested range
*/ if (!(map->m_flags & EXT4_MAP_QUERY_LAST_IN_LEAF) ||
map->m_len == orig_mlen) {
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
map->m_pblk, status, false); return retval;
}
/* * We pass in the magic EXT4_GET_BLOCKS_DELALLOC_RESERVE * indicates that the blocks and quotas has already been * checked when the data was copied into the page cache.
*/ if (map->m_flags & EXT4_MAP_DELAYED)
flags |= EXT4_GET_BLOCKS_DELALLOC_RESERVE;
/* * Here we clear m_flags because after allocating an new extent, * it will be set again.
*/
map->m_flags &= ~EXT4_MAP_FLAGS;
/* * We need to check for EXT4 here because migrate could have * changed the inode type in between.
*/ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
retval = ext4_ext_map_blocks(handle, inode, map, flags);
} else {
retval = ext4_ind_map_blocks(handle, inode, map, flags);
/* * We allocated new blocks which will result in i_data's * format changing. Force the migrate to fail by clearing * migrate flags.
*/ if (retval > 0 && map->m_flags & EXT4_MAP_NEW)
ext4_clear_inode_state(inode, EXT4_STATE_EXT_MIGRATE);
} if (retval <= 0) return retval;
if (unlikely(retval != map->m_len)) {
ext4_warning(inode->i_sb, "ES len assertion failed for inode %lu: " "retval %d != map->m_len %d",
inode->i_ino, retval, map->m_len);
WARN_ON(1);
}
/* * We have to zeroout blocks before inserting them into extent * status tree. Otherwise someone could look them up there and * use them before they are really zeroed. We also have to * unmap metadata before zeroing as otherwise writeback can * overwrite zeros with stale data from block device.
*/ if (flags & EXT4_GET_BLOCKS_ZERO &&
map->m_flags & EXT4_MAP_MAPPED && map->m_flags & EXT4_MAP_NEW) {
err = ext4_issue_zeroout(inode, map->m_lblk, map->m_pblk,
map->m_len); if (err) return err;
}
/* * If the extent has been zeroed out, we don't need to update * extent status tree.
*/ if (flags & EXT4_GET_BLOCKS_PRE_IO &&
ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) { if (ext4_es_is_written(&es)) return retval;
}
/* * The ext4_map_blocks() function tries to look up the requested blocks, * and returns if the blocks are already mapped. * * Otherwise it takes the write lock of the i_data_sem and allocate blocks * and store the allocated blocks in the result buffer head and mark it * mapped. * * If file type is extents based, it will call ext4_ext_map_blocks(), * Otherwise, call with ext4_ind_map_blocks() to handle indirect mapping * based files * * On success, it returns the number of blocks being mapped or allocated. * If flags doesn't contain EXT4_GET_BLOCKS_CREATE the blocks are * pre-allocated and unwritten, the resulting @map is marked as unwritten. * If the flags contain EXT4_GET_BLOCKS_CREATE, it will mark @map as mapped. * * It returns 0 if plain look up failed (blocks have not been allocated), in * that case, @map is returned as unmapped but we still do fill map->m_len to * indicate the length of a hole starting at map->m_lblk. * * It returns the error in case of allocation failure.
*/ int ext4_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags)
{ struct extent_status es; int retval; int ret = 0; unsignedint orig_mlen = map->m_len; #ifdef ES_AGGRESSIVE_TEST struct ext4_map_blocks orig_map;
/* * ext4_map_blocks returns an int, and m_len is an unsigned int
*/ if (unlikely(map->m_len > INT_MAX))
map->m_len = INT_MAX;
/* We can handle the block number less than EXT_MAX_BLOCKS */ if (unlikely(map->m_lblk >= EXT_MAX_BLOCKS)) return -EFSCORRUPTED;
/* * Callers from the context of data submission are the only exceptions * for regular files that do not hold the i_rwsem or invalidate_lock. * However, caching unrelated ranges is not permitted.
*/ if (flags & EXT4_GET_BLOCKS_IO_SUBMIT)
WARN_ON_ONCE(!(flags & EXT4_EX_NOCACHE)); else
ext4_check_map_extents_env(inode);
map->m_len = orig_mlen;
} /* * In the query cache no-wait mode, nothing we can do more if we * cannot find extent in the cache.
*/ if (flags & EXT4_GET_BLOCKS_CACHED_NOWAIT) return 0;
/* * Try to see if we can get the block without requesting a new * file system block.
*/
down_read(&EXT4_I(inode)->i_data_sem);
retval = ext4_map_query_blocks(handle, inode, map, flags);
up_read((&EXT4_I(inode)->i_data_sem));
found: if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
ret = check_block_validity(inode, map); if (ret != 0) return ret;
}
/* If it is only a block(s) look up */ if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) return retval;
/* * Returns if the blocks have already allocated * * Note that if blocks have been preallocated * ext4_ext_map_blocks() returns with buffer head unmapped
*/ if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) /* * If we need to convert extent to unwritten * we continue and do the actual work in * ext4_ext_map_blocks()
*/ if (!(flags & EXT4_GET_BLOCKS_CONVERT_UNWRITTEN)) return retval;
ext4_fc_track_inode(handle, inode); /* * New blocks allocate and/or writing to unwritten extent * will possibly result in updating i_data, so we take * the write lock of i_data_sem, and call get_block() * with create == 1 flag.
*/
down_write(&EXT4_I(inode)->i_data_sem);
retval = ext4_map_create_blocks(handle, inode, map, flags);
up_write((&EXT4_I(inode)->i_data_sem)); if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
ret = check_block_validity(inode, map); if (ret != 0) return ret;
/* * Inodes with freshly allocated blocks where contents will be * visible after transaction commit must be on transaction's * ordered data list.
*/ if (map->m_flags & EXT4_MAP_NEW &&
!(map->m_flags & EXT4_MAP_UNWRITTEN) &&
!(flags & EXT4_GET_BLOCKS_ZERO) &&
!ext4_is_quota_file(inode) &&
ext4_should_order_data(inode)) {
loff_t start_byte =
(loff_t)map->m_lblk << inode->i_blkbits;
loff_t length = (loff_t)map->m_len << inode->i_blkbits;
if (flags & EXT4_GET_BLOCKS_IO_SUBMIT)
ret = ext4_jbd2_inode_add_wait(handle, inode,
start_byte, length); else
ret = ext4_jbd2_inode_add_write(handle, inode,
start_byte, length); if (ret) return ret;
}
} if (retval > 0 && (map->m_flags & EXT4_MAP_UNWRITTEN ||
map->m_flags & EXT4_MAP_MAPPED))
ext4_fc_track_range(handle, inode, map->m_lblk,
map->m_lblk + map->m_len - 1); if (retval < 0)
ext_debug(inode, "failed with err %d\n", retval); return retval;
}
/* * Update EXT4_MAP_FLAGS in bh->b_state. For buffer heads attached to pages * we have to be careful as someone else may be manipulating b_state as well.
*/ staticvoid ext4_update_bh_state(struct buffer_head *bh, unsignedlong flags)
{ unsignedlong old_state; unsignedlong new_state;
flags &= EXT4_MAP_FLAGS;
/* Dummy buffer_head? Set non-atomically. */ if (!bh->b_folio) {
bh->b_state = (bh->b_state & ~EXT4_MAP_FLAGS) | flags; return;
} /* * Someone else may be modifying b_state. Be careful! This is ugly but * once we get rid of using bh as a container for mapping information * to pass to / from get_block functions, this can go away.
*/
old_state = READ_ONCE(bh->b_state); do {
new_state = (old_state & ~EXT4_MAP_FLAGS) | flags;
} while (unlikely(!try_cmpxchg(&bh->b_state, &old_state, new_state)));
}
/* * Make sure that the current journal transaction has enough credits to map * one extent. Return -EAGAIN if it cannot extend the current running * transaction.
*/ staticinlineint ext4_journal_ensure_extent_credits(handle_t *handle, struct inode *inode)
{ int credits; int ret;
/* Called from ext4_da_write_begin() which has no handle started? */ if (!handle) return 0;
credits = ext4_chunk_trans_blocks(inode, 1);
ret = __ext4_journal_ensure_credits(handle, credits, credits, 0); return ret <= 0 ? ret : -EAGAIN;
}
staticint _ext4_get_block(struct inode *inode, sector_t iblock, struct buffer_head *bh, int flags)
{ struct ext4_map_blocks map; int ret = 0;
/* * Get block function used when preparing for buffered write if we require * creating an unwritten extent if blocks haven't been allocated. The extent * will be converted to written after the IO is complete.
*/ int ext4_get_block_unwritten(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create)
{ int ret = 0;
ext4_debug("ext4_get_block_unwritten: inode %lu, create flag %d\n",
inode->i_ino, create);
ret = _ext4_get_block(inode, iblock, bh_result,
EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT);
/* * If the buffer is marked unwritten, mark it as new to make sure it is * zeroed out correctly in case of partial writes. Otherwise, there is * a chance of stale data getting exposed.
*/ if (ret == 0 && buffer_unwritten(bh_result))
set_buffer_new(bh_result);
return ret;
}
/* Maximum number of blocks we map for direct IO at once. */ #define DIO_MAX_BLOCKS 4096
/* * `handle' can be NULL if create is zero
*/ struct buffer_head *ext4_getblk(handle_t *handle, struct inode *inode,
ext4_lblk_t block, int map_flags)
{ struct ext4_map_blocks map; struct buffer_head *bh; int create = map_flags & EXT4_GET_BLOCKS_CREATE; bool nowait = map_flags & EXT4_GET_BLOCKS_CACHED_NOWAIT; int err;
if (err == 0) return create ? ERR_PTR(-ENOSPC) : NULL; if (err < 0) return ERR_PTR(err);
if (nowait) return sb_find_get_block(inode->i_sb, map.m_pblk);
/* * Since bh could introduce extra ref count such as referred by * journal_head etc. Try to avoid using __GFP_MOVABLE here * as it may fail the migration when journal_head remains.
*/
bh = getblk_unmovable(inode->i_sb->s_bdev, map.m_pblk,
inode->i_sb->s_blocksize);
if (unlikely(!bh)) return ERR_PTR(-ENOMEM); if (map.m_flags & EXT4_MAP_NEW) {
ASSERT(create != 0);
ASSERT((EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY)
|| (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");
err = ext4_journal_get_create_access(handle, inode->i_sb, bh,
EXT4_JTR_NONE); if (unlikely(err)) {
unlock_buffer(bh); goto errout;
} if (!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_handle_dirty_metadata");
err = ext4_handle_dirty_metadata(handle, inode, bh); if (unlikely(err)) goto errout;
} else
BUFFER_TRACE(bh, "not a new buffer"); return bh;
errout:
brelse(bh); return ERR_PTR(err);
}
struct buffer_head *ext4_bread(handle_t *handle, struct inode *inode,
ext4_lblk_t block, int map_flags)
{ struct buffer_head *bh; int ret;
bh = ext4_getblk(handle, inode, block, map_flags); if (IS_ERR(bh)) return bh; if (!bh || ext4_buffer_uptodate(bh)) return bh;
ret = ext4_read_bh_lock(bh, REQ_META | REQ_PRIO, true); if (ret) {
put_bh(bh); return ERR_PTR(ret);
} return bh;
}
/* Read a contiguous batch of blocks. */ int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, bool wait, struct buffer_head **bhs)
{ int i, err;
for (i = 0; i < bh_count; i++) {
bhs[i] = ext4_getblk(NULL, inode, block + i, 0 /* map_flags */); if (IS_ERR(bhs[i])) {
err = PTR_ERR(bhs[i]);
bh_count = i; goto out_brelse;
}
}
for (i = 0; i < bh_count; i++) /* Note that NULL bhs[i] is valid because of holes. */ if (bhs[i] && !ext4_buffer_uptodate(bhs[i]))
ext4_read_bh_lock(bhs[i], REQ_META | REQ_PRIO, false);
if (!wait) return 0;
for (i = 0; i < bh_count; i++) if (bhs[i])
wait_on_buffer(bhs[i]);
for (i = 0; i < bh_count; i++) { if (bhs[i] && !buffer_uptodate(bhs[i])) {
err = -EIO; goto out_brelse;
}
} return 0;
out_brelse: for (i = 0; i < bh_count; i++) {
brelse(bhs[i]);
bhs[i] = NULL;
} return err;
}
int ext4_walk_page_buffers(handle_t *handle, struct inode *inode, struct buffer_head *head, unsigned from, unsigned to, int *partial, int (*fn)(handle_t *handle, struct inode *inode, 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, inode, bh); if (!ret)
ret = err;
} return ret;
}
/* * Helper for handling dirtying of journalled data. We also mark the folio as * dirty so that writeback code knows about this page (and inode) contains * dirty data. ext4_writepages() then commits appropriate transaction to * make data stable.
*/ staticint ext4_dirty_journalled_data(handle_t *handle, struct buffer_head *bh)
{ struct folio *folio = bh->b_folio; struct inode *inode = folio->mapping->host;
/* only regular files have a_ops */ if (S_ISREG(inode->i_mode))
folio_mark_dirty(folio); return ext4_handle_dirty_metadata(handle, NULL, bh);
}
head = folio_buffers(folio); if (!head)
head = create_empty_buffers(folio, blocksize, 0);
bbits = ilog2(blocksize);
block = (sector_t)folio->index << (PAGE_SHIFT - bbits);
for (bh = head, block_start = 0; bh != head || !block_start;
block++, block_start = block_end, bh = bh->b_this_page) {
block_end = block_start + blocksize; if (block_end <= from || block_start >= to) { if (folio_test_uptodate(folio)) {
set_buffer_uptodate(bh);
} continue;
} if (WARN_ON_ONCE(buffer_new(bh)))
clear_buffer_new(bh); if (!buffer_mapped(bh)) {
WARN_ON(bh->b_size != blocksize);
err = ext4_journal_ensure_extent_credits(handle, inode); if (!err)
err = get_block(inode, block, bh, 1); if (err) break; if (buffer_new(bh)) { /* * We may be zeroing partial buffers or all new * buffers in case of failure. Prepare JBD2 for * that.
*/ if (should_journal_data)
do_journal_get_write_access(handle,
inode, bh); if (folio_test_uptodate(folio)) { /* * Unlike __block_write_begin() we leave * dirtying of new uptodate buffers to * ->write_end() time or * folio_zero_new_buffers().
*/
set_buffer_uptodate(bh); continue;
} if (block_end > to || block_start < from)
folio_zero_segments(folio, to,
block_end,
block_start, from); continue;
}
} if (folio_test_uptodate(folio)) {
set_buffer_uptodate(bh); continue;
} if (!buffer_uptodate(bh) && !buffer_delay(bh) &&
!buffer_unwritten(bh) &&
(block_start < from || block_end > to)) {
ext4_read_bh_lock(bh, 0, false);
wait[nr_wait++] = bh;
}
} /* * If we issued read requests, let them complete.
*/ for (i = 0; i < nr_wait; i++) {
wait_on_buffer(wait[i]); if (!buffer_uptodate(wait[i]))
err = -EIO;
} if (unlikely(err)) { if (should_journal_data)
ext4_journalled_zero_new_buffers(handle, inode, folio,
from, to); else
folio_zero_new_buffers(folio, from, to);
} elseif (fscrypt_inode_uses_fs_layer_crypto(inode)) { for (i = 0; i < nr_wait; i++) { int err2;
/* * 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 ext4_write_end(). So doing the jbd2_journal_start at the start of * ext4_write_begin() is the right place.
*/ staticint ext4_write_begin(conststruct kiocb *iocb, struct address_space *mapping,
loff_t pos, unsigned len, struct folio **foliop, void **fsdata)
{ struct inode *inode = mapping->host; int ret, needed_blocks;
handle_t *handle; int retries = 0; struct folio *folio;
pgoff_t index; unsigned from, to;
ret = ext4_emergency_state(inode->i_sb); if (unlikely(ret)) return ret;
trace_ext4_write_begin(inode, pos, len); /* * Reserve one block more for addition to orphan list in case * we allocate blocks but write fails for some reason
*/
needed_blocks = ext4_chunk_trans_extent(inode,
ext4_journal_blocks_per_folio(inode)) + 1;
index = pos >> PAGE_SHIFT;
if (ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
ret = ext4_try_to_write_inline_data(mapping, inode, pos, len,
foliop); if (ret < 0) return ret; if (ret == 1) return 0;
}
/* * write_begin_get_folio() can take a long time if the * system is thrashing due to memory pressure, or if the folio * is being written back. So grab it first before we start * the transaction handle. This also allows us to allocate * the folio (if needed) without using GFP_NOFS.
*/
retry_grab:
folio = write_begin_get_folio(iocb, mapping, index, len); if (IS_ERR(folio)) return PTR_ERR(folio);
if (pos + len > folio_pos(folio) + folio_size(folio))
len = folio_pos(folio) + folio_size(folio) - pos;
from = offset_in_folio(folio, pos);
to = from + len;
/* * The same as page allocation, we prealloc buffer heads before * starting the handle.
*/ if (!folio_buffers(folio))
create_empty_buffers(folio, inode->i_sb->s_blocksize, 0);
folio_lock(folio); if (folio->mapping != mapping) { /* The folio got truncated from under us */
folio_unlock(folio);
folio_put(folio);
ext4_journal_stop(handle); goto retry_grab;
} /* In case writeback began while the folio was unlocked */
folio_wait_stable(folio);
if (ext4_should_dioread_nolock(inode))
ret = ext4_block_write_begin(handle, folio, pos, len,
ext4_get_block_unwritten); else
ret = ext4_block_write_begin(handle, folio, pos, len,
ext4_get_block); if (!ret && ext4_should_journal_data(inode)) {
ret = ext4_walk_page_buffers(handle, inode,
folio_buffers(folio), from, to,
NULL, do_journal_get_write_access);
}
if (ret) { bool extended = (pos + len > inode->i_size) &&
!ext4_verity_in_progress(inode);
folio_unlock(folio); /* * ext4_block_write_begin may have instantiated a few blocks * outside i_size. Trim these off again. Don't need * i_size_read because we hold i_rwsem. * * Add inode to orphan list in case we crash before * truncate finishes
*/ if (extended && ext4_can_truncate(inode))
ext4_orphan_add(handle, inode);
ext4_journal_stop(handle); if (extended) {
ext4_truncate_failed_write(inode); /* * If truncate failed early the inode might * still be on the orphan list; we need to * make sure the inode is removed from the * orphan list in that case.
*/ if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
}
/* For write_end() in data=journal mode */ staticint write_end_fn(handle_t *handle, struct inode *inode, struct buffer_head *bh)
{ int ret; if (!buffer_mapped(bh) || buffer_freed(bh)) return 0;
set_buffer_uptodate(bh);
ret = ext4_dirty_journalled_data(handle, bh);
clear_buffer_meta(bh);
clear_buffer_prio(bh);
clear_buffer_new(bh); return ret;
}
/* * We need to pick up the new inode size which generic_commit_write gave us * `iocb` can be NULL - eg, when called from page_symlink(). * * ext4 never places buffers on inode->i_mapping->i_private_list. metadata * buffers are managed internally.
*/ staticint ext4_write_end(conststruct kiocb *iocb, struct address_space *mapping,
loff_t pos, unsigned len, unsigned copied, struct folio *folio, void *fsdata)
{
handle_t *handle = ext4_journal_current_handle(); struct inode *inode = mapping->host;
loff_t old_size = inode->i_size; int ret = 0, ret2; int i_size_changed = 0; bool verity = ext4_verity_in_progress(inode);
copied = block_write_end(pos, len, copied, folio); /* * it's important to update i_size while still holding folio lock: * page writeout could otherwise come in and zero beyond i_size. * * If FS_IOC_ENABLE_VERITY is running on this inode, then Merkle tree * blocks are being written past EOF, so skip the i_size update.
*/ if (!verity)
i_size_changed = ext4_update_inode_size(inode, pos + copied);
folio_unlock(folio);
folio_put(folio);
if (old_size < pos && !verity) {
pagecache_isize_extended(inode, old_size, pos);
ext4_zero_partial_blocks(handle, inode, old_size, pos - old_size);
} /* * Don't mark the inode dirty under folio lock. First, it unnecessarily * makes the holding time of folio lock longer. Second, it forces lock * ordering of folio lock and transaction start for journaling * filesystems.
*/ if (i_size_changed)
ret = ext4_mark_inode_dirty(handle, inode);
if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode)) /* if we have allocated more blocks and copied * less. We will have blocks allocated outside * inode->i_size. So truncate them
*/
ext4_orphan_add(handle, inode);
ret2 = ext4_journal_stop(handle); if (!ret)
ret = ret2;
if (pos + len > inode->i_size && !verity) {
ext4_truncate_failed_write(inode); /* * If truncate failed early the inode might still be * on the orphan list; we need to make sure the inode * is removed from the orphan list in that case.
*/ if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
}
return ret ? ret : copied;
}
/* * This is a private version of folio_zero_new_buffers() which doesn't * set the buffer to be dirty, since in data=journalled mode we need * to call ext4_dirty_journalled_data() instead.
*/ staticvoid ext4_journalled_zero_new_buffers(handle_t *handle, struct inode *inode, struct folio *folio, unsigned from, unsigned to)
{ unsignedint block_start = 0, block_end; struct buffer_head *head, *bh;
bh = head = folio_buffers(folio); do {
block_end = block_start + bh->b_size; if (buffer_new(bh)) { if (block_end > from && block_start < to) { if (!folio_test_uptodate(folio)) { unsigned start, size;
if (size_changed) {
ret2 = ext4_mark_inode_dirty(handle, inode); if (!ret)
ret = ret2;
}
if (pos + len > inode->i_size && !verity && ext4_can_truncate(inode)) /* if we have allocated more blocks and copied * less. We will have blocks allocated outside * inode->i_size. So truncate them
*/
ext4_orphan_add(handle, inode);
ret2 = ext4_journal_stop(handle); if (!ret)
ret = ret2; if (pos + len > inode->i_size && !verity) {
ext4_truncate_failed_write(inode); /* * If truncate failed early the inode might still be * on the orphan list; we need to make sure the inode * is removed from the orphan list in that case.
*/ if (inode->i_nlink)
ext4_orphan_del(NULL, inode);
}
return ret ? ret : copied;
}
/* * Reserve space for 'nr_resv' clusters
*/ staticint ext4_da_reserve_space(struct inode *inode, int nr_resv)
{ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct ext4_inode_info *ei = EXT4_I(inode); int ret;
/* * We will charge metadata quota at writeout time; this saves * us from metadata over-estimation, though we may go over by * a small amount in the end. Here we just reserve for data.
*/
ret = dquot_reserve_block(inode, EXT4_C2B(sbi, nr_resv)); if (ret) return ret;
trace_ext4_da_release_space(inode, to_free); if (unlikely(to_free > ei->i_reserved_data_blocks)) { /* * if there aren't enough reserved blocks, then the * counter is messed up somewhere. Since this * function is called from invalidate page, it's * harmless to return without any action.
*/
ext4_warning(inode->i_sb, "ext4_da_release_space: " "ino %lu, to_free %d with only %d reserved " "data blocks", inode->i_ino, to_free,
ei->i_reserved_data_blocks);
WARN_ON(1);
to_free = ei->i_reserved_data_blocks;
}
ei->i_reserved_data_blocks -= to_free;
/* update fs dirty data blocks counter */
percpu_counter_sub(&sbi->s_dirtyclusters_counter, to_free);
struct mpage_da_data { /* These are input fields for ext4_do_writepages() */ struct inode *inode; struct writeback_control *wbc; unsignedint can_map:1; /* Can writepages call map blocks? */
/* These are internal state of ext4_do_writepages() */
loff_t start_pos; /* The start pos to write */
loff_t next_pos; /* Current pos to examine */
loff_t end_pos; /* Last pos to examine */
/* * Extent to map - this can be after start_pos because that can be * fully mapped. We somewhat abuse m_flags to store whether the extent * is delalloc or unwritten.
*/ struct ext4_map_blocks map; struct ext4_io_submit io_submit; /* IO submission data */ unsignedint do_map:1; unsignedint scanned_until_end:1; unsignedint journalled_more_data:1;
};
/* This is necessary when next_pos == 0. */ if (mpd->start_pos >= mpd->next_pos) return;
mpd->scanned_until_end = 0; if (invalidate) {
ext4_lblk_t start, last;
start = EXT4_B_TO_LBLK(inode, mpd->start_pos);
last = mpd->next_pos >> inode->i_blkbits;
/* * avoid racing with extent status tree scans made by * ext4_insert_delayed_block()
*/
down_write(&EXT4_I(inode)->i_data_sem);
ext4_es_remove_extent(inode, start, last - start);
up_write(&EXT4_I(inode)->i_data_sem);
}
folio_batch_init(&fbatch);
index = mpd->start_pos >> PAGE_SHIFT;
end = mpd->next_pos >> PAGE_SHIFT; while (index < end) {
nr = filemap_get_folios(mapping, &index, end - 1, &fbatch); if (nr == 0) break; for (i = 0; i < nr; i++) { struct folio *folio = fbatch.folios[i];
if (folio_pos(folio) < mpd->start_pos) continue; if (folio_next_index(folio) > end) continue;
BUG_ON(!folio_test_locked(folio));
BUG_ON(folio_test_writeback(folio)); if (invalidate) { if (folio_mapped(folio))
folio_clear_dirty_for_io(folio);
block_invalidate_folio(folio, 0,
folio_size(folio));
folio_clear_uptodate(folio);
}
folio_unlock(folio);
}
folio_batch_release(&fbatch);
}
}
/* * Check whether the cluster containing lblk has been allocated or has * delalloc reservation. * * Returns 0 if the cluster doesn't have either, 1 if it has delalloc * reservation, 2 if it's already been allocated, negative error code on * failure.
*/ staticint ext4_clu_alloc_state(struct inode *inode, ext4_lblk_t lblk)
{ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); int ret;
/* Has delalloc reservation? */ if (ext4_es_scan_clu(inode, &ext4_es_is_delayed, lblk)) return 1;
/* Already been allocated? */ if (ext4_es_scan_clu(inode, &ext4_es_is_mapped, lblk)) return 2;
ret = ext4_clu_mapped(inode, EXT4_B2C(sbi, lblk)); if (ret < 0) return ret; if (ret > 0) return 2;
return 0;
}
/* * ext4_insert_delayed_blocks - adds a multiple delayed blocks to the extents * status tree, incrementing the reserved * cluster/block count or making pending * reservations where needed * * @inode - file containing the newly added block * @lblk - start logical block to be added * @len - length of blocks to be added * * Returns 0 on success, negative error code on failure.
*/ staticint ext4_insert_delayed_blocks(struct inode *inode, ext4_lblk_t lblk,
ext4_lblk_t len)
{ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); int ret; bool lclu_allocated = false; bool end_allocated = false;
ext4_lblk_t resv_clu;
ext4_lblk_t end = lblk + len - 1;
/* * If the cluster containing lblk or end is shared with a delayed, * written, or unwritten extent in a bigalloc file system, it's * already been accounted for and does not need to be reserved. * A pending reservation must be made for the cluster if it's * shared with a written or unwritten extent and doesn't already * have one. Written and unwritten extents can be purged from the * extents status tree if the system is under memory pressure, so * it's necessary to examine the extent tree if a search of the * extents status tree doesn't get a match.
*/ if (sbi->s_cluster_ratio == 1) {
ret = ext4_da_reserve_space(inode, len); if (ret != 0) /* ENOSPC */ return ret;
} else { /* bigalloc */
resv_clu = EXT4_B2C(sbi, end) - EXT4_B2C(sbi, lblk) + 1;
ret = ext4_clu_alloc_state(inode, lblk); if (ret < 0) return ret; if (ret > 0) {
resv_clu--;
lclu_allocated = (ret == 2);
}
if (EXT4_B2C(sbi, lblk) != EXT4_B2C(sbi, end)) {
ret = ext4_clu_alloc_state(inode, end); if (ret < 0) return ret; if (ret > 0) {
resv_clu--;
end_allocated = (ret == 2);
}
}
if (resv_clu) {
ret = ext4_da_reserve_space(inode, resv_clu); if (ret != 0) /* ENOSPC */ return ret;
}
}
/* * Looks up the requested blocks and sets the delalloc extent map. * First try to look up for the extent entry that contains the requested * blocks in the extent status tree without i_data_sem, then try to look * up for the ondisk extent mapping with i_data_sem in read mode, * finally hold i_data_sem in write mode, looks up again and add a * delalloc extent entry if it still couldn't find any extent. Pass out * the mapped extent through @map and return 0 on success.
*/ staticint ext4_da_map_blocks(struct inode *inode, struct ext4_map_blocks *map)
{ struct extent_status es; int retval; #ifdef ES_AGGRESSIVE_TEST struct ext4_map_blocks orig_map;
/* Lookup extent status tree firstly */ if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
map->m_len = min_t(unsignedint, map->m_len,
es.es_len - (map->m_lblk - es.es_lblk));
if (ext4_es_is_hole(&es)) goto add_delayed;
found: /* * Delayed extent could be allocated by fallocate. * So we need to check it.
*/ if (ext4_es_is_delayed(&es)) {
map->m_flags |= EXT4_MAP_DELAYED; return 0;
}
/* * Try to see if we can get the block without requesting a new * file system block.
*/
down_read(&EXT4_I(inode)->i_data_sem); if (ext4_has_inline_data(inode))
retval = 0; else
retval = ext4_map_query_blocks(NULL, inode, map, 0);
up_read(&EXT4_I(inode)->i_data_sem); if (retval) return retval < 0 ? retval : 0;
add_delayed:
down_write(&EXT4_I(inode)->i_data_sem); /* * Page fault path (ext4_page_mkwrite does not take i_rwsem) * and fallocate path (no folio lock) can race. Make sure we * lookup the extent status tree here again while i_data_sem * is held in write mode, before inserting a new da entry in * the extent status tree.
*/ if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) {
map->m_len = min_t(unsignedint, map->m_len,
es.es_len - (map->m_lblk - es.es_lblk));
/* * This is a special get_block_t callback which is used by * ext4_da_write_begin(). It will either return mapped block or * reserve space for a single block. * * For delayed buffer_head we have BH_Mapped, BH_New, BH_Delay set. * We also have b_blocknr = -1 and b_bdev initialized properly * * For unwritten buffer_head we have BH_Mapped, BH_New, BH_Unwritten set. * We also have b_blocknr = physicalblock mapping unwritten extent and b_bdev * initialized properly.
*/ int ext4_da_get_block_prep(struct inode *inode, sector_t iblock, struct buffer_head *bh, int create)
{ struct ext4_map_blocks map;
sector_t invalid_block = ~((sector_t) 0xffff); int ret = 0;
if (invalid_block < ext4_blocks_count(EXT4_SB(inode->i_sb)->s_es))
invalid_block = ~0;
map.m_lblk = iblock;
map.m_len = 1;
/* * first, we need to know whether the block is allocated already * preallocated blocks are unmapped but should treated * the same as allocated blocks.
*/
ret = ext4_da_map_blocks(inode, &map); if (ret < 0) return ret;
if (buffer_unwritten(bh)) { /* A delayed write to unwritten bh should be marked * new and mapped. Mapped ensures that we don't do * get_block multiple times when we write to the same * offset and new ensures that we do proper zero out * for partial write.
*/
set_buffer_new(bh);
set_buffer_mapped(bh);
} return 0;
}
WARN_ON_ONCE(folio_pos(folio) != mpd->start_pos);
folio_clear_dirty_for_io(folio); /* * We have to be very careful here! Nothing protects writeback path * against i_size changes and the page can be writeably mapped into * page tables. So an application can be growing i_size and writing * data through mmap while writeback runs. folio_clear_dirty_for_io() * write-protects our page in page tables and the page cannot get * written to again until we release folio lock. So only after * folio_clear_dirty_for_io() we are safe to sample i_size for * ext4_bio_write_folio() to zero-out tail of the written page. We rely * on the barrier provided by folio_test_clear_dirty() in * folio_clear_dirty_for_io() to make sure i_size is really sampled only * after page tables are updated.
*/
size = i_size_read(mpd->inode);
len = folio_size(folio); if (folio_pos(folio) + len > size &&
!ext4_verity_in_progress(mpd->inode))
len = size & (len - 1);
err = ext4_bio_write_folio(&mpd->io_submit, folio, len);
/* * mballoc gives us at most this number of blocks... * XXX: That seems to be only a limitation of ext4_mb_normalize_request(). * The rest of mballoc seems to handle chunks up to full group size.
*/ #define MAX_WRITEPAGES_EXTENT_LEN 2048
/* * mpage_add_bh_to_extent - try to add bh to extent of blocks to map * * @mpd - extent of blocks * @lblk - logical number of the block in the file * @bh - buffer head we want to add to the extent * * The function is used to collect contig. blocks in the same state. If the * buffer doesn't require mapping for writeback and we haven't started the * extent of buffers to map yet, the function returns 'true' immediately - the * caller can write the buffer right away. Otherwise the function returns true * if the block has been added to the extent, false if the block couldn't be * added.
*/ staticbool mpage_add_bh_to_extent(struct mpage_da_data *mpd, ext4_lblk_t lblk, struct buffer_head *bh)
{ struct ext4_map_blocks *map = &mpd->map;
/* Buffer that doesn't need mapping for writeback? */ if (!buffer_dirty(bh) || !buffer_mapped(bh) ||
(!buffer_delay(bh) && !buffer_unwritten(bh))) { /* So far no extent to map => we write the buffer right away */ if (map->m_len == 0) returntrue; returnfalse;
}
/* First block in the extent? */ if (map->m_len == 0) { /* We cannot map unless handle is started... */ if (!mpd->do_map) returnfalse;
map->m_lblk = lblk;
map->m_len = 1;
map->m_flags = bh->b_state & BH_FLAGS; returntrue;
}
/* Don't go larger than mballoc is willing to allocate */ if (map->m_len >= MAX_WRITEPAGES_EXTENT_LEN) returnfalse;
/* Can we merge the block to our big extent? */ if (lblk == map->m_lblk + map->m_len &&
(bh->b_state & BH_FLAGS) == map->m_flags) {
map->m_len++; returntrue;
} returnfalse;
}
/* * mpage_process_page_bufs - submit page buffers for IO or add them to extent * * @mpd - extent of blocks for mapping * @head - the first buffer in the page * @bh - buffer we should start processing from * @lblk - logical number of the block in the file corresponding to @bh * * Walk through page buffers from @bh upto @head (exclusive) and either submit * the page for IO if all buffers in this page were mapped and there's no * accumulated extent of buffers to map or add buffers in the page to the * extent of buffers to map. The function returns 1 if the caller can continue * by processing the next page, 0 if it should stop adding buffers to the * extent to map because we cannot extend it anymore. It can also return value * < 0 in case of error during IO submission.
*/ staticint mpage_process_page_bufs(struct mpage_da_data *mpd, struct buffer_head *head, struct buffer_head *bh,
ext4_lblk_t lblk)
{ struct inode *inode = mpd->inode; int err;
ext4_lblk_t blocks = (i_size_read(inode) + i_blocksize(inode) - 1)
>> inode->i_blkbits;
if (ext4_verity_in_progress(inode))
blocks = EXT_MAX_BLOCKS;
do {
BUG_ON(buffer_locked(bh));
if (lblk >= blocks || !mpage_add_bh_to_extent(mpd, lblk, bh)) { /* Found extent to map? */ if (mpd->map.m_len) return 0; /* Buffer needs mapping and handle is not started? */ if (!mpd->do_map) return 0; /* Everything mapped so far and we hit EOF */ break;
}
} while (lblk++, (bh = bh->b_this_page) != head); /* So far everything mapped? Submit the page for IO. */ if (mpd->map.m_len == 0) {
err = mpage_submit_folio(mpd, head->b_folio); if (err < 0) return err;
mpage_folio_done(mpd, head->b_folio);
} if (lblk >= blocks) {
mpd->scanned_until_end = 1; return 0;
} return 1;
}
/* * mpage_process_folio - update folio buffers corresponding to changed extent * and may submit fully mapped page for IO * @mpd: description of extent to map, on return next extent to map * @folio: Contains these buffers. * @m_lblk: logical block mapping. * @m_pblk: corresponding physical mapping. * @map_bh: determines on return whether this page requires any further * mapping or not. * * Scan given folio buffers corresponding to changed extent and update buffer * state according to new extent state. * We map delalloc buffers to their physical location, clear unwritten bits. * If the given folio is not fully mapped, we update @mpd to the next extent in * the given folio that needs mapping & return @map_bh as true.
*/ staticint mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio,
ext4_lblk_t *m_lblk, ext4_fsblk_t *m_pblk, bool *map_bh)
{ struct buffer_head *head, *bh;
ext4_io_end_t *io_end = mpd->io_submit.io_end;
ext4_lblk_t lblk = *m_lblk;
ext4_fsblk_t pblock = *m_pblk; int err = 0; int blkbits = mpd->inode->i_blkbits;
ssize_t io_end_size = 0; struct ext4_io_end_vec *io_end_vec = ext4_last_io_end_vec(io_end);
bh = head = folio_buffers(folio); do { if (lblk < mpd->map.m_lblk) continue; if (lblk >= mpd->map.m_lblk + mpd->map.m_len) { /* * Buffer after end of mapped extent. * Find next buffer in the folio to map.
*/
mpd->map.m_len = 0;
mpd->map.m_flags = 0;
io_end_vec->size += io_end_size;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.54Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.