5/5 EXT2/3 Updates
Subsystems (2)
PATCH 1/1 5/5 EXT2/3 Updates
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.1360 -> 1.1361
# include/linux/ext3_fs_i.h 1.1 -> 1.2
# include/linux/ext3_fs.h 1.8 -> 1.9
# include/linux/ext3_jbd.h 1.3 -> 1.4
# fs/ext3/Makefile 1.2 -> 1.3
# include/linux/rbtree.h 1.1 -> 1.2
# include/linux/ext2_fs_i.h 1.5 -> 1.6
# fs/ext3/dir.c 1.1 -> 1.2 # fs/ext3/namei.c 1.4 -> 1.5 # lib/rbtree.c 1.2 -> 1.3 # fs/ext3/inode.c 1.18 -> 1.19 # fs/ext3/super.c 1.13 -> 1.14
# include/linux/ext3_fs_sb.h 1.2 -> 1.3
# fs/ext3/file.c 1.4 -> 1.5
# (new) -> 1.1 fs/ext3/hash.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 04/01/01 [email protected] 1.1361
# Apply all ext2/ext3 htree indexed directory patches, including
# bugfixes from the Linux 2.6.0 tree. These patches are quite # well tested and have been proven very reliable. # # Includes ext2/3 patches: 4100. 4101. 4102, 4103, 4108, 4109, 4112, # 4201, 4204, 4205 # -------------------------------------------- # diff -Nru a/fs/ext3/Makefile b/fs/ext3/Makefile --- a/fs/ext3/Makefile Thu Jan 1 16:29:25 2004 +++ b/fs/ext3/Makefile Thu Jan 1 16:29:25 2004 @@ -10,7 +10,7 @@ O_TARGET := ext3.o
obj-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
- ioctl.o namei.o super.o symlink.o
+ ioctl.o namei.o super.o symlink.o hash.o
obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make
fs/ext3/dir.c +327 -7
@@ -21,20 +21,40 @@#include <linux/fs.h> #include <linux/jbd.h> #include <linux/ext3_fs.h> +#include <linux/slab.h> +#include <linux/rbtree.h> static unsigned char ext3_filetype_table[] = {DT_UNKNOWN ,DT_REG ,DT_DIR ,DT_CHR ,DT_BLK ,DT_FIFO ,DT_SOCK ,DT_LNK };static int ext3_readdir (struct file *,void *,filldir_t ); +static int ext3_dx_readdir (struct file * filp, +void * dirent,filldir_t filldir); +static int ext3_release_dir (struct inode * inode, +struct file * filp);struct file_operations ext3_dir_operations = { read: generic_read_dir, readdir: ext3_readdir,/* BKL held */ ioctl: ext3_ioctl,/* BKL held */ fsync: ext3_sync_file,/* BKL held */ +#ifdef CONFIG_EXT3_INDEX + release: ext3_release_dir +#endif }; + +static unsigned char get_dtype (struct super_block *sb,int filetype) +{ +if (!EXT3_HAS_INCOMPAT_FEATURE (sb,EXT3_FEATURE_INCOMPAT_FILETYPE ) || + (filetype >=EXT3_FT_MAX )) +return DT_UNKNOWN ; + +return (ext3_filetype_table[filetype]); +} + + int ext3_check_dir_entry (const char * function, struct inode * dir, struct ext3_dir_entry_2 * de, struct buffer_head * bh,
@@ -79,6 +99,21 @@ sb = inode->i_sb; +#ifdef CONFIG_EXT3_INDEX +if (EXT3_HAS_COMPAT_FEATURE (inode->i_sb, +EXT3_FEATURE_COMPAT_DIR_INDEX ) && + ((EXT3_I (inode)->i_flags &EXT3_INDEX_FL ) || + ((inode->i_size >> sb->s_blocksize_bits) ==1 ))) { + err =ext3_dx_readdir (filp, dirent, filldir); +if (err !=ERR_BAD_DX_DIR ) +return err; +/* +* We don't set the inode dirty flag since it's not +* critical that it get flushed back to the disk. +*/ +EXT3_I (filp->f_dentry->d_inode)->i_flags &= ~EXT3_INDEX_FL ; + } +#endif stored =0 ; bh =NULL ; offset = filp->f_pos & (sb->s_blocksize -1 );
@@ -162,18 +197,12 @@* during the copy operation. */ unsigned long version = filp->f_version; -unsigned char d_type =DT_UNKNOWN ; -if (EXT3_HAS_INCOMPAT_FEATURE (sb, -EXT3_FEATURE_INCOMPAT_FILETYPE ) - && de->file_type <EXT3_FT_MAX ) - d_type = - ext3_filetype_table[de->file_type]; error =filldir (dirent, de->name, de->name_len, filp->f_pos,le32_to_cpu (de->inode), - d_type); +get_dtype (sb, de->file_type));if (error)break ;if (version != filp->f_version)
@@ -188,3 +217,294 @@UPDATE_ATIME (inode);return 0 ; } + +#ifdef CONFIG_EXT3_INDEX +/* +* These functions convert from the major/minor hash to an f_pos +* value. +* +* Currently we only use major hash numer. This is unfortunate, but +* on 32-bit machines, the same VFS interface is used for lseek and +* llseek, so if we use the 64 bit offset, then the 32-bit versions of +* lseek/telldir/seekdir will blow out spectacularly, and from within +* the ext2 low-level routine, we don't know if we're being called by +* a 64-bit version of the system call or the 32-bit version of the +* system call. Worse yet, NFSv2 only allows for a 32-bit readdir +* cookie. Sigh. +*/ +#define hash2pos (major, minor) (major >> 1) +#define pos2maj_hash (pos) ((pos << 1) & 0xffffffff) +#define pos2min_hash (pos) (0) + +/* +* This structure holds the nodes of the red-black tree used to store +* the directory entry in hash order. +*/ +struct fname { +__u32 hash; +__u32 minor_hash; +rb_node_t rb_hash; +struct fname *next; +__u32 inode; +__u8 name_len; +__u8 file_type; +char name[0 ]; +}; + +/* +* This functoin implements a non-recursive way of freeing all of the +* nodes in the red-black tree. +*/ +static void free_rb_tree_fname (rb_root_t *root) +{ +rb_node_t *n = root->rb_node; +rb_node_t *parent; +struct fname *fname; + +while (n) { +/* Do the node's children first */ +if ((n)->rb_left) { + n = n->rb_left; +continue ; + } +if (n->rb_right) { + n = n->rb_right; +continue ; + } +/* +* The node has no children; free it, and then zero +* out parent's link to it. Finally go to the +* beginning of the loop and try to free the parent +* node. +*/ + parent = n->rb_parent; + fname = rb_entry(n, struct fname, rb_hash); +while (fname) { +struct fname * old = fname; + fname = fname->next; +kfree (old); + } +if (!parent) + root->rb_node =0 ; +else if (parent->rb_left == n) + parent->rb_left =0 ; +else if (parent->rb_right == n) + parent->rb_right =0 ; + n = parent; + } + root->rb_node =0 ; +} + + +struct dir_private_info *create_dir_info (loff_t pos) +{ +struct dir_private_info *p; + + p =kmalloc (sizeof (struct dir_private_info ),GFP_KERNEL ); +if (!p) +return NULL ; + p->root.rb_node =0 ; + p->curr_node =0 ; + p->extra_fname =0 ; + p->last_pos =0 ; + p->curr_hash =pos2maj_hash (pos); + p->curr_minor_hash =pos2min_hash (pos); + p->next_hash =0 ; +return p; +} + +void ext3_htree_free_dir_info (struct dir_private_info *p) +{ +free_rb_tree_fname (&p->root); +kfree (p); +} + +/* +* Given a directory entry, enter it into the fname rb tree. +*/ +int ext3_htree_store_dirent (struct file *dir_file,__u32 hash, +__u32 minor_hash, +struct ext3_dir_entry_2 *dirent) +{ +rb_node_t **p, *parent =NULL ; +struct fname * fname, *new_fn; +struct dir_private_info *info; +int len; + + info = (struct dir_private_info *) dir_file->private_data; + p = &info->root.rb_node; + +/* Create and allocate the fname structure */ + len =sizeof (struct fname ) + dirent->name_len +1 ; + new_fn =kmalloc (len,GFP_KERNEL ); +if (!new_fn) +return -ENOMEM ; +memset (new_fn,0 , len); + new_fn->hash = hash; + new_fn->minor_hash = minor_hash; + new_fn->inode =le32_to_cpu (dirent->inode); + new_fn->name_len = dirent->name_len; + new_fn->file_type = dirent->file_type; +memcpy (new_fn->name, dirent->name, dirent->name_len); + new_fn->name[dirent->name_len] =0 ; + +while (*p) { + parent = *p; + fname = rb_entry(parent, struct fname, rb_hash); + +/* +* If the hash and minor hash match up, then we put +* them on a linked list. This rarely happens... +*/ +if ((new_fn->hash == fname->hash) && + (new_fn->minor_hash == fname->minor_hash)) { + new_fn->next = fname->next; + fname->next = new_fn; +return 0 ; + } + +if (new_fn->hash < fname->hash) + p = &(*p)->rb_left; +else if (new_fn->hash > fname->hash) + p = &(*p)->rb_right; +else if (new_fn->minor_hash < fname->minor_hash) + p = &(*p)->rb_left; +else /* if (new_fn->minor_hash > fname->minor_hash) */ + p = &(*p)->rb_right; + } + +rb_link_node (&new_fn->rb_hash, parent, p); +rb_insert_color (&new_fn->rb_hash, &info->root); +return 0 ; +} + + + +/* +* This is a helper function for ext3_dx_readdir. It calls filldir +* for all entres on the fname linked list. (Normally there is only +* one entry on the linked list, unless there are 62 bit hash collisions.) +*/ +static int call_filldir (struct file * filp,void * dirent, +filldir_t filldir,struct fname *fname) +{ +struct dir_private_info *info = filp->private_data; +loff_t curr_pos; +struct inode *inode = filp->f_dentry->d_inode; +struct super_block * sb; +int error; + + sb = inode->i_sb; + +if (!fname) { +printk ("call_filldir: called with null fname?!?\n" ); +return 0 ; + } + curr_pos =hash2pos (fname->hash, fname->minor_hash); +while (fname) { + error =filldir (dirent, fname->name, + fname->name_len, curr_pos, + fname->inode, +get_dtype (sb, fname->file_type)); +if (error) { + filp->f_pos = curr_pos; + info->extra_fname = fname->next; +return error; + } + fname = fname->next; + } +return 0 ; +} + +static int ext3_dx_readdir (struct file * filp, +void * dirent,filldir_t filldir) +{ +struct dir_private_info *info = filp->private_data; +struct inode *inode = filp->f_dentry->d_inode; +struct fname *fname; +int ret; + +if (!info) { + info =create_dir_info (filp->f_pos); +if (!info) +return -ENOMEM ; + filp->private_data = info; + } + +if (filp->f_pos ==EXT3_HTREE_EOF ) +return 0 ;/* EOF */ + +/* Some one has messed with f_pos; reset the world */ +if (info->last_pos != filp->f_pos) { +free_rb_tree_fname (&info->root); + info->curr_node =0 ; + info->extra_fname =0 ; + info->curr_hash =pos2maj_hash (filp->f_pos); + info->curr_minor_hash =pos2min_hash (filp->f_pos); + } + +/* +* If there are any leftover names on the hash collision +* chain, return them first. +*/ +if (info->extra_fname && +call_filldir (filp, dirent, filldir, info->extra_fname)) + goto finished; + +if (!info->curr_node) + info->curr_node =rb_get_first (&info->root); + +while (1 ) { +/* +* Fill the rbtree if we have no more entries, +* or the inode has changed since we last read in the +* cached entries. +*/ +if ((!info->curr_node) || + (filp->f_version != inode->i_version)) { + info->curr_node =0 ; +free_rb_tree_fname (&info->root); + filp->f_version = inode->i_version; + ret =ext3_htree_fill_tree (filp, info->curr_hash, + info->curr_minor_hash, + &info->next_hash); +if (ret <0 ) +return ret; +if (ret ==0 ) { + filp->f_pos =EXT3_HTREE_EOF ; +break ; + } + info->curr_node =rb_get_first (&info->root); + } + + fname = rb_entry(info->curr_node, struct fname, rb_hash); + info->curr_hash = fname->hash; + info->curr_minor_hash = fname->minor_hash; +if (call_filldir (filp, dirent, filldir, fname)) +break ; + + info->curr_node =rb_get_next (info->curr_node); +if (!info->curr_node) { +if (info->next_hash == ~0 ) { + filp->f_pos =EXT3_HTREE_EOF ; +break ; + } + info->curr_hash = info->next_hash; + info->curr_minor_hash =0 ; + } + } +finished: + info->last_pos = filp->f_pos; +UPDATE_ATIME (inode); +return 0 ; +} + +static int ext3_release_dir (struct inode * inode,struct file * filp) +{ +if (filp->private_data) +ext3_htree_free_dir_info (filp->private_data); + +return 0 ; +} + +#endif
fs/ext3/file.c +3 -0
@@ -35,6 +35,9 @@ {if (filp->f_mode &FMODE_WRITE )ext3_discard_prealloc (inode); +if (is_dx (inode) && filp->private_data) +ext3_htree_free_dir_info (filp->private_data); +return 0 ; }
fs/ext3/hash.c new file +218 -0
218 lines not on this page · raw message
fs/ext3/inode.c +0 -18
@@ -33,13 +33,6 @@#include <linux/module.h> /* -* SEARCH_FROM_ZERO forces each block allocation to search from the start -* of the filesystem. This is to force rapid reallocation of recently-freed -* blocks. The file fragmentation is horrendous. -*/ -#undef SEARCH_FROM_ZERO - -/* * Test whether an inode is a fast symlink. */ static inline int ext3_inode_is_fast_symlink (struct inode *inode)
@@ -507,10 +500,6 @@ inode->u.ext3_i.i_next_alloc_block++; inode->u.ext3_i.i_next_alloc_goal++; } -#ifdef SEARCH_FROM_ZERO - inode->u.ext3_i.i_next_alloc_block =0 ; - inode->u.ext3_i.i_next_alloc_goal =0 ; -#endif /* Writer: end */ /* Reader: pointers, ->i_next_alloc* */ if (verify_chain (chain, partial)) {
@@ -522,9 +511,6 @@ *goal = inode->u.ext3_i.i_next_alloc_goal;if (!*goal) *goal =ext3_find_near (inode, partial); -#ifdef SEARCH_FROM_ZERO - *goal =0 ; -#endif return 0 ; }/* Reader: end */
@@ -669,10 +655,6 @@ *where->p = where->key; inode->u.ext3_i.i_next_alloc_block = block; inode->u.ext3_i.i_next_alloc_goal =le32_to_cpu (where[num-1 ].key); -#ifdef SEARCH_FROM_ZERO - inode->u.ext3_i.i_next_alloc_block =0 ; - inode->u.ext3_i.i_next_alloc_goal =0 ; -#endif /* Writer: end */ /* We are done with atomic stuff, now do the rest of housekeeping */
fs/ext3/namei.c +1323 -151
1,729 lines not on this page · raw message
fs/ext3/super.c +6 -0
@@ -712,6 +712,7 @@ es->s_mtime =cpu_to_le32 (CURRENT_TIME );ext3_update_dynamic_rev (sb);EXT3_SET_INCOMPAT_FEATURE (sb,EXT3_FEATURE_INCOMPAT_RECOVER ); +ext3_commit_super (sb, es,1 );if (test_opt (sb,DEBUG )) printk (KERN_INFO
@@ -722,6 +723,7 @@EXT3_BLOCKS_PER_GROUP (sb),EXT3_INODES_PER_GROUP (sb), sbi->s_mount_opt); +printk (KERN_INFO "EXT3 FS " EXT3FS_VERSION ", " EXT3FS_DATE " on %s, " ,bdevname (sb->s_dev));if (EXT3_SB (sb)->s_journal->j_inode ==NULL ) {
@@ -915,6 +917,7 @@return res; } +struct super_block *ext3_read_super (struct super_block * sb,void * data,int silent) {
@@ -1094,6 +1097,9 @@ sbi->s_mount_state =le16_to_cpu (es->s_state); sbi->s_addr_per_block_bits =log2 (EXT3_ADDR_PER_BLOCK (sb)); sbi->s_desc_per_block_bits =log2 (EXT3_DESC_PER_BLOCK (sb)); +for (i=0 ; i <4 ; i++) + sbi->s_hash_seed[i] =le32_to_cpu (es->s_hash_seed[i]); + sbi->s_def_hash_version = es->s_def_hash_version;if (sbi->s_blocks_per_group > blocksize *8 ) { printk (KERN_ERR
include/linux/ext2_fs_i.h +22 -0
@@ -29,8 +29,30 @@__u32 i_file_acl;__u32 i_dir_acl;__u32 i_dtime; + +/* +* i_block_group is the number of the block group which contains +* this file's inode. Constant across the lifetime of the inode, +* it is ued for making block allocation decisions - we try to +* place a file's data blocks near its inode block, and new inodes +* near to their parent directory's inode. +*/ __u32 i_block_group; + +/* +* i_next_alloc_block is the logical (file-relative) number of the +* most-recently-allocated block in this file. Yes, it is misnamed. +* We use this for detecting linearly ascending allocation requests. +*/ __u32 i_next_alloc_block; + +/* +* i_next_alloc_goal is the *physical* companion to +* i_next_alloc_block. it the the physical block number of +* the block which was most-recently allocated to this file. +* This give us the goal (target) for the next allocation when +* we detect linearly ascending requests. +*/ __u32 i_next_alloc_goal;__u32 i_prealloc_block;__u32 i_prealloc_count;
include/linux/ext3_fs.h +79 -3
118 lines not on this page · raw message
include/linux/ext3_fs_i.h +21 -0
@@ -33,9 +33,30 @@__u32 i_file_acl;__u32 i_dir_acl;__u32 i_dtime; + +/* +* i_block_group is the number of the block group which contains +* this file's inode. Constant across the lifetime of the inode, +* it is ued for making block allocation decisions - we try to +* place a file's data blocks near its inode block, and new inodes +* near to their parent directory's inode. +*/ __u32 i_block_group;__u32 i_state;/* Dynamic state flags for ext3 */ + +/* +* i_next_alloc_block is the logical (file-relative) number of the +* most-recently-allocated block in this file. Yes, it is misnamed. +* We use this for detecting linearly ascending allocation requests. +*/ __u32 i_next_alloc_block; + +/* +* i_next_alloc_goal is the *physical* companion to i_next_alloc_block. +* it the the physical block number of the block which was most-recently +* allocated to this file. This give us the goal (target) for the next +* allocation when we detect linearly ascending requests. +*/ __u32 i_next_alloc_goal;#ifdef EXT3_PREALLOCATE __u32 i_prealloc_block;
include/linux/ext3_fs_sb.h +2 -0
@@ -62,6 +62,8 @@int s_inode_size;int s_first_ino;u32 s_next_generation; +u32 s_hash_seed[4 ]; +int s_def_hash_version;/* Journaling */ struct inode * s_journal_inode;
include/linux/ext3_jbd.h +2 -0
@@ -63,6 +63,8 @@#define EXT3_RESERVE_TRANS_BLOCKS 12U +#define EXT3_INDEX_EXTRA_TRANS_BLOCKS 8 + int ext3_mark_iloc_dirty(handle_t *handle, struct inode *inode,
include/linux/rbtree.h +2 -0
@@ -120,6 +120,8 @@extern void rb_insert_color (rb_node_t *,rb_root_t *);extern void rb_erase (rb_node_t *,rb_root_t *); +extern rb_node_t *rb_get_first (rb_root_t *root); +extern rb_node_t *rb_get_next (rb_node_t *n);static inline void rb_link_node (rb_node_t * node,rb_node_t * parent,rb_node_t ** rb_link) {
lib/rbtree.c +42 -0
51 lines not on this page · raw message