return name_is_dot_or_dotdot(lname->ln_name, lname->ln_namelen);
}
+/**
+ * Determine if filename should be considered a "temporary" name.
+ *
+ * For temporary names, use only the main part of the filename and ignore
+ * the suffix, so that the filename will hash to the same MDT after it is
+ * renamed. That avoids creating spurious remote entries for rsync, dcp,
+ * vi, and other tools that create a temporary name before renaming the file.
+ *
+ * The "CRUSH" and "CRUSH2" hash types are slightly different, and should
+ * not be modified without introducing a new hash type. The hash algorithm
+ * forms an important part of the network protocol for striped directories,
+ * so if the hash function were "fixed" in any way it would prevent clients
+ * from looking up a filename on the right MDT. LU-15692.
+ *
+ * \param[in] name filename
+ * \param[in] namelen length of @name
+ * \param[in] dot_prefix if @name needs a leading '.' to be temporary
+ * \param[in] suffixlen number of characters after '.' in @name to check
+ * \param[in] crush2 whether CRUSH or CRUSH2 heuristic should be used
+ */
static inline bool lu_name_is_temp_file(const char *name, int namelen,
- bool dot_prefix, int suffixlen)
+ bool dot_prefix, int suffixlen,
+ bool crush2)
{
int lower = 0;
int upper = 0;
name[namelen - suffixlen - 1] != '.')
return false;
+ /* Any non-alphanumeric chars in the suffix for CRUSH2 mean the
+ * filename is *not* temporary. The original CRUSH was incorrectly
+ * matching if a '.' happens to be in the right place, for example
+ * file.mdtest.12.12345 or output.6334.log, which is bad. LU-15692
+ */
while (len) {
- lower += islower(name[namelen - len]);
- upper += isupper(name[namelen - len]);
- digit += isdigit(name[namelen - len]);
+ if (islower(name[namelen - len]))
+ lower++;
+ else if (isupper(name[namelen - len]))
+ upper++;
+ else if (isdigit(name[namelen - len]))
+ digit++;
+ else if (crush2)
+ return false;
len--;
}
- /* mktemp() filename suffixes will have a mix of upper- and lower-case
- * letters and/or numbers, not all numbers, or all upper or lower-case.
- * About 0.07% of randomly-generated names will slip through,
+
+ /* mktemp() suffixes normally have a mix of upper- and lower-case
+ * letters and/or digits, rarely all upper- or lower-case or digits.
+ * Random all-digit suffixes are rare (1/45k for suffixlen=6), but
+ * common in normal usage (incrementing versions, dates, ranks, etc),
+ * so are considered non-temporary even if 1 or 2 non-numeric chars.
+ *
+ * About 0.07% of randomly-generated names will slip through, which
+ * only means that they may be renamed to a different MDT (slowdown),
* but this avoids 99.93% of cross-MDT renames for those files.
*/
- if ((digit >= suffixlen - 1 && !isdigit(name[namelen - suffixlen])) ||
- upper == suffixlen || lower == suffixlen)
+ if (upper == suffixlen || lower == suffixlen)
return false;
+ if (crush2) {
+ if (digit >= suffixlen - 1 &&
+ isdigit(name[namelen - suffixlen]))
+ return false;
+ } else { /* old crush incorrectly returns "true" for all-digit suffix */
+ if (digit >= suffixlen - 1 &&
+ !isdigit(name[namelen - suffixlen]))
+ return false;
+ }
+
return true;
}
CDEBUG_LIMIT(mask,
"dump LMV: refs %u magic=%#x count=%u index=%u hash=%s:%#x max_inherit=%hhu max_inherit_rr=%hhu version=%u migrate_offset=%u migrate_hash=%s:%x pool=%.*s\n",
- lsm->lsm_md_magic, atomic_read(&lsmo->lso_refs),
+ atomic_read(&lsmo->lso_refs), lsm->lsm_md_magic,
lsm->lsm_md_stripe_count, lsm->lsm_md_master_mdt_index,
lmv_is_known_hash_type(lsm->lsm_md_hash_type) ?
mdt_hash_name[lsm->lsm_md_hash_type & LMV_HASH_TYPE_MASK] :
* algorithm.
*/
static inline unsigned int
-lmv_hash_crush(unsigned int count, const char *name, int namelen)
+lmv_hash_crush(unsigned int count, const char *name, int namelen, bool crush2)
{
unsigned long long straw;
unsigned long long highest_straw = 0;
* 1. rsync: .<target>.XXXXXX
* 2. dstripe: <target>.XXXXXXXX
*/
- if (lu_name_is_temp_file(name, namelen, true, 6)) {
+ if (lu_name_is_temp_file(name, namelen, true, 6, crush2)) {
name++;
namelen -= 8;
- } else if (lu_name_is_temp_file(name, namelen, false, 8)) {
+ } else if (lu_name_is_temp_file(name, namelen, false, 8, crush2)) {
namelen -= 9;
} else if (lu_name_is_backup_file(name, namelen, &i)) {
LASSERT(i < namelen);
break;
case LMV_HASH_TYPE_CRUSH:
stripe_index = lmv_hash_crush(stripe_count, name,
- namelen);
+ namelen, false);
+ break;
+ case LMV_HASH_TYPE_CRUSH2:
+ stripe_index = lmv_hash_crush(stripe_count, name,
+ namelen, true);
break;
default:
return -EBADFD;
lum_magic == LMV_MAGIC_FOREIGN;
}
+#define LMV_DEBUG(mask, lmv, msg) \
+ CDEBUG_LIMIT(mask, \
+ "%s LMV: magic=%#x count=%u index=%u hash=%s:%#x version=%u migrate_offset=%u migrate_hash=%s:%x pool=%.*s\n",\
+ msg, (lmv)->lmv_magic, (lmv)->lmv_stripe_count, \
+ (lmv)->lmv_master_mdt_index, \
+ lmv_is_known_hash_type((lmv)->lmv_hash_type) ? \
+ mdt_hash_name[(lmv)->lmv_hash_type & LMV_HASH_TYPE_MASK] : \
+ "invalid", (lmv)->lmv_hash_type, \
+ (lmv)->lmv_layout_version, (lmv)->lmv_migrate_offset, \
+ lmv_is_known_hash_type((lmv)->lmv_migrate_hash) ? \
+ mdt_hash_name[(lmv)->lmv_migrate_hash & LMV_HASH_TYPE_MASK] : \
+ "invalid", (lmv)->lmv_migrate_hash, \
+ LOV_MAXPOOLNAME, lmv->lmv_pool_name)
+
/* master LMV is sane */
static inline bool lmv_is_sane(const struct lmv_mds_md_v1 *lmv)
{
return true;
insane:
- LMV_DEBUG(D_ERROR, lmv, "insane");
+ LMV_DEBUG(D_ERROR, lmv, "unknown layout");
return false;
}
return true;
insane:
- LMV_DEBUG(D_ERROR, lmv, "insane");
+ LMV_DEBUG(D_ERROR, lmv, "unknown layout");
return false;
}
#define OBD_FAIL_MIGRATE_BAD_HASH 0x1802
/* LMV */
-#define OBD_FAIL_UNKNOWN_LMV_STRIPE 0x1901
+#define OBD_FAIL_LMV_UNKNOWN_STRIPE 0x1901
/* FLR */
#define OBD_FAIL_FLR_LV_DELAY 0x1A01
struct lu_fid lmv_stripe_fids[0]; /* FIDs for each stripe */
};
-#define LMV_DEBUG(mask, lmv, msg) \
- CDEBUG(mask, \
- "%s LMV: magic=%#x count=%u index=%u hash=%#x version=%u migrate offset=%u migrate hash=%u.\n", \
- msg, (lmv)->lmv_magic, (lmv)->lmv_stripe_count, \
- (lmv)->lmv_master_mdt_index, (lmv)->lmv_hash_type, \
- (lmv)->lmv_layout_version, (lmv)->lmv_migrate_offset, \
- (lmv)->lmv_migrate_hash)
-
/* stripe count before directory split */
#define lmv_split_offset lmv_migrate_offset
/* stripe count after directory merge */
enum lmv_hash_type {
LMV_HASH_TYPE_UNKNOWN = 0, /* 0 is reserved for testing purpose */
- LMV_HASH_TYPE_ALL_CHARS = 1,
- LMV_HASH_TYPE_FNV_1A_64 = 2,
- LMV_HASH_TYPE_CRUSH = 3,
+ LMV_HASH_TYPE_ALL_CHARS = 1, /* simple sum of characters */
+ LMV_HASH_TYPE_FNV_1A_64 = 2, /* reasonable non-cryptographic hash */
+ LMV_HASH_TYPE_CRUSH = 3, /* double-hash to optimize migration */
+ LMV_HASH_TYPE_CRUSH2 = 4, /* CRUSH with small fixes, LU-15692 */
LMV_HASH_TYPE_MAX,
+ LMV_HASH_TYPE_DEFAULT = LMV_HASH_TYPE_FNV_1A_64
};
static __attribute__((unused)) const char *mdt_hash_name[] = {
"all_char",
"fnv_1a_64",
"crush",
+ "crush2",
};
-#define LMV_HASH_TYPE_DEFAULT LMV_HASH_TYPE_FNV_1A_64
/* Right now only the lower part(0-16bits) of lmv_hash_type is being used,
* and the higher part will be the flag to indicate the status of object,
static inline bool lmv_is_known_hash_type(__u32 type)
{
- return (type & LMV_HASH_TYPE_MASK) == LMV_HASH_TYPE_FNV_1A_64 ||
- (type & LMV_HASH_TYPE_MASK) == LMV_HASH_TYPE_ALL_CHARS ||
- (type & LMV_HASH_TYPE_MASK) == LMV_HASH_TYPE_CRUSH;
+ return (type & LMV_HASH_TYPE_MASK) > LMV_HASH_TYPE_UNKNOWN &&
+ (type & LMV_HASH_TYPE_MASK) < LMV_HASH_TYPE_MAX;
}
/* This flag indicates that overstriping (>1 stripe per MDT) is desired */
if (lump->lum_magic != LMV_MAGIC_FOREIGN) {
CDEBUG(D_VFSTRACE,
- "VFS Op:inode="DFID"(%p) name %s stripe_offset %d, stripe_count: %u\n",
+ "VFS Op:inode="DFID"(%p) name=%s stripe_offset=%d stripe_count=%u, hash_type=%x\n",
PFID(ll_inode2fid(parent)), parent, dirname,
- (int)lump->lum_stripe_offset, lump->lum_stripe_count);
+ (int)lump->lum_stripe_offset, lump->lum_stripe_count,
+ lump->lum_hash_type);
} else {
struct lmv_foreign_md *lfm = (struct lmv_foreign_md *)lump;
/* MDS < 2.14 doesn't support 'crush' hash type, and cannot handle
* unknown hash if client doesn't set a valid one. switch to fnv_1a_64.
*/
- if (!(exp_connect_flags2(sbi->ll_md_exp) & OBD_CONNECT2_CRUSH)) {
+ if (CFS_FAIL_CHECK(OBD_FAIL_LMV_UNKNOWN_STRIPE)) {
+ lump->lum_hash_type = cfs_fail_val;
+ } else if (!(exp_connect_flags2(sbi->ll_md_exp) & OBD_CONNECT2_CRUSH)) {
enum lmv_hash_type type = lump->lum_hash_type &
LMV_HASH_TYPE_MASK;
lsm->lsm_md_magic = le32_to_cpu(lmm1->lmv_magic);
lsm->lsm_md_stripe_count = le32_to_cpu(lmm1->lmv_stripe_count);
lsm->lsm_md_master_mdt_index = le32_to_cpu(lmm1->lmv_master_mdt_index);
- if (OBD_FAIL_CHECK(OBD_FAIL_UNKNOWN_LMV_STRIPE))
- lsm->lsm_md_hash_type = LMV_HASH_TYPE_UNKNOWN;
+ if (CFS_FAIL_CHECK(OBD_FAIL_LMV_UNKNOWN_STRIPE))
+ lsm->lsm_md_hash_type = cfs_fail_val ?: LMV_HASH_TYPE_UNKNOWN;
else
lsm->lsm_md_hash_type = le32_to_cpu(lmm1->lmv_hash_type);
lsm->lsm_md_layout_version = le32_to_cpu(lmm1->lmv_layout_version);
struct dt_object_format *dof,
struct thandle *th)
{
- struct lod_object *lo = lod_dt_obj(dt);
- struct lmv_user_md_v1 *lum = lum_buf->lb_buf;
- int rc;
- ENTRY;
+ struct lod_object *lo = lod_dt_obj(dt);
+ struct lmv_user_md_v1 *lum = lum_buf->lb_buf;
+ int rc;
+ ENTRY;
LASSERT(lum != NULL);
- CDEBUG(D_INFO, "lum magic = %x count = %u offset = %d\n",
- le32_to_cpu(lum->lum_magic), le32_to_cpu(lum->lum_stripe_count),
- (int)le32_to_cpu(lum->lum_stripe_offset));
+ CDEBUG(D_INFO,
+ "lum magic=%x hash=%x count=%u offset=%d inherit=%u rr=%u\n",
+ le32_to_cpu(lum->lum_magic), le32_to_cpu(lum->lum_hash_type),
+ le32_to_cpu(lum->lum_stripe_count),
+ (int)le32_to_cpu(lum->lum_stripe_offset),
+ lum->lum_max_inherit, lum->lum_max_inherit_rr);
if (lo->ldo_dir_stripe_count == 0) {
if (lo->ldo_is_foreign) {
*
* \param[in] env execution environment
* \param[in] dt target object
- * \param[in] buf LMV buf which contains source stripe fids
+ * \param[in] lmv_buf LMV buf which contains source stripe FIDs
* \param[in] fl set or replace
* \param[in] th transaction handle
*
*/
static int lod_dir_layout_set(const struct lu_env *env,
struct dt_object *dt,
- const struct lu_buf *buf,
+ const struct lu_buf *lmv_buf,
int fl,
struct thandle *th)
{
struct dt_object *next = dt_object_child(dt);
struct lod_object *lo = lod_dt_obj(dt);
struct lod_device *lod = lu2lod_dev(lod2lu_obj(lo)->lo_dev);
- struct lmv_mds_md_v1 *lmv = buf->lb_buf;
+ struct lmv_mds_md_v1 *lmv = lmv_buf->lb_buf;
struct lmv_mds_md_v1 *slave_lmv;
struct lu_buf slave_buf;
int i;
LMV_DEBUG(D_INFO, lmv, "set");
- rc = lod_sub_xattr_set(env, next, buf, XATTR_NAME_LMV, fl, th);
+ rc = lod_sub_xattr_set(env, next, lmv_buf, XATTR_NAME_LMV, fl, th);
if (rc)
RETURN(rc);
struct lod_layout_component *def_comp =
&lds->lds_def_comp_entries[i];
- CDEBUG(D_LAYOUT, "Inherit from default: flags=%#x "
- "size=%hu nr=%u offset=%u pattern=%#x pool=%s\n",
+ CDEBUG(D_LAYOUT,
+ "inherit "DFID" file layout from default: flags=%#x size=%hu nr=%u offset=%u pattern=%#x pool=%s\n",
+ PFID(lu_object_fid(&lo->ldo_obj.do_lu)),
def_comp->llc_flags,
def_comp->llc_stripe_size,
def_comp->llc_stripe_count,
if (lo->ldo_dir_stripe_offset == -1)
lo->ldo_dir_stripe_offset =
lds->lds_dir_def_stripe_offset;
- if (lo->ldo_dir_hash_type == 0)
+ if (lo->ldo_dir_hash_type == LMV_HASH_TYPE_UNKNOWN)
lo->ldo_dir_hash_type = lds->lds_dir_def_hash_type;
- CDEBUG(D_LAYOUT, "striping from default dir: count:%hu, "
- "offset:%u, hash_type:%u\n",
+ CDEBUG(D_LAYOUT,
+ "inherit "DFID" dir layout from default: count=%hu offset=%u hash_type=%x\n",
+ PFID(lu_object_fid(&lo->ldo_obj.do_lu)),
lo->ldo_dir_stripe_count, lo->ldo_dir_stripe_offset,
lo->ldo_dir_hash_type);
}
* This method is used to make a decision on the striping configuration for the
* object being created. It can be taken from the \a parent object if it exists,
* or filesystem's default. The resulting configuration (number of stripes,
- * stripe size/offset, pool name, etc) is stored in the object itself and will
- * be used by the methods like ->doo_declare_create().
+ * stripe size/offset, pool name, hash_type, etc.) is stored in the object
+ * itself and will be used by the methods like ->doo_declare_create().
*
* \see dt_object_operations::do_ah_init() in the API description for details.
*/
lc->ldo_dir_hash_type =
le32_to_cpu(lum1->lum_hash_type);
CDEBUG(D_INFO,
- "set dirstripe: count %hu, offset %d, hash %u\n",
+ "set dirstripe: count %hu, offset %d, hash %x\n",
lc->ldo_dir_stripe_count,
(int)lc->ldo_dir_stripe_offset,
lc->ldo_dir_hash_type);
lc->ldo_dir_stripe_count = 0;
}
- if (!(lc->ldo_dir_hash_type & LMV_HASH_TYPE_MASK))
- lc->ldo_dir_hash_type |=
+ if (!lmv_is_known_hash_type(lc->ldo_dir_hash_type))
+ lc->ldo_dir_hash_type =
+ (lc->ldo_dir_hash_type & LMV_HASH_FLAG_KNOWN) |
d->lod_mdt_descs.ltd_lmv_desc.ld_pattern;
/* make sure all fscrypt metadata stays on same mdt */
struct lod_device *lod = dt2lod_dev(dt);
char *hash;
int len;
+ int rc = -EINVAL;
int i;
hash = kstrndup(buffer, count, GFP_KERNEL);
if (!hash)
return -ENOMEM;
+ if (kstrtoint(hash, 10, &i) == 0 && lmv_is_known_hash_type(i)) {
+ lod->lod_mdt_descs.ltd_lmv_desc.ld_pattern = i;
+ GOTO(out, rc = count);
+ }
+
len = strcspn(hash, "\n ");
hash[len] = '\0';
- for (i = LMV_HASH_TYPE_ALL_CHARS; i < LMV_HASH_TYPE_MAX; i++) {
- if (!strcmp(hash, mdt_hash_name[i])) {
+ for (i = LMV_HASH_TYPE_ALL_CHARS; i < ARRAY_SIZE(mdt_hash_name); i++) {
+ if (strcmp(hash, mdt_hash_name[i]) == 0) {
lod->lod_mdt_descs.ltd_lmv_desc.ld_pattern = i;
- kfree(hash);
- return count;
+ GOTO(out, rc = count);
}
}
+out:
kfree(hash);
- return -EINVAL;
+ return rc;
}
LUSTRE_RW_ATTR(mdt_hash);
if (!lmv_user_magic_supported(le32_to_cpu(lum->lum_magic)) &&
le32_to_cpu(lum->lum_magic) != LMV_USER_MAGIC_V0) {
rc = -EINVAL;
- CERROR("%s: invalid lmv_user_md: magic = %x, "
- "stripe_offset = %d, stripe_count = %u: "
- "rc = %d\n", mdd2obd_dev(m)->obd_name,
- le32_to_cpu(lum->lum_magic),
+ CERROR("%s: invalid lmv_user_md: magic=%x hash=%x stripe_offset=%d stripe_count=%u: rc = %d\n",
+ mdd2obd_dev(m)->obd_name,
+ le32_to_cpu(lum->lum_magic),
+ le32_to_cpu(lum->lum_hash_type),
(int)le32_to_cpu(lum->lum_stripe_offset),
le32_to_cpu(lum->lum_stripe_count), rc);
- return rc;
+
+ RETURN(rc);
}
}
}
if ((!(exp_connect_flags2(exp) & OBD_CONNECT2_CRUSH)) &&
- (le32_to_cpu(lum->lum_hash_type) & LMV_HASH_TYPE_MASK) ==
+ (le32_to_cpu(lum->lum_hash_type) & LMV_HASH_TYPE_MASK) >=
LMV_HASH_TYPE_CRUSH)
RETURN(-EPROTO);
if ((lmv_is_splitting(lmv) &&
idx >= le32_to_cpu(lmv->lmv_split_offset)) ||
(lmv_is_merging(lmv) &&
- (le32_to_cpu(lmv->lmv_hash_type) & LMV_HASH_TYPE_MASK) ==
- LMV_HASH_TYPE_CRUSH &&
+ ((le32_to_cpu(lmv->lmv_hash_type) & LMV_HASH_TYPE_MASK) ==
+ LMV_HASH_TYPE_CRUSH ||
+ (le32_to_cpu(lmv->lmv_hash_type) & LMV_HASH_TYPE_MASK) ==
+ LMV_HASH_TYPE_CRUSH2) &&
idx < le32_to_cpu(lmv->lmv_merge_offset))) {
/* new stripes doesn't need to migrate sub files in dir
* split, neither for target stripes in dir merge if hash type
- * is CRUSH.
+ * is CRUSH or CRUSH2.
*/
rc = mdt_restripe_migrate_finish(info, stripe, lmv);
RETURN(rc);
$DIR/$tdir/${tdir}2 ||
error "$DIR/$tdir/${tdir}2: create failed"
+ $LFS getdirstripe -v $DIR/$tdir/${tdir}2
+
$LFS getdirstripe -v $DIR/$tdir/${tdir}2 |
grep "lfm_magic:.*0x0CD50CD0" ||
error "$DIR/$tdir/${tdir}2: invalid LMV EA magic"
}
run_test 33g "nonroot user create already existing root created file"
-test_33h() {
- [ $MDSCOUNT -lt 2 ] && skip_env "needs >= 2 MDTs"
- [ $MDS1_VERSION -lt $(version_code 2.13.50) ] &&
- skip "Need MDS version at least 2.13.50"
+sub_33h() {
+ local hash_type=$1
+ local count=250
- test_mkdir -c $MDSCOUNT -H crush $DIR/$tdir ||
- error "mkdir $tdir failed"
+ test_mkdir -c $MDSCOUNT -H $hash_type $DIR/$tdir ||
+ error "lfs mkdir -H $hash_type $tdir failed"
touch $DIR/$tdir/$tfile || error "touch $tfile failed"
local index=$($LFS getstripe -m $DIR/$tdir/$tfile)
local index2
+ local fname
for fname in $DIR/$tdir/$tfile.bak \
$DIR/$tdir/$tfile.SAV \
$DIR/$tdir/$tfile.orig \
$DIR/$tdir/$tfile~; do
- touch $fname || error "touch $fname failed"
+ touch $fname || error "touch $fname failed"
index2=$($LFS getstripe -m $fname)
- [ $index -eq $index2 ] ||
+ (( $index == $index2 )) ||
error "$fname MDT index mismatch $index != $index2"
done
local failed=0
- for i in {1..250}; do
- for fname in $(mktemp -u $DIR/$tdir/.$tfile.XXXXXX) \
- $(mktemp $DIR/$tdir/$tfile.XXXXXXXX); do
- touch $fname || error "touch $fname failed"
+ local patterns=(".$tfile.XXXXXX" "$tfile.XXXXXXXX")
+ local pattern
+
+ for pattern in ${patterns[*]}; do
+ echo "pattern $pattern"
+ fname=$DIR/$tdir/$pattern
+ for (( i = 0; i < $count; i++ )); do
+ fname=$(mktemp $DIR/$tdir/$pattern) ||
+ error "mktemp $DIR/$tdir/$pattern failed"
index2=$($LFS getstripe -m $fname)
- if [[ $index != $index2 ]]; then
- failed=$((failed + 1))
- echo "$fname MDT index mismatch $index != $index2"
- fi
+ (( $index == $index2 )) && continue
+
+ failed=$((failed + 1))
+ echo "$fname MDT index mismatch $index != $index2"
+ done
+ done
+
+ echo "$failed/$count MDT index mismatches, expect ~2-4"
+ (( failed < 10 )) || error "MDT index mismatch $failed/$count times"
+
+ local same=0
+ local expect
+
+ # verify that "crush" is still broken with all files on same MDT,
+ # crush2 should have about 1/MDSCOUNT files on each MDT, with margin
+ [[ "$hash_type" == "crush" ]] && expect=$count ||
+ expect=$((count / MDSCOUNT))
+
+ # crush2 doesn't put all-numeric suffixes on the same MDT,
+ # filename like $tfile.12345678 should *not* be considered temp
+ for pattern in ${patterns[*]}; do
+ local base=${pattern%%X*}
+ local suff=${pattern#$base}
+
+ echo "pattern $pattern"
+ for (( i = 0; i < $count; i++ )); do
+ fname=$DIR/$tdir/$base$((${suff//X/1} + i))
+ touch $fname || error "touch $fname failed"
+ index2=$($LFS getstripe -m $fname)
+ (( $index != $index2 )) && continue
+
+ same=$((same + 1))
+ done
+ done
+
+ # the number of "bad" hashes is random, as it depends on the random
+ # filenames generated by "mktemp". Allow some margin in the results.
+ echo "$((same/${#patterns[*]}))/$count matches, expect ~$expect for $1"
+ (( same / ${#patterns[*]} <= expect * 9 / 7 &&
+ same / ${#patterns[*]} > expect * 5 / 7 )) ||
+ error "MDT index match $((same / ${#patterns[*]}))/$count times"
+ same=0
+
+ # crush2 doesn't put suffixes with special characters on the same MDT
+ # filename like $tfile.txt.1234 should *not* be considered temp
+ for pattern in ${patterns[*]}; do
+ local base=${pattern%%X*}
+ local suff=${pattern#$base}
+
+ pattern=$base...${suff/XXX}
+ echo "pattern=$pattern"
+ for (( i = 0; i < $count; i++ )); do
+ fname=$(mktemp $DIR/$tdir/$pattern) ||
+ error "touch $fname failed"
+ index2=$($LFS getstripe -m $fname)
+ (( $index != $index2 )) && continue
+
+ same=$((same + 1))
done
done
- echo "$failed MDT index mismatches"
- (( failed < 20 )) || error "MDT index mismatch $failed times"
+ echo "$((same/${#patterns[*]}))/$count matches, expect ~$expect for $1"
+ (( same / ${#patterns[*]} < expect * 5 / 4 &&
+ same / ${#patterns[*]} > expect * 4 / 5 )) ||
+ error "MDT index match $((same / ${#patterns[*]}))/$count times"
+}
+
+test_33h() {
+ (( $MDSCOUNT >= 2 )) || skip "needs >= 2 MDTs"
+ (( $MDS1_VERSION >= $(version_code 2.13.50) )) ||
+ skip "Need MDS version at least 2.13.50"
+
+ sub_33h crush
}
-run_test 33h "temp file is located on the same MDT as target"
+run_test 33h "temp file is located on the same MDT as target (crush)"
+
+test_33hh() {
+ (( $MDSCOUNT >= 2 )) || skip "needs >= 2 MDTs"
+ echo "MDS1_VERSION=$MDS1_VERSION version_code=$(version_code 2.14.0.144)"
+ (( $MDS1_VERSION >= $(version_code 2.14.0.144) )) ||
+ skip "Need MDS version at least 2.14.0.144 for crush2"
+
+ sub_33h crush2
+}
+run_test 33hh "temp file is located on the same MDT as target (crush2)"
TEST_34_SIZE=${TEST_34_SIZE:-2000000000000}
test_34a() {
run_test 300h "check default striped directory for striped directory"
test_300i() {
- [ $PARALLEL == "yes" ] && skip "skip parallel run"
- [ $MDSCOUNT -lt 2 ] && skip_env "needs >= 2 MDTs"
- [ $MDS1_VERSION -lt $(version_code 2.7.55) ] &&
+ [[ $PARALLEL == "yes" ]] && skip "skip parallel run"
+ (( $MDSCOUNT >= 2 )) || skip_env "needs >= 2 MDTs"
+ (( $MDS1_VERSION >= $(version_code 2.7.55) )) ||
skip "Need MDS version at least 2.7.55"
local stripe_count
$LFS find -H fnv_1a_64,crush $DIR/$tdir/hashdir
local dircnt=$($LFS find -H fnv_1a_64,crush $DIR/$tdir/hashdir | wc -l)
- [ $dircnt -eq 2 ] || error "lfs find striped dir got:$dircnt,except:1"
-
- #set the stripe to be unknown hash type
- #define OBD_FAIL_UNKNOWN_LMV_STRIPE 0x1901
- $LCTL set_param fail_loc=0x1901
+ (( $dircnt == 2 )) || error "lfs find striped dir got $dircnt != 2"
+
+ if (( $MDS1_VERSION >= $(version_code 2.14.0.144) )); then
+ set_opencache 1
+ stack_trap "restore_opencache"
+ $LFS mkdir -i0 -c$MDSCOUNT -H crush2 $DIR/$tdir/hashdir/d3 ||
+ error "create crush2 dir $tdir/hashdir/d3 failed"
+ $LFS find -H crush2 $DIR/$tdir/hashdir
+ dircnt=$($LFS find -H crush2 $DIR/$tdir/hashdir | wc -l)
+ (( $dircnt == 1 )) || error "find crush2 dir got $dircnt != 1"
+
+ # mkdir with an invalid hash type (hash=fail_val) from client
+ # should be replaced on MDS with a valid (default) hash type
+ #define OBD_FAIL_LMV_UNKNOWN_STRIPE 0x1901
+ $LCTL set_param fail_loc=0x1901 fail_val=99
+ $LFS mkdir -i 0 -c2 $DIR/$tdir/hashdir/d99
+
+ $LFS getdirstripe $DIR/$tdir/hashdir/d99
+ local hash=$($LFS getdirstripe -H $DIR/$tdir/hashdir/d99)
+ local expect=$(do_facet mds1 \
+ $LCTL get_param -n lod.$FSNAME-MDT0000-mdtlov.mdt_hash)
+ [[ $hash == $expect ]] ||
+ error "d99 hash '$hash' != expected hash '$expect'"
+ fi
+
+ #set the stripe to be unknown hash type on read
+ #define OBD_FAIL_LMV_UNKNOWN_STRIPE 0x1901
+ $LCTL set_param fail_loc=0x1901 fail_val=99
for ((i = 0; i < 10; i++)); do
$CHECKSTAT -t file $DIR/$tdir/striped_dir/f-$i ||
error "stat f-$i failed"
# code is useful for comparison two version strings to see which is newer.
version_code() {
# split arguments like "1.8.6-wc3" into "1", "8", "6", "3"
- eval set -- $(tr "[:punct:][a-z]" " " <<< $*)
+ eval set -- $(tr "[:punct:][a-zA-Z]" " " <<< $*)
echo -n $(((${1:-0}<<24) | (${2:-0}<<16) | (${3:-0}<<8) | (${4:-0})))
}
local overstripe_count
local stripe_command="-c"
+ (( $MDS1_VERSION > $(version_code 2.14.0.149) )) &&
+ hash_name+=("crush2")
+
while getopts "c:C:H:i:p" opt; do
case $opt in
c) dirstripe_count=$OPTARG;;
int i;
/* numeric hash type */
- if (hashtype && strlen(hashtype) == 1 &&
- (type_num > 0 && type_num < LMV_HASH_TYPE_MAX))
+ if (hashtype && lmv_is_known_hash_type(type_num))
return type_num;
/* string hash type */
- for (i = LMV_HASH_TYPE_ALL_CHARS; i < LMV_HASH_TYPE_MAX; i++)
+ for (i = LMV_HASH_TYPE_ALL_CHARS; i < ARRAY_SIZE(mdt_hash_name); i++)
if (strcmp(hashtype, mdt_hash_name[i]) == 0)
return i;
__u32 flag;
} mhflist[] = {
{"migrating", LMV_HASH_FLAG_MIGRATION},
+ {"bad_type", LMV_HASH_FLAG_BAD_TYPE},
{"badtype", LMV_HASH_FLAG_BAD_TYPE},
+ {"lost_lmv", LMV_HASH_FLAG_LOST_LMV},
{"lostlmv", LMV_HASH_FLAG_LOST_LMV},
};
lsa.lsa_pattern = check_hashtype(optarg);
if (lsa.lsa_pattern == 0) {
fprintf(stderr,
- "%s %s: bad stripe hash type '%s'\n",
+ "%s %s: bad directory hash type '%s'\n",
progname, argv[0], optarg);
return CMD_HELP;
}