Ext3 code review regarding 64bit block number

Author: Laurent Vivier <Laurent.Vivier@bull.net>
Status: Draft
Last Modification: 2005/06/20
Copyright (c) 2005 Bull S.A.

RED shows fields that should be changed to address 64bit block number.
ORANGE shows fields that should be changed if we want to count blocks on a 64bit value (for instance, number of blocks in a partition)
GREEN shows fields we should add.

1. ON-DISK structures

1.1. include/linux/ext3_fs.h

1.1.1. SuperBlock

struct ext3_super_block {
/*00*/  __le32  s_inodes_count;         /* Inodes count */
        __le32  s_blocks_count;         /* Blocks count */
        __le32  s_r_blocks_count;       /* Reserved blocks count */
        __le32  s_free_blocks_count;    /* Free blocks count */

These fields count the total number of block in a partition.
If partition is big enough to address block using 64bit values,
we can have a total number of block (a counter) on 64bit.

/*10*/ 
__le32  s_free_inodes_count;    /* Free inodes count */
        __le32  s_first_data_block;     /* First Data Block */

s_first_data_block should remain __le32 : its value is ever 0 or 1.


        __le32  s_log_block_size;       /* Block size */
        __le32  s_log_frag_size;        /* Fragment size */
/*20*/  __le32  s_blocks_per_group;     /* # Blocks per group */
        __le32  s_frags_per_group;      /* # Fragments per group */
        __le32  s_inodes_per_group;     /* # Inodes per group */

blocks, frags and inodes per group are limited by the bitmap size.
64bit counter should not be needed.

        __le32  s_mtime;                /* Mount time */

/*30*/  __le32  s_wtime;                /* Write time */
        __le16  s_mnt_count;            /* Mount count */
        __le16  s_max_mnt_count;        /* Maximal mount count */
        __le16  s_magic;                /* Magic signature */
        __le16  s_state;                /* File system state */
        __le16  s_errors;               /* Behaviour when detecting errors */
        __le16  s_minor_rev_level;      /* minor revision level */
/*40*/  __le32  s_lastcheck;            /* time of last check */
        __le32  s_checkinterval;        /* max. time between checks */
        __le32  s_creator_os;           /* OS */
        __le32  s_rev_level;            /* Revision level */
/*50*/  __le16  s_def_resuid;           /* Default uid for reserved blocks */
        __le16  s_def_resgid;           /* Default gid for reserved blocks */
        __le32  s_first_ino;            /* First non-reserved inode */
        __le16  s_inode_size;           /* size of inode structure */

See struct ext3_inode for comment

        __le16  s_block_group_nr;       /* block group # of this superblock */

Numbers of block in a group is 8 x numbers_of_bits_in_a_block.

With 4096 bytes block, we can manage 32768 (215) blocks in a group,
and thus maximum size of a group is 32768 x 4096 = 128 MiB,
 
and if the maximum number of blocks in a partition is 232
( = 16 TiB filesystem size with 4Ki block size),  the needed number of block
is
232/ 215 = 232-15217 = 131072 groups.

If we have a 64 bit disk, maximum number of groups is

264
/ 215 = 264-15249

        __le32  s_feature_compat;       /* compatible feature set */
/*60*/  __le32  s_feature_incompat;     /* incompatible feature set */
        __le32  s_feature_ro_compat;    /* readonly-compatible feature set */
/*68*/  __u8    s_uuid[16];             /* 128-bit uuid for volume */
/*78*/  char    s_volume_name[16];      /* volume name */
/*88*/  char    s_last_mounted[64];     /* directory where last mounted */
/*C8*/  __le32  s_algorithm_usage_bitmap; /* For compression */
        __u8    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
        __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
        __u16   s_reserved_gdt_blocks;  /* Per group desc for online growth */
/*D0*/  __u8    s_journal_uuid[16];     /* uuid of journal superblock */
/*E0*/  __le32  s_journal_inum;         /* inode number of journal file */
        __le32  s_journal_dev;          /* device number of journal file */
        __le32  s_last_orphan;          /* start of list of inodes to delete */
        __le32  s_hash_seed[4];         /* HTREE hash seed */
        __u8    s_def_hash_version;     /* Default hash version to use */
        __u8    s_reserved_char_pad;
        __u16   s_reserved_word_pad;
        __le32  s_default_mount_opts;
        __le32  s_first_meta_bg;        /* First metablock block group */
/*FC*/  __u32   s_reserved[190];        /* Padding to the end of the block */
};

