From: Patrick Farrell Date: Mon, 3 Jun 2019 17:43:16 +0000 (+0300) Subject: LU-10070 lod: SEL: Layout sanity checking X-Git-Tag: 2.12.56~20 X-Git-Url: https://git.whamcloud.com/?a=commitdiff_plain;h=4eca26ddab3186a68888862218fa8904f812e5a1;p=fs%2Flustre-release.git LU-10070 lod: SEL: Layout sanity checking Add layout sanity checking for self-extending layouts. This requires a more complex method checking layouts, checking the entire layout rather than just individual components against their immediate neighbors. This is implemented with a layout sanity callback which walks the layout. Incorporate mirror sanity checks from lfs.c. Cray-bug-id: LUS-2528 Signed-off-by: Patrick Farrell Change-Id: I960a4ce96ace54f7fe4305b9197e27c540f81211 Reviewed-on: https://review.whamcloud.com/33784 Reviewed-by: Patrick Farrell Tested-by: jenkins Tested-by: Maloo Reviewed-by: Alexey Lyashkov Reviewed-by: Oleg Drokin --- diff --git a/lustre/include/lustre/lustreapi.h b/lustre/include/lustre/lustreapi.h index 831143c..6ae3e66 100644 --- a/lustre/include/lustre/lustreapi.h +++ b/lustre/include/lustre/lustreapi.h @@ -1064,6 +1064,8 @@ int llapi_mirror_copy(int fd, unsigned int src, unsigned int dst, int llapi_heat_get(int fd, struct lu_heat *heat); int llapi_heat_set(int fd, __u64 flags); +int llapi_layout_sanity(struct llapi_layout *layout, bool incomplete, bool flr); +void llapi_layout_sanity_perror(int error); /** @} llapi */ diff --git a/lustre/include/uapi/linux/lustre/lustre_user.h b/lustre/include/uapi/linux/lustre/lustre_user.h index bb93afb..14c10f7 100644 --- a/lustre/include/uapi/linux/lustre/lustre_user.h +++ b/lustre/include/uapi/linux/lustre/lustre_user.h @@ -711,6 +711,10 @@ enum lov_comp_md_entry_flags { LCME_FL_PREF_RW | LCME_FL_NOSYNC | \ LCME_FL_EXTENSION) +/* The component flags can be set by users at creation/modification time. */ +#define LCME_USER_COMP_FLAGS (LCME_FL_PREF_RW | LCME_FL_NOSYNC | \ + LCME_FL_EXTENSION) + /* The mirror flags can be set by users at creation time. */ #define LCME_USER_MIRROR_FLAGS (LCME_FL_PREF_RW) diff --git a/lustre/tests/llapi_layout_test.c b/lustre/tests/llapi_layout_test.c index 3352fa5..0bae91f 100644 --- a/lustre/tests/llapi_layout_test.c +++ b/lustre/tests/llapi_layout_test.c @@ -1362,8 +1362,10 @@ void test30(void) ASSERTF(rc == 0, "errno %d", errno); /* set non-contiguous extent will fail */ - rc = llapi_layout_comp_extent_set(layout, end[0] * 2, end[1]); - ASSERTF(rc == -1 && errno == EINVAL, "rc %d, errno %d", rc, errno); + rc = llapi_layout_comp_extent_set(layout, start[1] * 2, end[1]); + ASSERTF(rc == 0, "errno %d", errno); + rc = llapi_layout_sanity(layout, false, false); + ASSERTF(rc == 12 /*LSE_NOT_ADJACENT_PREV*/, "rc %d", rc); rc = llapi_layout_comp_extent_set(layout, start[1], end[1]); ASSERTF(rc == 0, "errno %d", errno); @@ -1622,6 +1624,118 @@ void test33(void) free(lmdbuf); } +#define T34FILE "f34" +#define T34_DESC "create simple valid & invalid self extending layouts" +void test34(void) +{ + int rc, fd; + uint64_t start[4], end[4]; + struct llapi_layout *layout; + char path[PATH_MAX]; + + start[0] = 0; + end[0] = 10 * 1024 * 1024; /* 10m */ + start[1] = end[0]; + end[1] = 1024 * 1024 * 1024; /* 1G */ + start[2] = end[1]; + end[2] = 10ull * 1024 * 1024 * 1024; /* 10G */ + start[3] = end[2]; + end[3] = LUSTRE_EOF; + + if (num_osts < 2) + return; + + snprintf(path, sizeof(path), "%s/%s", lustre_dir, T34FILE); + + rc = unlink(path); + ASSERTF(rc >= 0 || errno == ENOENT, "errno = %d", errno); + + layout = llapi_layout_alloc(); + ASSERTF(layout != NULL, "errno %d", errno); + + rc = llapi_layout_stripe_count_set(layout, 1); + ASSERTF(rc == 0, "errno %d", errno); + + /* add component without adjusting previous component's extent + * end will fail. + */ + rc = llapi_layout_comp_add(layout); + ASSERTF(rc == -1 && errno == EINVAL, "rc %d, errno %d", rc, errno); + + rc = llapi_layout_comp_extent_set(layout, start[0], end[0]); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_add(layout); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_extent_set(layout, start[1], end[1]); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_flags_set(layout, LCME_FL_EXTENSION); + ASSERTF(rc == 0, "errno %d", errno); + + /* Invalid size, too small - < 64 MiB */ + rc = llapi_layout_extension_size_set(layout, 32 << 20); + ASSERTF(rc == -1, "errno %d", errno); + + /* too large - > 4 TiB */ + rc = llapi_layout_extension_size_set(layout, 5ull << 40); + ASSERTF(rc == -1, "errno %d", errno); + + /* Valid size, 64 MiB */ + rc = llapi_layout_extension_size_set(layout, 64 << 20); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_add(layout); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_extent_set(layout, start[2], end[2]); + ASSERTF(rc == 0, "errno %d", errno); + + /* Set extension space flag on adjacent components: + * This is invalid, but can't be checked until we try to create the + * file. */ + rc = llapi_layout_comp_flags_set(layout, LCME_FL_EXTENSION); + ASSERTF(rc == 0, "errno %d", errno); + + fd = llapi_layout_file_create(path, 0, 0660, layout); + ASSERTF(fd = -1, "path = %s, fd = %d, errno = %d", path, fd, errno); + + /* Delete incorrect component */ + rc = llapi_layout_comp_del(layout); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_add(layout); + ASSERTF(rc == 0, "errno %d", errno); + + /* Convert this comp to zero-length so it can be followed by extension + * space */ + rc = llapi_layout_comp_extent_set(layout, start[2], start[2]); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_add(layout); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_extent_set(layout, start[2], end[3]); + ASSERTF(rc == 0, "errno %d", errno); + + rc = llapi_layout_comp_flags_set(layout, LCME_FL_EXTENSION); + ASSERTF(rc == 0, "errno %d", errno); + + /* create composite file */ + fd = llapi_layout_file_create(path, 0, 0660, layout); + ASSERTF(fd >= 0, "path = %s, fd = %d, errno = %d", path, fd, errno); + + llapi_layout_free(layout); + + /* traverse & verify all components */ + layout = llapi_layout_get_by_path(path, 0); + ASSERTF(layout != NULL, "errno = %d", errno); + + rc = llapi_layout_sanity(layout, false, false); + ASSERTF(rc == 0, "errno %d", errno); +} + #define TEST_DESC_LEN 80 struct test_tbl_entry { void (*tte_fn)(void); @@ -1664,6 +1778,7 @@ static struct test_tbl_entry test_tbl[] = { { .tte_fn = &test31, .tte_desc = T31_DESC, .tte_skip = false }, { .tte_fn = &test32, .tte_desc = T32_DESC, .tte_skip = false }, { .tte_fn = &test33, .tte_desc = T33_DESC, .tte_skip = false }, + { .tte_fn = &test34, .tte_desc = T34_DESC, .tte_skip = false }, }; #define NUM_TESTS (sizeof(test_tbl) / sizeof(struct test_tbl_entry)) diff --git a/lustre/tests/sanity-pfl.sh b/lustre/tests/sanity-pfl.sh index 7da57de..e6f709c 100644 --- a/lustre/tests/sanity-pfl.sh +++ b/lustre/tests/sanity-pfl.sh @@ -1192,6 +1192,24 @@ test_19e() { } run_test 19e "Replay of layout instantiation & extension" +test_19f() { + [ $OSTCOUNT -lt 2 ] && skip "needs >= 2 OSTs" && return + [ $(lustre_version_code $SINGLEMDS) -lt $(version_code $SEL_VER) ] && + skip "skipped for lustre < $SEL_VER" + + local comp_file=$DIR/$tdir/$tfile + local flg_opts="" + local found="" + + test_mkdir -p $DIR/$tdir + + $LFS setstripe -E 256M --comp-flags extension -E -1 $comp_file + + [ $? != 0 ] || error "should not be able to manually set extension flag" + +} +run_test 19f "Rejection of invalid layouts" + # Test out of space behavior test_20a() { [ $OSTCOUNT -lt 2 ] && skip "needs >= 2 OSTs" && return diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index d98df7a..bc26d2d 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -2472,6 +2472,8 @@ test_27D() { [ $MDS1_VERSION -lt $(version_code 2.9.55) ] || [ $CLIENT_VERSION -lt $(version_code 2.9.55) ] && skip27D+=" -s 30,31" + [ $(lustre_version_code $SINGLEMDS) -lt $(version_code $SEL_VER) ] && + skip27D+="-s 32" [[ ! $($LCTL get_param mdc.*.import) =~ connect_flags.*overstriping || $OSTCOUNT -ge $(($LOV_MAX_STRIPE_COUNT / 2)) ]] && skip27D+=" -s 32,33" diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index b0ff694..67595de 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -1083,6 +1083,13 @@ static int lfs_component_set(char *fname, int comp_id, } if (neg_flags) { + if (neg_flags & LCME_FL_STALE) { + fprintf(stderr, "%s: cannot clear 'stale' flags from " + "component. Please use lfs-mirror-resync(1) " + "instead\n", progname); + return -EINVAL; + } + ids[count] = comp_id; flags_array[count] = neg_flags | LCME_FL_NEG; ++count; @@ -1315,69 +1322,6 @@ struct mirror_args { struct mirror_args *m_next; }; -static int mirror_sanity_check_flags(struct llapi_layout *layout, void *unused) -{ - uint32_t flags; - int rc; - - rc = llapi_layout_comp_flags_get(layout, &flags); - if (rc) - return -errno; - - if (flags & LCME_FL_NEG) { - fprintf(stderr, "error: %s: negative flags are not supported\n", - progname); - return -EINVAL; - } - - if (flags & LCME_FL_STALE) { - fprintf(stderr, "error: %s: setting '%s' is not supported\n", - progname, comp_flags_table[LCME_FL_STALE].cfn_name); - return -EINVAL; - } - - return LLAPI_LAYOUT_ITER_CONT; -} - -static inline int mirror_sanity_check_one(struct llapi_layout *layout) -{ - uint64_t start, end; - uint64_t pattern; - int rc; - - /* LU-10112: do not support dom+flr in phase 1 */ - rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST); - if (rc) - return -errno; - - rc = llapi_layout_pattern_get(layout, &pattern); - if (rc) - return -errno; - - if (pattern == LOV_PATTERN_MDT || pattern == LLAPI_LAYOUT_MDT) { - fprintf(stderr, "error: %s: doesn't support dom+flr for now\n", - progname); - return -ENOTSUP; - } - - rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_LAST); - if (rc) - return -errno; - - rc = llapi_layout_comp_extent_get(layout, &start, &end); - if (rc) - return -errno; - - if (end != LUSTRE_EOF) { - fprintf(stderr, "error: %s: mirror layout doesn't reach eof\n", - progname); - return -EINVAL; - } - - rc = llapi_layout_comp_iterate(layout, mirror_sanity_check_flags, NULL); - return rc; -} - /** * enum mirror_flags - Flags for extending a mirrored file. * @MF_NO_VERIFY: Indicates not to verify the mirror(s) from victim file(s) @@ -1424,11 +1368,14 @@ static int mirror_create_sanity_check(const char *fname, return -ENODATA; } - rc = mirror_sanity_check_one(layout); + rc = llapi_layout_sanity(layout, false, true); + llapi_layout_free(layout); - if (rc) + if (rc) { + llapi_layout_sanity_perror(rc); return rc; + } } while (list != NULL) { @@ -1453,9 +1400,11 @@ static int mirror_create_sanity_check(const char *fname, } } - rc = mirror_sanity_check_one(list->m_layout); - if (rc) + rc = llapi_layout_sanity(list->m_layout, false, true); + if (rc) { + llapi_layout_sanity_perror(rc); return rc; + } list = list->m_next; } @@ -1864,9 +1813,11 @@ static int mirror_split(const char *fname, __u32 id, return -EINVAL; } - rc = mirror_sanity_check_one(layout); - if (rc) + rc = llapi_layout_sanity(layout, false, true); + if (rc) { + llapi_layout_sanity_perror(rc); goto free_layout; + } rc = llapi_layout_mirror_count_get(layout, &mirror_count); if (rc) { @@ -2190,7 +2141,8 @@ new_comp: if (layout == NULL) { fprintf(stderr, "Alloc llapi_layout failed. %s\n", strerror(errno)); - return -ENOMEM; + errno = ENOMEM; + return -1; } *composite = layout; lsa->lsa_first_comp = true; @@ -2259,31 +2211,36 @@ new_comp: fprintf(stderr, "Option 'stripe-count' can't be " "specified with Data-on-MDT component: %lld\n", lsa->lsa_stripe_count); - return -EINVAL; + errno = EINVAL; + return -1; } if (lsa->lsa_stripe_size != LLAPI_LAYOUT_DEFAULT) { fprintf(stderr, "Option 'stripe-size' can't be " "specified with Data-on-MDT component: %llu\n", lsa->lsa_stripe_size); - return -EINVAL; + errno = EINVAL; + return -1; } if (lsa->lsa_nr_tgts != 0) { fprintf(stderr, "Option 'ost-list' can't be specified " "with Data-on-MDT component: '%i'\n", lsa->lsa_nr_tgts); - return -EINVAL; + errno = EINVAL; + return -1; } if (lsa->lsa_stripe_off != LLAPI_LAYOUT_DEFAULT) { fprintf(stderr, "Option 'stripe-offset' can't be " "specified with Data-on-MDT component: %lld\n", lsa->lsa_stripe_off); - return -EINVAL; + errno = EINVAL; + return -1; } if (lsa->lsa_pool_name != 0) { fprintf(stderr, "Option 'pool' can't be specified " "with Data-on-MDT component: '%s'\n", lsa->lsa_pool_name); - return -EINVAL; + errno = EINVAL; + return -1; } rc = llapi_layout_pattern_set(layout, lsa->lsa_pattern); @@ -2347,7 +2304,8 @@ new_comp: lsa->lsa_nr_tgts != lsa->lsa_stripe_count) { fprintf(stderr, "stripe_count(%lld) != nr_tgts(%d)\n", lsa->lsa_stripe_count, lsa->lsa_nr_tgts); - return -EINVAL; + errno = EINVAL; + return -1; } for (i = 0; i < lsa->lsa_nr_tgts; i++) { rc = llapi_layout_ost_index_set(layout, i, @@ -2372,7 +2330,7 @@ new_comp: goto new_comp; } - return 0; + return rc; } static int build_component(struct llapi_layout **layout, @@ -2871,14 +2829,6 @@ static int lfs_setstripe_internal(int argc, char **argv, progname); goto usage_error; } - if (lsa.lsa_comp_neg_flags & LCME_FL_STALE) { - fprintf(stderr, - "%s: cannot clear 'stale' flags from component. Please use lfs-mirror-resync(1) instead\n", - progname); - result = -EINVAL; - goto error; - } - break; case LFS_COMP_SET_OPT: comp_set = 1; diff --git a/lustre/utils/liblustreapi_layout.c b/lustre/utils/liblustreapi_layout.c index ffee3fc..b3662cb 100644 --- a/lustre/utils/liblustreapi_layout.c +++ b/lustre/utils/liblustreapi_layout.c @@ -63,6 +63,7 @@ struct llapi_layout_comp { uint64_t llc_timestamp; /* snapshot timestamp */ struct list_head llc_list; /* linked to the llapi_layout components list */ + bool llc_ondisk; }; /** @@ -589,6 +590,7 @@ struct llapi_layout *llapi_layout_get_by_xattr(void *lov_xattr, comp->llc_stripe_offset = comp->llc_objects[0].l_ost_idx; + comp->llc_ondisk = true; list_add_tail(&comp->llc_list, &layout->llot_comp_list); layout->llot_cur_comp = comp; } @@ -1572,6 +1574,10 @@ int llapi_layout_file_open(const char *path, int open_flags, mode_t mode, return -1; } + if (layout && llapi_layout_sanity((struct llapi_layout *)layout, false, + !!(layout->llot_mirror_count > 1))) + return -1; + /* Object creation must be postponed until after layout attributes * have been applied. */ if (layout != NULL && (open_flags & O_CREAT)) @@ -1789,7 +1795,7 @@ int llapi_layout_comp_extent_get(const struct llapi_layout *layout, int llapi_layout_comp_extent_set(struct llapi_layout *layout, uint64_t start, uint64_t end) { - struct llapi_layout_comp *prev, *next, *comp; + struct llapi_layout_comp *comp; comp = __llapi_layout_cur_comp(layout); if (comp == NULL) @@ -1800,29 +1806,6 @@ int llapi_layout_comp_extent_set(struct llapi_layout *layout, return -1; } - /* - * We need to make sure the extent to be set is valid: the new - * extent must be adjacent with the prev & next component. - */ - if (comp->llc_list.prev != &layout->llot_comp_list) { - prev = list_entry(comp->llc_list.prev, typeof(*prev), - llc_list); - if (start != 0 && start != prev->llc_extent.e_end) { - errno = EINVAL; - return -1; - } - } - - if (comp->llc_list.next != &layout->llot_comp_list) { - next = list_entry(comp->llc_list.next, typeof(*next), - llc_list); - if (next->llc_extent.e_start != 0 && - end != next->llc_extent.e_start) { - errno = EINVAL; - return -1; - } - } - comp->llc_extent.e_start = start; comp->llc_extent.e_end = end; layout->llot_is_composite = true; @@ -1971,6 +1954,7 @@ int llapi_layout_mirror_id_get(const struct llapi_layout *layout, uint32_t *id) int llapi_layout_comp_add(struct llapi_layout *layout) { struct llapi_layout_comp *last, *comp, *new; + bool composite = layout->llot_is_composite; comp = __llapi_layout_cur_comp(layout); if (comp == NULL) @@ -1983,16 +1967,23 @@ int llapi_layout_comp_add(struct llapi_layout *layout) last = list_entry(layout->llot_comp_list.prev, typeof(*last), llc_list); - if (new->llc_extent.e_end <= last->llc_extent.e_end) { - __llapi_comp_free(new); - errno = EINVAL; - return -1; - } - new->llc_extent.e_start = last->llc_extent.e_end; - list_add_tail(&new->llc_list, &layout->llot_comp_list); - layout->llot_cur_comp = new; + + /* We must mark the layout composite for the sanity check, but it may + * not stay that way if the check fails */ layout->llot_is_composite = true; + layout->llot_cur_comp = new; + + /* We need to set a temporary non-zero value for "end" when we call + * comp_extent_set, so we use LUSTRE_EOF-1, which is > all allowed + * for the end of the previous component. (If we're adding this + * component, the end of the previous component cannot be EOF.) */ + if (llapi_layout_comp_extent_set(layout, last->llc_extent.e_end, + LUSTRE_EOF - 1)) { + llapi_layout_comp_del(layout); + layout->llot_is_composite = composite; + return -1; + } return 0; } @@ -2057,14 +2048,11 @@ int llapi_layout_comp_del(struct llapi_layout *layout) errno = EINVAL; return -1; } - /* It can't be the only one on the list */ - if (comp->llc_list.prev == &layout->llot_comp_list) { - errno = EINVAL; - return -1; - } - layout->llot_cur_comp = list_entry(comp->llc_list.prev, typeof(*comp), llc_list); + if (comp->llc_list.prev == &layout->llot_comp_list) + layout->llot_cur_comp = NULL; + list_del_init(&comp->llc_list); __llapi_comp_free(comp); @@ -2182,8 +2170,9 @@ int llapi_layout_comp_use(struct llapi_layout *layout, int llapi_layout_file_comp_add(const char *path, const struct llapi_layout *layout) { - int rc, fd, lum_size, tmp_errno = 0; - struct lov_user_md *lum; + int rc, fd = -1, lum_size, tmp_errno = 0; + struct llapi_layout *existing_layout = NULL; + struct lov_user_md *lum = NULL; if (path == NULL || layout == NULL || layout->llot_magic != LLAPI_LAYOUT_MAGIC) { @@ -2191,34 +2180,58 @@ int llapi_layout_file_comp_add(const char *path, return -1; } - lum = llapi_layout_to_lum(layout); - if (lum == NULL) - return -1; + fd = open(path, O_RDWR); + if (fd < 0) { + tmp_errno = errno; + rc = -1; + goto out; + } - if (lum->lmm_magic != LOV_USER_MAGIC_COMP_V1) { - free(lum); - errno = EINVAL; - return -1; + existing_layout = llapi_layout_get_by_fd(fd, 0); + if (existing_layout == NULL) { + tmp_errno = errno; + rc = -1; + goto out; } - lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size; - fd = open(path, O_RDWR); - if (fd < 0) { + rc = llapi_layout_merge(&existing_layout, layout); + if (rc) { + tmp_errno = errno; + rc = -1; + goto out; + } + + if (llapi_layout_sanity(existing_layout, false, false)) { tmp_errno = errno; rc = -1; goto out; } + lum = llapi_layout_to_lum(layout); + if (lum == NULL) { + tmp_errno = errno; + rc = -1; + goto out; + } + + if (lum->lmm_magic != LOV_USER_MAGIC_COMP_V1) { + tmp_errno = EINVAL; + rc = -1; + goto out; + } + lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size; + rc = fsetxattr(fd, XATTR_LUSTRE_LOV".add", lum, lum_size, 0); if (rc < 0) { tmp_errno = errno; - close(fd); rc = -1; goto out; } - close(fd); out: + if (fd >= 0) + close(fd); free(lum); + llapi_layout_free(existing_layout); errno = tmp_errno; return rc; } @@ -2234,18 +2247,20 @@ out: */ int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags) { - int rc, fd, lum_size; + int rc = 0, fd = -1, lum_size, tmp_errno = 0; struct llapi_layout *layout; - struct llapi_layout_comp *comp; - struct lov_user_md *lum; + struct llapi_layout_comp *comp, *next; + struct llapi_layout *existing_layout = NULL; + struct lov_user_md *lum = NULL; if (path == NULL || id > LCME_ID_MAX || (flags & ~LCME_KNOWN_FLAGS)) { errno = EINVAL; return -1; } - /* Can only specify ID or flags, not both. */ - if (id != 0 && flags != 0) { + /* Can only specify ID or flags, not both, not none. */ + if ((id != LCME_ID_INVAL && flags != 0) || + (id == LCME_ID_INVAL && flags == 0)) { errno = EINVAL; return -1; } @@ -2257,8 +2272,9 @@ int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags) llapi_layout_comp_extent_set(layout, 0, LUSTRE_EOF); comp = __llapi_layout_cur_comp(layout); if (comp == NULL) { - llapi_layout_free(layout); - return -1; + tmp_errno = errno; + rc = -1; + goto out; } comp->llc_id = id; @@ -2266,38 +2282,153 @@ int llapi_layout_file_comp_del(const char *path, uint32_t id, uint32_t flags) lum = llapi_layout_to_lum(layout); if (lum == NULL) { - llapi_layout_free(layout); - return -1; + tmp_errno = errno; + rc = -1; + goto out; } lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size; fd = open(path, O_RDWR); if (fd < 0) { + tmp_errno = errno; + rc = -1; + goto out; + } + + existing_layout = llapi_layout_get_by_fd(fd, 0); + if (existing_layout == NULL) { + tmp_errno = errno; + rc = -1; + goto out; + } + + comp = NULL; + next = NULL; + while (rc == 0 && existing_layout->llot_cur_comp != NULL) { + rc = llapi_layout_comp_use(existing_layout, comp ? + LLAPI_LAYOUT_COMP_USE_PREV : + LLAPI_LAYOUT_COMP_USE_LAST); + if (rc != 0) + break; + + next = comp; + comp = __llapi_layout_cur_comp(existing_layout); + if (comp == NULL) { + rc = -1; + break; + } + + if (id != LCME_ID_INVAL && id != comp->llc_id) + continue; + else if ((flags & LCME_FL_NEG) && (flags & comp->llc_flags)) + continue; + else if (flags && !(flags & comp->llc_flags)) + continue; + + rc = llapi_layout_comp_del(existing_layout); + /* the layout position is moved to previous one, adjust */ + comp = next; + } + if (rc < 0) { + tmp_errno = errno; + goto out; + } + + if (llapi_layout_sanity(existing_layout, false, false)) { + tmp_errno = errno; rc = -1; goto out; } rc = fsetxattr(fd, XATTR_LUSTRE_LOV".del", lum, lum_size, 0); if (rc < 0) { - int tmp_errno = errno; - close(fd); - errno = tmp_errno; + tmp_errno = errno; rc = -1; goto out; } - close(fd); + out: + if (fd >= 0) + close(fd); free(lum); llapi_layout_free(layout); + llapi_layout_free(existing_layout); + errno = tmp_errno; + return rc; } +/* Internal utility function to apply flags for sanity checking */ +static void llapi_layout_comp_apply_flags(struct llapi_layout_comp *comp, + uint32_t flags) +{ + if (flags & LCME_FL_NEG) + comp->llc_flags &= ~flags; + else + comp->llc_flags |= flags; +} + +struct llapi_layout_apply_flags_args { + uint32_t *lfa_ids; + uint32_t *lfa_flags; + int lfa_count; + int lfa_rc; +}; + + +static int llapi_layout_apply_flags_cb(struct llapi_layout *layout, + void *arg) +{ + struct llapi_layout_apply_flags_args *args = arg; + struct llapi_layout_comp *comp; + int i = 0; + + comp = __llapi_layout_cur_comp(layout); + if (comp == NULL) { + args->lfa_rc = -1; + return LLAPI_LAYOUT_ITER_STOP; + } + + for (i = 0; i < args->lfa_count; i++) { + if (comp->llc_id == args->lfa_ids[i]) + llapi_layout_comp_apply_flags(comp, args->lfa_flags[i]); + } + + return LLAPI_LAYOUT_ITER_CONT; +} + +/* Apply flags to the layout for sanity checking */ +static int llapi_layout_apply_flags(struct llapi_layout *layout, uint32_t *ids, + uint32_t *flags, int count) +{ + struct llapi_layout_apply_flags_args args; + int rc = 0; + + if (!ids || !flags || count == 0) { + errno = EINVAL; + return -1; + } + + args.lfa_ids = ids; + args.lfa_flags = flags; + args.lfa_count = count; + args.lfa_rc = 0; + + rc = llapi_layout_comp_iterate(layout, + llapi_layout_apply_flags_cb, + &args); + if (errno == ENOENT) + errno = 0; + + if (rc != LLAPI_LAYOUT_ITER_CONT) + rc = args.lfa_rc; + + return rc; +} /** - * Change flags or other parameters of the component(s) by component ID of an - * existing file. The component to be modified is specified by the - * comp->lcme_id value, which must be an unique component ID. The new - * attributes are passed in by @comp and @valid is used to specify which - * attributes in the component are going to be changed. + * Change flags by component ID of components of an existing file. + * The component to be modified is specified by the comp->lcme_id value, + * which must be a unique component ID. * * \param[in] path path name of the file * \param[in] ids An array of component IDs @@ -2308,9 +2439,10 @@ out: int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags, size_t count) { - int rc = -1, fd = -1, i; + int rc = -1, fd = -1, i, tmp_errno = 0; size_t lum_size; - struct llapi_layout *layout; + struct llapi_layout *existing_layout = NULL; + struct llapi_layout *layout = NULL; struct llapi_layout_comp *comp; struct lov_user_md *lum = NULL; @@ -2340,15 +2472,47 @@ int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags, } } + fd = open(path, O_RDWR); + if (fd < 0) { + tmp_errno = errno; + rc = -1; + goto out; + } + + existing_layout = llapi_layout_get_by_fd(fd, 0); + if (existing_layout == NULL) { + tmp_errno = errno; + rc = -1; + goto out; + } + + if (llapi_layout_apply_flags(existing_layout, ids, flags, count)) { + tmp_errno = errno; + rc = -1; + goto out; + } + + if (llapi_layout_sanity(existing_layout, false, false)) { + tmp_errno = errno; + rc = -1; + goto out; + } + layout = __llapi_layout_alloc(); - if (layout == NULL) - return -1; + if (layout == NULL) { + tmp_errno = errno; + rc = -1; + goto out; + } layout->llot_is_composite = true; for (i = 0; i < count; i++) { comp = __llapi_comp_alloc(0); - if (comp == NULL) + if (comp == NULL) { + tmp_errno = errno; + rc = -1; goto out; + } comp->llc_id = ids[i]; comp->llc_flags = flags[i]; @@ -2358,39 +2522,38 @@ int llapi_layout_file_comp_set(const char *path, uint32_t *ids, uint32_t *flags, } lum = llapi_layout_to_lum(layout); - if (lum == NULL) + if (lum == NULL) { + tmp_errno = errno; + rc = -1; goto out; + } lum_size = ((struct lov_comp_md_v1 *)lum)->lcm_size; - fd = open(path, O_RDWR); - if (fd < 0) - goto out; - /* flush cached pages from clients */ rc = llapi_file_flush(fd); if (rc) { - errno = -rc; + tmp_errno = -rc; rc = -1; - goto out_close; + goto out; } rc = fsetxattr(fd, XATTR_LUSTRE_LOV".set.flags", lum, lum_size, 0); - if (rc < 0) - goto out_close; + if (rc < 0) { + tmp_errno = errno; + goto out; + } rc = 0; -out_close: - if (fd >= 0) { - int tmp_errno = errno; - close(fd); - errno = tmp_errno; - } out: - if (lum) - free(lum); + if (fd >= 0) + close(fd); + + free(lum); + llapi_layout_free(existing_layout); llapi_layout_free(layout); + errno = tmp_errno; return rc; } @@ -2789,3 +2952,319 @@ int llapi_mirror_resync_many(int fd, struct llapi_layout *layout, /* partially successful is successful */ return 0; } + +enum llapi_layout_comp_sanity_error { + LSE_OK, + LSE_INCOMPLETE_MIRROR, + LSE_ADJACENT_EXTENSION, + LSE_INIT_EXTENSION, + LSE_FLAGS, + LSE_DOM_EXTENSION, + LSE_DOM_EXTENSION_FOLLOWING, + LSE_DOM_FLR, + LSE_SET_COMP_START, + LSE_NOT_ZERO_LENGTH_EXTENDABLE, + LSE_END_NOT_GREATER, + LSE_ZERO_LENGTH_NORMAL, + LSE_NOT_ADJACENT_PREV, + LSE_START_GT_END, + LSE_ALIGN_END, + LSE_ALIGN_EXT, +}; + +struct llapi_layout_sanity_args { + bool lsa_incomplete; + bool lsa_flr; + bool lsa_ondisk; + int lsa_rc; +}; + +static int llapi_layout_sanity_cb(struct llapi_layout *layout, + void *arg) +{ + struct llapi_layout_comp *comp, *next, *prev; + struct llapi_layout_sanity_args *args = arg; + bool first_comp = false; + + comp = __llapi_layout_cur_comp(layout); + if (comp == NULL) { + args->lsa_rc = -1; + goto out_err; + } + + if (comp->llc_list.prev != &layout->llot_comp_list) + prev = list_entry(comp->llc_list.prev, typeof(*prev), + llc_list); + else + prev = NULL; + + if (comp->llc_list.next != &layout->llot_comp_list) + next = list_entry(comp->llc_list.next, typeof(*next), + llc_list); + else + next = NULL; + + /* Start of zero implies a new mirror */ + if (comp->llc_extent.e_start == 0) { + first_comp = true; + /* Most checks apply only within one mirror, this is an + * exception. */ + if (prev && prev->llc_extent.e_end != LUSTRE_EOF) { + args->lsa_rc = LSE_INCOMPLETE_MIRROR; + goto out_err; + } + + prev = NULL; + } + + if (next && next->llc_extent.e_start == 0) + next = NULL; + + /* Flag sanity checks */ + /* No adjacent extension components */ + if ((comp->llc_flags & LCME_FL_EXTENSION) && next && + (next->llc_flags & LCME_FL_EXTENSION)) { + args->lsa_rc = LSE_ADJACENT_EXTENSION; + goto out_err; + } + + /* Extension flag cannot be applied to init components and the first + * component of each mirror is automatically init */ + if ((comp->llc_flags & LCME_FL_EXTENSION) && + (comp->llc_flags & LCME_FL_INIT || first_comp)) { + args->lsa_rc = LSE_INIT_EXTENSION; + goto out_err; + } + + if (comp->llc_ondisk) { + if (comp->llc_flags & LCME_FL_NEG) + args->lsa_rc = LSE_FLAGS; + } else if (!args->lsa_incomplete) { + if (args->lsa_flr) { + if (comp->llc_flags & ~LCME_USER_COMP_FLAGS) + args->lsa_rc = LSE_FLAGS; + } else { + if (comp->llc_flags & ~LCME_FL_EXTENSION) + args->lsa_rc = LSE_FLAGS; + } + } + if (args->lsa_rc) + goto out_err; + + /* DoM sanity checks */ + if (comp->llc_pattern == LLAPI_LAYOUT_MDT || + comp->llc_pattern == LOV_PATTERN_MDT) { + /* DoM components can't be extension components */ + if (comp->llc_flags & LCME_FL_EXTENSION) { + args->lsa_rc = LSE_DOM_EXTENSION; + goto out_err; + } + /* DoM components cannot be followed by an extension comp */ + if (next && (next->llc_flags & LCME_FL_EXTENSION)) { + args->lsa_rc = LSE_DOM_EXTENSION_FOLLOWING; + goto out_err; + } + + /* DoM and FLR are not supported together */ + if (args->lsa_flr && first_comp) { + args->lsa_rc = LSE_DOM_FLR; + errno = ENOTSUP; + goto out_err; + } + } + + /* Extent sanity checks */ + /* Must set previous component extent before adding another */ + if (prev && prev->llc_extent.e_start == 0 && + prev->llc_extent.e_end == 0) { + args->lsa_rc = LSE_SET_COMP_START; + goto out_err; + } + + if (!args->lsa_incomplete) { + /* Components followed by extension space (extendable + * components) must be zero length before initialization. + * (Except for first comp, which will be initialized on + * creation). */ + if (next && (next->llc_flags & LCME_FL_EXTENSION) && + !first_comp && !(comp->llc_flags & LCME_FL_INIT) && + comp->llc_extent.e_start != comp->llc_extent.e_end) { + args->lsa_rc = LSE_NOT_ZERO_LENGTH_EXTENDABLE; + goto out_err; + } + + /* End must come after end of previous comp */ + if (prev && comp->llc_extent.e_end < prev->llc_extent.e_end) { + args->lsa_rc = LSE_END_NOT_GREATER; + goto out_err; + } + + /* Components not followed by ext space must have length > 0. */ + if (comp->llc_extent.e_start == comp->llc_extent.e_end && + next && !(next->llc_flags & LCME_FL_EXTENSION)) { + args->lsa_rc = LSE_ZERO_LENGTH_NORMAL; + goto out_err; + } + + /* The component end must be aligned by the stripe size */ + if ((comp->llc_flags & LCME_FL_EXTENSION) && + (prev->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) { + if (comp->llc_extent.e_end != LUSTRE_EOF && + comp->llc_extent.e_end % prev->llc_stripe_size) { + args->lsa_rc = LSE_ALIGN_END; + goto out_err; + } + if ((comp->llc_stripe_size * SEL_UNIT_SIZE) % + prev->llc_stripe_size) { + args->lsa_rc = LSE_ALIGN_EXT; + goto out_err; + } + } else if (!(comp->llc_flags & LCME_FL_EXTENSION) && + (comp->llc_stripe_size != LLAPI_LAYOUT_DEFAULT)) { + if (comp->llc_extent.e_end != LUSTRE_EOF && + comp->llc_extent.e_end % comp->llc_stripe_size) { + args->lsa_rc = LSE_ALIGN_END; + goto out_err; + } + } + } + + /* Components must have start == prev->end */ + if (prev && comp->llc_extent.e_start != 0 && + comp->llc_extent.e_start != prev->llc_extent.e_end) { + args->lsa_rc = LSE_NOT_ADJACENT_PREV; + goto out_err; + } + + /* Components must have start <= end */ + if (comp->llc_extent.e_start > comp->llc_extent.e_end) { + args->lsa_rc = LSE_START_GT_END; + goto out_err; + } + + return LLAPI_LAYOUT_ITER_CONT; + +out_err: + errno = errno ? errno : EINVAL; + return LLAPI_LAYOUT_ITER_STOP; +} + +/* Print explanation of layout error */ +void llapi_layout_sanity_perror(int error) +{ + char *msg = NULL; + + switch (error) { + case LSE_OK: + break; + case LSE_INCOMPLETE_MIRROR: + msg = "Incomplete mirror - must go to EOF"; + break; + case LSE_ADJACENT_EXTENSION: + msg = "No adjacent extension space components"; + break; + case LSE_INIT_EXTENSION: + msg = "Cannot apply extension flag to init components"; + break; + case LSE_FLAGS: + msg = "Wrong flags"; + break; + case LSE_DOM_EXTENSION: + msg = "DoM components can't be extension space"; + break; + case LSE_DOM_EXTENSION_FOLLOWING: + msg = "DoM components cannot be followed by extension space"; + break; + case LSE_DOM_FLR: + msg = "FLR and DoM are not supported together"; + break; + case LSE_SET_COMP_START: + msg = "Must set previous component extent before adding next"; + break; + case LSE_NOT_ZERO_LENGTH_EXTENDABLE: + msg = "Extendable component must start out zero-length"; + break; + case LSE_END_NOT_GREATER: + msg = "Component end is before end of previous component"; + break; + case LSE_ZERO_LENGTH_NORMAL: + msg = "Zero length components must be followed by extension"; + break; + case LSE_NOT_ADJACENT_PREV: + msg = "Components not adjacent (end != next->start"; + break; + case LSE_START_GT_END: + msg = "Component start is > end"; + case LSE_ALIGN_END: + msg = "The component end must be aligned by the stripe size"; + break; + case LSE_ALIGN_EXT: + msg = "The extension size must be aligned by the stripe size"; + break; + default: + fprintf(stdout, "Invalid layout, unrecognized error: %d\n", + error); + } + + if (msg) + fprintf(stdout, "Invalid layout: %s\n", msg); +} + +/* Walk a layout and enforce sanity checks that apply to > 1 component + * + * The core idea here is that of sanity checking individual tokens vs semantic + * checking. + * We cannot check everything at the individual component level ('token'), + * instead we must check whether or not the full layout has a valid meaning. + * + * An example of a component level check is "is stripe size valid?". That is + * handled when setting stripe size. + * + * An example of a layout level check is "are the extents of these components + * valid when adjacent to one another", or "can we set these flags on adjacent + * components"? + * + * \param[in] layout component layout list. + * \param[in] incomplete if layout is complete or not - some checks can + * only be done on complete layouts. + * \param[in] flr set when this is called from FLR mirror create + * + * \retval 0, success, positive: various errors, see + * llapi_layout_sanity_perror, -1, failure + */ +int llapi_layout_sanity(struct llapi_layout *layout, bool incomplete, bool flr) +{ + struct llapi_layout_sanity_args args; + struct llapi_layout_comp *curr; + int rc = 0; + + if (!layout) + return 0; + + curr = layout->llot_cur_comp; + if (!curr) + return 0; + + /* Set up args */ + args.lsa_rc = 0; + args.lsa_flr = flr; + args.lsa_incomplete = incomplete; + + /* When we modify an existing layout, this tells us if it's FLR */ + if (mirror_id_of(curr->llc_id) > 0) + args.lsa_flr = true; + + errno = 0; + rc = llapi_layout_comp_iterate(layout, + llapi_layout_sanity_cb, + &args); + if (errno == ENOENT) + errno = 0; + + if (rc != LLAPI_LAYOUT_ITER_CONT) + rc = args.lsa_rc; + + layout->llot_cur_comp = curr; + + return rc; +}