2012-02-16 10:51:43

by Robin Dong

[permalink] [raw]
Subject: [RFC] new infrastructure for manipulating different extent format

Hi Ted, Andreas and the list,

After the bigalloc-feature is completed in ext4, we could have much
more big size of block-group (also bigger continuous space), so larger
extent size which is above 128MB is necessary, such as:

struct ext4_extent_large {
? ? ? ?__le64 ?ee_block; ? ? ? /* first logical block extent covers */
? ? ? ?__le64 ?ee_start; ? ? ? /* starting physical block */
? ? ? ?__le32 ?ee_len; ? ? ? ? /* number of blocks covered by extent */
? ? ? ?__le32 ?ee_flags; ? ? ? /* flags and future extension */
};

On the other hand, people who use ext4 in desktop environment may need
more compact format like:

struct ext4_extent_packed {
? ? ? ?__le32 ?ee_start_lo;
? ? ? ?__le16 ?ee_start_hi;
? ? ? ?__le16 ?ee_len;
};

which they can fit 6 extents in the inode.

Therefore, supporting multiple extent formats is the solution which
satisfied both.

My Plan:

First, abstracting the operation on disk extent format out:

struct ext4_ext_operation {
? ? ? ? __u16 ? ? ? ? ? (*ext_size) (void);
/* the size of struct ext4_extent_### */
? ? ? ? ext4_fsblk_t ? ?(*ext_pblock) (void *extent);
? ? ? ? void ? ? ? ? ? ?(*ext_store_pblock) (void *extent, __u64 pb);
? ? ? ? ext4_lblk_t ? ? (*ext_lblock) (void *extent);
? ? ? ? ext4_lblk_t ? ? (*ext_len) (void *extent);
? ? ? ? int ? ? ? ? ? ? (*is_uninit) (void *extent);
? ? ? ? void ? ? ? ? ? ?(*mark_uninit) (void *extent);
? ? ? ? void ? ? ? ? ? ?(*mark_init) (void *extent);

? ? ? ? __u16 ? ? ? ? ? (*idx_size) (void);
? ? ? ? ext4_fsblk_t ? ?(*idx_pblock) (void *idx);
? ? ? ? void ? ? ? ? ? ?(*idx_store_pblock) (void *idx, __u64 pb);
? ? ? ? ext4_lblk_t ? ? (*idx_lblock) (void *idx);
};

We can use this single entry to handle different extent format through
different implementation of ext4_ext_operation (choosing different
implementation by different eh_magic number),
and the extent format which is used now could be implemented like:

static const struct ext4_ext_operation ext4_normal_eops= {
? ? ? ? .ext_size ? ? ? ? ? ? ? = ext4_ext_size,
? ? ? ? .ext_pblock ? ? ? ? ? ? = ext4_ext_pblock,
? ? ? ? .ext_store_pblock ? ? ? = ext4_ext_store_pblock,
? ? ? ? .ext_lblock ? ? ? ? ? ? = ext4_ext_lblock,
? ? ? ? .ext_len ? ? ? ? ? ? ? ?= ext4_ext_get_actual_len,
? ? ? ? .is_uninit ? ? ? ? ? ? ?= ext4_ext_is_uninitialized,
? ? ? ? .mark_uninit ? ? ? ? ? ?= ext4_ext_mark_uninitialized,
? ? ? ? .mark_init ? ? ? ? ? ? ?= ext4_ext_mark_initialized,

? ? ? ? .idx_size ? ? ? ? ? ? ? = ext4_idx_size,
? ? ? ? .idx_pblock ? ? ? ? ? ? = ext4_idx_pblock,
? ? ? ? .idx_store_pblock ? ? ? = ext4_idx_store_pblock,
? ? ? ? .idx_lblock ? ? ? ? ? ? = ext4_idx_lblock,
};

For instance, the function ext4_ext_binsearch_idx() may look like:

static void ext4_ext_binsearch_idx(struct inode *inode,
? ? ? ? ? ? ? ? ? ? ? ? struct ext4_ext_path *path, ext4_lblk_t block)
{
? ? ? ? struct ext4_extent_header *eh = path->p_hdr;
? ? ? ? struct void *r, *l, *m;
? ? ? ? struct ext4_ext_operation *op = (struct
ext4_ext_operation*)inode->private;

? ? ? ? l = (char *) (eh) + sizeof(struct ext4_extent_header) +
op->idx_size();
? ? ? ? r = l + (le16_to_cpu(eh)->eh_entries) * op->idx_size();
? ? ? ? while (l <= r) {
? ? ? ? ? ? ? ? m = l + op->idx_size() * ((r - l) / op->idx_size())/
2;
? ? ? ? ? ? ? ? if (block < op->idx_lblock(m))
? ? ? ? ? ? ? ? ? ? ? ? r = m - op->idx_size();
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? ? ? l = m + op->idx_size();
}
path->p_idx = l - op->idx_size();
}

The only problem is the performance regression caused by frequent
function calls.

Any suggestions will be appreciated.

--
--
Best Regard
Robin Dong