should become:

struct ext3_super_block {
/*00*/  __le32  s_inodes_count;         /* Inodes count */
        __le32  s_blocks_count;         /* Blocks count */
        __le32  s_r_blocks_count;       /* Reserved blocks count */
        __le32  s_free_blocks_count;    /* Free blocks count */
/*10*/  __le32  s_free_inodes_count;    /* Free inodes count */
        __le32  s_first_data_block;     /* First Data Block */
        __le32  s_log_block_size;       /* Block size */
        __le32  s_log_frag_size;        /* Fragment size */
/*20*/  __le32  s_blocks_per_group;     /* # Blocks per group */
        __le32  s_frags_per_group;      /* # Fragments per group */
        __le32  s_inodes_per_group;     /* # Inodes per group */
        __le32  s_mtime;                /* Mount time */
/*30*/  __le32  s_wtime;                /* Write time */
        __le16  s_mnt_count;            /* Mount count */
        __le16  s_max_mnt_count;        /* Maximal mount count */
        __le16  s_magic;                /* Magic signature */
        __le16  s_state;                /* File system state */
        __le16  s_errors;               /* Behaviour when detecting errors */
        __le16  s_minor_rev_level;      /* minor revision level */
/*40*/  __le32  s_lastcheck;            /* time of last check */
        __le32  s_checkinterval;        /* max. time between checks */
        __le32  s_creator_os;           /* OS */
        __le32  s_rev_level;            /* Revision level */
/*50*/  __le16  s_def_resuid;           /* Default uid for reserved blocks */
        __le16  s_def_resgid;           /* Default gid for reserved blocks */
        __le32  s_first_ino;            /* First non-reserved inode */
        __le16  s_inode_size;           /* size of inode structure */
        __le16  s_block_group_nr;       /* block group # of this superblock */
        __le32  s_feature_compat;       /* compatible feature set */
/*60*/  __le32  s_feature_incompat;     /* incompatible feature set */
        __le32  s_feature_ro_compat;    /* readonly-compatible feature set */
/*68*/  __u8    s_uuid[16];             /* 128-bit uuid for volume */
/*78*/  char    s_volume_name[16];      /* volume name */
/*88*/  char    s_last_mounted[64];     /* directory where last mounted */
/*C8*/  __le32  s_algorithm_usage_bitmap; /* For compression */
        __u8    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
        __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
        __u16   s_reserved_gdt_blocks;  /* Per group desc for online growth */
/*D0*/  __u8    s_journal_uuid[16];     /* uuid of journal superblock */
/*E0*/  __le32  s_journal_inum;         /* inode number of journal file */
        __le32  s_journal_dev;          /* device number of journal file */
        __le32  s_last_orphan;          /* start of list of inodes to delete */
        __le32  s_hash_seed[4];         /* HTREE hash seed */
/*F0*/  __u8    s_def_hash_version;     /* Default hash version to use */
        __u8    s_reserved_char_pad;
        __u16   s_reserved_word_pad;
        __le32  s_default_mount_opts;
        __le32  s_first_meta_bg;        /* First metablock block group */
/*FC*/  __le32  s_hi_blocks_count;         /* Blocks count */
        __le32  s_hi_r_blocks_count;       /* Reserved blocks count */
        __le32  s_hi_free_blocks_count;    /* Free blocks count */
        __le32  s_hi_block_group_nr;       /* block group # of this superblock */
/*10C*/ __u32   s_reserved[180];        /* Padding to the end of the block */
};

If we want to reduce structure size, we could let s_block_group_nr to le16 as it seems it is only used for debug purpose.

1.1.2. Group Descriptor

struct ext3_group_desc
{
        __le32  bg_block_bitmap;        /* Blocks bitmap block */
        __le32  bg_inode_bitmap;        /* Inodes bitmap block */
        __le32  bg_inode_table;         /* Inodes table block */
        __le16  bg_free_blocks_count;   /* Free blocks count */
        __le16  bg_free_inodes_count;   /* Free inodes count */
        __le16  bg_used_dirs_count;     /* Directories count */
        __u16   bg_pad;
        __le32  bg_reserved[3];
};

