From 08c3b5cf1effbbf24373f2beab0b8a66f9e148d3 Mon Sep 17 00:00:00 2001 From: nikita Date: Tue, 30 May 2006 19:20:37 +0000 Subject: [PATCH] iam unit tests framework. --- .../kernel_patches/series/ldiskfs-2.6-rhel4.series | 1 + ldiskfs/ldiskfs/Makefile.in | 2 +- .../kernel_patches/patches/ext3-iam-separate.patch | 102 ++++---- lustre/kernel_patches/patches/ext3-iam-uapi.patch | 284 +++++++++++++++++++++ .../kernel_patches/series/ldiskfs-2.6-rhel4.series | 1 + lustre/ldiskfs/Makefile.in | 2 +- lustre/tests/Makefile.am | 1 + lustre/tests/iam_ut | Bin 0 -> 56203 bytes lustre/utils/create_iam.c | 2 + 9 files changed, 348 insertions(+), 47 deletions(-) create mode 100644 lustre/kernel_patches/patches/ext3-iam-uapi.patch create mode 100755 lustre/tests/iam_ut diff --git a/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series b/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series index 3934e40..f6d42cd 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series @@ -18,3 +18,4 @@ ext3-hash-selection.patch ext3-htree-comments.patch ext3-iam-ops.patch ext3-iam-separate.patch +ext3-iam-uapi.patch diff --git a/ldiskfs/ldiskfs/Makefile.in b/ldiskfs/ldiskfs/Makefile.in index df5199d..9c27db4 100644 --- a/ldiskfs/ldiskfs/Makefile.in +++ b/ldiskfs/ldiskfs/Makefile.in @@ -11,7 +11,7 @@ ext3_headers := $(wildcard @LINUX@/fs/ext3/*.h) linux_headers := $(wildcard @LINUX@/include/linux/ext3*.h) ext3_sources := $(filter-out %.mod.c,$(wildcard @LINUX@/fs/ext3/*.c)) -new_sources := iopen.c iopen.h extents.c mballoc.c iam.c iam_lfix.c +new_sources := iopen.c iopen.h extents.c mballoc.c iam.c iam_lfix.c iam-uapi.c new_headers := ext3_extents.h ldiskfs_patched_sources := $(notdir $(ext3_sources) $(ext3_headers)) $(new_sources) $(new_headers) ldiskfs_sources := $(ldiskfs_patched_sources) diff --git a/lustre/kernel_patches/patches/ext3-iam-separate.patch b/lustre/kernel_patches/patches/ext3-iam-separate.patch index 46b89af..bd76f6c 100644 --- a/lustre/kernel_patches/patches/ext3-iam-separate.patch +++ b/lustre/kernel_patches/patches/ext3-iam-separate.patch @@ -1,7 +1,7 @@ Index: iam/fs/ext3/Makefile =================================================================== --- iam.orig/fs/ext3/Makefile 2006-05-27 19:58:43.000000000 +0400 -+++ iam/fs/ext3/Makefile 2006-05-27 20:03:07.000000000 +0400 ++++ iam/fs/ext3/Makefile 2006-05-30 23:07:25.000000000 +0400 @@ -6,7 +6,7 @@ obj-$(CONFIG_EXT3_FS) += ext3.o ext3-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o iopen.o \ @@ -14,7 +14,7 @@ Index: iam/fs/ext3/Makefile Index: iam/fs/ext3/iam.c =================================================================== --- iam.orig/fs/ext3/iam.c 2004-04-06 17:27:52.000000000 +0400 -+++ iam/fs/ext3/iam.c 2006-05-29 22:49:31.000000000 +0400 ++++ iam/fs/ext3/iam.c 2006-05-30 18:26:37.000000000 +0400 @@ -0,0 +1,1021 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: @@ -763,6 +763,10 @@ Index: iam/fs/ext3/iam.c + err = split_index_node(handle, path); + if (err == 0) { + err = iam_new_leaf(handle, &path->ip_leaf); ++ /* ++ * XXX: if insertion point moved into new leaf, path ++ * and path->ip_leaf have to be updated. ++ */ + if (err == 0) + err = iam_leaf_rec_add(handle, path); + } @@ -788,11 +792,7 @@ Index: iam/fs/ext3/iam.c + int result; + + assert(it_state(it) == IAM_IT_ATTACHED && it->ii_flags&IAM_IT_WRITE); -+#if 0 -+ /*XXX remove this assert temporarily, since if the il_at point to the hearder, -+ * this assert might has some problems*/ + assert(it_keycmp(it, iam_it_key_get(it, it_scratch_key(it, 0)), k) < 0); -+#endif + result = iam_add_rec(h, &it->ii_path, k, r); + if (result == 0) { + /* place record and key info freed space. Leaf node is already @@ -1040,8 +1040,8 @@ Index: iam/fs/ext3/iam.c Index: iam/fs/ext3/iam_lfix.c =================================================================== --- iam.orig/fs/ext3/iam_lfix.c 2004-04-06 17:27:52.000000000 +0400 -+++ iam/fs/ext3/iam_lfix.c 2006-05-29 23:50:12.000000000 +0400 -@@ -0,0 +1,445 @@ ++++ iam/fs/ext3/iam_lfix.c 2006-05-30 23:08:00.000000000 +0400 +@@ -0,0 +1,448 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * @@ -1399,6 +1399,7 @@ Index: iam/fs/ext3/iam_lfix.c + __le64 ilr_magic; + __le16 ilr_keysize; + __le16 ilr_recsize; ++ __le16 ilr_ptrsize; + __le16 ilr_indirect_levels; + __le16 ilr_padding; +}; @@ -1449,7 +1450,8 @@ Index: iam/fs/ext3/iam_lfix.c + .id_node_init = iam_lfix_node_init, + .id_node_check = iam_lfix_node_check, + .id_create = iam_lfix_node_create, -+ .id_keycmp = iam_lfix_keycmp ++ .id_keycmp = iam_lfix_keycmp, ++ .id_name = "lfix" +}; + +static int iam_lfix_guess(struct iam_container *c) @@ -1469,6 +1471,7 @@ Index: iam/fs/ext3/iam_lfix.c + descr = c->ic_descr; + descr->id_key_size = le16_to_cpu(root->ilr_keysize); + descr->id_rec_size = le16_to_cpu(root->ilr_recsize); ++ descr->id_ptr_size = le16_to_cpu(root->ilr_ptrsize); + descr->id_root_gap = sizeof(struct iam_lfix_root); + descr->id_node_gap = 0; + descr->id_ops = &iam_lfix_ops; @@ -1490,7 +1493,7 @@ Index: iam/fs/ext3/iam_lfix.c Index: iam/fs/ext3/namei.c =================================================================== --- iam.orig/fs/ext3/namei.c 2006-05-27 19:58:44.000000000 +0400 -+++ iam/fs/ext3/namei.c 2006-05-29 19:44:45.000000000 +0400 ++++ iam/fs/ext3/namei.c 2006-05-30 19:01:04.000000000 +0400 @@ -24,81 +24,6 @@ * Theodore Ts'o, 2002 */ @@ -1963,7 +1966,7 @@ Index: iam/fs/ext3/namei.c return key; } -@@ -540,68 +176,70 @@ static inline struct iam_key *iam_key_at +@@ -540,68 +176,71 @@ static inline struct iam_key *iam_key_at return (struct iam_key *)entry; } @@ -2048,7 +2051,8 @@ Index: iam/fs/ext3/namei.c + .id_node_check = htree_node_check, + .id_node_init = htree_node_init, + .id_node_read = iam_node_read, -+ .id_keycmp = htree_keycmp ++ .id_keycmp = htree_keycmp, ++ .id_name = "htree" +}; + +/* @@ -2076,7 +2080,7 @@ Index: iam/fs/ext3/namei.c static int dx_node_check(struct iam_path *p, struct iam_frame *f) { struct iam_entry *e; -@@ -614,10 +252,10 @@ static int dx_node_check(struct iam_path +@@ -614,10 +253,10 @@ static int dx_node_check(struct iam_path count = dx_get_count(e); e = iam_entry_shift(p, e, 1); for (i = 0; i < count - 1; ++i, e = iam_entry_shift(p, e, 1)) { @@ -2090,7 +2094,7 @@ Index: iam/fs/ext3/namei.c return 0; } return 1; -@@ -636,13 +274,17 @@ static int htree_node_check(struct iam_p +@@ -636,13 +275,17 @@ static int htree_node_check(struct iam_p data = frame->bh->b_data; entries = dx_node_get_entries(path, frame); @@ -2110,7 +2114,7 @@ Index: iam/fs/ext3/namei.c if (root->info.hash_version > DX_HASH_MAX) { ext3_warning(sb, __FUNCTION__, "Unrecognised inode hash code %d", -@@ -669,15 +311,17 @@ static int htree_node_check(struct iam_p +@@ -669,15 +312,17 @@ static int htree_node_check(struct iam_p root->info.info_length)); assert(dx_get_limit(entries) == dx_root_limit(path)); @@ -2135,7 +2139,7 @@ Index: iam/fs/ext3/namei.c assert(dx_get_limit(entries) == dx_node_limit(path)); } frame->entries = frame->at = entries; -@@ -697,8 +341,8 @@ static int htree_node_init(struct iam_co +@@ -697,8 +342,8 @@ static int htree_node_init(struct iam_co return 0; } @@ -2146,7 +2150,7 @@ Index: iam/fs/ext3/namei.c { int result = 0; -@@ -708,8 +352,8 @@ static int htree_node_read(struct iam_co +@@ -708,8 +353,8 @@ static int htree_node_read(struct iam_co return result; } @@ -2157,7 +2161,7 @@ Index: iam/fs/ext3/namei.c { __u32 p1 = le32_to_cpu(*(__u32 *)k1); __u32 p2 = le32_to_cpu(*(__u32 *)k2); -@@ -800,7 +444,7 @@ struct stats dx_show_entries(struct dx_h +@@ -800,7 +445,7 @@ struct stats dx_show_entries(struct dx_h } #endif /* DX_DEBUG */ @@ -2166,7 +2170,7 @@ Index: iam/fs/ext3/namei.c { u32 ptr; int err = 0; -@@ -810,11 +454,11 @@ static int dx_lookup(struct iam_path *pa +@@ -810,11 +455,11 @@ static int dx_lookup(struct iam_path *pa struct iam_frame *frame; struct iam_container *c; @@ -2180,7 +2184,7 @@ Index: iam/fs/ext3/namei.c i <= path->ip_indirect; ptr = dx_get_block(path, frame->at), ++frame, ++i) { struct iam_entry *entries; -@@ -823,10 +467,11 @@ static int dx_lookup(struct iam_path *pa +@@ -823,10 +468,11 @@ static int dx_lookup(struct iam_path *pa struct iam_entry *m; unsigned count; @@ -2194,7 +2198,7 @@ Index: iam/fs/ext3/namei.c if (err != 0) break; -@@ -837,12 +482,27 @@ static int dx_lookup(struct iam_path *pa +@@ -837,12 +483,27 @@ static int dx_lookup(struct iam_path *pa assert(count && count <= dx_get_limit(entries)); p = iam_entry_shift(path, entries, 1); q = iam_entry_shift(path, entries, count - 1); @@ -2224,7 +2228,7 @@ Index: iam/fs/ext3/namei.c q = iam_entry_shift(path, m, -1); else p = iam_entry_shift(path, m, +1); -@@ -857,12 +517,12 @@ static int dx_lookup(struct iam_path *pa +@@ -857,12 +518,12 @@ static int dx_lookup(struct iam_path *pa while (n--) { dxtrace(printk(",")); at = iam_entry_shift(path, at, +1); @@ -2240,7 +2244,7 @@ Index: iam/fs/ext3/namei.c path->ip_key_target)); } at = iam_entry_shift(path, at, -1); -@@ -891,508 +551,20 @@ static int dx_probe(struct dentry *dentr +@@ -891,508 +552,20 @@ static int dx_probe(struct dentry *dentr struct dx_hash_info *hinfo, struct iam_path *path) { int err; @@ -2755,7 +2759,7 @@ Index: iam/fs/ext3/namei.c * This function increments the frame pointer to search the next leaf * block, and reads in the necessary intervening nodes if the search * should be necessary. Whether or not the search is necessary is -@@ -1409,16 +581,15 @@ EXPORT_SYMBOL(iam_update); +@@ -1409,16 +582,15 @@ EXPORT_SYMBOL(iam_update); * If start_hash is non-null, it will be filled in with the starting * hash of the next page. */ @@ -2775,7 +2779,7 @@ Index: iam/fs/ext3/namei.c p = path->ip_frame; /* * Find the next leaf page by incrementing the frame pointer. -@@ -1438,28 +609,34 @@ static int ext3_htree_next_block(struct +@@ -1438,28 +610,34 @@ static int ext3_htree_next_block(struct --p; } @@ -2826,7 +2830,7 @@ Index: iam/fs/ext3/namei.c if (err != 0) return err; /* Failure */ ++p; -@@ -1471,6 +648,16 @@ static int ext3_htree_next_block(struct +@@ -1471,6 +649,16 @@ static int ext3_htree_next_block(struct return 1; } @@ -2843,7 +2847,7 @@ Index: iam/fs/ext3/namei.c /* * p is at least 6 bytes before the end of page -@@ -1662,21 +849,30 @@ static void dx_sort_map (struct dx_map_e +@@ -1662,21 +850,30 @@ static void dx_sort_map (struct dx_map_e } while(more); } @@ -2880,7 +2884,7 @@ Index: iam/fs/ext3/namei.c #endif -@@ -1897,14 +1093,15 @@ static struct buffer_head * ext3_dx_find +@@ -1897,14 +1094,15 @@ static struct buffer_head * ext3_dx_find if (*err != 0) return NULL; } else { @@ -2899,7 +2903,7 @@ Index: iam/fs/ext3/namei.c if (*err != 0) goto errout; de = (struct ext3_dir_entry_2 *) bh->b_data; -@@ -2067,7 +1264,7 @@ static struct ext3_dir_entry_2 *do_split +@@ -2067,7 +1265,7 @@ static struct ext3_dir_entry_2 *do_split struct buffer_head **bh,struct iam_frame *frame, struct dx_hash_info *hinfo, int *error) { @@ -2908,7 +2912,7 @@ Index: iam/fs/ext3/namei.c unsigned blocksize = dir->i_sb->s_blocksize; unsigned count, continued; struct buffer_head *bh2; -@@ -2392,18 +1589,25 @@ static int ext3_add_entry (handle_t *han +@@ -2392,18 +1590,25 @@ static int ext3_add_entry (handle_t *han } #ifdef CONFIG_EXT3_INDEX @@ -2937,7 +2941,7 @@ Index: iam/fs/ext3/namei.c frame = path->ip_frame; entries = frame->entries; -@@ -2442,7 +1646,8 @@ static int split_index_node(handle_t *ha +@@ -2442,7 +1647,8 @@ static int split_index_node(handle_t *ha for (frame = safe + 1, i = 0; i < nr_splet; ++i, ++frame) { bh_new[i] = ext3_append (handle, dir, &newblock[i], &err); if (!bh_new[i] || @@ -2947,7 +2951,7 @@ Index: iam/fs/ext3/namei.c goto cleanup; BUFFER_TRACE(frame->bh, "get_write_access"); err = ext3_journal_get_write_access(handle, frame->bh); -@@ -2516,9 +1721,9 @@ static int split_index_node(handle_t *ha +@@ -2516,9 +1722,9 @@ static int split_index_node(handle_t *ha unsigned count1 = count/2, count2 = count - count1; unsigned hash2; @@ -2960,7 +2964,7 @@ Index: iam/fs/ext3/namei.c dxtrace(printk("Split index %i/%i\n", count1, count2)); -@@ -2578,7 +1783,7 @@ static int ext3_dx_add_entry(handle_t *h +@@ -2578,7 +1784,7 @@ static int ext3_dx_add_entry(handle_t *h size_t isize; iam_path_compat_init(&cpath, dir); @@ -2969,7 +2973,7 @@ Index: iam/fs/ext3/namei.c err = dx_probe(dentry, NULL, &hinfo, path); if (err != 0) -@@ -2588,8 +1793,9 @@ static int ext3_dx_add_entry(handle_t *h +@@ -2588,8 +1794,9 @@ static int ext3_dx_add_entry(handle_t *h /* XXX nikita: global serialization! */ isize = dir->i_size; @@ -2981,7 +2985,7 @@ Index: iam/fs/ext3/namei.c if (err != 0) goto cleanup; -@@ -2724,12 +1930,12 @@ static struct inode * ext3_new_inode_wan +@@ -2724,12 +1931,12 @@ static struct inode * ext3_new_inode_wan * is so far negative - it has no inode. * * If the create succeeds, we fill in the inode information @@ -2999,8 +3003,8 @@ Index: iam/fs/ext3/namei.c Index: iam/include/linux/lustre_iam.h =================================================================== --- iam.orig/include/linux/lustre_iam.h 2006-05-27 19:58:44.000000000 +0400 -+++ iam/include/linux/lustre_iam.h 2006-05-29 22:41:51.000000000 +0400 -@@ -1,9 +1,64 @@ ++++ iam/include/linux/lustre_iam.h 2006-05-30 23:07:25.000000000 +0400 +@@ -1,9 +1,68 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * @@ -3063,11 +3067,15 @@ Index: iam/include/linux/lustre_iam.h + * + * [3] reserved for index operations. + */ -+ DX_SCRATCH_KEYS = 4 ++ DX_SCRATCH_KEYS = 4, ++ /* ++ * Maximal format name length. ++ */ ++ DX_FMT_NAME_LEN = 16 }; /* -@@ -30,6 +85,11 @@ struct iam_key; +@@ -30,6 +89,11 @@ struct iam_key; /* Incomplete type use to refer to the records stored in iam containers. */ struct iam_rec; @@ -3079,7 +3087,7 @@ Index: iam/include/linux/lustre_iam.h typedef __u64 iam_ptr_t; /* -@@ -41,45 +101,25 @@ struct iam_frame { +@@ -41,45 +105,25 @@ struct iam_frame { struct iam_entry *at; /* target entry, found by binary search */ }; @@ -3139,7 +3147,7 @@ Index: iam/include/linux/lustre_iam.h /* * Returns pointer (in the same sense as pointer in index entry) to * the root node. -@@ -102,8 +142,8 @@ struct iam_descr { +@@ -102,8 +146,8 @@ struct iam_descr { /* * Key comparison function. Returns -1, 0, +1. */ @@ -3150,7 +3158,7 @@ Index: iam/include/linux/lustre_iam.h /* * Create new container. * -@@ -111,25 +151,109 @@ struct iam_descr { +@@ -111,25 +155,113 @@ struct iam_descr { * contains single record with the smallest possible key. */ int (*id_create)(struct iam_container *c); @@ -3173,6 +3181,10 @@ Index: iam/include/linux/lustre_iam.h - struct iam_rec *(*rec)(struct iam_container *c, - struct iam_leaf *l); - } id_leaf; ++ /* ++ * Format name. ++ */ ++ char id_name[DX_FMT_NAME_LEN]; +}; + +struct iam_leaf_operations { @@ -3279,7 +3291,7 @@ Index: iam/include/linux/lustre_iam.h }; struct iam_container { -@@ -142,10 +266,17 @@ struct iam_container { +@@ -142,10 +274,17 @@ struct iam_container { * container flavor. */ struct iam_descr *ic_descr; @@ -3299,7 +3311,7 @@ Index: iam/include/linux/lustre_iam.h }; /* -@@ -172,34 +303,238 @@ struct iam_path { +@@ -172,34 +311,238 @@ struct iam_path { /* * Leaf node: a child of ->ip_frame. */ @@ -3553,7 +3565,7 @@ Index: iam/include/linux/lustre_iam.h /* * Initialize container @c, acquires additional reference on @inode. */ -@@ -210,3 +545,170 @@ int iam_container_init(struct iam_contai +@@ -210,3 +553,170 @@ int iam_container_init(struct iam_contai */ void iam_container_fini(struct iam_container *c); diff --git a/lustre/kernel_patches/patches/ext3-iam-uapi.patch b/lustre/kernel_patches/patches/ext3-iam-uapi.patch new file mode 100644 index 0000000..86a399c --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-iam-uapi.patch @@ -0,0 +1,284 @@ +Index: iam/fs/ext3/Makefile +=================================================================== +--- iam.orig/fs/ext3/Makefile 2006-05-30 23:07:25.000000000 +0400 ++++ iam/fs/ext3/Makefile 2006-05-30 23:08:12.000000000 +0400 +@@ -6,7 +6,7 @@ obj-$(CONFIG_EXT3_FS) += ext3.o + + ext3-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o iopen.o \ + ioctl.o namei.o super.o symlink.o hash.o resize.o \ +- extents.o mballoc.o iam.o iam_lfix.o ++ extents.o mballoc.o iam.o iam_lfix.o iam_uapi.o + + ext3-$(CONFIG_EXT3_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o + ext3-$(CONFIG_EXT3_FS_POSIX_ACL) += acl.o +Index: iam/fs/ext3/file.c +=================================================================== +--- iam.orig/fs/ext3/file.c 2006-05-30 23:07:25.000000000 +0400 ++++ iam/fs/ext3/file.c 2006-05-30 23:11:11.000000000 +0400 +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include "xattr.h" + #include "acl.h" + +@@ -37,8 +38,12 @@ static int ext3_release_file (struct ino + if ((filp->f_mode & FMODE_WRITE) && + (atomic_read(&inode->i_writecount) == 1)) + ext3_discard_reservation(inode); +- if (is_dx(inode) && filp->private_data) +- ext3_htree_free_dir_info(filp->private_data); ++ if (is_dx(inode) && filp->private_data) { ++ if (S_ISDIR(inode->i_mode)) ++ ext3_htree_free_dir_info(filp->private_data); ++ else ++ ext3_iam_free_info(filp->private_data); ++ } + + return 0; + } +@@ -110,7 +115,7 @@ ext3_file_write(struct kiocb *iocb, cons + + force_commit: + err = ext3_force_commit(inode->i_sb); +- if (err) ++ if (err) + return err; + return ret; + } +Index: iam/fs/ext3/iam-uapi.c +=================================================================== +--- iam.orig/fs/ext3/iam-uapi.c 2004-04-06 17:27:52.000000000 +0400 ++++ iam/fs/ext3/iam-uapi.c 2006-05-30 23:08:12.000000000 +0400 +@@ -0,0 +1,164 @@ ++/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- ++ * vim:expandtab:shiftwidth=8:tabstop=8: ++ * ++ * iam_uapi.c ++ * User-level interface to iam (ioctl based) ++ * ++ * Copyright (c) 2006 Cluster File Systems, Inc. ++ * Author: Nikita Danilov ++ * ++ * This file is part of the Lustre file system, http://www.lustre.org ++ * Lustre is a trademark of Cluster File Systems, Inc. ++ * ++ * You may have signed or agreed to another license before downloading ++ * this software. If so, you are bound by the terms and conditions ++ * of that agreement, and the following does not apply to you. See the ++ * LICENSE file included with this distribution for more information. ++ * ++ * If you did not agree to a different license, then this copy of Lustre ++ * is open source software; you can redistribute it and/or modify it ++ * under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * In either case, Lustre is distributed in the hope that it will be ++ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty ++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * license text for more details. ++ */ ++ ++#include ++#include ++/* ext3_error() */ ++#include ++ ++#include ++ ++#include ++#include ++ ++struct iam_uapi_insert { ++}; ++ ++struct iam_uapi_lookup { ++}; ++ ++struct iam_private_info { ++ struct iam_container ipi_bag; ++ struct iam_descr ipi_descr; ++ struct iam_path_descr *ipi_ipd; ++ struct rw_semaphore ipi_sem; ++}; ++ ++struct iam_private_info *get_ipi(struct file *filp) ++{ ++ return filp->private_data; ++} ++ ++static struct iam_private_info *ext3_iam_alloc_info(gfp_t flags) ++{ ++ struct iam_private_info *info; ++ ++ info = kzalloc(sizeof *info, flags); ++ if (info != NULL) ++ init_rwsem(&info->ipi_sem); ++ return info; ++} ++ ++void ext3_iam_free_info(struct iam_private_info *info) ++{ ++ iam_container_fini(&info->ipi_bag); ++ if (info->ipi_ipd != NULL) ++ iam_ipd_free(info->ipi_ipd); ++ ++ kfree(info); ++} ++ ++static int iam_uapi_init(struct inode *inode, ++ struct file *filp, struct iam_uapi_info *ua) ++{ ++ int result; ++ struct iam_private_info *info; ++ ++ info = ext3_iam_alloc_info(GFP_KERNEL); ++ if (info != NULL) { ++ struct iam_container *bag; ++ struct iam_descr *des; ++ ++ bag = &info->ipi_bag; ++ des = &info->ipi_descr; ++ result = iam_container_init(bag, des, inode); ++ if (result == 0) { ++ result = iam_container_setup(bag); ++ if (result == 0) { ++ info->ipi_ipd = iam_ipd_alloc(des->id_key_size); ++ if (info->ipi_ipd != NULL) { ++ filp->private_data = info; ++ EXT3_I(inode)->i_flags |= EXT3_INDEX_FL; ++ } else ++ result = -ENOMEM; ++ } ++ } ++ } else ++ result = -ENOMEM; ++ return result; ++} ++ ++ ++int getua(struct iam_uapi_info *ua, unsigned long arg) ++{ ++ if (copy_from_user(ua, (struct iam_uapi_info __user *)arg, sizeof *ua)) ++ return -EFAULT; ++ else ++ return 0; ++} ++ ++int putua(struct iam_uapi_info *ua, unsigned long arg) ++{ ++ if (copy_to_user((struct iam_uapi_info __user *)arg, ua, sizeof *ua)) ++ return -EFAULT; ++ else ++ return 0; ++} ++ ++int iam_uapi_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, ++ unsigned long arg) ++{ ++ int result; ++ struct iam_uapi_info ua; ++ ++ if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER)) ++ result = -EACCES; ++ else if (S_ISREG(inode->i_mode)) { ++ switch (cmd) { ++ case IAM_IOC_INIT: ++ if (!is_dx(inode)) { ++ result = getua(&ua, arg); ++ if (result == 0) ++ result = iam_uapi_init(inode, filp, &ua); ++ } else ++ result = -EBUSY; ++ break; ++ case IAM_IOC_GETINFO: ++ if (is_dx(inode)) { ++ struct iam_descr *des; ++ ++ des = &get_ipi(filp)->ipi_descr; ++ ua.iui_keysize = des->id_key_size; ++ ua.iui_recsize = des->id_rec_size; ++ ua.iui_ptrsize = des->id_ptr_size; ++ ua.iui_height = 0; /* not yet */ ++ memcpy(ua.iui_fmt_name, des->id_ops->id_name, ++ ARRAY_SIZE(ua.iui_fmt_name)); ++ result = putua(&ua, arg); ++ } else ++ result = -EINVAL; ++ break; ++ case IAM_IOC_INSERT: ++ case IAM_IOC_LOOKUP: ++ default: ++ result = -ENOTTY; ++ } ++ } else ++ result = -EINVAL; ++ return result; ++} +Index: iam/include/linux/lustre_iam.h +=================================================================== +--- iam.orig/include/linux/lustre_iam.h 2006-05-30 23:07:25.000000000 +0400 ++++ iam/include/linux/lustre_iam.h 2006-05-30 23:08:12.000000000 +0400 +@@ -30,9 +30,6 @@ + #ifndef __LINUX_LUSTRE_IAM_H__ + #define __LINUX_LUSTRE_IAM_H__ + +-/* handle_t, journal_start(), journal_stop() */ +-#include +- + /* + * linux/include/linux/lustre_iam.h + */ +@@ -65,6 +62,10 @@ enum { + DX_FMT_NAME_LEN = 16 + }; + ++#ifdef __KERNEL__ ++/* handle_t, journal_start(), journal_stop() */ ++#include ++ + /* + * Entry within index tree node. Consists of a key immediately followed + * (without padding) by a pointer to the child node. +@@ -718,5 +719,40 @@ void iam_format_register(struct iam_form + + void iam_lfix_format_init(void); + ++struct iam_private_info; ++ ++void ext3_iam_free_info(struct iam_private_info *info); ++ ++int iam_uapi_ioctl(struct inode * inode, struct file * filp, unsigned int cmd, ++ unsigned long arg); ++ ++/* __KERNEL__ */ ++#endif ++ ++/* ++ * User level API. Copy exists in lustre/lustre/tests/iam_ut.c ++ */ ++ ++struct iam_uapi_info { ++ __u16 iui_keysize; ++ __u16 iui_recsize; ++ __u16 iui_ptrsize; ++ __u16 iui_height; ++ char iui_fmt_name[DX_FMT_NAME_LEN]; ++}; ++ ++struct iam_uapi_insert { ++}; ++ ++struct iam_uapi_lookup { ++}; ++ ++enum iam_ioctl_cmd { ++ IAM_IOC_INIT = _IOW('i', 1, struct iam_uapi_info), ++ IAM_IOC_GETINFO = _IOR('i', 2, struct iam_uapi_info), ++ IAM_IOC_INSERT = _IOW('i', 3, struct iam_uapi_insert), ++ IAM_IOC_LOOKUP = _IOR('i', 4, struct iam_uapi_lookup) ++}; ++ + /* __LINUX_LUSTRE_IAM_H__ */ + #endif diff --git a/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series b/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series index 3934e40..f6d42cd 100644 --- a/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series +++ b/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series @@ -18,3 +18,4 @@ ext3-hash-selection.patch ext3-htree-comments.patch ext3-iam-ops.patch ext3-iam-separate.patch +ext3-iam-uapi.patch diff --git a/lustre/ldiskfs/Makefile.in b/lustre/ldiskfs/Makefile.in index df5199d..9c27db4 100644 --- a/lustre/ldiskfs/Makefile.in +++ b/lustre/ldiskfs/Makefile.in @@ -11,7 +11,7 @@ ext3_headers := $(wildcard @LINUX@/fs/ext3/*.h) linux_headers := $(wildcard @LINUX@/include/linux/ext3*.h) ext3_sources := $(filter-out %.mod.c,$(wildcard @LINUX@/fs/ext3/*.c)) -new_sources := iopen.c iopen.h extents.c mballoc.c iam.c iam_lfix.c +new_sources := iopen.c iopen.h extents.c mballoc.c iam.c iam_lfix.c iam-uapi.c new_headers := ext3_extents.h ldiskfs_patched_sources := $(notdir $(ext3_sources) $(ext3_headers)) $(new_sources) $(new_headers) ldiskfs_sources := $(ldiskfs_patched_sources) diff --git a/lustre/tests/Makefile.am b/lustre/tests/Makefile.am index a7d0a0a..8713a35 100644 --- a/lustre/tests/Makefile.am +++ b/lustre/tests/Makefile.am @@ -31,6 +31,7 @@ noinst_PROGRAMS += openfilleddirunlink rename_many memhog iopentest1 iopentest2 noinst_PROGRAMS += mmap_sanity flock_test writemany random-reads if MPITESTS noinst_PROGRAMS += parallel_grouplock write_append_truncate createmany_mpi +noinst_PROGRAMS += iam_ut endif # noinst_PROGRAMS += ldaptest copy_attr mkdirdeep bin_PROGRAMS = mcreate munlink diff --git a/lustre/tests/iam_ut b/lustre/tests/iam_ut new file mode 100755 index 0000000000000000000000000000000000000000..9126980f2d38479921e7e3ae7b1fa99941b9b595 GIT binary patch literal 56203 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