Add structs representing on-disk format of the commit header and tlvs in the commit header. Also, add helpers to arrange data in on-disk fast commit format. Signed-off-by: Harshad Shirwadkar <harshadshirwadkar@xxxxxxxxx> --- fs/ext4/ext4_jbd2.c | 163 ++++++++++++++++++++++++++++++++++++++++++++ fs/ext4/ext4_jbd2.h | 45 ++++++++++++ 2 files changed, 208 insertions(+) diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 9e060ba927c1..9e34aa560ea1 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -4,6 +4,7 @@ */ #include "ext4_jbd2.h" +#include "ext4_extents.h" #include <trace/events/ext4.h> @@ -604,6 +605,168 @@ void ext4_fc_track_range(struct inode *inode, ext4_lblk_t start, trace_ext4_fc_track_range(inode, start, end, ret); } + +/* + * Adds tag, length and value at memory pointed to by dst. Returns + * true if tlv was added. Returns false if there's not enough space. + * If successful also updates *dst to point to the end of this tlv. + */ +static bool fc_try_add_tlv(u8 **dst, u8 *end, u16 tag, u16 len, u8 *val) +{ + struct ext4_fc_tl tl; + + if (*dst + sizeof(tl) + len >= end) + return false; + + tl.fc_tag = cpu_to_le16(tag); + tl.fc_len = cpu_to_le16(len); + memcpy(*dst, &tl, sizeof(tl)); + memcpy(*dst + sizeof(tl), val, len); + + *dst = *dst + sizeof(tl) + len; + return true; +} + +/* Same as above, but tries to add dentry tlv. */ +static bool fc_try_add_dentry_info_tlv(u8 **dst, u8 *end, u16 tag, + int parent_ino, int ino, int dlen, + const unsigned char *dname) +{ + struct ext4_fc_dentry_info fcd; + struct ext4_fc_tl tl; + + + if (*dst + sizeof(tl) + sizeof(fcd) + dlen >= end) + return false; + + fcd.fc_parent_ino = cpu_to_le32(parent_ino); + fcd.fc_ino = cpu_to_le32(ino); + tl.fc_tag = cpu_to_le16(tag); + tl.fc_len = cpu_to_le16(sizeof(fcd) + dlen); + memcpy(*dst, &tl, sizeof(tl)); + *dst += sizeof(tl); + memcpy(*dst, &fcd, sizeof(fcd)); + *dst += sizeof(fcd); + memcpy(*dst, dname, dlen); + *dst += dlen; + + return true; +} + +/* Get length of a particular tlv */ +static int fc_tag_len(struct ext4_fc_tl *tl) +{ + return le16_to_cpu(tl->fc_len); +} + +/* Get a pointer to "value" of a tlv */ +static u8 *fc_tag_val(struct ext4_fc_tl *tl) +{ + return (u8 *)tl + sizeof(*tl); +} + +/* + * Writes fast commit header and inode structure at memory + * pointed to by start. Returns 0 on success, error on failure. + * If successful, *last is upadated to point to the end of + * inode that was copied. + */ +static int fc_write_hdr(struct inode *inode, u8 *start, u8 *end, + u8 **last) +{ + struct ext4_fc_commit_hdr *fc_hdr = (struct ext4_fc_commit_hdr *)start; + struct ext4_inode_info *ei = EXT4_I(inode); + int inode_len = EXT4_GOOD_OLD_INODE_SIZE; + struct ext4_iloc iloc; + u8 *cur = start; + int ret; + + if (ext4_is_inode_fc_ineligible(inode)) + return -ECANCELED; + + ret = ext4_get_inode_loc(inode, &iloc); + if (ret) + return ret; + + fc_hdr->fc_magic = cpu_to_le32(EXT4_FC_MAGIC); + fc_hdr->fc_ino = cpu_to_le32(inode->i_ino); + fc_hdr->fc_features = 0; + fc_hdr->fc_csum = 0; + + cur = (u8 *)(fc_hdr + 1); + if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) + inode_len += ei->i_extra_isize; + if (cur + inode_len >= end) + return -ECANCELED; + + memcpy(cur, ext4_raw_inode(&iloc), inode_len); + cur += inode_len; + *last = cur; + + return 0; +} + +/* + * Writes data tags (EXT4_FC_TAG_ADD_RANGE / EXT4_FC_TAG_DEL_RANGE) + * at memory pointed to by start. Returns number of TLVs that were + * added if successfully. Returns errors otherwise. + */ +static int fc_write_data(struct inode *inode, u8 *start, u8 *end, + u8 **last) +{ + ext4_lblk_t old_blk_size, cur_lblk_off, new_blk_size; + struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_map_blocks map; + struct ext4_extent extent; + struct ext4_fc_lrange lrange; + u8 *cur = start; + int num_tlvs = 0; + int ret; + + write_lock(&ei->i_fc_lock); + old_blk_size = ei->i_fc_lblk_start; + new_blk_size = ei->i_fc_lblk_end; + ei->i_fc_lblk_start = ei->i_fc_lblk_end; + write_unlock(&ei->i_fc_lock); + + cur_lblk_off = old_blk_size; + jbd_debug(1, "%s: will try writing %ld to %ld for inode %ld\n", + __func__, cur_lblk_off, new_blk_size, inode->i_ino); + while (cur_lblk_off <= new_blk_size) { + map.m_lblk = cur_lblk_off; + map.m_len = new_blk_size - cur_lblk_off + 1; + ret = ext4_map_blocks(NULL, inode, &map, 0); + if (ret < 0) + return ret; + if (map.m_len == 0) + return -ECANCELED; + if (map.m_flags & EXT4_MAP_UNWRITTEN) + return -ECANCELED; + + cur_lblk_off += map.m_len; + if (ret == 0) { + lrange.fc_lblk = cpu_to_le32(map.m_lblk); + lrange.fc_len = cpu_to_le32(map.m_len); + if (!fc_try_add_tlv(&cur, end, EXT4_FC_TAG_DEL_RANGE, + sizeof(lrange), (u8 *)&lrange)) + return -ENOSPC; + + } else { + extent.ee_block = cpu_to_le32(map.m_lblk); + extent.ee_len = cpu_to_le16(map.m_len); + ext4_ext_store_pblock(&extent, map.m_pblk); + ext4_ext_mark_initialized(&extent); + if (!fc_try_add_tlv(&cur, end, EXT4_FC_TAG_ADD_RANGE, + sizeof(struct ext4_extent), (u8 *)&extent)) + return -ENOSPC; + } + num_tlvs++; + } + *last = cur; + + return num_tlvs; +} + void ext4_init_fast_commit(struct super_block *sb, journal_t *journal) { if (!ext4_should_fast_commit(sb)) diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 60f484377c2e..d3a951d2e58d 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -470,7 +470,52 @@ static inline int ext4_should_dioread_nolock(struct inode *inode) return 1; } +/* Ext4 fast commit related info */ + +/* Magic of fast commit header */ +#define EXT4_FC_MAGIC 0xE2540090 + #define EXT4_NUM_FC_BLKS 128 + +struct ext4_fc_commit_hdr { + /* Fast commit magic, should be EXT4_FC_MAGIC */ + __le32 fc_magic; + /* Features used by this fast commit block */ + __u8 fc_features; + /* Number of TLVs in this fast commmit block */ + __le16 fc_num_tlvs; + /* Inode number */ + __le32 fc_ino; + /* Csum(hdr+contents) */ + __le32 fc_csum; +}; + +/* Fast commit on disk tag length structure */ +struct ext4_fc_tl { + __le16 fc_tag; + __le16 fc_len; +}; + +/* On disk fast commit tlv value structure for dirent tags: + * - EXT4_FC_TAG_CREATE_DENTRY + * - EXT4_FC_TAG_ADD_DENTRY + * - EXT4_FC_TAG_DEL_DENTRY + */ +struct ext4_fc_dentry_info { + __le32 fc_parent_ino; + __le32 fc_ino; + u8 fc_dname[0]; +}; + +/* + * On disk fast commit tlv value structure for tag + * EXT4_FC_TAG_HOLE. + */ +struct ext4_fc_lrange { + __le32 fc_lblk; + __le32 fc_len; +}; + void ext4_init_fast_commit(struct super_block *sb, journal_t *journal); void ext4_init_inode_fc_info(struct inode *inode); void ext4_fc_track_range(struct inode *inode, ext4_lblk_t start, -- 2.24.1.735.g03f4e72817-goog