should become:

struct ext3_group_desc
{
        __le32  bg_block_bitmap;        /* Blocks bitmap block */
        __le32  bg_inode_bitmap;        /* Inodes bitmap block */
        __le32  bg_inode_table;         /* Inodes table block */
        __le16  bg_free_blocks_count;   /* Free blocks count */
        __le16  bg_free_inodes_count;   /* Free inodes count */
        __le16  bg_used_dirs_count;     /* Directories count */
        __u16   bg_pad;
        __le32  bg_hi_block_bitmap;     /* Blocks bitmap block */
        __le32  bg_hi_inode_bitmap;     /* Inodes bitmap block */
        __le32  bg_hi_inode_table;      /* Inodes table block */
};

If we don't want to store high bits for all these fields, we can use a relative addressing mode to the group base address.
For instance, if bg_block_bitmap is lesser than the base address of the group, we add the base address to this 32bit value.

And according to Andreas Dilger, we should swap to the METABG layout:

"A quick calculation shows that the group descriptor table itself
will become too large to store in the first group before the filesystem
grows to 512TB:

<2^15 blocks/group * 2^12 bytes/block / 32 bytes/gdt < 2^22 gdt/group
2^15 blocks/group * 2^12 bytes/block * 2^22 group = 2^49 bytes = 512TB

This means at or before 512TB we will have to go to the METABG layout
to store the group descriptors in their respective groups, so we may
as well do this at 16TB and just do as you propose.
"

1.1.3. Inode

struct ext3_inode {
/*00*/  __le16  i_mode;         /* File mode */
        __le16  i_uid;          /* Low 16 bits of Owner Uid */
        __le32  i_size;         /* Size in bytes */
        __le32  i_atime;        /* Access time */
        __le32  i_ctime;        /* Creation time */
/*10*/  __le32  i_mtime;        /* Modification time */
        __le32  i_dtime;        /* Deletion Time */
        __le16  i_gid;          /* Low 16 bits of Group Id */
        __le16  i_links_count;  /* Links count */
        __le32  i_blocks;       /* Blocks count */
/*20*/  __le32  i_flags;        /* File flags */
        union {
                struct {
                        __u32  l_i_reserved1;
                } linux1;
                struct {
                        __u32  h_i_translator;
                } hurd1;
                struct {
                        __u32  m_i_reserved1;
                } masix1;
        } osd1;                         /* OS dependent 1 */
/*28*/  __le32  i_block[EXT3_N_BLOCKS];/* Pointers to blocks */
/*64*/  __le32  i_generation;   /* File version (for NFS) */
        __le32  i_file_acl;     /* File ACL */
        __le32  i_dir_acl;      /* Directory ACL */

i_size and i_dir_acl are used to define a 64bit i_size
(in fs/ext3/inode.c, ext3_read_inode())

/*70*/  __le32  i_faddr;        /* Fragment address */

This fields (and following) seems not used in linux.
 
        union {
                struct {
                        __u8    l_i_frag;       /* Fragment number */
                        __u8    l_i_fsize;      /* Fragment size */
                        __u16   i_pad1;
                        __le16  l_i_uid_high;   /* these 2 fields    */
                        __le16  l_i_gid_high;   /* were reserved2[0] */
                        __u32   l_i_reserved2;
                } linux2;
                struct {
                        __u8    h_i_frag;       /* Fragment number */
                        __u8    h_i_fsize;      /* Fragment size */
                        __u16   h_i_mode_high;
                        __u16   h_i_uid_high;
                        __u16   h_i_gid_high;
                        __u32   h_i_author;
                } hurd2;
                struct {
                        __u8    m_i_frag;       /* Fragment number */
                        __u8    m_i_fsize;      /* Fragment size */
                        __u16   m_pad1;
                        __u32   m_i_reserved2[2];
                } masix2;
        } osd2;                         /* OS dependent 2 */
/*80*/  __le16  i_extra_isize;
        __le16  i_pad1;
/*84*/};

WE SHOULD USE DYNAMIC INODE SIZE TO STORE 64BIT BLOCK NUMBER (i_extra_size)
or
SHOULD BE BASED ON EXTENTS PATCH (use 48bit block number)

struct ext3_inode {
/*00*/  __le16  i_mode;         /* File mode */
        __le16  i_uid;          /* Low 16 bits of Owner Uid */
        __le32  i_size;         /* Size in bytes */
        __le32  i_atime;        /* Access time */
        __le32  i_ctime;        /* Creation time */
/*10*/  __le32  i_mtime;        /* Modification time */
        __le32  i_dtime;        /* Deletion Time */
        __le16  i_gid;          /* Low 16 bits of Group Id */
        __le16  i_links_count;  /* Links count */
        __le32  i_blocks;       /* Blocks count */
/*20*/  __le32  i_flags;        /* File flags */
        union {
                struct {
                        __u32  l_i_reserved1;
                } linux1;
                struct {
                        __u32  h_i_translator;
                } hurd1;
                struct {
                        __u32  m_i_reserved1;
                } masix1;
        } osd1;                         /* OS dependent 1 */
/*28*/  __le32  i_block[EXT3_N_BLOCKS];/* Pointers to blocks */
/*64*/  __le32  i_generation;   /* File version (for NFS) */
        __le32  i_file_acl;     /* File ACL */
        __le32  i_dir_acl;      /* Directory ACL */
/*70*/  __le32  i_faddr;        /* Fragment address */
        union {
                struct {
                        __u8    l_i_frag;       /* Fragment number */
                        __u8    l_i_fsize;      /* Fragment size */
                        __u16   i_pad1;
                        __le16  l_i_uid_high;   /* these 2 fields    */
                        __le16  l_i_gid_high;   /* were reserved2[0] */
                        __u32   l_i_reserved2;
                } linux2;
                struct {
                        __u8    h_i_frag;       /* Fragment number */
                        __u8    h_i_fsize;      /* Fragment size */
                        __u16   h_i_mode_high;
                        __u16   h_i_uid_high;
                        __u16   h_i_gid_high;
                        __u32   h_i_author;
                } hurd2;
                struct {
                        __u8    m_i_frag;       /* Fragment number */
                        __u8    m_i_fsize;      /* Fragment size */
                        __u16   m_pad1;
                        __u32   m_i_reserved2[2];
                } masix2;
        } osd2;                         /* OS dependent 2 */
/*80*/  __le16  i_extra_isize;
        __le16  i_pad1;
/*84*/  __le32  i_hi_blocks;       /* Blocks count */
        __le32  i_hi_block[EXT3_N_BLOCKS];/* Pointers to blocks */
/*C0*/};

could impact superblock s_inode_size field.

If we use extents patch, we don't need to increase inode size:

1.1.4. New Group

struct ext3_new_group_input {
        __u32 group;            /* Group number for this data */
        __u32 block_bitmap;     /* Absolute block number of block bitmap */
        __u32 inode_bitmap;     /* Absolute block number of inode bitmap */
        __u32 inode_table;      /* Absolute block number of inode table start */
        __u32 blocks_count;     /* Total number of blocks in this group */
        __u16 reserved_blocks;  /* Number of reserved blocks in this group */
        __u16 unused;
};

struct ext3_new_group_data {
        __u32 group;
        __u32 block_bitmap;
        __u32 inode_bitmap;
        __u32 inode_table;
        __u32 blocks_count;
        __u16 reserved_blocks;
        __u16 unused;
        __u32 free_blocks_count;
};

As a size of a group is limited by the size of the bitmap block, blocks_count and
free_blocks_count can stay a __u32.

These structures are used in an ioctl(), the best solution to keep compatibility is to define new 64bit structure and new ioctl() calls.

struct ext3_new_group_input64 {
        __u32 group;            /* Group number for this data */
        __u64 block_bitmap;     /* Absolute block number of block bitmap */
        __u64 inode_bitmap;     /* Absolute block number of inode bitmap */
        __u64 inode_table;      /* Absolute block number of inode table start */
        __u32 blocks_count;     /* Total number of blocks in this group */
        __u16 reserved_blocks;  /* Number of reserved blocks in this group */
        __u16 unused;
};

struct ext3_new_group_data64 {
        __u32 group;
        __u64 block_bitmap;
        __u64 inode_bitmap;
        __u64 inode_table;
        __u32 blocks_count;
        __u16 reserved_blocks;
        __u16 unused;
        __u32 free_blocks_count;
};

and we should define a new iocl command:

#define EXT3_IOC_GROUP_ADD64              _IOW('f', 9,struct ext3_new_group_input64)

2. IN-MEMORY structures

2.1. include/linux/ext3_fs_i.h

struct ext3_reserve_window {
        __u32                   _rsv_start;     /* First byte reserved */
        __u32                   _rsv_end;       /* Last byte reserved or 0 */
};

struct ext3_inode_info {
        __le32  i_data[15];     /* unconverted */
        __u32   i_flags;
#ifdef EXT3_FRAGMENTS
        __u32   i_faddr;
        __u8    i_frag_no;
        __u8    i_frag_size;
#endif
        __u32   i_file_acl;
        __u32   i_dir_acl;
        __u32   i_dtime;
        __u32   i_block_group;
        __u32   i_state;                /* Dynamic state flags for ext3 */
        __u32   i_next_alloc_block;
        __u32   i_next_alloc_goal;
        struct ext3_reserve_window_node i_rsv_window;

Size of i_rsv_window changes according to ext3_reserve_window node
                                                                             
        __u32   i_dir_start_lookup;
#ifdef CONFIG_EXT3_FS_XATTR
        struct rw_semaphore xattr_sem;
#endif
#ifdef CONFIG_EXT3_FS_POSIX_ACL
        struct posix_acl        *i_acl;
        struct posix_acl        *i_default_acl;
#endif
                                                                                                      
        struct list_head i_orphan;      /* unlinked but open inodes */
        loff_t  i_disksize;
 
        /* on-disk additional length */
        __u16 i_extra_isize;
 
        struct semaphore truncate_sem;
        struct inode vfs_inode;
};

should become

struct ext3_reserve_window {
        sector_t                   _rsv_start;     /* First byte reserved */
        sector_t                   _rsv_end;       /* Last byte reserved or 0 */
};

struct ext3_inode_info {
        __le32  i_data[15];     /* unconverted */
        __u32   i_flags;
#ifdef EXT3_FRAGMENTS
        sector_t i_faddr;
        __u8    i_frag_no;
        __u8    i_frag_size;
#endif
        __u32   i_file_acl;
        __u32   i_dir_acl;
        __u32   i_dtime;
        group_t i_block_group;
        __u32   i_state;                /* Dynamic state flags for ext3 */
        sector_t i_next_alloc_block;
        sector_t i_next_alloc_goal;
        struct ext3_reserve_window_node i_rsv_window;
        __u32   i_dir_start_lookup;
#ifdef CONFIG_EXT3_FS_XATTR
        struct rw_semaphore xattr_sem;
#endif
#ifdef CONFIG_EXT3_FS_POSIX_ACL
        struct posix_acl        *i_acl;
        struct posix_acl        *i_default_acl;
#endif
                                                                                                      
        struct list_head i_orphan;      /* unlinked but open inodes */
        loff_t  i_disksize;
 
        /* on-disk additional length */
        __u16 i_extra_isize;
 
        struct semaphore truncate_sem;
        struct inode vfs_inode;
};

2.2. include/linux/ext3_fs_sb.h

struct ext3_sb_info {
        unsigned long s_frag_size;      /* Size of a fragment in bytes */
        unsigned long s_frags_per_block;/* Number of fragments per block */
        unsigned long s_inodes_per_block;/* Number of inodes per block */
        unsigned long s_frags_per_group;/* Number of fragments in a group */
        unsigned long s_blocks_per_group;/* Number of blocks in a group */
        unsigned long s_inodes_per_group;/* Number of inodes in a group */
        unsigned long s_itb_per_group;  /* Number of inode table blocks per group */
        unsigned long s_gdb_count;      /* Number of group descriptor blocks */
        unsigned long s_desc_per_block; /* Number of group descriptors per block */
        unsigned long s_groups_count;   /* Number of groups in the fs */

See before

        struct buffer_head * s_sbh;     /* Buffer containing the super block */
        struct ext3_super_block * s_es; /* Pointer to the super block in the buffer */
        struct buffer_head ** s_group_desc;
        unsigned long  s_mount_opt;
        uid_t s_resuid;
        gid_t s_resgid;
        unsigned short s_mount_state;
        unsigned short s_pad;
        int s_addr_per_block_bits;
        int s_desc_per_block_bits;
        int s_inode_size;
        int s_first_ino;
        spinlock_t s_next_gen_lock;
        u32 s_next_generation;
        u32 s_hash_seed[4];
        int s_def_hash_version;
        struct percpu_counter s_freeblocks_counter;
        struct percpu_counter s_freeinodes_counter;
        struct percpu_counter s_dirs_counter;

should manage sector_t type.

        struct blockgroup_lock s_blockgroup_lock;
                                                                               
        /* root of the per fs reservation window tree */
        spinlock_t s_rsv_window_lock;
        struct rb_root s_rsv_window_root;
        struct ext3_reserve_window_node s_rsv_window_head;
                                                                               
        /* Journaling */
        struct inode * s_journal_inode;
        struct journal_s * s_journal;
        struct list_head s_orphan;
        unsigned long s_commit_interval;
        struct block_device *journal_bdev;
#ifdef CONFIG_JBD_DEBUG
        struct timer_list turn_ro_timer;        /* For turning read-only (crash
simulation) */
        wait_queue_head_t ro_wait_queue;        /* For people waiting for the fs to go read-only */
#endif
#ifdef CONFIG_QUOTA
        char *s_qf_names[MAXQUOTAS];            /* Names of quota files with journalled quota */
        int s_jquota_fmt;                       /* Format of quota to use */
#endif
};

should become

struct ext3_sb_info {
        unsigned long s_frag_size;      /* Size of a fragment in bytes */
        unsigned long s_frags_per_block;/* Number of fragments per block */
        unsigned long s_inodes_per_block;/* Number of inodes per block */
        unsigned long s_frags_per_group;/* Number of fragments in a group */
        unsigned long s_blocks_per_group;/* Number of blocks in a group */
        unsigned long s_inodes_per_group;/* Number of inodes in a group */
        unsigned long s_itb_per_group;  /* Number of inode table blocks per group */
        unsigned long s_gdb_count;      /* Number of group descriptor blocks */
        unsigned long s_desc_per_block; /* Number of group descriptors per block */
        group_t s_groups_count;   /* Number of groups in the fs */
        struct buffer_head * s_sbh;     /* Buffer containing the super block */
        struct ext3_super_block * s_es; /* Pointer to the super block in the buffer */
        struct buffer_head ** s_group_desc;
        unsigned long  s_mount_opt;
        uid_t s_resuid;
        gid_t s_resgid;
        unsigned short s_mount_state;
        unsigned short s_pad;
        int s_addr_per_block_bits;
        int s_desc_per_block_bits;
        int s_inode_size;
        int s_first_ino;
        spinlock_t s_next_gen_lock;
        u32 s_next_generation;
        u32 s_hash_seed[4];
        int s_def_hash_version;
        struct percpu_counter s_freeblocks_counter;
        struct percpu_counter s_freeinodes_counter;
        struct percpu_counter s_dirs_counter;
        struct blockgroup_lock s_blockgroup_lock;
                                                                               
        /* root of the per fs reservation window tree */
        spinlock_t s_rsv_window_lock;
        struct rb_root s_rsv_window_root;
        struct ext3_reserve_window_node s_rsv_window_head;
                                                                               
        /* Journaling */
        struct inode * s_journal_inode;
        struct journal_s * s_journal;
        struct list_head s_orphan;
        unsigned long s_commit_interval;
        struct block_device *journal_bdev;
#ifdef CONFIG_JBD_DEBUG
        struct timer_list turn_ro_timer;        /* For turning read-only (crash simulation) */
        wait_queue_head_t ro_wait_queue;        /* For people waiting for the fs to go read-only */
#endif
#ifdef CONFIG_QUOTA
        char *s_qf_names[MAXQUOTAS];            /* Names of quota files with journalled quota */
        int s_jquota_fmt;                       /* Format of quota to use */
#endif
};

2.3. include/linux/percpu_counter.h

Add a 64bit compatible version of "percpu_counter".

#ifdef CONFIG_SMP
                                                                                               
struct percpu_counter {
        spinlock_t lock;
        long count;
        long *counters;
};
...
#else
...
struct percpu_counter {
        long count;
};

should become

#ifdef CONFIG_SMP
                                                                                               
struct percpu_counter {
        spinlock_t lock;
#ifdef CONFIG_LBD
        long long count;
        long long *counters;
#else
        long count;
        long *counters;

#endif
};
...
#else
...
struct percpu_counter {
#ifdef CONFIG_LBD
        long long count;
#else
        long count;
#endif
};

3. Functions

3.1. Helpers

We use some functions inspired by JBD 64bit support patch by Zach Brown in "jbd and 64 bit block numbers"
at http://sourceforge.net/mailarchive/message.php?msg_id=9996513
void ext3_write_split_le64(struct super_block * sb, __le32 *hi, __le32 *lo, u64 val)
{
    *low = cpu_to_le32(val & (u32)~0);
    if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_64BIT))
    *high = cpu_to_le32(val >> 32);
}

u64 ext3_read_split_le64(struct super_block * sb, __le32 *hi, __le32 *lo)
{
    u64 ret = le32_to_cpu(*low);
    if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_64BIT))
    ret |= (u64)le32_to_cpu(*high) << 32;
    return ret;
}

We use "sector_t" as defined in "include/asm-<arch>/types.h". It should be "u64" if "CONFIG_LBD" is defined, 
"unsigned long" otherwise (see "include/linux/types.h").

We should define "group_t" to count the number of groups (see 1.1.1.), based on "CONFIG_LBD" too.

3.2. fs/ext3/acl.c

N/A

3.3. fs/ext3/acl.h

N/A

3.4. fs/ext3/balloc.c

3.4.1. ext3_get_group_desc()

block_group, group_desc and desc are group numbers and thus should become "group_t".
struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb,
                                             unsigned int block_group,
                                             struct buffer_head ** bh)
{
        unsigned long group_desc;
        unsigned long desc;
        struct ext3_group_desc * gdp;
                                                                               
        if (block_group >= EXT3_SB(sb)->s_groups_count) {
                ext3_error (sb, "ext3_get_group_desc",
                            "block_group >= groups_count - "
                            "block_group = %d, groups_count = %lu",
                            block_group, EXT3_SB(sb)->s_groups_count);
...
                ext3_error (sb, "ext3_get_group_desc",
                            "Group descriptor not loaded - "
                            "block_group = %d, group_desc = %lu, desc = %lu",
                             block_group, group_desc, desc);
...

should become

struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb,
                                             group_t block_group,
                                             struct buffer_head ** bh)
{
        group_t group_desc;
        group_t desc;
        struct ext3_group_desc * gdp;
                                                                               
        if (block_group >= EXT3_SB(sb)->s_groups_count) {
                ext3_error (sb, "ext3_get_group_desc",
                            "block_group >= groups_count - "
                            "block_group = %lld, groups_count = %llu",
                            (unsigned long long)block_group,
                            (unsigned long long)EXT3_SB(sb)->s_groups_count);
...
                ext3_error (sb, "ext3_get_group_desc",
                            "Group descriptor not loaded - "
                            "block_group = %lld, group_desc = %llu, desc = %llu",
                             (unsigned long long)block_group,
                             (unsigned long long)group_desc,
                             (unsigned long long)desc);
...

3.4.1. read_block_bitmap()

block_group is a block group number (group_t) and bg_block_bitmap is a sector number (sector_t).

static struct buffer_head *
read_block_bitmap(struct super_block *sb, unsigned int block_group)
...
        bh = sb_bread(sb, le32_to_cpu(desc->bg_block_bitmap));
        if (!bh)
                ext3_error (sb, "read_block_bitmap",
                            "Cannot read block bitmap - "
                            "block_group = %d, block_bitmap = %u",
                            block_group, le32_to_cpu(desc->bg_block_bitmap));

should become

static struct buffer_head *
read_block_bitmap(struct super_block *sb, group_t block_group)
...
        bh = sb_bread(sb, ext3_read_split_le64(sb, desc->bg_hi_block_bitmap,
                                                   desc->bg_block_bitmap
));
        if (!bh)
                ext3_error (sb, "read_block_bitmap",
                            "Cannot read block bitmap - "
                            "block_group = %lld, block_bitmap = %llu",
                            (unsigned long long)block_group,
                            ext3_read_split_le64
(sb, desc->bg_hi_block_bitmap,
                                                     desc->bg_block_bitmap
));</