diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml index c0cf1c5257b..108c764f81a 100644 --- a/.github/workflows/ossf-scorecard.yml +++ b/.github/workflows/ossf-scorecard.yml @@ -33,12 +33,12 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif @@ -71,6 +71,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/upload-sarif@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: sarif_file: results.sarif diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index dd8b3c3db7c..f2ca5ef18bc 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -23,7 +23,7 @@ jobs: security-events: write steps: - name: Checkout code - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Run Trivy vulnerability scanner in filesystem mode (table format) uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1 @@ -58,7 +58,7 @@ jobs: trivy-config: 'utils/trivy/trivy.yaml' - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3 + uses: github/codeql-action/upload-sarif@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7 with: sarif_file: 'trivy-results.sarif' diff --git a/.gitignore b/.gitignore index 0960ff64e30..cdef98717dc 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,4 @@ utils/githooks/*.d/*-user-* .vscode src/include/daos_version.h utils/rpms/mock.cfg +trivy-report-daos.txt diff --git a/Jenkinsfile b/Jenkinsfile index 078cc2e8c0b..8b996b2c659 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -291,10 +291,10 @@ pipeline { defaultValue: true, description: 'Run the Functional on EL 8 test stage') booleanParam(name: 'CI_FUNCTIONAL_el9_TEST', - defaultValue: true, + defaultValue: false, description: 'Run the Functional on EL 9 test stage') booleanParam(name: 'CI_FUNCTIONAL_leap15_TEST', - defaultValue: true, + defaultValue: false, description: 'Run the Functional on Leap 15 test stage' + ' Requires CI_MORE_FUNCTIONAL_PR_TESTS') booleanParam(name: 'CI_FUNCTIONAL_ubuntu20_TEST', @@ -311,7 +311,7 @@ pipeline { defaultValue: false, description: 'Run the Functional Hardware Medium MD on SSD test stage') booleanParam(name: 'CI_medium_vmd_TEST', - defaultValue: true, + defaultValue: false, description: 'Run the Functional Hardware Medium VMD test stage') booleanParam(name: 'CI_medium_verbs_provider_TEST', defaultValue: false, diff --git a/ci/provisioning/post_provision_config_nodes_LEAP.sh b/ci/provisioning/post_provision_config_nodes_LEAP.sh index 4e6dc3e4e41..5a2a553e56f 100644 --- a/ci/provisioning/post_provision_config_nodes_LEAP.sh +++ b/ci/provisioning/post_provision_config_nodes_LEAP.sh @@ -9,7 +9,7 @@ bootstrap_dnf() { rm -rf "$REPOS_DIR" ln -s ../zypp/repos.d "$REPOS_DIR" dnf -y remove lua54 lua-lmod - dnf -y --nogpgcheck install lua-lmod '--repo=*lua*' --repo '*network-cluster*' + dnf -y --nogpgcheck install lua-lmod --repo '*lua*' --repo '*network-cluster*' --repo '*oss-proxy*' } group_repo_post() { diff --git a/src/cart/crt_iv.c b/src/cart/crt_iv.c index c1d9c9e85cb..9913686e5f1 100644 --- a/src/cart/crt_iv.c +++ b/src/cart/crt_iv.c @@ -1843,11 +1843,10 @@ crt_hdlr_iv_sync_aux(void *arg) grp_ver = ivns_internal->cii_grp_priv->gp_membs_ver; D_RWLOCK_UNLOCK(&ivns_internal->cii_grp_priv->gp_rwlock); if (grp_ver != input->ivs_grp_ver) { - D_DEBUG(DB_ALL, - "Group (%s) version mismatch. Local: %d Remote :%d\n", - ivns_id.ii_group_name, grp_ver, - input->ivs_grp_ver); - D_GOTO(exit, rc = -DER_GRPVER); + rc = -DER_GRPVER; + DL_INFO(rc, "Group (%s) version mismatch. Local: %d Remote :%d", + ivns_id.ii_group_name, grp_ver, input->ivs_grp_ver); + goto exit; } iv_ops = crt_iv_ops_get(ivns_internal, input->ivs_class_id); @@ -1922,7 +1921,7 @@ crt_hdlr_iv_sync_aux(void *arg) if (need_put && iv_ops) iv_ops->ivo_on_put(ivns_internal, NULL, user_priv); - output->rc = rc; + output->ivs_rc = rc; crt_reply_send(rpc_req); /* ADDREF done in lookup above */ @@ -2002,7 +2001,7 @@ crt_hdlr_iv_sync(crt_rpc_t *rpc_req) IVNS_DECREF(ivns_internal); return; exit: - output->rc = rc; + output->ivs_rc = rc; crt_reply_send(rpc_req); if (ivns_internal) { @@ -2022,9 +2021,9 @@ crt_iv_sync_corpc_aggregate(crt_rpc_t *source, crt_rpc_t *result, void *arg) output_result = crt_reply_get(result); /* Only set new rc if so far rc is 0 */ - if (output_result->rc == 0) { - if (output_source->rc != 0) - output_result->rc = output_source->rc; + if (output_result->ivs_rc == 0) { + if (output_source->ivs_rc != 0) + output_result->ivs_rc = output_source->ivs_rc; } return 0; @@ -2163,14 +2162,16 @@ handle_ivsync_response(const struct crt_cb_info *cb_info) { struct iv_sync_cb_info *iv_sync = cb_info->cci_arg; struct crt_iv_ops *iv_ops; + crt_rpc_t *rpc = cb_info->cci_rpc; + struct crt_iv_sync_out *output = crt_reply_get(rpc); if (iv_sync->isc_bulk_hdl != CRT_BULK_NULL) crt_bulk_free(iv_sync->isc_bulk_hdl); /* do_callback is set based on sync value specified */ if (iv_sync->isc_do_callback) { - if (cb_info->cci_rc != 0) - iv_sync->isc_update_rc = cb_info->cci_rc; + if (cb_info->cci_rc || output->ivs_rc) + iv_sync->isc_update_rc = cb_info->cci_rc ? cb_info->cci_rc : output->ivs_rc; iv_sync->isc_update_comp_cb(iv_sync->isc_ivns_internal, iv_sync->isc_class_id, @@ -2412,7 +2413,7 @@ finalize_transfer_back(struct update_cb_info *cb_info, int rc) struct crt_iv_update_out *child_output; child_output = crt_reply_get(cb_info->uci_child_rpc); - child_output->rc = rc; + child_output->ivo_rc = rc; ivns = cb_info->uci_ivns_internal; crt_reply_send(cb_info->uci_child_rpc); @@ -2528,15 +2529,15 @@ handle_ivupdate_response(const struct crt_cb_info *cb_info) if (iv_info->uci_bulk_hdl == CRT_BULK_NULL) d_sgl_buf_copy(&iv_info->uci_iv_value, &output->ivo_iv_sgl); iv_ops->ivo_on_refresh(iv_info->uci_ivns_internal, &input->ivu_key, 0, - &iv_info->uci_iv_value, false, cb_info->cci_rc ?: output->rc, - iv_info->uci_user_priv); + &iv_info->uci_iv_value, false, + cb_info->cci_rc ?: output->ivo_rc, iv_info->uci_user_priv); if (input->ivu_iv_value_bulk == CRT_BULK_NULL && iv_info->uci_child_rpc != NULL) { child_output = crt_reply_get(iv_info->uci_child_rpc); child_output->ivo_iv_sgl = iv_info->uci_iv_value; } - transfer_back_to_child(&input->ivu_key, iv_info, cb_info->cci_rc ?: output->rc); + transfer_back_to_child(&input->ivu_key, iv_info, cb_info->cci_rc ?: output->ivo_rc); D_GOTO(exit, 0); } @@ -2549,10 +2550,10 @@ handle_ivupdate_response(const struct crt_cb_info *cb_info) iv_ops->ivo_on_put(iv_info->uci_ivns_internal, &iv_info->uci_iv_value, iv_info->uci_user_priv); - child_output->rc = output->rc; + child_output->ivo_rc = output->ivo_rc; if (cb_info->cci_rc != 0) - child_output->rc = cb_info->cci_rc; + child_output->ivo_rc = cb_info->cci_rc; /* Respond back to child; might fail if child is not alive */ if (crt_reply_send(iv_info->uci_child_rpc) != DER_SUCCESS) @@ -2568,7 +2569,7 @@ handle_ivupdate_response(const struct crt_cb_info *cb_info) else tmp_iv_value = &iv_info->uci_iv_value; - rc = output->rc; + rc = output->ivo_rc; if (cb_info->cci_rc != 0) rc = cb_info->cci_rc; @@ -2897,7 +2898,7 @@ bulk_update_transfer_done_aux(const struct crt_bulk_cb_info *info) D_GOTO(exit, rc); } rc = crt_bulk_free(cb_info->buc_bulk_hdl); - output->rc = rc; + output->ivo_rc = rc; iv_ops->ivo_on_put(ivns_internal, &cb_info->buc_iv_value, cb_info->buc_user_priv); crt_reply_send(info->bci_bulk_desc->bd_rpc); @@ -2913,7 +2914,7 @@ bulk_update_transfer_done_aux(const struct crt_bulk_cb_info *info) send_error: /* send back whatever error got us here */ - output->rc = rc; + output->ivo_rc = rc; rc = crt_bulk_free(cb_info->buc_bulk_hdl); if (rc != 0) DL_ERROR(rc, "crt_bulk_free() failed"); @@ -3016,7 +3017,7 @@ bulk_update_transfer_done(const struct crt_bulk_cb_info *info) return rc; send_error: - output->rc = rc; + output->ivo_rc = rc; crt_reply_send(info->bci_bulk_desc->bd_rpc); crt_bulk_free(cb_info->buc_bulk_hdl); @@ -3121,7 +3122,7 @@ crt_iv_update_inline_hdlr(crt_rpc_t *rpc, struct crt_ivns_internal *ivns_interna D_GOTO(exit, rc); } } else if (rc == 0) { - output->rc = rc; + output->ivo_rc = rc; crt_reply_send(rpc); iv_ops->ivo_on_put(ivns_internal, &iv_value, user_priv); } else { @@ -3136,7 +3137,7 @@ crt_iv_update_inline_hdlr(crt_rpc_t *rpc, struct crt_ivns_internal *ivns_interna iv_ops->ivo_on_put(ivns_internal, &iv_value, user_priv); send_error: - output->rc = rc; + output->ivo_rc = rc; crt_reply_send(rpc); if (update_cb_info) D_FREE(update_cb_info); @@ -3270,7 +3271,7 @@ crt_hdlr_iv_update(crt_rpc_t *rpc_req) } } else if (rc == 0) { - output->rc = rc; + output->ivo_rc = rc; rc = crt_reply_send(rpc_req); } else { D_GOTO(send_error, rc); @@ -3340,7 +3341,7 @@ crt_hdlr_iv_update(crt_rpc_t *rpc_req) return; send_error: - output->rc = rc; + output->ivo_rc = rc; crt_reply_send(rpc_req); if (put_needed) diff --git a/src/cart/crt_rpc.h b/src/cart/crt_rpc.h index 904ca89dc40..f55a608974f 100644 --- a/src/cart/crt_rpc.h +++ b/src/cart/crt_rpc.h @@ -513,7 +513,7 @@ CRT_RPC_DECLARE(crt_iv_fetch, CRT_ISEQ_IV_FETCH, CRT_OSEQ_IV_FETCH) ((uint32_t) (padding) CRT_VAR) #define CRT_OSEQ_IV_UPDATE /* output fields */ \ - ((uint64_t) (rc) CRT_VAR) \ + ((uint64_t) (ivo_rc) CRT_VAR) \ ((d_sg_list_t) (ivo_iv_sgl) CRT_VAR) CRT_RPC_DECLARE(crt_iv_update, CRT_ISEQ_IV_UPDATE, CRT_OSEQ_IV_UPDATE) @@ -532,7 +532,7 @@ CRT_RPC_DECLARE(crt_iv_update, CRT_ISEQ_IV_UPDATE, CRT_OSEQ_IV_UPDATE) ((uint32_t) (ivs_class_id) CRT_VAR) \ #define CRT_OSEQ_IV_SYNC /* output fields */ \ - ((int32_t) (rc) CRT_VAR) + ((int32_t) (ivs_rc) CRT_VAR) CRT_RPC_DECLARE(crt_iv_sync, CRT_ISEQ_IV_SYNC, CRT_OSEQ_IV_SYNC) diff --git a/src/client/dfuse/dfuse.h b/src/client/dfuse/dfuse.h index 47725aaed71..eaa527eff1f 100644 --- a/src/client/dfuse/dfuse.h +++ b/src/client/dfuse/dfuse.h @@ -1,6 +1,7 @@ /** * (C) Copyright 2016-2024 Intel Corporation. * (C) Copyright 2025 Google LLC + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -28,6 +29,12 @@ struct dfuse_info { struct fuse_session *di_session; char *di_group; char *di_mountpoint; + char *di_handles_file; + bool di_dump_handles; + bool di_read_handles; + daos_handle_t di_poh; + daos_handle_t di_coh; + dfs_t *di_dfs; int32_t di_thread_count; uint32_t di_eq_count; bool di_foreground; diff --git a/src/client/dfuse/dfuse_core.c b/src/client/dfuse/dfuse_core.c index 77bce41bd04..55e49d89f97 100644 --- a/src/client/dfuse/dfuse_core.c +++ b/src/client/dfuse/dfuse_core.c @@ -500,14 +500,24 @@ dfuse_pool_connect(struct dfuse_info *dfuse_info, const char *label, struct dfus if (label) { daos_pool_info_t p_info = {}; - rc = daos_pool_connect(label, dfuse_info->di_group, DAOS_PC_RO, &dfp->dfp_poh, - &p_info, NULL); - if (rc) { - if (rc == -DER_NO_PERM || rc == -DER_NONEXIST) - DHL_INFO(dfp, rc, "daos_pool_connect() failed"); - else - DHL_ERROR(dfp, rc, "daos_pool_connect() failed"); - D_GOTO(err_free, rc = daos_der2errno(rc)); + /** if we read the pool handle, query for the info */ + if (dfuse_info->di_read_handles) { + dfp->dfp_poh = dfuse_info->di_poh; + rc = daos_pool_query(dfp->dfp_poh, NULL, &p_info, NULL, NULL); + if (rc) { + DHL_ERROR(dfp, rc, "daos_pool_query() failed"); + D_GOTO(err_free, rc = daos_der2errno(rc)); + } + } else { + rc = daos_pool_connect(label, dfuse_info->di_group, DAOS_PC_RO, + &dfp->dfp_poh, &p_info, NULL); + if (rc) { + if (rc == -DER_NO_PERM || rc == -DER_NONEXIST) + DHL_INFO(dfp, rc, "daos_pool_connect() failed"); + else + DHL_ERROR(dfp, rc, "daos_pool_connect() failed"); + D_GOTO(err_free, rc = daos_der2errno(rc)); + } } uuid_copy(dfp->dfp_uuid, p_info.pi_uuid); @@ -848,17 +858,23 @@ dfuse_cont_open(struct dfuse_info *dfuse_info, struct dfuse_pool *dfp, const cha int dfs_flags = O_RDWR; dfc->dfs_ops = &dfuse_dfs_ops; - if (dfuse_info->di_read_only) { - dfs_flags = O_RDONLY; - rc = daos_cont_open(dfp->dfp_poh, label, DAOS_COO_RO, &dfc->dfs_coh, - &c_info, NULL); + + if (dfuse_info->di_read_handles) { + dfc->dfs_coh = dfuse_info->di_coh; + rc = daos_cont_query(dfc->dfs_coh, &c_info, NULL, NULL); } else { - rc = daos_cont_open(dfp->dfp_poh, label, DAOS_COO_RW, &dfc->dfs_coh, - &c_info, NULL); - if (rc == -DER_NO_PERM) { + if (dfuse_info->di_read_only) { dfs_flags = O_RDONLY; rc = daos_cont_open(dfp->dfp_poh, label, DAOS_COO_RO, &dfc->dfs_coh, &c_info, NULL); + } else { + rc = daos_cont_open(dfp->dfp_poh, label, DAOS_COO_RW, &dfc->dfs_coh, + &c_info, NULL); + if (rc == -DER_NO_PERM) { + dfs_flags = O_RDONLY; + rc = daos_cont_open(dfp->dfp_poh, label, DAOS_COO_RO, + &dfc->dfs_coh, &c_info, NULL); + } } } if (rc != -DER_SUCCESS) { @@ -869,10 +885,14 @@ dfuse_cont_open(struct dfuse_info *dfuse_info, struct dfuse_pool *dfp, const cha D_GOTO(err_free, rc = daos_der2errno(rc)); } - rc = dfs_mount(dfp->dfp_poh, dfc->dfs_coh, dfs_flags, &dfc->dfs_ns); - if (rc) { - DHS_ERROR(dfc, rc, "dfs mount() failed"); - D_GOTO(err_close, rc); + if (dfuse_info->di_read_handles) + dfc->dfs_ns = dfuse_info->di_dfs; + else { + rc = dfs_mount(dfp->dfp_poh, dfc->dfs_coh, dfs_flags, &dfc->dfs_ns); + if (rc) { + DHS_ERROR(dfc, rc, "dfs mount() failed"); + D_GOTO(err_close, rc); + } } uuid_copy(dfc->dfc_uuid, c_info.ci_uuid); diff --git a/src/client/dfuse/dfuse_main.c b/src/client/dfuse/dfuse_main.c index 6c0611b48a8..4587b08e2d1 100644 --- a/src/client/dfuse/dfuse_main.c +++ b/src/client/dfuse/dfuse_main.c @@ -292,6 +292,10 @@ show_help(char *name) "\n" " --multi-user Run dfuse in multi user mode\n" " --read-only Mount dfuse read-only\n" + " --dump-handles=file Serialize and dump the pool, container, and dfs handles" + " to file using the local2global DAOS functionality\n" + " --read-handles=file Read the deserialize the pool, cont, and dfs handles," + " and use them instead of connecting to the server\n" "\n" " -h --help Show this help\n" " -v --version Show version\n" @@ -368,6 +372,11 @@ show_help(char *name) " The default is --enable-wb-cache.\n" "* If --disable-caching and --enable-wb-cache are both specified,\n" " the --enable-wb-cache option is ignored and no caching is performed.\n" + "* If --dump-handles is used from one dfuse mount and --read-handles is used for\n" + " starting other dfuse mounts, the dfuse mount that dumps the handles cannot be\n" + " umounted before the others, otherwise the other mounts will become inaccessible\n" + " since the g2l & l2g method for sharing DAOS handles requires the main process\n" + " that establishes the pool and container connections to keep those alive.\n" "\n" "Version: %s\n", name, DAOS_VERSION); @@ -431,6 +440,8 @@ main(int argc, char **argv) {"enable-wb-cache", no_argument, 0, 'F'}, {"disable-caching", no_argument, 0, 'A'}, {"disable-wb-cache", no_argument, 0, 'B'}, + {"dump-handles", required_argument, 0, 'D'}, + {"read-handles", required_argument, 0, 'R'}, {"read-only", no_argument, 0, 'r'}, {"options", required_argument, 0, 'o'}, {"version", no_argument, 0, 'v'}, @@ -450,6 +461,8 @@ main(int argc, char **argv) dfuse_info->di_caching = true; dfuse_info->di_wb_cache = true; dfuse_info->di_eq_count = 1; + dfuse_info->di_dump_handles = false; + dfuse_info->di_read_handles = false; while (1) { c = getopt_long(argc, argv, "Mm:t:o:fhe:v", long_options, NULL); @@ -484,6 +497,14 @@ main(int argc, char **argv) case 'm': dfuse_info->di_mountpoint = optarg; break; + case 'D': + dfuse_info->di_handles_file = optarg; + dfuse_info->di_dump_handles = true; + break; + case 'R': + dfuse_info->di_handles_file = optarg; + dfuse_info->di_read_handles = true; + break; case 'M': dfuse_info->di_multi_user = true; break; @@ -540,17 +561,16 @@ main(int argc, char **argv) } } - if (!dfuse_info->di_foreground && d_isenv_def("PMIX_RANK")) { - DFUSE_TRA_WARNING(dfuse_info, - "Not running in background under orterun"); - dfuse_info->di_foreground = true; - } - if (!dfuse_info->di_mountpoint) { printf("Mountpoint is required\n"); show_help(argv[0]); D_GOTO(out_debug, rc = -DER_INVAL); } + if (dfuse_info->di_dump_handles && dfuse_info->di_read_handles) { + printf("Cannot dump and read handles file at the same time\n"); + show_help(argv[0]); + D_GOTO(out_debug, rc = -DER_INVAL); + } /* If the number of threads has been specified on the command line then use that, otherwise * check CPU binding. If bound to a number of cores then launch that number of threads, @@ -698,6 +718,91 @@ main(int argc, char **argv) D_GOTO(out_daos, rc = daos_errno2der(rc)); } + /** if user passes a handle file, read it and store the serialized handles */ + if (dfuse_info->di_read_handles && dfuse_info->di_handles_file) { + d_iov_t pool_hdl = {NULL, 0, 0}; + d_iov_t cont_hdl = {NULL, 0, 0}; + d_iov_t dfs_hdl = {NULL, 0, 0}; + char *buf = NULL; + daos_size_t buf_size; + size_t sizes[3]; + FILE *fp; + int dfs_flags = O_RDWR; + + fp = fopen(dfuse_info->di_handles_file, "rb"); + if (!fp) { + rc = errno; + DHL_ERROR(dfuse_info, daos_errno2der(rc), "Failed to open file %s", + dfuse_info->di_handles_file); + D_GOTO(out_daos, rc = daos_errno2der(rc)); + } + + /** read the 3 size_t values for the 3 handles */ + if (fread(sizes, sizeof(size_t), 3, fp) != 3) { + rc = errno; + DHL_ERROR(dfuse_info, daos_errno2der(rc), "Failed to read file %s", + dfuse_info->di_handles_file); + fclose(fp); + D_GOTO(out_daos, rc = daos_errno2der(rc)); + } + pool_hdl.iov_buf_len = pool_hdl.iov_len = sizes[0]; + cont_hdl.iov_buf_len = cont_hdl.iov_len = sizes[1]; + dfs_hdl.iov_buf_len = dfs_hdl.iov_len = sizes[2]; + + buf_size = sizes[0] + sizes[1] + sizes[2]; + D_ALLOC(buf, buf_size); + if (buf == NULL) { + fclose(fp); + D_GOTO(out_daos, rc = -DER_NOMEM); + } + + if (fread(buf, 1, buf_size, fp) != buf_size) { + rc = errno; + DHL_ERROR(dfuse_info, daos_errno2der(rc), "Failed to read file %s", + dfuse_info->di_handles_file); + fclose(fp); + D_FREE(buf); + D_GOTO(out_daos, rc = daos_errno2der(rc)); + } + + pool_hdl.iov_buf = buf; + cont_hdl.iov_buf = buf + pool_hdl.iov_buf_len; + dfs_hdl.iov_buf = buf + pool_hdl.iov_buf_len + cont_hdl.iov_buf_len; + + rc = daos_pool_global2local(pool_hdl, &dfuse_info->di_poh); + if (rc) { + fclose(fp); + D_FREE(buf); + DHL_ERROR(dfuse_info, rc, "daos_pool_global2local() failed"); + D_GOTO(out_daos, rc); + } + + rc = daos_cont_global2local(dfuse_info->di_poh, cont_hdl, &dfuse_info->di_coh); + if (rc) { + fclose(fp); + D_FREE(buf); + daos_pool_disconnect(dfuse_info->di_poh, NULL); + DHL_ERROR(dfuse_info, rc, "daos_cont_global2local() failed"); + D_GOTO(out_daos, rc); + } + + if (dfuse_info->di_read_only) + dfs_flags = O_RDONLY; + rc = dfs_global2local(dfuse_info->di_poh, dfuse_info->di_coh, dfs_flags, dfs_hdl, + &dfuse_info->di_dfs); + if (rc) { + fclose(fp); + D_FREE(buf); + daos_cont_close(dfuse_info->di_coh, NULL); + daos_pool_disconnect(dfuse_info->di_poh, NULL); + DHL_ERROR(dfuse_info, rc, "dfs_global2local() failed"); + D_GOTO(out_daos, rc); + } + + fclose(fp); + D_FREE(buf); + } + /* Connect to a pool */ rc = dfuse_pool_connect(dfuse_info, pool_name[0] ? pool_name : NULL, &dfp); if (rc != 0) { @@ -711,6 +816,91 @@ main(int argc, char **argv) D_GOTO(out_pool, rc = daos_errno2der(rc)); } + /** if user asked to dump the handles, serialize them and write them to the file */ + if (dfuse_info->di_dump_handles && dfuse_info->di_handles_file) { + d_iov_t pool_hdl = {NULL, 0, 0}; + d_iov_t cont_hdl = {NULL, 0, 0}; + d_iov_t dfs_hdl = {NULL, 0, 0}; + char *buf = NULL; + daos_size_t buf_size; + FILE *fp; + + rc = daos_pool_local2global(dfp->dfp_poh, &pool_hdl); + if (rc) { + DHL_ERROR(dfuse_info, rc, "daos_pool_local2global() failed"); + goto out_cont; + } + rc = daos_cont_local2global(dfs->dfs_coh, &cont_hdl); + if (rc) { + DHL_ERROR(dfuse_info, rc, "daos_cont_local2global() failed"); + goto out_cont; + } + rc = dfs_local2global(dfs->dfs_ns, &dfs_hdl); + if (rc) { + DHL_ERROR(dfuse_info, rc, "dfs_local2global() failed"); + goto out_cont; + } + + buf_size = pool_hdl.iov_buf_len + cont_hdl.iov_buf_len + dfs_hdl.iov_buf_len; + D_ALLOC(buf, buf_size); + if (buf == NULL) + D_GOTO(out_cont, rc = -DER_NOMEM); + + pool_hdl.iov_buf = buf; + cont_hdl.iov_buf = buf + pool_hdl.iov_buf_len; + dfs_hdl.iov_buf = buf + pool_hdl.iov_buf_len + cont_hdl.iov_buf_len; + + rc = daos_pool_local2global(dfp->dfp_poh, &pool_hdl); + if (rc) { + DHL_ERROR(dfuse_info, rc, "daos_pool_local2global() failed"); + D_FREE(buf); + goto out_cont; + } + rc = daos_cont_local2global(dfs->dfs_coh, &cont_hdl); + if (rc) { + DHL_ERROR(dfuse_info, rc, "daos_cont_local2global() failed"); + D_FREE(buf); + goto out_cont; + } + rc = dfs_local2global(dfs->dfs_ns, &dfs_hdl); + if (rc) { + DHL_ERROR(dfuse_info, rc, "dfs_local2global() failed"); + D_FREE(buf); + goto out_cont; + } + + fp = fopen(dfuse_info->di_handles_file, "wb+"); + if (!fp) { + rc = errno; + DHL_ERROR(dfuse_info, daos_errno2der(rc), "Failed to open file %s", + dfuse_info->di_handles_file); + D_FREE(buf); + D_GOTO(out_cont, rc = daos_errno2der(rc)); + } + + size_t sizes[3] = {pool_hdl.iov_buf_len, cont_hdl.iov_buf_len, dfs_hdl.iov_buf_len}; + + if (fwrite(sizes, sizeof(size_t), 3, fp) != 3) { + rc = errno; + fclose(fp); + D_FREE(buf); + DHL_ERROR(dfuse_info, daos_errno2der(rc), "Failed to write file %s", + dfuse_info->di_handles_file); + D_GOTO(out_cont, rc = daos_errno2der(rc)); + } + + if (fwrite(buf, 1, buf_size, fp) != buf_size) { + rc = errno; + fclose(fp); + D_FREE(buf); + DHL_ERROR(dfuse_info, daos_errno2der(rc), "Failed to write file %s", + dfuse_info->di_handles_file); + D_GOTO(out_cont, rc = daos_errno2der(rc)); + } + fclose(fp); + D_FREE(buf); + } + rc = dfuse_fs_start(dfuse_info, dfs); if (rc != -DER_SUCCESS) D_GOTO(out_cont, rc); diff --git a/src/client/java/daos-java/pom.xml b/src/client/java/daos-java/pom.xml index 6b26378ac6f..b6d6b6996f3 100644 --- a/src/client/java/daos-java/pom.xml +++ b/src/client/java/daos-java/pom.xml @@ -41,7 +41,7 @@ io.netty netty-buffer - 4.1.115.Final + 4.1.125.Final com.google.code.findbugs diff --git a/src/client/java/hadoop-daos/pom.xml b/src/client/java/hadoop-daos/pom.xml index 1bba3428580..28576e3ec63 100644 --- a/src/client/java/hadoop-daos/pom.xml +++ b/src/client/java/hadoop-daos/pom.xml @@ -15,7 +15,7 @@ jar - 3.4.0 + 3.4.2 ${project.basedir}/build ${project.basedir}/install diff --git a/src/common/misc.c b/src/common/misc.c index e5dddf0a64e..1a7abbf2025 100644 --- a/src/common/misc.c +++ b/src/common/misc.c @@ -220,15 +220,17 @@ daos_sgl_merge(d_sg_list_t *dst, d_sg_list_t *src) } daos_size_t -daos_sgl_data_len(d_sg_list_t *sgl) +daos_sgl_data_len(d_sg_list_t *sgl, bool out) { daos_size_t len; - int i; + uint32_t sg_nr; + uint32_t i; if (sgl == NULL || sgl->sg_iovs == NULL) return 0; - for (i = 0, len = 0; i < sgl->sg_nr; i++) + sg_nr = out ? sgl->sg_nr_out : sgl->sg_nr; + for (i = 0, len = 0; i < sg_nr; i++) len += sgl->sg_iovs[i].iov_len; return len; diff --git a/src/common/tests_lib.c b/src/common/tests_lib.c index 9cb2ebbcb95..7ee661f4c6a 100644 --- a/src/common/tests_lib.c +++ b/src/common/tests_lib.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2015-2022 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -279,7 +280,7 @@ td_init(struct test_data *td, uint32_t iod_nr, struct td_init_args args) /* Initialize and create some data */ dts_sgl_generate(sgl, 1, args.ca_data_size, 0xAB); - D_ASSERT(daos_sgl_data_len(sgl) == data_len); + D_ASSERT(daos_sgl_data_len(sgl, false) == data_len); iod->iod_type = iod_types[i]; dts_iov_alloc_str(&iod->iod_name, "akey"); diff --git a/src/container/cli.c b/src/container/cli.c index a0dc96fec00..18b6a75a590 100644 --- a/src/container/cli.c +++ b/src/container/cli.c @@ -190,6 +190,7 @@ dup_cont_create_props(daos_handle_t poh, daos_prop_t **prop_out, int rc = 0; uid_t uid = geteuid(); gid_t gid = getegid(); + uint32_t co_ver = 0; entries = (prop_in == NULL) ? 0 : prop_in->dpp_nr; @@ -224,6 +225,10 @@ dup_cont_create_props(daos_handle_t poh, daos_prop_t **prop_out, entries++; } + if (!daos_prop_has_entry(prop_in, DAOS_PROP_CO_OBJ_VERSION)) { + co_ver = DAOS_POOL_OBJ_VERSION_2; + entries++; + } if (!daos_prop_has_entry(prop_in, DAOS_PROP_CO_ROOTS)) { uint64_t rf; @@ -294,6 +299,11 @@ dup_cont_create_props(daos_handle_t poh, daos_prop_t **prop_out, roots = NULL; /* prop is responsible for it now */ idx++; } + if (co_ver != 0) { + final_prop->dpp_entries[idx].dpe_type = DAOS_PROP_CO_OBJ_VERSION; + final_prop->dpp_entries[idx].dpe_val = co_ver; + idx++; + } } *prop_out = final_prop; @@ -999,7 +1009,7 @@ dc_cont_open(tse_task_t *task) if (tpriv->cont == NULL) D_GOTO(err_pool, rc = -DER_NOMEM); uuid_generate(tpriv->cont->dc_cont_hdl); - tpriv->cont->dc_capas = args->flags; + tpriv->cont->dc_capas = args->flags | DAOS_COO_NEW_LAYOUT; } D_DEBUG(DB_MD, DF_UUID ":%s: opening: hdl=" DF_UUIDF " flags=%x\n", DP_UUID(pool->dp_pool), diff --git a/src/container/container_iv.c b/src/container/container_iv.c index 43d024d02a5..a063c685b9c 100644 --- a/src/container/container_iv.c +++ b/src/container/container_iv.c @@ -449,6 +449,7 @@ cont_iv_ent_fetch(struct ds_iv_entry *entry, struct ds_iv_key *key, d_sg_list_t *dst, void **priv) { struct cont_iv_entry *src_iv; + struct cont_iv_entry iv_entry = {0}; daos_handle_t root_hdl; d_iov_t key_iov; d_iov_t val_iov; @@ -496,7 +497,6 @@ cont_iv_ent_fetch(struct ds_iv_entry *entry, struct ds_iv_key *key, rc1 = ds_cont_hdl_rdb_lookup(entry->ns->iv_pool_uuid, civ_key->cont_uuid, &chdl); if (rc1 == 0) { - struct cont_iv_entry iv_entry = { 0 }; daos_prop_t *prop = NULL; struct daos_prop_entry *prop_entry; struct daos_co_status stat = { 0 }; @@ -546,8 +546,38 @@ cont_iv_ent_fetch(struct ds_iv_entry *entry, struct ds_iv_key *key, } else { rc = -DER_NONEXIST; } + } else if (class_id == IV_CONT_AGG_EPOCH_BOUNDRY) { + uint64_t ec_agg_eph; + + rc = ds_cont_ec_agg_eph_rdb_lookup(entry->ns->iv_pool_uuid, + civ_key->cont_uuid, &ec_agg_eph); + if (rc == 0) { + uuid_copy(iv_entry.cont_uuid, civ_key->cont_uuid); + iv_entry.iv_agg_eph.eph = ec_agg_eph; + iv_entry.iv_agg_eph.rank = dss_self_rank(); + d_iov_set(&val_iov, &iv_entry, sizeof(iv_entry)); + rc = dbtree_update(root_hdl, &key_iov, &val_iov); + DL_CDEBUG( + rc != 0, DLOG_ERR, DB_MD, rc, + DF_CONT ": dbtree_update ec_agg_eph " DF_X64, + DP_CONT(entry->ns->iv_pool_uuid, civ_key->cont_uuid), + ec_agg_eph); + if (rc) + goto failed; + /* on master node will not trigger on_refresh callback, + * so need to explicitly refresh its own cont child's + * sc_ec_agg_eph_boundary especially for the case of + * restart that the value is zero. + */ + rc = ds_cont_tgt_refresh_agg_eph(entry->ns->iv_pool_uuid, + civ_key->cont_uuid, + ec_agg_eph); + if (rc == 0) + goto again; + } } } +failed: D_DEBUG(DB_MGMT, DF_CONT "lookup cont: rc " DF_RC "\n", DP_CONT(entry->ns->iv_pool_uuid, civ_key->cont_uuid), DP_RC(rc)); D_GOTO(out, rc); @@ -653,8 +683,15 @@ cont_iv_ent_update(struct ds_iv_entry *entry, struct ds_iv_key *key, } else if (entry->iv_class->iv_class_id == IV_CONT_AGG_EPOCH_BOUNDRY) { rc = cont_iv_ent_agg_eph_refresh(entry, key, src); - if (rc) + if (rc) { + if (rc == -DER_NONEXIST) { + DL_INFO( + rc, DF_CONT " cont_iv_ent_agg_eph_refresh ignore", + DP_CONT(entry->ns->iv_pool_uuid, civ_key->cont_uuid)); + rc = 0; + } D_GOTO(out, rc); + } } } @@ -1116,8 +1153,7 @@ cont_iv_ec_agg_eph_update(void *ns, uuid_t cont_uuid, daos_epoch_t eph) int cont_iv_ec_agg_eph_refresh(void *ns, uuid_t cont_uuid, daos_epoch_t eph) { - return cont_iv_ec_agg_eph_update_internal(ns, cont_uuid, eph, - 0, CRT_IV_SYNC_LAZY, + return cont_iv_ec_agg_eph_update_internal(ns, cont_uuid, eph, 0, CRT_IV_SYNC_EAGER, IV_CONT_AGG_EPOCH_BOUNDRY); } diff --git a/src/container/srv_container.c b/src/container/srv_container.c index 5dc44e2758d..0971752888e 100644 --- a/src/container/srv_container.c +++ b/src/container/srv_container.c @@ -702,10 +702,12 @@ cont_create_prop_prepare(struct ds_pool_hdl *pool_hdl, } break; case DAOS_PROP_CO_GLOBAL_VERSION: - case DAOS_PROP_CO_OBJ_VERSION: - D_ERROR("container global/obj %u version could be not set\n", - entry->dpe_type); + D_ERROR("container global %u version could be not set\n", entry->dpe_type); return -DER_INVAL; + case DAOS_PROP_CO_OBJ_VERSION: + /* this is a walkaround for 2.6 only */ + entry_def->dpe_val = entry->dpe_val; + break; default: D_ASSERTF(0, "bad dpt_type %d.\n", entry->dpe_type); break; @@ -760,8 +762,9 @@ cont_create_prop_prepare(struct ds_pool_hdl *pool_hdl, /* inherit object version from pool*/ entry_def = daos_prop_entry_get(prop_def, DAOS_PROP_CO_OBJ_VERSION); - if (entry_def) + if (entry_def && entry_def->dpe_val == 0) entry_def->dpe_val = pool_hdl->sph_obj_ver; + /* for new container set HEALTHY status with current pm ver */ entry_def = daos_prop_entry_get(prop_def, DAOS_PROP_CO_STATUS); D_ASSERT(entry_def != NULL); @@ -1688,6 +1691,7 @@ cont_ec_agg_alloc(struct cont_svc *cont_svc, uuid_t cont_uuid, uuid_copy(ec_agg->ea_cont_uuid, cont_uuid); ec_agg->ea_servers_num = rank_nr; ec_agg->ea_current_eph = 0; + ec_agg->ea_rdb_eph = 0; for (i = 0; i < rank_nr; i++) { ec_agg->ea_server_ephs[i].rank = doms[i].do_comp.co_rank; ec_agg->ea_server_ephs[i].eph = 0; @@ -1802,6 +1806,7 @@ cont_refresh_vos_agg_eph_one(void *data) if (cont_child->sc_ec_agg_eph_boundary < arg->min_eph) cont_child->sc_ec_agg_eph_boundary = arg->min_eph; + cont_child->sc_ec_agg_eph_valid = 1; ds_cont_child_put(cont_child); return rc; } @@ -1811,6 +1816,8 @@ ds_cont_tgt_refresh_agg_eph(uuid_t pool_uuid, uuid_t cont_uuid, daos_epoch_t eph) { struct refresh_vos_agg_eph_arg arg; + static uint64_t cnt; + static uint64_t gap = 1; int rc; uuid_copy(arg.pool_uuid, pool_uuid); @@ -1820,116 +1827,286 @@ ds_cont_tgt_refresh_agg_eph(uuid_t pool_uuid, uuid_t cont_uuid, rc = ds_pool_task_collective(pool_uuid, PO_COMP_ST_NEW | PO_COMP_ST_DOWN | PO_COMP_ST_DOWNOUT, cont_refresh_vos_agg_eph_one, &arg, DSS_ULT_FL_PERIODIC); + if (rc) { + DL_ERROR(rc, DF_CONT ": refresh ec_agg_eph " DF_X64 " failed.", + DP_CONT(pool_uuid, cont_uuid), eph); + } else { + if (cnt++ % gap == 0) { + D_INFO(DF_CONT ": refresh ec_agg_eph " DF_X64, + DP_CONT(pool_uuid, cont_uuid), eph); + if (gap <= 256) + gap <<= 1; + } else { + D_DEBUG(DB_MD, DF_CONT ": refresh ec_agg_eph " DF_X64, + DP_CONT(pool_uuid, cont_uuid), eph); + } + } return rc; } -#define EC_AGG_EPH_INTV (10ULL * 1000) /* seconds interval to check*/ -static void -cont_agg_eph_leader_ult(void *arg) +static int +cont_agg_eph_load(struct cont_svc *svc, uuid_t cont_uuid, uint64_t *ec_agg_eph) { - struct cont_svc *svc = arg; - struct ds_pool *pool = svc->cs_pool; - struct cont_ec_agg *ec_agg; - struct cont_ec_agg *tmp; - uint64_t cur_eph, new_eph; - int rc = 0; + struct rdb_tx tx; + struct cont *cont = NULL; + uint64_t agg_eph; + d_iov_t value; + int rc; - if (svc->cs_ec_leader_ephs_req == NULL) + D_ASSERT(dss_get_module_info()->dmi_xs_id == 0); + rc = rdb_tx_begin(svc->cs_rsvc->s_db, svc->cs_rsvc->s_term, &tx); + if (rc != 0) { + DL_CDEBUG(rc != -DER_NOTLEADER, DLOG_ERR, DB_MD, rc, + DF_CONT ": Failed to start rdb tx.", + DP_CONT(svc->cs_pool_uuid, cont_uuid)); + return rc; + } + + ABT_rwlock_rdlock(svc->cs_lock); + rc = cont_lookup(&tx, svc, cont_uuid, &cont); + if (rc != 0) { + D_ERROR(DF_CONT ": Failed to look container: %d\n", + DP_CONT(svc->cs_pool_uuid, cont_uuid), rc); + D_GOTO(out_lock, rc); + } + + d_iov_set(&value, &agg_eph, sizeof(agg_eph)); + rc = rdb_tx_lookup(&tx, &cont->c_prop, &ds_cont_prop_ec_agg_eph, &value); + DL_CDEBUG(rc != 0 && rc != -DER_NONEXIST && rc != -DER_NOTLEADER, DLOG_ERR, DB_MD, rc, + DF_CONT ": rdb_tx_lookup ec_agg_eph " DF_X64, + DP_CONT(svc->cs_pool_uuid, cont_uuid), agg_eph); + if (rc == -DER_NONEXIST) { + agg_eph = 0; + rc = 0; + } + if (rc == 0) + *ec_agg_eph = agg_eph; + cont_put(cont); + +out_lock: + ABT_rwlock_unlock(svc->cs_lock); + rdb_tx_end(&tx); + return rc; +} + +int +ds_cont_ec_agg_eph_rdb_lookup(uuid_t pool_uuid, uuid_t cont_uuid, uint64_t *ec_agg_eph) +{ + struct cont_svc *svc; + int rc; + + rc = cont_svc_lookup_leader(pool_uuid, 0 /* id */, &svc, NULL); + if (rc != 0) { + DL_ERROR(rc, DF_CONT ": find leader failed.", DP_CONT(pool_uuid, cont_uuid)); + return rc; + } + + rc = cont_agg_eph_load(svc, cont_uuid, ec_agg_eph); + cont_svc_put_leader(svc); + if (rc) + DL_ERROR(rc, DF_CONT ": cont_agg_eph_load failed.", DP_CONT(pool_uuid, cont_uuid)); + return rc; +} + +static int +cont_agg_eph_store(struct cont_svc *svc, uuid_t cont_uuid, uint64_t ec_agg_eph, uint64_t *rdb_eph) +{ + struct rdb_tx tx; + struct cont *cont = NULL; + d_iov_t value; + uint64_t old_eph; + int rc; + + D_ASSERT(dss_get_module_info()->dmi_xs_id == 0); + rc = rdb_tx_begin(svc->cs_rsvc->s_db, svc->cs_rsvc->s_term, &tx); + if (rc != 0) { + DL_CDEBUG(rc != -DER_NOTLEADER, DLOG_ERR, DB_MD, rc, + DF_CONT ": Failed to start rdb tx.", + DP_CONT(svc->cs_pool_uuid, cont_uuid)); + return rc; + } + + ABT_rwlock_wrlock(svc->cs_lock); + rc = cont_lookup(&tx, svc, cont_uuid, &cont); + if (rc != 0) { + D_ERROR(DF_CONT ": Failed to look container: %d\n", + DP_CONT(svc->cs_pool_uuid, cont_uuid), rc); + D_GOTO(out_lock, rc); + } + + d_iov_set(&value, &old_eph, sizeof(old_eph)); + rc = rdb_tx_lookup(&tx, &cont->c_prop, &ds_cont_prop_ec_agg_eph, &value); + if (rc == -DER_NONEXIST) { + rc = 0; + old_eph = 0; + } + if (rc != 0) goto out; - while (!dss_ult_exiting(svc->cs_ec_leader_ephs_req)) { - d_rank_list_t fail_ranks = { 0 }; + *rdb_eph = old_eph; + if (ec_agg_eph > old_eph) { + d_iov_set(&value, &ec_agg_eph, sizeof(ec_agg_eph)); + rc = rdb_tx_update(&tx, &cont->c_prop, &ds_cont_prop_ec_agg_eph, &value); + if (rc == 0) + rc = rdb_tx_commit(&tx); + if (rc == 0) + *rdb_eph = ec_agg_eph; + } else { + D_DEBUG(DB_MD, + DF_CONT ": bypass rdb update ec_agg_eph " DF_X64 ", in rdb eph " DF_X64, + DP_CONT(svc->cs_pool_uuid, cont_uuid), ec_agg_eph, old_eph); + } + +out: + DL_CDEBUG(rc != 0 && rc != -DER_NOTLEADER, DLOG_ERR, DB_MD, rc, + DF_CONT ": rdb_tx_update ec_agg_eph " DF_X64, + DP_CONT(svc->cs_pool_uuid, cont_uuid), ec_agg_eph); + cont_put(cont); +out_lock: + ABT_rwlock_unlock(svc->cs_lock); + rdb_tx_end(&tx); + return rc; +} + +static void +cont_agg_eph_sync(struct ds_pool *pool, struct cont_svc *svc) +{ + d_rank_list_t fail_ranks = {0}; + struct cont_ec_agg *ec_agg; + struct cont_ec_agg *tmp; + daos_epoch_t cur_eph, new_eph; + daos_epoch_t min_eph; + d_rank_t rank; + int i; + int rc; + + rc = map_ranks_init(pool->sp_map, PO_COMP_ST_DOWNOUT | PO_COMP_ST_DOWN, &fail_ranks); + if (rc) { + D_ERROR(DF_UUID ": ranks init failed: %d\n", DP_UUID(pool->sp_uuid), rc); + return; + } - if (pool->sp_rebuilding) { - D_DEBUG(DB_MD, DF_UUID "skip during rebuilding.\n", - DP_UUID(pool->sp_uuid)); - goto yield; + d_list_for_each_entry_safe(ec_agg, tmp, &svc->cs_ec_agg_list, ea_list) { + if (ec_agg->ea_deleted) { + d_list_del(&ec_agg->ea_list); + D_FREE(ec_agg->ea_server_ephs); + D_FREE(ec_agg); + continue; } - rc = map_ranks_init(pool->sp_map, PO_COMP_ST_DOWNOUT | PO_COMP_ST_DOWN, - &fail_ranks); - if (rc) { - D_ERROR(DF_UUID": ranks init failed: %d\n", - DP_UUID(pool->sp_uuid), rc); - goto yield; + if (ec_agg->ea_rdb_eph == 0) { + rc = cont_agg_eph_load(svc, ec_agg->ea_cont_uuid, &ec_agg->ea_rdb_eph); + if (rc) + DL_ERROR(rc, DF_CONT ": cont_agg_eph_load failed.", + DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid)); } - d_list_for_each_entry_safe(ec_agg, tmp, &svc->cs_ec_agg_list, ea_list) { - daos_epoch_t min_eph = DAOS_EPOCH_MAX; - int i; + min_eph = DAOS_EPOCH_MAX; + for (i = 0; i < ec_agg->ea_servers_num; i++) { + rank = ec_agg->ea_server_ephs[i].rank; - if (ec_agg->ea_deleted) { - d_list_del(&ec_agg->ea_list); - D_FREE(ec_agg->ea_server_ephs); - D_FREE(ec_agg); + if (d_rank_in_rank_list(&fail_ranks, rank)) { + D_DEBUG(DB_MD, DF_CONT " skip %u\n", + DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid), rank); continue; } - for (i = 0; i < ec_agg->ea_servers_num; i++) { - d_rank_t rank = ec_agg->ea_server_ephs[i].rank; + if (ec_agg->ea_server_ephs[i].eph < min_eph) + min_eph = ec_agg->ea_server_ephs[i].eph; + } - if (d_rank_in_rank_list(&fail_ranks, rank)) { - D_DEBUG(DB_MD, DF_CONT" skip %u\n", - DP_CONT(svc->cs_pool_uuid, - ec_agg->ea_cont_uuid), - rank); - continue; - } + /* for reboot case the ea_rdb_eph possibly higher than min_eph */ + if (min_eph < ec_agg->ea_rdb_eph) + min_eph = ec_agg->ea_rdb_eph; - if (ec_agg->ea_server_ephs[i].eph < min_eph) - min_eph = ec_agg->ea_server_ephs[i].eph; - } + if (min_eph == ec_agg->ea_current_eph) + continue; - if (min_eph == ec_agg->ea_current_eph) - continue; + /** + * NB: during extending or reintegration, the new + * server might cause the minimum epoch is less than + * ea_current_eph. + */ + D_DEBUG(DB_MD, DF_CONT " minimum " DF_U64 " current " DF_X64 "\n", + DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid), min_eph, + ec_agg->ea_current_eph); + + cur_eph = d_hlc2sec(ec_agg->ea_current_eph); + new_eph = d_hlc2sec(min_eph); + if (cur_eph && new_eph > cur_eph && (new_eph - cur_eph) >= 600) + D_WARN(DF_CONT ": Sluggish EC boundary reporting. " + "cur:" DF_U64 " new:" DF_U64 " gap:" DF_U64 "\n", + DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid), cur_eph, new_eph, + new_eph - cur_eph); + + if (min_eph > ec_agg->ea_rdb_eph) { + rc = cont_agg_eph_store(svc, ec_agg->ea_cont_uuid, min_eph, + &ec_agg->ea_rdb_eph); + if (rc) + DL_ERROR(rc, + DF_CONT ": rdb_tx_update ec_agg_eph " DF_X64 " failed.", + DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid), min_eph); + } - /** - * NB: during extending or reintegration, the new - * server might cause the minimum epoch is less than - * ea_current_eph. - */ - D_DEBUG(DB_MD, DF_CONT" minimum "DF_U64" current "DF_X64"\n", - DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid), - min_eph, ec_agg->ea_current_eph); - - cur_eph = d_hlc2sec(ec_agg->ea_current_eph); - new_eph = d_hlc2sec(min_eph); - if (cur_eph && new_eph > cur_eph && (new_eph - cur_eph) >= 600) - D_WARN(DF_CONT": Sluggish EC boundary reporting. " - "cur:"DF_U64" new:"DF_U64" gap:"DF_U64"\n", - DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid), - cur_eph, new_eph, new_eph - cur_eph); - - rc = cont_iv_ec_agg_eph_refresh(pool->sp_iv_ns, - ec_agg->ea_cont_uuid, - min_eph); - if (rc) { - DL_CDEBUG(rc == -DER_NONEXIST, DLOG_INFO, DLOG_ERR, rc, - DF_CONT ": refresh failed", - DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid)); - - /* If there are network error or pool map inconsistency, - * let's skip the following eph sync, which will fail - * anyway. - */ - if (daos_crt_network_error(rc) || rc == -DER_GRPVER) { - D_INFO(DF_UUID": skip refresh due to: "DF_RC"\n", - DP_UUID(svc->cs_pool_uuid), DP_RC(rc)); - break; - } + rc = cont_iv_ec_agg_eph_refresh(pool->sp_iv_ns, ec_agg->ea_cont_uuid, min_eph); + if (rc) { + DL_CDEBUG(rc == -DER_NONEXIST, DLOG_INFO, DLOG_ERR, rc, + DF_CONT ": refresh failed", + DP_CONT(svc->cs_pool_uuid, ec_agg->ea_cont_uuid)); - continue; - } - ec_agg->ea_current_eph = min_eph; - if (pool->sp_rebuilding) + /* If there are network error or pool map inconsistency, + * let's skip the following eph sync, which will fail + * anyway. + */ + if (daos_crt_network_error(rc) || rc == -DER_GRPVER) { + D_INFO(DF_UUID ": skip refresh due to: " DF_RC "\n", + DP_UUID(svc->cs_pool_uuid), DP_RC(rc)); break; + } + + continue; } + ec_agg->ea_current_eph = min_eph; + } + + map_ranks_fini(&fail_ranks); +} + +int +ds_cont_svc_refresh_agg_eph(uuid_t pool_uuid) +{ + struct cont_svc *svc; + int rc; + + rc = cont_svc_lookup_leader(pool_uuid, 0 /* id */, &svc, NULL /* hint */); + if (rc != 0) + return rc; - map_ranks_fini(&fail_ranks); + cont_agg_eph_sync(svc->cs_pool, svc); + + cont_svc_put_leader(svc); + return 0; +} + +#define EC_AGG_EPH_INTV (10ULL * 1000) /* seconds interval to check*/ +static void +cont_agg_eph_leader_ult(void *arg) +{ + struct cont_svc *svc = arg; + struct ds_pool *pool = svc->cs_pool; + struct cont_ec_agg *ec_agg; + struct cont_ec_agg *tmp; + int rc = 0; + + if (svc->cs_ec_leader_ephs_req == NULL) + goto out; + + while (!dss_ult_exiting(svc->cs_ec_leader_ephs_req)) { + cont_agg_eph_sync(pool, svc); if (dss_ult_exiting(svc->cs_ec_leader_ephs_req)) break; -yield: + sched_req_sleep(svc->cs_ec_leader_ephs_req, EC_AGG_EPH_INTV); } @@ -2272,6 +2449,17 @@ cont_open(struct rdb_tx *tx, struct ds_pool_hdl *pool_hdl, struct cont *cont, cr D_GOTO(out, rc); D_ASSERT(prop != NULL); D_ASSERT(prop->dpp_nr <= CONT_PROP_NUM); + if (!(flags & DAOS_COO_NEW_LAYOUT)) { + struct daos_prop_entry *entry; + + entry = daos_prop_entry_get(prop, DAOS_PROP_CO_OBJ_VERSION); + if (entry && entry->dpe_val >= DAOS_POOL_OBJ_VERSION_2) { + D_ERROR(DF_CONT ": refusing attempt to open with new layout: " DF_X64 "\n", + DP_CONT(cont->c_svc->cs_pool_uuid, cont->c_uuid), entry->dpe_val); + rc = -DER_NOTSUPPORTED; + D_GOTO(out, rc); + } + } get_cont_prop_access_info(prop, &owner, &acl); @@ -4612,7 +4800,7 @@ upgrade_cont_cb(daos_handle_t ih, d_iov_t *key, d_iov_t *val, void *varg) } if (DAOS_FAIL_CHECK(DAOS_FORCE_OBJ_UPGRADE)) { - obj_ver = DS_POOL_OBJ_VERSION; + obj_ver = DAOS_POOL_OBJ_VERSION; d_iov_set(&value, &obj_ver, sizeof(obj_ver)); rc = rdb_tx_update(ap->tx, &cont->c_prop, &ds_cont_prop_cont_obj_version, &value); @@ -4707,9 +4895,9 @@ upgrade_cont_cb(daos_handle_t ih, d_iov_t *key, d_iov_t *val, void *varg) entry = daos_prop_entry_get(prop, DAOS_PROP_CO_OBJ_VERSION); D_ASSERT(entry != NULL); - entry->dpe_val = DS_POOL_OBJ_VERSION; + entry->dpe_val = DAOS_POOL_OBJ_VERSION; entry->dpe_flags &= ~DAOS_PROP_ENTRY_NOT_SET; - obj_ver = DS_POOL_OBJ_VERSION; + obj_ver = DAOS_POOL_OBJ_VERSION; d_iov_set(&value, &obj_ver, sizeof(obj_ver)); rc = rdb_tx_update(ap->tx, &cont->c_prop, &ds_cont_prop_cont_obj_version, &value); if (rc) { diff --git a/src/container/srv_internal.h b/src/container/srv_internal.h index ae5e5ec8a75..bb257a73212 100644 --- a/src/container/srv_internal.h +++ b/src/container/srv_internal.h @@ -67,10 +67,11 @@ struct ec_eph { struct cont_ec_agg { uuid_t ea_cont_uuid; daos_epoch_t ea_current_eph; + daos_epoch_t ea_rdb_eph; struct ec_eph *ea_server_ephs; d_list_t ea_list; int ea_servers_num; - uint32_t ea_deleted:1; + uint32_t ea_deleted : 1; }; /* @@ -310,4 +311,7 @@ int cont_child_gather_oids(struct ds_cont_child *cont, uuid_t coh_uuid, int ds_cont_hdl_rdb_lookup(uuid_t pool_uuid, uuid_t cont_hdl_uuid, struct container_hdl *chdl); +int +ds_cont_ec_agg_eph_rdb_lookup(uuid_t pool_uuid, uuid_t cont_uuid, uint64_t *ec_agg_eph); + #endif /* __CONTAINER_SRV_INTERNAL_H__ */ diff --git a/src/container/srv_layout.c b/src/container/srv_layout.c index 35bb16e0b71..ae1b16b3ae2 100644 --- a/src/container/srv_layout.c +++ b/src/container/srv_layout.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2017-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -54,6 +55,7 @@ RDB_STRING_KEY(ds_cont_prop_, scrubber_disabled); RDB_STRING_KEY(ds_cont_prop_, co_md_times); RDB_STRING_KEY(ds_cont_prop_, cont_obj_version); RDB_STRING_KEY(ds_cont_prop_, nhandles); +RDB_STRING_KEY(ds_cont_prop_, ec_agg_eph); /* dummy value for container roots, avoid malloc on demand */ static struct daos_prop_co_roots dummy_roots; diff --git a/src/container/srv_layout.h b/src/container/srv_layout.h index d4f2622aba2..7f10d948368 100644 --- a/src/container/srv_layout.h +++ b/src/container/srv_layout.h @@ -1,5 +1,6 @@ /* * (C) Copyright 2016-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -137,6 +138,7 @@ extern d_iov_t ds_cont_prop_co_md_times; /* co_md_times */ extern d_iov_t ds_cont_prop_cont_obj_version; /* uint32_t */ extern d_iov_t ds_cont_prop_nhandles; /* uint32_t */ extern d_iov_t ds_cont_prop_oit_oids; /* snapshot OIT OID KVS */ +extern d_iov_t ds_cont_prop_ec_agg_eph; /* uint64_t */ /* Please read the IMPORTANT notes above before adding new keys. */ struct co_md_times { diff --git a/src/include/daos/common.h b/src/include/daos/common.h index 082c64484b1..e8b599e2ce8 100644 --- a/src/include/daos/common.h +++ b/src/include/daos/common.h @@ -388,7 +388,10 @@ int daos_sgl_copy_data(d_sg_list_t *dst, d_sg_list_t *src); int daos_sgl_alloc_copy_data(d_sg_list_t *dst, d_sg_list_t *src); int daos_sgls_alloc(d_sg_list_t *dst, d_sg_list_t *src, int nr); int daos_sgl_merge(d_sg_list_t *dst, d_sg_list_t *src); -daos_size_t daos_sgl_data_len(d_sg_list_t *sgl); +daos_size_t +daos_sgl_data_len(d_sg_list_t *sgl, bool out); +daos_size_t + daos_sgl_out_data_len(d_sg_list_t *sgl); daos_size_t daos_sgl_buf_size(d_sg_list_t *sgl); daos_size_t daos_sgls_buf_size(d_sg_list_t *sgls, int nr); daos_size_t daos_sgls_packed_size(d_sg_list_t *sgls, int nr, diff --git a/src/include/daos/object.h b/src/include/daos/object.h index feb59d24d0c..bdd7abb2cc7 100644 --- a/src/include/daos/object.h +++ b/src/include/daos/object.h @@ -653,6 +653,8 @@ enum daos_io_flags { DIOF_RECX_REVERSE = 0x800, /* Use for rebuild fetch epoch selection */ DIOF_FETCH_EPOCH_EC_AGG_BOUNDARY = 0x1000, + /* Do not degrade enumeration/fetch if data shard failed */ + DIOF_EC_NO_DEGRADE = 0x2000, }; /** diff --git a/src/include/daos/pool.h b/src/include/daos/pool.h index c98ce1a19b0..036122133f2 100644 --- a/src/include/daos/pool.h +++ b/src/include/daos/pool.h @@ -1,6 +1,7 @@ /* * (C) Copyright 2016-2024 Intel Corporation. * (C) Copyright 2025 Google LLC + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -86,6 +87,16 @@ */ #define DAOS_POOL_GLOBAL_VERSION 3 +/** + * Each individual object layout format, like oid layout, dkey to group, + * dkey to EC group start. + */ +enum { + DAOS_POOL_OBJ_VERSION_1 = 1, + DAOS_POOL_OBJ_VERSION_2 = 2, + DAOS_POOL_OBJ_VERSION = DAOS_POOL_OBJ_VERSION_2, +}; + int dc_pool_init(void); void dc_pool_fini(void); diff --git a/src/include/daos_cont.h b/src/include/daos_cont.h index 632eafe8130..6e6e5b98adc 100644 --- a/src/include/daos_cont.h +++ b/src/include/daos_cont.h @@ -1,5 +1,6 @@ /* * (C) Copyright 2020-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -73,8 +74,15 @@ extern "C" { */ #define DAOS_COO_EVICT_ALL (1U << 6) +/** + * When clients attempt to open a container using the new hash layout, + * the server will reject the request if this flag is not set, + * even if the container is already in the new layout format. + */ +#define DAOS_COO_NEW_LAYOUT (1U << 7) + /** Number of bits in the container open mode flag, DAOS_COO_ bits */ -#define DAOS_COO_NBITS (7) +#define DAOS_COO_NBITS (8) /** Mask for all of the bits in the container open mode flag, DAOS_COO_ bits */ #define DAOS_COO_MASK ((1U << DAOS_COO_NBITS) - 1) diff --git a/src/include/daos_srv/container.h b/src/include/daos_srv/container.h index 5beea88fe1f..ccd5b43e0a0 100644 --- a/src/include/daos_srv/container.h +++ b/src/include/daos_srv/container.h @@ -37,6 +37,8 @@ void ds_cont_svc_step_down(struct cont_svc *svc); int ds_cont_svc_set_prop(uuid_t pool_uuid, const char *cont_id, d_rank_list_t *ranks, daos_prop_t *prop); +int + ds_cont_svc_refresh_agg_eph(uuid_t pool_uuid); int ds_cont_list(uuid_t pool_uuid, struct daos_pool_cont_info **conts, uint64_t *ncont); int ds_cont_filter(uuid_t pool_uuid, daos_pool_cont_filter_t *filt, struct daos_pool_cont_info2 **conts, uint64_t *ncont); @@ -72,7 +74,9 @@ struct ds_cont_child { sc_dtx_delay_reset : 1, sc_dtx_registered : 1, sc_props_fetched : 1, sc_stopping : 1, sc_destroying : 1, sc_vos_agg_active : 1, sc_ec_agg_active : 1, /* flag of CONT_CAPA_READ_DATA/_WRITE_DATA disabled */ - sc_rw_disabled : 1, sc_scrubbing : 1, sc_rebuilding : 1; + sc_rw_disabled : 1, sc_scrubbing : 1, sc_rebuilding : 1, + /* flag of sc_ec_agg_eph_boundary valid */ + sc_ec_agg_eph_valid : 1; /* Tracks the schedule request for aggregation ULT */ struct sched_request *sc_agg_req; diff --git a/src/include/daos_srv/pool.h b/src/include/daos_srv/pool.h index aa602ef7aeb..8d0b1450a32 100644 --- a/src/include/daos_srv/pool.h +++ b/src/include/daos_srv/pool.h @@ -35,12 +35,6 @@ struct ds_pool_svc; error from the server in the case there is fault injection to return such an error.*/ extern __thread bool firewall_blocking_flag; -/** - * Each individual object layout format, like oid layout, dkey to group, - * dkey to EC group start. - */ -#define DS_POOL_OBJ_VERSION 1 - /* age of an entry in svc_ops KVS before it may be evicted */ #define DEFAULT_SVC_OPS_ENTRY_AGE_SEC_MAX 300ULL diff --git a/src/include/daos_srv/rebuild.h b/src/include/daos_srv/rebuild.h index 4799b78b693..96e2911903a 100644 --- a/src/include/daos_srv/rebuild.h +++ b/src/include/daos_srv/rebuild.h @@ -102,7 +102,8 @@ int ds_rebuild_query(uuid_t pool_uuid, struct daos_rebuild_status *status); void ds_rebuild_running_query(uuid_t pool_uuid, uint32_t opc, uint32_t *rebuild_ver, daos_epoch_t *current_eph, uint32_t *rebuild_gen); -int ds_rebuild_regenerate_task(struct ds_pool *pool, daos_prop_t *prop); +int + ds_rebuild_regenerate_task(struct ds_pool *pool, daos_prop_t *prop, uint64_t delay_sec); void ds_rebuild_leader_stop_all(void); void ds_rebuild_abort(uuid_t pool_uuid, unsigned int version, uint32_t rebuild_gen, uint64_t term); diff --git a/src/object/cli_ec.c b/src/object/cli_ec.c index 2a6ee7409f2..73897d51abe 100644 --- a/src/object/cli_ec.c +++ b/src/object/cli_ec.c @@ -1885,7 +1885,9 @@ obj_ec_fetch_set_sgl(struct dc_object *obj, struct obj_reasb_req *reasb_req, uiod = &uiods[i]; riod = &riods[i]; usgl = &usgls[i]; - usgl->sg_nr_out = 0; + /* for data recovery case, the sg_nr_out possibly already set by obj_ec_sgl_copy */ + if (!reasb_req->orr_recov_data) + usgl->sg_nr_out = 0; tail_hole_size = 0; size_in_iod = daos_iods_len(uiod, 1); if (uiod->iod_size == 0) @@ -1933,7 +1935,7 @@ obj_ec_fetch_set_sgl(struct dc_object *obj, struct obj_reasb_req *reasb_req, * is set by obj_ec_recov_fill_back(). */ if (!reasb_req->orr_recov_data || - (size_in_iod - tail_hole_size) > daos_sgl_data_len(usgl)) + (size_in_iod - tail_hole_size) > daos_sgl_data_len(usgl, true)) dc_sgl_out_set(usgl, size_in_iod - tail_hole_size); return; @@ -2677,6 +2679,11 @@ obj_ec_sgl_copy(d_sg_list_t *sgl, uint64_t off, void *buf, uint64_t size) /* to copy data from [buf, buf + size) to sgl */ rc = daos_sgl_processor(sgl, true, &sgl_idx, size, oes_copy, &arg); D_ASSERT(rc == 0); + if (sgl_idx.iov_offset == 0) + sgl->sg_nr_out = sgl_idx.iov_idx; + else + sgl->sg_nr_out = sgl_idx.iov_idx + 1; + ; } /* copy the recovered data back to missed (to be recovered) recx list */ diff --git a/src/object/cli_obj.c b/src/object/cli_obj.c index 9cf67485cd6..c0de8cd4a18 100644 --- a/src/object/cli_obj.c +++ b/src/object/cli_obj.c @@ -5075,49 +5075,48 @@ obj_dup_sgls_free(struct obj_auxi_args *obj_auxi) d_sg_list_t *sg_orig = &ctx->sgls_orig[i]; uint32_t dup_sg_idx = 0; uint32_t dup_buf_size = 0; - uint32_t sg_nr_out = 0; + uint32_t dup_data_len = 0; char *dup_buf; if (!ctx->alloc_bitmaps || !ctx->alloc_bitmaps[i]) continue; D_ASSERT(ctx->merged_bitmaps[i] != NULL); - for (j = 0; j < sg_orig->sg_nr && dup_sg_idx < sg_dup->sg_nr_out;) { + for (j = 0; j < sg_orig->sg_nr && dup_sg_idx < sg_dup->sg_nr_out; j++) { iov = &sg_orig->sg_iovs[j]; iov_dup = &sg_dup->sg_iovs[dup_sg_idx]; - if (skip_sgl_iov(false, iov)) { - j++; + if (skip_sgl_iov(false, iov)) continue; - } /* Direct copy if entry wasn't modified */ if (!isset_range((uint8_t *)ctx->merged_bitmaps[i], j, j)) { *iov = *iov_dup; - D_ASSERT(dup_buf_size == 0); - j++; - sg_nr_out++; + D_ASSERT(dup_data_len == 0); dup_sg_idx++; continue; } - if (dup_buf_size == 0) { + if (dup_data_len == 0) { + dup_data_len = iov_dup->iov_len; dup_buf_size = iov_dup->iov_buf_len; dup_buf = (char *)iov_dup->iov_buf; } + /* Update iov_len for short read */ + if (dup_data_len < iov->iov_len) + iov->iov_len = dup_data_len; /* Copy data from duplicate buffer to original buffer */ - D_ASSERT(dup_buf_size >= iov->iov_buf_len); - memcpy((char *)iov->iov_buf, dup_buf, iov->iov_buf_len); - dup_buf_size -= iov->iov_buf_len; - dup_buf += iov->iov_buf_len; - sg_nr_out++; - j++; + D_ASSERT(dup_buf_size >= iov->iov_len); + memcpy((char *)iov->iov_buf, dup_buf, iov->iov_len); + dup_data_len -= iov->iov_len; + dup_buf += iov->iov_len; + dup_buf_size -= iov->iov_len; /* When current duplicate buffer is exhausted, get next entry */ - if (dup_buf_size == 0) + if (dup_data_len == 0) dup_sg_idx++; } - sg_orig->sg_nr_out = sg_nr_out; + sg_orig->sg_nr_out = j; } } @@ -5142,7 +5141,8 @@ obj_reasb_io_fini(struct obj_auxi_args *obj_auxi, bool retry) } obj_bulk_fini(obj_auxi); obj_auxi_free_failed_tgt_list(obj_auxi); - obj_dup_sgls_free(obj_auxi); + if (!retry) + obj_dup_sgls_free(obj_auxi); obj_reasb_req_fini(&obj_auxi->reasb_req, obj_auxi->iod_nr); obj_auxi->req_reasbed = false; @@ -5914,6 +5914,12 @@ obj_ec_fetch_shards_get(struct dc_object *obj, daos_obj_fetch_t *args, unsigned if (likely(ec_deg_tgt == tgt_idx)) continue; + if (args->extra_flags & DIOF_EC_NO_DEGRADE) { + D_WARN(DF_OID "have to degraded fetch for %u => %u, but sponsor forbid.\n", + DP_OID(obj->cob_md.omd_id), tgt_idx, ec_deg_tgt); + D_GOTO(out, rc = -DER_IO); + } + if (obj_auxi->ec_in_recov) { D_DEBUG(DB_IO, DF_OID " shard %d failed recovery.\n", DP_OID(obj->cob_md.omd_id), grp_start + tgt_idx); diff --git a/src/object/cli_shard.c b/src/object/cli_shard.c index 9f385b98f52..d6f85a71bf1 100644 --- a/src/object/cli_shard.c +++ b/src/object/cli_shard.c @@ -921,16 +921,18 @@ dc_rw_cb(tse_task_t *task, void *arg) orwo->orw_rels.ca_arrays, orwo->orw_rels.ca_count); if (rc) { - D_ERROR(DF_UOID " obj_ec_parity_check failed, " DF_RC "\n", - DP_UOID(orw->orw_oid), DP_RC(rc)); + DL_ERROR(rc, DF_CONT ", " DF_UOID " obj_ec_parity_check failed", + DP_CONT(orw->orw_pool_uuid, orw->orw_co_uuid), + DP_UOID(orw->orw_oid)); goto out; } } rc = dc_shard_update_size(rw_args, 0); if (rc) { - D_ERROR(DF_UOID " dc_shard_update_size failed, " DF_RC "\n", - DP_UOID(orw->orw_oid), DP_RC(rc)); + DL_ERROR(rc, DF_CONT ", " DF_UOID " dc_shard_update_size failed", + DP_CONT(orw->orw_pool_uuid, orw->orw_co_uuid), + DP_UOID(orw->orw_oid)); goto out; } diff --git a/src/object/obj_class.c b/src/object/obj_class.c index f37ff1e6ee7..654a316efe0 100644 --- a/src/object/obj_class.c +++ b/src/object/obj_class.c @@ -812,9 +812,22 @@ dc_set_oclass(uint32_t rf, int domain_nr, int target_nr, enum daos_otype_t otype } break; case DAOS_PROP_CO_REDUN_RF3: - /** EC not supported here */ - *ord = OR_RP_4; - grp_size = 4; + if ((rdd == DAOS_OCH_RDD_EC || (rdd == 0 && daos_is_array_type(otype))) && + domain_nr >= 10) { + if (domain_nr >= 22) { + *ord = OR_RS_16P3; + grp_size = 19; + } else if (domain_nr >= 14) { + *ord = OR_RS_8P3; + grp_size = 11; + } else { + *ord = OR_RS_4P3; + grp_size = 7; + } + } else { + *ord = OR_RP_4; + grp_size = 4; + } break; case DAOS_PROP_CO_REDUN_RF4: /** EC not supported here */ diff --git a/src/object/obj_verify.c b/src/object/obj_verify.c index beffd5744ab..46df0b6c93d 100644 --- a/src/object/obj_verify.c +++ b/src/object/obj_verify.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2019-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -31,9 +32,9 @@ dc_obj_verify_list(struct dc_obj_verify_args *dova) memset(dova->kds, 0, sizeof(daos_key_desc_t) * DOVA_NUM); memset(dova->list_buf, 0, dova->list_buf_len); - dova->list_sgl.sg_nr = 1; - dova->list_sgl.sg_nr_out = 1; - dova->list_sgl.sg_iovs = &dova->list_iov; + dova->list_sgl.sg_nr = 1; + dova->list_sgl.sg_nr_out = 0; + dova->list_sgl.sg_iovs = &dova->list_iov; dova->size = 0; dova->num = DOVA_NUM; @@ -113,9 +114,9 @@ dc_obj_verify_fetch(struct dc_obj_verify_args *dova) dova->fetch_iov.iov_buf = dova->fetch_buf; dova->fetch_iov.iov_buf_len = dova->fetch_buf_len; - dova->fetch_sgl.sg_nr = 1; - dova->fetch_sgl.sg_nr_out = 1; - dova->fetch_sgl.sg_iovs = &dova->fetch_iov; + dova->fetch_sgl.sg_nr = 1; + dova->fetch_sgl.sg_nr_out = 0; + dova->fetch_sgl.sg_iovs = &dova->fetch_iov; shard = dc_obj_anchor2shard(&dova->dkey_anchor); rc = dc_obj_fetch_task_create(dova->oh, dova->th, 0, &cursor->dkey, 1, @@ -677,18 +678,18 @@ dc_obj_verify_ec_cb(struct dc_obj_enum_unpack_io *io, void *arg) D_GOTO(out, rc = -DER_NOMEM); d_iov_set(&iovs[idx], data, size); - sgls[idx].sg_nr = 1; - sgls[idx].sg_nr_out = 1; - sgls[idx].sg_iovs = &iovs[idx]; + sgls[idx].sg_nr = 1; + sgls[idx].sg_nr_out = 0; + sgls[idx].sg_iovs = &iovs[idx]; D_ALLOC(data_verify, size); if (data_verify == NULL) D_GOTO(out, rc = -DER_NOMEM); d_iov_set(&iovs_verify[idx], data_verify, size); - sgls_verify[idx].sg_nr = 1; - sgls_verify[idx].sg_nr_out = 1; - sgls_verify[idx].sg_iovs = &iovs_verify[idx]; + sgls_verify[idx].sg_nr = 1; + sgls_verify[idx].sg_nr_out = 0; + sgls_verify[idx].sg_iovs = &iovs_verify[idx]; if (iod->iod_type == DAOS_IOD_ARRAY) { rc = obj_recx_ec2_daos(obj_get_oca(obj), tgt_off, &iod->iod_recxs, NULL, &iod->iod_nr, true); @@ -705,10 +706,13 @@ dc_obj_verify_ec_cb(struct dc_obj_enum_unpack_io *io, void *arg) return 0; } - /* Fetch by specific shard */ - rc = dc_obj_fetch_task_create(dova->oh, dova->th, 0, &io->ui_dkey, idx, - 0, iods, sgls, NULL, &shard, NULL, NULL, NULL, - &task); + /* + * NOTE: Fetch data from data shard (including current one). The EC fetch task may touch + * multiple data shards. That is no matter as long as it does not trigger degraded + * fetch. Using DIOF_EC_NO_DEGRADE flag for such purpose. + */ + rc = dc_obj_fetch_task_create(dova->oh, dova->th, 0, &io->ui_dkey, idx, DIOF_EC_NO_DEGRADE, + iods, sgls, NULL, &shard, NULL, NULL, NULL, &task); if (rc != 0) { D_ERROR(DF_OID" sgl num %u shard "DF_U64"\n", DP_OID(obj->cob_md.omd_id), idx, shard); @@ -731,10 +735,9 @@ dc_obj_verify_ec_cb(struct dc_obj_enum_unpack_io *io, void *arg) D_GOTO(out, rc); } - daos_fail_loc_set(DAOS_OBJ_FORCE_DEGRADE | DAOS_FAIL_ONCE); rc = dc_obj_fetch_task_create(dova->oh, dova->th, 0, &io->ui_dkey, idx, - 0, iods, sgls_verify, NULL, &shard, NULL, NULL, - NULL, &verify_task); + DIOF_FOR_FORCE_DEGRADE, iods, sgls_verify, NULL, &shard, NULL, + NULL, NULL, &verify_task); if (rc != 0) { D_ERROR(DF_OID" sgl num %u shard "DF_U64"\n", DP_OID(obj->cob_md.omd_id), idx, shard); @@ -756,7 +759,6 @@ dc_obj_verify_ec_cb(struct dc_obj_enum_unpack_io *io, void *arg) io->ui_iods[i].iod_size, DP_RC(rc)); D_GOTO(out, rc); } - daos_fail_loc_set(0); for (i = 0; i < idx; i++) { if (sgls[i].sg_iovs[0].iov_len != sgls_verify[i].sg_iovs[0].iov_len || @@ -810,10 +812,10 @@ dc_obj_verify_ec_rdg(struct dc_object *obj, struct dc_obj_verify_args *dova, dc_obj_shard2anchor(&dova->dkey_anchor, start + i); daos_anchor_set_flags(&dova->dkey_anchor, DIOF_TO_SPEC_SHARD | DIOF_WITH_SPEC_EPOCH); - dova->th = th; - dova->eof = 0; - dova->non_exist = 0; - dova->current_shard = i; + dova->th = th; + dova->eof = 0; + dova->non_exist = 0; + dova->current_shard = start + i; memset(cursor, 0, sizeof(*cursor)); cursor->iod.iod_nr = 1; cursor->iod.iod_recxs = &cursor->recx; diff --git a/src/object/srv_ec_aggregate.c b/src/object/srv_ec_aggregate.c index 9af73b918a6..b6fee91c619 100644 --- a/src/object/srv_ec_aggregate.c +++ b/src/object/srv_ec_aggregate.c @@ -1075,7 +1075,7 @@ agg_update_parity(struct ec_agg_entry *entry, uint8_t *bit_map, buf = entry->ae_sgl.sg_iovs[AGG_IOV_DATA].iov_buf; diff = entry->ae_sgl.sg_iovs[AGG_IOV_DIFF].iov_buf; - for (i = 0, j = 0; i < cell_cnt; i++) { + for (i = 0, j = 0; i < cell_cnt; i++, j++) { old = &obuf[i * cell_bytes]; new = &buf[i * cell_bytes]; vects[0] = old; @@ -1086,6 +1086,7 @@ agg_update_parity(struct ec_agg_entry *entry, uint8_t *bit_map, goto out; while (!isset(bit_map, j)) j++; + D_ASSERTF(j < k, "bad cell_idx %d, number of data shards %d\n", j, k); agg_diff_preprocess(entry, diff, j); ec_encode_data_update(cell_bytes, k, p, j, entry->ae_codec->ec_gftbls, diff, @@ -1951,15 +1952,15 @@ agg_process_stripe(struct ec_agg_param *agg_param, struct ec_agg_entry *entry) /* offload of ds_obj_update to push remote parity */ rc = agg_peer_update(entry, write_parity); if (rc) - D_ERROR("agg_peer_update fail: "DF_RC"\n", - DP_RC(rc)); + DL_ERROR(rc, "agg_peer_update failed, write_parity %d", + write_parity); } if (rc == 0) { rc = agg_update_vos(agg_param, entry, write_parity); if (rc) - D_ERROR("agg_update_vos failed: "DF_RC"\n", - DP_RC(rc)); + DL_ERROR(rc, "agg_update_vos failed, write_parity %d", + write_parity); } } diff --git a/src/object/srv_obj.c b/src/object/srv_obj.c index c80f68630c2..620e63aef65 100644 --- a/src/object/srv_obj.c +++ b/src/object/srv_obj.c @@ -1389,6 +1389,25 @@ obj_rw_recx_list_post(struct obj_rw_in *orw, struct obj_rw_out *orwo, uint8_t *s return rc; } +struct ec_agg_boundary_arg { + struct ds_pool *eab_pool; + uuid_t eab_co_uuid; +}; + +static int +obj_fetch_ec_agg_boundary(void *data) +{ + struct ec_agg_boundary_arg *arg = data; + int rc; + + rc = ds_cont_fetch_ec_agg_boundary(arg->eab_pool->sp_iv_ns, arg->eab_co_uuid); + if (rc) + DL_ERROR(rc, DF_CONT ", ds_cont_fetch_ec_agg_boundary failed.", + DP_CONT(arg->eab_pool->sp_uuid, arg->eab_co_uuid)); + + return rc; +} + static int obj_local_rw_internal(crt_rpc_t *rpc, struct obj_io_context *ioc, daos_iod_t *iods, struct dcs_iod_csums *iod_csums, uint64_t *offs, uint8_t *skips, @@ -1509,6 +1528,32 @@ obj_local_rw_internal(crt_rpc_t *rpc, struct obj_io_context *ioc, daos_iod_t *io get_parity_list = ec_recov && is_parity_shard && ((orw->orw_flags & ORF_EC_RECOV_SNAP) == 0); } + if ((ec_deg_fetch || (ec_recov && get_parity_list)) && + ioc->ioc_coc->sc_ec_agg_eph_valid == 0) { + struct ec_agg_boundary_arg arg; + + arg.eab_pool = ioc->ioc_coc->sc_pool->spc_pool; + uuid_copy(arg.eab_co_uuid, ioc->ioc_coc->sc_uuid); + rc = dss_ult_execute(obj_fetch_ec_agg_boundary, &arg, NULL, NULL, + DSS_XS_SYS, 0, 0); + if (rc) { + DL_ERROR(rc, DF_CONT ", " DF_UOID " fetch ec_agg_boundary failed.", + DP_CONT(ioc->ioc_coc->sc_pool_uuid, ioc->ioc_coc->sc_uuid), + DP_UOID(orw->orw_oid)); + goto out; + } + if (ioc->ioc_coc->sc_ec_agg_eph_valid == 0) { + rc = -DER_FETCH_AGAIN; + DL_INFO(rc, DF_CONT ", " DF_UOID " zero ec_agg_boundary.", + DP_CONT(ioc->ioc_coc->sc_pool_uuid, ioc->ioc_coc->sc_uuid), + DP_UOID(orw->orw_oid)); + goto out; + } + D_DEBUG(DB_IO, + DF_CONT ", " DF_UOID " fetched ec_agg_eph_boundary " DF_X64 "\n", + DP_CONT(ioc->ioc_coc->sc_pool_uuid, ioc->ioc_coc->sc_uuid), + DP_UOID(orw->orw_oid), ioc->ioc_coc->sc_ec_agg_eph_boundary); + } if (get_parity_list) { D_ASSERT(!ec_deg_fetch); fetch_flags |= VOS_OF_FETCH_RECX_LIST; diff --git a/src/object/srv_obj_migrate.c b/src/object/srv_obj_migrate.c index 7772bcdd4e0..2a91c7f1722 100644 --- a/src/object/srv_obj_migrate.c +++ b/src/object/srv_obj_migrate.c @@ -764,7 +764,7 @@ mrone_obj_fetch_internal(struct migrate_one *mrone, daos_handle_t oh, d_sg_list_ retry: rc = dsc_obj_fetch(oh, eph, &mrone->mo_dkey, iod_num, iods, sgls, NULL, flags, extra_arg, csum_iov_fetch); - if (rc == -DER_TIMEDOUT && + if ((rc == -DER_TIMEDOUT || rc == -DER_FETCH_AGAIN) && tls->mpt_version + 1 >= tls->mpt_pool->spc_map_version) { if (tls->mpt_fini) { DL_ERROR(rc, DF_RB ": dsc_obj_fetch " DF_UOID "failed when mpt_fini", diff --git a/src/pipeline/cli_pipeline.c b/src/pipeline/cli_pipeline.c index b30d8366b79..9d76456f331 100644 --- a/src/pipeline/cli_pipeline.c +++ b/src/pipeline/cli_pipeline.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2021-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -544,7 +545,7 @@ shard_task_sched(tse_task_t *task, void *arg) static int pipeline_create_layout(daos_handle_t coh, struct dc_pool *pool, struct daos_obj_md *obj_md, - struct pl_obj_layout **layout) + struct pl_obj_layout **layout, uint16_t layout_gl_version) { int rc = 0; struct pl_map *map; @@ -555,7 +556,7 @@ pipeline_create_layout(daos_handle_t coh, struct dc_pool *pool, struct daos_obj_ D_GOTO(out, rc = -DER_INVAL); } - rc = pl_obj_place(map, 0, obj_md, 0, NULL, layout); + rc = pl_obj_place(map, layout_gl_version, obj_md, 0, NULL, layout); pl_map_decref(map); if (rc != 0) { D_DEBUG(DB_PL, "Failed to generate object layout\n"); @@ -609,6 +610,7 @@ dc_pipeline_run(tse_task_t *api_task) int total_replicas; int shard; struct pipeline_comp_cb_args comp_cb_args; + uint16_t layout_gl_ver; coh = dc_obj_hdl2cont_hdl(api_args->oh); rc = dc_obj_hdl2obj_md(api_args->oh, &obj_md); @@ -629,7 +631,8 @@ dc_pipeline_run(tse_task_t *api_task) /** get layout */ - rc = pipeline_create_layout(coh, pool, &obj_md, &layout); + layout_gl_ver = dc_obj_hdl2layout_ver(api_args->oh); + rc = pipeline_create_layout(coh, pool, &obj_md, &layout, layout_gl_ver); dc_pool_put(pool); if (rc != 0) D_GOTO(out, rc); @@ -687,7 +690,7 @@ dc_pipeline_run(tse_task_t *api_task) /** object id */ oid.id_pub = obj_md.omd_id; oid.id_shard = shard; - oid.id_layout_ver = dc_obj_hdl2layout_ver(api_args->oh); + oid.id_layout_ver = layout_gl_ver; oid.id_padding = 0; /** queue shard task */ diff --git a/src/placement/jump_map.c b/src/placement/jump_map.c index 179a3788ea5..70057d8e7d8 100644 --- a/src/placement/jump_map.c +++ b/src/placement/jump_map.c @@ -1,6 +1,7 @@ /** * * (C) Copyright 2016-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -46,7 +47,7 @@ jm_obj_placement_fini(struct jm_obj_placement *jmop) #define LOCAL_PD_ARRAY_SIZE (4) static int jm_obj_pd_init(struct pl_jump_map *jmap, struct daos_obj_md *md, struct pool_domain *root, - struct jm_obj_placement *jmop) + struct jm_obj_placement *jmop, uint32_t layout_ver) { struct pool_domain *pds, *pd; uint8_t *pd_used = NULL; @@ -114,9 +115,9 @@ jm_obj_pd_init(struct pl_jump_map *jmap, struct daos_obj_md *md, struct pool_dom } oid = md->omd_id; - key = oid.hi ^ oid.lo; + key = jm_oid_hash(layout_ver, oid); for (i = 0; i < jmop->jmop_pd_nr; i++) { - key = crc(key, i); + key = (layout_ver <= 1) ? crc(key, i) : jm_crc(key, i, 0xcafebabe); selected_pd = d_hash_jump(key, jmap->jmp_pd_nr); do { selected_pd = selected_pd % jmap->jmp_pd_nr; @@ -242,8 +243,8 @@ layout_find_diff(struct pl_jump_map *jmap, struct pl_obj_layout *original, */ static int jm_obj_placement_init(struct pl_jump_map *jmap, struct daos_obj_md *md, - struct daos_obj_shard_md *shard_md, - struct jm_obj_placement *jmop) + struct daos_obj_shard_md *shard_md, struct jm_obj_placement *jmop, + uint32_t layout_ver) { struct daos_oclass_attr *oc_attr; struct pool_domain *root; @@ -305,7 +306,7 @@ jm_obj_placement_init(struct pl_jump_map *jmap, struct daos_obj_md *md, D_ASSERT(jmop->jmop_grp_nr > 0); D_ASSERT(jmop->jmop_grp_size > 0); - rc = jm_obj_pd_init(jmap, md, root, jmop); + rc = jm_obj_pd_init(jmap, md, root, jmop, layout_ver); if (rc == 0) D_DEBUG(DB_PL, "obj="DF_OID"/ grp_size=%u grp_nr=%d, pd_nr=%u pd_grp_size=%u\n", DP_OID(oid), jmop->jmop_grp_size, jmop->jmop_grp_nr, @@ -404,7 +405,7 @@ obj_remap_shards(struct pl_jump_map *jmap, uint32_t layout_ver, struct daos_obj_ current = remap_list->next; spare_tgt = NULL; oid = md->omd_id; - key = oid.hi ^ oid.lo; + key = jm_oid_hash(layout_ver, oid); spares_left = count_available_spares(jmap, layout, failed_in_layout); rc = pool_map_find_domain(jmap->jmp_map.pl_poolmap, PO_COMP_TP_ROOT, @@ -435,13 +436,16 @@ obj_remap_shards(struct pl_jump_map *jmap, uint32_t layout_ver, struct daos_obj_ dgu = f_shard->fs_data; D_ASSERT(dgu != NULL); - rebuild_key = crc(key, f_shard->fs_shard_idx); + if (layout_ver <= 1) + rebuild_key = crc(key, crc(key, f_shard->fs_shard_idx)); + else /* hash OID differently so we don't land to the same target */ + rebuild_key = jm_crc(oid.lo, oid.hi, 0xDead2Bad); + curr_pd = jm_obj_shard_pd(jmop, shard_id); - get_target(root, curr_pd, layout_ver, &spare_tgt, &spare_dom, - crc(key, rebuild_key), dom_used, dom_full, - dgu->dgu_used, dgu->dgu_real, tgts_used, - shard_id, allow_version, gen_mode, fdom_lvl, - jmop->jmop_grp_size, &spares_left, &spare_avail); + get_target(root, curr_pd, layout_ver, &spare_tgt, &spare_dom, rebuild_key, + dom_used, dom_full, dgu->dgu_used, dgu->dgu_real, tgts_used, + shard_id, allow_version, gen_mode, fdom_lvl, jmop->jmop_grp_size, + &spares_left, &spare_avail); if (layout_ver > 0) { /* * After 2.4 (layout_ver > 0), it will always assign each shard @@ -644,7 +648,8 @@ get_object_layout(struct pl_jump_map *jmap, uint32_t layout_ver, struct pl_obj_l D_GOTO(out, rc = -DER_NOMEM); oid = md->omd_id; - key = oid.hi ^ oid.lo; + key = jm_oid_hash(layout_ver, oid); + if (daos_obj_is_srank(oid)) spec_oid = true; @@ -1012,7 +1017,7 @@ jump_map_obj_place(struct pl_map *map, uint32_t layout_version, struct daos_obj_ D_DEBUG(DB_PL, "Determining location for object: "DF_OID", ver: %d, pda %u\n", DP_OID(oid), md->omd_ver, md->omd_pda); - rc = jm_obj_placement_init(jmap, md, shard_md, &jmop); + rc = jm_obj_placement_init(jmap, md, shard_md, &jmop, layout_version); if (rc) { D_ERROR("jm_obj_placement_init failed, rc "DF_RC"\n", DP_RC(rc)); return rc; @@ -1115,7 +1120,7 @@ jump_map_obj_find_diff(struct pl_map *map, uint32_t layout_ver, struct daos_obj_ } jmap = pl_map2jmap(map); - rc = jm_obj_placement_init(jmap, md, shard_md, &jop); + rc = jm_obj_placement_init(jmap, md, shard_md, &jop, layout_ver); if (rc) { D_ERROR("jm_obj_placement_init failed, rc %d.\n", rc); return rc; diff --git a/src/placement/jump_map.h b/src/placement/jump_map.h index 1fd39d3ce58..330872d842f 100644 --- a/src/placement/jump_map.h +++ b/src/placement/jump_map.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2022-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -61,4 +62,22 @@ get_target(struct pool_domain *root, struct pool_domain *curr_pd, uint32_t layou uint8_t *dom_full, uint8_t *dom_cur_grp_used, uint8_t *dom_cur_grp_real, uint8_t *tgts_used, int shard_num, uint32_t allow_version, enum layout_gen_mode gen_mode, pool_comp_type_t fdom_lvl, uint32_t grp_size, uint32_t *spare_left, bool *spare_avail); + +static inline uint64_t +jm_crc(uint64_t val0, uint64_t val1, uint64_t val2) +{ + uint64_t data[3]; + + data[0] = val0; + data[1] = val1; + data[2] = val2; + return crc64_ecma_refl(0xbabecafe, (uint8_t *)data, 24); +} + +static inline uint64_t +jm_oid_hash(uint32_t layout_ver, daos_obj_id_t oid) +{ + return layout_ver <= 1 ? (oid.hi ^ oid.lo) : jm_crc(oid.hi, oid.lo, 0xbabeface); +} + #endif /* __JUMP_MAP_H__ */ diff --git a/src/placement/jump_map_versions.c b/src/placement/jump_map_versions.c index b943949ad6a..f817ed96b04 100644 --- a/src/placement/jump_map_versions.c +++ b/src/placement/jump_map_versions.c @@ -1,6 +1,7 @@ /** * * (C) Copyright 2016-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -414,6 +415,216 @@ __get_target_v1(struct pool_domain *root_pos, struct pool_domain *curr_pd, } while (!found_target); } +/** + * This function is identical to __get_target_v1, except how they call CRC functions, + * the V1 function may cause a large number of hash collisions in certain cases. + * + * Search for jm_crc() and crc() to see how they differ. + */ +static void +__get_target_v2(struct pool_domain *root_pos, struct pool_domain *curr_pd, + struct pool_target **target, struct pool_domain **dom, uint64_t obj_key, + uint8_t *dom_used, uint8_t *dom_full, uint8_t *dom_cur_grp_used, uint8_t *tgts_used, + int shard_num, uint32_t allow_version, enum layout_gen_mode gen_mode, + pool_comp_type_t fdom_lvl, uint32_t grp_size, bool *pd_ignored) +{ + int range_set; + uint8_t found_target = 0; + struct pool_domain *curr_dom; + struct pool_domain *dom_stack[MAX_STACK] = {0}; + int top = -1; + + curr_dom = curr_pd; + do { + uint32_t avail_doms; + uint64_t key; + + /* Retrieve number of nodes in this domain */ + avail_doms = get_num_domains(curr_dom, allow_version, gen_mode, fdom_lvl); + + /* If choosing target (lowest fault domain level) */ + if (curr_dom->do_comp.co_type == fdom_lvl) { + uint32_t fail_num = 0; + uint32_t tgt_idx; + uint32_t start_tgt; + uint32_t end_tgt; + uint32_t selected_tgt; + + start_tgt = curr_dom->do_targets - root_pos->do_targets; + end_tgt = start_tgt + (curr_dom->do_target_nr - 1); + + range_set = tgt_isset_range(root_pos->do_targets, tgts_used, start_tgt, + end_tgt, allow_version, gen_mode); + if (range_set) { + /* Used up all targets in this domain */ + D_ASSERT(top != -1); + curr_dom = dom_stack[top--]; /* try parent */ + continue; + } + + /* + * Must crc key because jump consistent hash + * requires an even distribution or it will + * not work + */ + key = jm_crc(obj_key, shard_num, fail_num++); + /* Get target for shard */ + selected_tgt = d_hash_jump(key, avail_doms); + do { + selected_tgt = selected_tgt % avail_doms; + /* Retrieve actual target using index */ + *target = + _get_target(curr_dom, selected_tgt, allow_version, gen_mode); + /* Get target id to check if target used */ + tgt_idx = *target - root_pos->do_targets; + selected_tgt++; + } while (isset(tgts_used, tgt_idx)); + + setbit(tgts_used, tgt_idx); + D_DEBUG(DB_PL, "selected tgt %d\n", tgt_idx); + D_ASSERTF(isclr(dom_full, (uint32_t)(curr_dom - root_pos)), + "selected_tgt %u\n", (uint32_t)(curr_dom - root_pos)); + range_set = tgt_isset_range(root_pos->do_targets, tgts_used, start_tgt, + end_tgt, allow_version, gen_mode); + if (range_set) { + /* Used up all targets in this domain */ + setbit(dom_full, curr_dom - root_pos); + D_DEBUG(DB_PL, "dom %d used up\n", (int)(curr_dom - root_pos)); + /* Check and set if all of its parent are full */ + while (top != -1) { + if (is_dom_full(dom_stack[top], root_pos, dom_full, + allow_version, gen_mode)) { + uint32_t off = dom_stack[top] - root_pos; + + D_DEBUG(DB_PL, "dom %u used up\n", off); + setbit(dom_full, off); + } + --top; + } + } + *dom = curr_dom; + /* Found target (which may be available or not) */ + found_target = 1; + } else { + uint32_t selected_dom; + uint32_t fail_num = 0; + uint32_t start_dom; + uint32_t end_dom; + + start_dom = (curr_dom->do_children) - root_pos; + end_dom = start_dom + (curr_dom->do_child_nr - 1); + + D_DEBUG(DB_TRACE, "start_dom %u end_dom %u\n", start_dom, end_dom); + /* Check if all targets under the domain range has been + * used up (occupied), go back to its parent if it does. + */ + range_set = + is_dom_full(curr_dom, root_pos, dom_full, allow_version, gen_mode); + if (range_set) { + if (top == -1) { + if (curr_pd != root_pos) { + /* all domains within the PD are full, ignore the + * PD restrict. + */ + D_DEBUG(DB_PL, + "PD[%d] all doms are full, weak the " + "PD restrict\n", + (int)(curr_dom - root_pos)); + curr_pd = root_pos; + curr_dom = curr_pd; + *pd_ignored = true; + continue; + } + /* shard nr > target nr, no extra target for the shard */ + *target = NULL; + return; + } + setbit(dom_full, curr_dom - root_pos); + D_DEBUG(DB_PL, "used up dom %d\n", (int)(curr_dom - root_pos)); + setbit(dom_cur_grp_used, curr_dom - root_pos); + curr_dom = dom_stack[top--]; + continue; + } + + /* Check if all children has been used for the current group or full */ + range_set = dom_isset_2ranges(root_pos, dom_full, dom_cur_grp_used, + start_dom, end_dom, allow_version, gen_mode); + if (range_set) { + if (top == -1) { + if (curr_pd != root_pos && grp_size > 1) { + /* All domains within the PD are full, ignore the + * PD restrict. For non-replica (grp_size 1) case, + * keep the PD restrict until all targets under the + * domain range has been used up (see above check + * for dom_full bitmap). + */ + D_DEBUG(DB_PL, + "PD[%d] all doms are full, weak the " + "PD restrict\n", + (int)(curr_dom - root_pos)); + curr_pd = root_pos; + curr_dom = curr_pd; + *pd_ignored = true; + continue; + } + *target = NULL; + return; + } + setbit(dom_cur_grp_used, curr_dom - root_pos); + D_DEBUG(DB_PL, "set grp_used %d\n", (int)(curr_dom - root_pos)); + curr_dom = dom_stack[top--]; + continue; + } + + /* Check if all children has been used for the current group or the + * object. + */ + range_set = dom_isset_2ranges(root_pos, dom_used, dom_cur_grp_used, + start_dom, end_dom, allow_version, gen_mode); + if (range_set) { + int idx; + + for (idx = start_dom; idx <= end_dom; ++idx) + if (!isset(dom_full, idx)) { + clrbit(dom_used, idx); + D_DEBUG(DB_PL, "clrbit dom_used %d\n", idx); + } + if (top == -1) + curr_dom = curr_pd; + else + curr_dom = dom_stack[top--]; + continue; + } + + /* Keep choosing the new domain until the one has not been used. */ + do { + struct pool_domain *_dom; + + key = jm_crc(obj_key, shard_num, fail_num++); + selected_dom = d_hash_jump(key, avail_doms); + _dom = _get_dom(curr_dom, selected_dom, allow_version, gen_mode); + if (_dom != NULL) + selected_dom = _dom - curr_dom->do_children; + } while (isset(dom_used, start_dom + selected_dom) || + isset(dom_cur_grp_used, start_dom + selected_dom)); + + D_ASSERTF(isclr(dom_full, start_dom + selected_dom), "selected_dom %u\n", + selected_dom); + /* Mark this domain as used */ + if (curr_dom == curr_pd && curr_pd != root_pos) + setbit(dom_used, (int)(curr_dom - root_pos)); + D_DEBUG(DB_PL, "selected dom %d\n", start_dom + selected_dom); + setbit(dom_used, start_dom + selected_dom); + setbit(dom_cur_grp_used, start_dom + selected_dom); + D_ASSERT(top < MAX_STACK - 1); + top++; + dom_stack[top] = curr_dom; + curr_dom = &curr_dom->do_children[selected_dom]; + obj_key = jm_crc(obj_key, curr_dom->do_comp.co_id, 0xbabecafe); + } + } while (!found_target); +} + /* Only reset all used dom/tgts bits if all domain has been used. */ static void dom_reset_bit(struct pool_domain *dom, uint8_t *dom_bits, struct pool_domain *root, @@ -604,11 +815,11 @@ reset_dom_cur_grp_v1(struct pool_domain *root, struct pool_domain *curr_pd, } static void -get_target_v1(struct pool_domain *root, struct pool_domain *curr_pd, struct pool_target **target, - struct pool_domain **dom, uint64_t key, uint8_t *dom_used, uint8_t *dom_full, - uint8_t *dom_cur_grp_used, uint8_t *dom_cur_grp_real, uint8_t *tgts_used, - int shard_num, uint32_t allow_version, enum layout_gen_mode gen_mode, - pool_comp_type_t fdom_lvl, uint32_t grp_size) +get_target_new(struct pool_domain *root, struct pool_domain *curr_pd, uint32_t layout_ver, + struct pool_target **target, struct pool_domain **dom, uint64_t key, + uint8_t *dom_used, uint8_t *dom_full, uint8_t *dom_cur_grp_used, + uint8_t *dom_cur_grp_real, uint8_t *tgts_used, int shard_num, uint32_t allow_version, + enum layout_gen_mode gen_mode, pool_comp_type_t fdom_lvl, uint32_t grp_size) { struct pool_target *found = NULL; bool pd_ignored; @@ -622,9 +833,17 @@ get_target_v1(struct pool_domain *root, struct pool_domain *curr_pd, struct pool */ while (found == NULL) { pd_ignored = false; - __get_target_v1(root, curr_pd, &found, dom, key, dom_used, dom_full, - dom_cur_grp_used, tgts_used, shard_num, allow_version, gen_mode, - fdom_lvl, grp_size, &pd_ignored); + if (layout_ver == 1) { + __get_target_v1(root, curr_pd, &found, dom, key, dom_used, dom_full, + dom_cur_grp_used, tgts_used, shard_num, allow_version, + gen_mode, fdom_lvl, grp_size, &pd_ignored); + } else { /* Fix CRC calls */ + D_ASSERT(layout_ver == 2); + __get_target_v2(root, curr_pd, &found, dom, key, dom_used, dom_full, + dom_cur_grp_used, tgts_used, shard_num, allow_version, + gen_mode, fdom_lvl, grp_size, &pd_ignored); + } + if (found == NULL) { if (pd_ignored) reset_dom_cur_grp_v1(root, root, dom_cur_grp_used, dom_cur_grp_real, @@ -901,14 +1120,15 @@ get_target(struct pool_domain *root, struct pool_domain *curr_pd, uint32_t layou } break; case 1: + case 2: /** * New layout(>= 1) will always try to assign target to the shard due to the high * overhead of the EC degraded fetch, so it might put multiple shards in the * same target, i.e. it will never check the spare target here. */ - get_target_v1(root, curr_pd, target, dom, key, dom_used, dom_full, dom_cur_grp_used, - dom_cur_grp_real, tgts_used, shard_num, allow_version, gen_mode, - fdom_lvl, grp_size); + get_target_new(root, curr_pd, layout_ver, target, dom, key, dom_used, dom_full, + dom_cur_grp_used, dom_cur_grp_real, tgts_used, shard_num, + allow_version, gen_mode, fdom_lvl, grp_size); if (spare_avail) *spare_avail = true; break; diff --git a/src/placement/tests/jump_map_place_obj.c b/src/placement/tests/jump_map_place_obj.c index 8e257aad82f..b2e5e1d21e7 100644 --- a/src/placement/tests/jump_map_place_obj.c +++ b/src/placement/tests/jump_map_place_obj.c @@ -1196,10 +1196,10 @@ down_back_to_up_in_same_order(void **state) * healthy targets because of the retry/collision mechanism of the jump * map algorithm. * Due to layout colocation, if the oid has been changed, then it could - * be 2 or even 3 as well, with current oid setting, this is 1. + * be 2 or even 3 as well, with current oid setting, this is 2. */ - assert_int_equal(1, ctx.reint.out_nr); - jtc_assert_rebuild_reint_new(ctx, 1, 0, 1, 1); + assert_int_equal(2, ctx.reint.out_nr); + jtc_assert_rebuild_reint_new(ctx, 2, 0, 2, 2); /* Take second downed target up */ jtc_set_status_on_target(&ctx, UP, orig_shard_targets[1]); @@ -1674,14 +1674,14 @@ placement_handles_multiple_states_with_addition(void **state) rebuilding = jtc_get_layout_rebuild_count(&ctx); /* 1 each for down, up, new ... maybe? */ - assert_true(rebuilding == 2 || rebuilding == 3); + assert_true(rebuilding == 2 || rebuilding == 3 || rebuilding == 4); /* Both DOWN and UP target will be remapped during remap */ - assert_int_equal(ctx.rebuild.out_nr, 2); + assert_int_equal(ctx.rebuild.out_nr, 3); /* Adding new targets might make original shard to be remapped * to new location. */ - assert_true(ctx.reint.out_nr == 1 || ctx.reint.out_nr == 2); + assert_true(ctx.reint.out_nr == 1 || ctx.reint.out_nr == 2 || ctx.reint.out_nr == 3); /* JCH might cause multiple shards remap to the new target */ assert_true(ctx.new.out_nr >= 1); diff --git a/src/placement/tests/place_obj_common.h b/src/placement/tests/place_obj_common.h index 537c02a8de7..537fbe963a2 100644 --- a/src/placement/tests/place_obj_common.h +++ b/src/placement/tests/place_obj_common.h @@ -1,5 +1,6 @@ /** * (C) Copyright 2016-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -28,7 +29,7 @@ extern bool g_verbose; #define is_true assert_true #define is_false assert_false -#define PLT_LAYOUT_VERSION 1 +#define PLT_LAYOUT_VERSION 2 extern bool fail_domain_node; void print_layout(struct pl_obj_layout *layout); diff --git a/src/pool/srv_layout.c b/src/pool/srv_layout.c index dbddb92ef6f..1456cbeaf46 100644 --- a/src/pool/srv_layout.c +++ b/src/pool/srv_layout.c @@ -1,5 +1,6 @@ /* * (C) Copyright 2017-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -133,7 +134,7 @@ struct daos_prop_entry pool_prop_entries_default[DAOS_PROP_PO_NUM] = { }, { .dpe_type = DAOS_PROP_PO_OBJ_VERSION, - .dpe_val = DS_POOL_OBJ_VERSION, + .dpe_val = DAOS_POOL_OBJ_VERSION, }, { .dpe_type = DAOS_PROP_PO_PERF_DOMAIN, diff --git a/src/pool/srv_pool.c b/src/pool/srv_pool.c index 4e0f5db7e4f..96b3729a2cd 100644 --- a/src/pool/srv_pool.c +++ b/src/pool/srv_pool.c @@ -666,7 +666,7 @@ pool_prop_write(struct rdb_tx *tx, const rdb_path_t *kvs, daos_prop_t *prop) rc = rdb_tx_update(tx, kvs, &ds_pool_prop_svc_redun_fac, &value); break; case DAOS_PROP_PO_OBJ_VERSION: - if (entry->dpe_val > DS_POOL_OBJ_VERSION) { + if (entry->dpe_val > DAOS_POOL_OBJ_VERSION) { rc = -DER_INVAL; break; } @@ -2352,7 +2352,7 @@ pool_svc_step_up_cb(struct ds_rsvc *rsvc) struct pool_iv_conns *iv_hdls = NULL; bool cont_svc_up = false; bool events_initialized = false; - d_rank_t rank = dss_self_rank(); + d_rank_t rank = dss_self_rank(); int rc; D_ASSERTF(svc->ps_error == 0, "ps_error: " DF_RC "\n", DP_RC(svc->ps_error)); @@ -2461,7 +2461,7 @@ pool_svc_step_up_cb(struct ds_rsvc *rsvc) if (rc != 0) goto out; - rc = ds_rebuild_regenerate_task(svc->ps_pool, prop); + rc = ds_rebuild_regenerate_task(svc->ps_pool, prop, 0); if (rc != 0) goto out; @@ -6126,7 +6126,7 @@ __ds_pool_mark_upgrade_completed(uuid_t pool_uuid, struct pool_svc *svc, int rc) obj_version = (uint32_t)fail_val; } else { - obj_version = DS_POOL_OBJ_VERSION; + obj_version = DAOS_POOL_OBJ_VERSION; } d_iov_set(&value, &obj_version, sizeof(obj_version)); @@ -6187,10 +6187,9 @@ pool_check_upgrade_object_layout(struct rdb_tx *tx, struct pool_svc *svc, else if (rc == -DER_NONEXIST) current_layout_ver = 0; - if (current_layout_ver < DS_POOL_OBJ_VERSION) { - rc = ds_rebuild_schedule(svc->ps_pool, svc->ps_pool->sp_map_version, - upgrade_eph, DS_POOL_OBJ_VERSION, NULL, - RB_OP_UPGRADE, 0); + if (current_layout_ver < DAOS_POOL_OBJ_VERSION) { + rc = ds_rebuild_schedule(svc->ps_pool, svc->ps_pool->sp_map_version, upgrade_eph, + DAOS_POOL_OBJ_VERSION, NULL, RB_OP_UPGRADE, 0); if (rc == 0) *scheduled_layout_upgrade = true; } diff --git a/src/pool/srv_target.c b/src/pool/srv_target.c index 082bab6cb40..46c7fadaeb4 100644 --- a/src/pool/srv_target.c +++ b/src/pool/srv_target.c @@ -2441,8 +2441,13 @@ cont_discard_cb(daos_handle_t ih, vos_iter_entry_t *entry, put: ds_cont_child_put(cont); + /* don't destroy vos container, to avoid ds_cont_tgt_refresh_agg_eph() failure, + * later depend on container recovery process to handle it. + */ +#if 0 if (rc == 0) rc = ds_cont_child_destroy(arg->tgt_discard->pool_uuid, entry->ie_couuid); +#endif return rc; } @@ -2603,7 +2608,7 @@ ds_pool_task_collective(uuid_t pool_uuid, uint32_t ex_status, int (*coll_func)(v } /* Discard the objects by epoch in this pool */ -static void +static int ds_pool_tgt_discard_ult(void *data) { struct ds_pool *pool; @@ -2630,6 +2635,7 @@ ds_pool_tgt_discard_ult(void *data) ds_pool_put(pool); free: tgt_discard_arg_free(arg); + return rc; } void @@ -2662,7 +2668,7 @@ ds_pool_tgt_discard_handler(crt_rpc_t *rpc) pool->sp_need_discard = 1; pool->sp_discard_status = 0; - rc = dss_ult_create(ds_pool_tgt_discard_ult, arg, DSS_XS_SYS, 0, 0, NULL); + rc = dss_ult_execute(ds_pool_tgt_discard_ult, arg, NULL, NULL, DSS_XS_SYS, 0, 0); ds_pool_put(pool); out: diff --git a/src/rebuild/srv.c b/src/rebuild/srv.c index 76971149ef8..817dda33ac4 100644 --- a/src/rebuild/srv.c +++ b/src/rebuild/srv.c @@ -1601,8 +1601,7 @@ rebuild_task_ult(void *arg) cur_ts = daos_gettime_coarse(); D_ASSERT(task->dst_schedule_time != (uint64_t)-1); if (cur_ts < task->dst_schedule_time) { - D_DEBUG(DB_REBUILD, "rebuild task sleep "DF_U64" second\n", - task->dst_schedule_time - cur_ts); + D_INFO("rebuild task sleep " DF_U64 " second\n", task->dst_schedule_time - cur_ts); dss_sleep((task->dst_schedule_time - cur_ts) * 1000); } @@ -1613,6 +1612,13 @@ rebuild_task_ult(void *arg) D_GOTO(out_task, rc = -DER_NONEXIST); } + rc = ds_cont_svc_refresh_agg_eph(task->dst_pool_uuid); + if (rc) { + D_ERROR(DF_UUID ": ds_cont_svc_refresh_agg_eph failed, " DF_RC "\n", + DP_UUID(task->dst_pool_uuid), DP_RC(rc)); + goto out_pool; + } + while (1) { /* Check if the leader pool map has been synced to all other targets * to avoid -DER_GRP error. @@ -2180,7 +2186,7 @@ regenerate_task_of_type(struct ds_pool *pool, pool_comp_state_t match_states, ui /* Regenerate the rebuild tasks when changing the leader. */ int -ds_rebuild_regenerate_task(struct ds_pool *pool, daos_prop_t *prop) +ds_rebuild_regenerate_task(struct ds_pool *pool, daos_prop_t *prop, uint64_t delay_sec) { struct daos_prop_entry *entry; char *env; @@ -2206,12 +2212,13 @@ ds_rebuild_regenerate_task(struct ds_pool *pool, daos_prop_t *prop) entry = daos_prop_entry_get(prop, DAOS_PROP_PO_SELF_HEAL); D_ASSERT(entry != NULL); if (entry->dpe_val & (DAOS_SELF_HEAL_AUTO_REBUILD | DAOS_SELF_HEAL_DELAY_REBUILD)) { - rc = regenerate_task_of_type(pool, PO_COMP_ST_DOWN, - entry->dpe_val & DAOS_SELF_HEAL_DELAY_REBUILD ? -1 : 0); + rc = regenerate_task_of_type( + pool, PO_COMP_ST_DOWN, + entry->dpe_val & DAOS_SELF_HEAL_DELAY_REBUILD ? -1 : delay_sec); if (rc != 0) return rc; - rc = regenerate_task_of_type(pool, PO_COMP_ST_DRAIN, 0); + rc = regenerate_task_of_type(pool, PO_COMP_ST_DRAIN, delay_sec); if (rc != 0) return rc; } else { @@ -2229,7 +2236,7 @@ ds_rebuild_regenerate_task(struct ds_pool *pool, daos_prop_t *prop) * discarding on an empty targets is harmless. So it is ok to use REINT to * do EXTEND here. */ - rc = regenerate_task_of_type(pool, PO_COMP_ST_UP, 0); + rc = regenerate_task_of_type(pool, PO_COMP_ST_UP, delay_sec); if (rc != 0) return rc; diff --git a/src/tests/ftest/pool/create_capacity.yaml b/src/tests/ftest/pool/create_capacity.yaml index 9ba2c5652fe..1eee1e71e6a 100644 --- a/src/tests/ftest/pool/create_capacity.yaml +++ b/src/tests/ftest/pool/create_capacity.yaml @@ -9,7 +9,7 @@ timeouts: server_config: name: daos_server engines_per_host: 2 - restart_duration: 120 + restart_duration: 140 engines: 0: pinned_numa_node: 0 diff --git a/src/tests/suite/daos_obj_array.c b/src/tests/suite/daos_obj_array.c index 00742f54991..858b70e6ff2 100644 --- a/src/tests/suite/daos_obj_array.c +++ b/src/tests/suite/daos_obj_array.c @@ -1729,6 +1729,83 @@ array_recx_write_mixed_iov_read_single(void **state) daos_obj_close(oh, NULL); } +#define ONE_KB (1024) +#define TWO_MB (2 * 1024 * 1024) + +void +test_sgl_merge_short_read(void **state) +{ + test_arg_t *arg = *state; + daos_obj_id_t oid; + daos_handle_t oh; + d_iov_t dkey; + d_sg_list_t sgl; + daos_iod_t iod; + daos_recx_t recx; + char *wbuf, *rbuf; + d_iov_t *sg_iovs = NULL; + int total_iovs = TWO_MB / ONE_KB; + int data_offset = 0; + int rc, i; + + oid = daos_test_oid_gen(arg->coh, OC_SX, 0, 0, arg->myrank); + rc = daos_obj_open(arg->coh, oid, DAOS_OO_RW, &oh, NULL); + assert_rc_equal(rc, 0); + + /* Buffer allocation with guard pages */ + wbuf = malloc(ONE_MB); + assert_true(wbuf != NULL); + dts_buf_render(wbuf, ONE_MB); + + /* Allocate 2MB read buffer with guard pages */ + rbuf = calloc(1, TWO_MB + FOUR_KB); + assert_true(rbuf != NULL); + + /* Build scatter-gather list for 1KB chunks */ + sg_iovs = calloc(total_iovs, sizeof(d_iov_t)); + assert_true(sg_iovs != NULL); + + /* 1MB data read in 1KB chunks */ + for (i = 0; i < total_iovs; i++) { + d_iov_set(&sg_iovs[data_offset], rbuf + i * ONE_KB, ONE_KB); + data_offset++; + } + + /* Configure scatter-gather list */ + sgl = (d_sg_list_t){.sg_nr = total_iovs, .sg_nr_out = 0, .sg_iovs = sg_iovs}; + + /* Prepare I/O descriptor */ + d_iov_set(&dkey, "short_key", 9); + d_iov_set(&iod.iod_name, "big_akey", 8); + iod.iod_nr = 1; + iod.iod_size = 1; + iod.iod_type = DAOS_IOD_ARRAY; + iod.iod_recxs = &recx; + recx.rx_idx = 0; + recx.rx_nr = ONE_MB - 4; + + /* Write initial data with single IOV */ + d_sg_list_t write_sgl = { + .sg_nr = 1, + .sg_iovs = &(d_iov_t){.iov_buf = wbuf, .iov_len = ONE_MB - 4, .iov_buf_len = ONE_MB}}; + rc = daos_obj_update(oh, DAOS_TX_NONE, 0, &dkey, 1, &iod, &write_sgl, NULL); + assert_rc_equal(rc, 0); + + /* Read back using 1KB chunks */ + rc = daos_obj_fetch(oh, DAOS_TX_NONE, 0, &dkey, 1, &iod, &sgl, NULL, NULL); + assert_rc_equal(rc, 0); + assert_rc_equal(sgl.sg_nr_out, 1024); + assert_rc_equal(sg_iovs[1023].iov_len, 1020); + + /* Verify only 1MB - 4 was read successfully */ + assert_memory_equal(wbuf, rbuf, ONE_MB - 4); + + free(wbuf); + free(rbuf); + free(sg_iovs); + daos_obj_close(oh, NULL); +} + static const struct CMUnitTest array_tests[] = { {"ARRAY0: small_sgl", small_sgl, NULL, test_case_teardown}, {"ARRAY1: byte array with buffer on stack", byte_array_simple_stack, NULL, test_case_teardown}, @@ -1759,6 +1836,7 @@ static const struct CMUnitTest array_tests[] = { {"ARRAY20: recx read mixed iov", array_recx_read_mixed_iov, NULL, test_case_teardown}, {"ARRAY21: recx write mixed iov", array_recx_write_mixed_iov_read_single, NULL, test_case_teardown}, + {"ARRAY22: recx short read", test_sgl_merge_short_read, NULL, test_case_teardown}, }; static int diff --git a/src/tests/suite/daos_rebuild_common.c b/src/tests/suite/daos_rebuild_common.c index 799b52fede8..18017191280 100644 --- a/src/tests/suite/daos_rebuild_common.c +++ b/src/tests/suite/daos_rebuild_common.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2016-2023 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -624,19 +625,24 @@ void make_buffer(char *buffer, char start, int total) static void write_ec(struct ioreq *req, int index, char *data, daos_off_t off, int size) { - char key[32]; - daos_recx_t recx; - int i; - char single_data[LARGE_SINGLE_VALUE_SIZE]; + char key[32]; + daos_recx_t recx; + int small_size = 5; + int i; + char *single_data; + + assert_true(small_size <= size); + + D_ALLOC(single_data, LARGE_SINGLE_VALUE_SIZE); + assert_non_null(single_data); for (i = 0; i < KEY_NR; i++) { req->iod_type = DAOS_IOD_ARRAY; sprintf(key, "dkey_small_%d", index); - recx.rx_nr = 5; + recx.rx_nr = small_size; recx.rx_idx = off + i * 10485760; - insert_recxs(key, "a_key", 1, DAOS_TX_NONE, &recx, 1, - data, size, req); + insert_recxs(key, "a_key", 1, DAOS_TX_NONE, &recx, 1, data, small_size, req); sprintf(key, "dkey_%d", index); recx.rx_nr = size; @@ -658,6 +664,8 @@ write_ec(struct ioreq *req, int index, char *data, daos_off_t off, int size) insert_single(key, "a_key", 0, single_data, LARGE_SINGLE_VALUE_SIZE, DAOS_TX_NONE, req); } + + D_FREE(single_data); } static void diff --git a/src/tests/suite/dfs_unit_test.c b/src/tests/suite/dfs_unit_test.c index 84c77453e88..cb2ad322f89 100644 --- a/src/tests/suite/dfs_unit_test.c +++ b/src/tests/suite/dfs_unit_test.c @@ -2475,6 +2475,58 @@ dfs_test_oclass_hints(void **state) rc = daos_cont_destroy(arg->pool.poh, "oc_cont2", 0, NULL); assert_success(rc); + /** create container with RF = 3 */ + print_message("DFS object class hints with container RF3:\n"); + prop->dpp_entries[0].dpe_type = DAOS_PROP_CO_REDUN_FAC; + prop->dpp_entries[0].dpe_val = DAOS_PROP_CO_REDUN_RF3; + rc = dfs_cont_create_with_label(arg->pool.poh, "oc_cont3", &dattr, NULL, &coh, &dfs_l); + assert_int_equal(rc, 0); + + /** set the expect EC object class ID based on domain nr */ + if (attr.pa_domain_nr >= 22) + ecidx = OC_EC_16P3GX; + else if (attr.pa_domain_nr >= 14) + ecidx = OC_EC_8P3GX; + else if (attr.pa_domain_nr >= 10) + ecidx = OC_EC_4P3GX; + else + ecidx = OC_RP_4GX; + + rc = dfs_suggest_oclass(dfs_l, "file:single", &cid); + assert_int_equal(rc, 0); + daos_oclass_id2name(cid, oclass_name); + print_message("oclass suggested for \"file:single\" = %s\n", oclass_name); + rc = compare_oclass(coh, cid, OC_RP_4G1); + assert_rc_equal(rc, 0); + + rc = dfs_suggest_oclass(dfs_l, "File:max", &cid); + assert_int_equal(rc, 0); + daos_oclass_id2name(cid, oclass_name); + print_message("oclass suggested for \"File:max\" = %s\n", oclass_name); + rc = compare_oclass(coh, cid, ecidx); + assert_rc_equal(rc, 0); + + rc = dfs_suggest_oclass(dfs_l, "dir:single", &cid); + assert_int_equal(rc, 0); + daos_oclass_id2name(cid, oclass_name); + print_message("oclass suggested for \"dir:single\" = %s\n", oclass_name); + rc = compare_oclass(coh, cid, OC_RP_4G1); + assert_rc_equal(rc, 0); + + rc = dfs_suggest_oclass(dfs_l, "Directory:max", &cid); + assert_int_equal(rc, 0); + daos_oclass_id2name(cid, oclass_name); + print_message("oclass suggested for \"Directory:max\" = %s\n", oclass_name); + rc = compare_oclass(coh, cid, OC_RP_4GX); + assert_rc_equal(rc, 0); + + rc = dfs_umount(dfs_l); + assert_int_equal(rc, 0); + rc = daos_cont_close(coh, NULL); + assert_success(rc); + rc = daos_cont_destroy(arg->pool.poh, "oc_cont3", 0, NULL); + assert_success(rc); + daos_prop_free(prop); } diff --git a/src/vos/vos_io.c b/src/vos/vos_io.c index 8615fb56b47..1b25ffb1015 100644 --- a/src/vos/vos_io.c +++ b/src/vos/vos_io.c @@ -1,5 +1,6 @@ /** * (C) Copyright 2018-2024 Intel Corporation. + * (C) Copyright 2025 Hewlett Packard Enterprise Development LP * * SPDX-License-Identifier: BSD-2-Clause-Patent */ @@ -1007,14 +1008,44 @@ akey_fetch_recx(daos_handle_t toh, const daos_epoch_range_t *epr, holes += lo - index; } - /* Hole extent, with_shadow case only used for EC obj */ - if (bio_addr_is_hole(&ent->en_addr) || - (with_shadow && (ent->en_epoch < shadow_ep))) { + if ((with_shadow && (ent->en_epoch < shadow_ep))) { index = lo + nr; holes += nr; continue; } + /* Hole extent, with_shadow case only used for EC obj */ + if (bio_addr_is_hole(&ent->en_addr)) { + if (with_shadow) { + if (holes != 0) { + /* process the prev hole */ + rc = save_recx(ioc, lo - holes, holes, shadow_ep, inob, + DRT_SHADOW); + if (rc != 0) + goto failed; + biov_set_hole(&biov, holes * inob); + /* skip the hole */ + rc = iod_fetch(ioc, &biov); + if (rc != 0) + goto failed; + holes = 0; + } + /* process this punch ext, for degraded fetch just skip it */ + index = lo + nr; + holes = nr; + biov_set_hole(&biov, holes * inob); + /* skip the hole */ + rc = iod_fetch(ioc, &biov); + if (rc != 0) + goto failed; + holes = 0; + } else { + index = lo + nr; + holes += nr; + } + continue; + } + if (holes != 0) { if (with_shadow) { rc = save_recx(ioc, lo - holes, holes, diff --git a/utils/node_local_test.py b/utils/node_local_test.py index 62271c25fe6..03d31d940ab 100755 --- a/utils/node_local_test.py +++ b/utils/node_local_test.py @@ -1309,7 +1309,8 @@ class DFuse(): # pylint: disable-next=too-many-arguments def __init__(self, daos, conf, pool=None, container=None, mount_path=None, uns_path=None, - caching=True, wbcache=True, multi_user=False, ro=False): + caching=True, wbcache=True, multi_user=False, ro=False, dump_h=False, + read_h=False, file_h=None): if mount_path: self.dir = mount_path else: @@ -1334,6 +1335,9 @@ def __init__(self, daos, conf, pool=None, container=None, mount_path=None, uns_p self.log_mask = None self.log_file = None self._ro = ro + self.dump_h = dump_h + self.read_h = read_h + self.file_h = file_h self.valgrind = None if not os.path.exists(self.dir): @@ -1406,6 +1410,11 @@ def start(self, v_hint=None, use_oopt=False): if not self.wbcache: cmd.append('--disable-wb-cache') + if self.dump_h: + cmd.extend(['--dump-handles', self.file_h]) + if self.read_h: + cmd.extend(['--read-handles', self.file_h]) + if self._ro: cmd.append('--read-only') @@ -2408,15 +2417,29 @@ def test_pre_read(self): def test_two_mounts(self): """Create two mounts, and check that a file created in one can be read from the other""" + + try: + fd, tmpfile = tempfile.mkstemp(prefix="my_temp_file_", dir="/tmp") + print(f"Created temp file: {tmpfile}") + os.close(fd) + + except OSError as e: + print(f"mkstemp failed: {e}", file=sys.stderr) + sys.exit(1) + dfuse0 = DFuse(self.server, self.conf, caching=False, + dump_h=True, + file_h=tmpfile, container=self.container) dfuse0.start(v_hint='two_0') dfuse1 = DFuse(self.server, self.conf, caching=True, + read_h=True, + file_h=tmpfile, container=self.container) dfuse1.start(v_hint='two_1') @@ -2432,10 +2455,10 @@ def test_two_mounts(self): with open(file0, 'w') as fd: fd.write('test') - if dfuse0.stop(): - self.fatal_errors = True if dfuse1.stop(): self.fatal_errors = True + if dfuse0.stop(): + self.fatal_errors = True def test_cache_expire(self): """Check that data and readdir cache expire correctly diff --git a/utils/trivy/.trivyignore b/utils/trivy/.trivyignore index 2a44fd66369..c3452b8f4fa 100644 --- a/utils/trivy/.trivyignore +++ b/utils/trivy/.trivyignore @@ -1,34 +1,12 @@ -## Ignored hadoop 3.3.6 related CVE -## CVE-2023-52428,MEDIUM,,"Denial of Service in Connect2id Nimbus JOSE+JWT","com.nimbusds:nimbus-jose-jwt","9.8.1","9.37.2",https://avd.aquasec.com/nvd/cve-2023-52428 -CVE-2023-52428 -## CVE-2023-39410,HIGH,7.5,"apache-avro: Apache Avro Java SDK: Memory when deserializing untrusted data in Avro Java SDK","org.apache.avro:avro","1.7.7","1.11.3",https://avd.aquasec.com/nvd/cve-2023-39410 -CVE-2023-39410 -## CVE-2024-25710,HIGH,5.5,"commons-compress: Denial of service caused by an infinite loop for a corrupted DUMP file","org.apache.commons:commons-compress","1.21","1.26.0",https://avd.aquasec.com/nvd/cve-2024-25710 -CVE-2024-25710 -## CVE-2024-26308,HIGH,5.5,"commons-compress: OutOfMemoryError unpacking broken Pack200 file","org.apache.commons:commons-compress","1.21","1.26.0",https://avd.aquasec.com/nvd/cve-2024-26308 -CVE-2024-26308 -## CVE-2024-29131,MEDIUM,,"commons-configuration: StackOverflowError adding property in AbstractListDelimiterHandler.flattenIterator()","org.apache.commons:commons-configuration2","2.8.0","2.10.1",https://avd.aquasec.com/nvd/cve-2024-29131 -CVE-2024-29131 -## CVE-2024-29133,MEDIUM,,"commons-configuration: StackOverflowError calling ListDelimiterHandler.flatten(Object, int) with a cyclical object tree","org.apache.commons:commons-configuration2","2.8.0","2.10.1",https://avd.aquasec.com/nvd/cve-2024-29133 -CVE-2024-29133 -## CVE-2024-25638,HIGH,,"dnsjava: Improper response validation allowing DNSSEC bypass","dnsjava:dnsjava","2.1.7","3.6.0",https://avd.aquasec.com/nvd/cve-2024-25638 -CVE-2024-25638 +### Ignored hadoop 3.4.2 related CVEs +## CVE-2025-52999,HIGH,,"com.fasterxml.jackson.core/jackson-core: jackson-core Potential StackoverflowError","com.fasterxml.jackson.core:jackson-core","2.10.2","2.15.0",https://avd.aquasec.com/nvd/cve-2025-52999 +CVE-2025-52999 -## Ignored hadoop 3.4.0 related CVE -## CVE-2024-47561,CRITICAL,,"apache-avro: Schema parsing may trigger Remote Code Execution (RCE)","org.apache.avro:avro","1.9.2","1.11.4",https://avd.aquasec.com/nvd/cve-2024-47561 -CVE-2024-47561 -## CVE-2023-33201,MEDIUM,5.3,"bouncycastle: potential blind LDAP injection attack using a self-signed certificate","org.bouncycastle:bcprov-jdk15on","1.70","",https://avd.aquasec.com/nvd/cve-2023-33201 -CVE-2023-33201 -## CVE-2024-29857,MEDIUM,,"org.bouncycastle: Importing an EC certificate with crafted F2m parameters may lead to Denial of Service","org.bouncycastle:bcprov-jdk15on","1.70","1.78",https://avd.aquasec.com/nvd/cve-2024-29857 -CVE-2024-29857 -## CVE-2024-30171,MEDIUM,,"bc-java: BouncyCastle vulnerable to a timing variant of Bleichenbacher (Marvin Attack)","org.bouncycastle:bcprov-jdk15on","1.70","1.78",https://avd.aquasec.com/nvd/cve-2024-30171 -CVE-2024-30171 -## CVE-2024-30172,MEDIUM,,"org.bouncycastle:bcprov-jdk18on: Infinite loop in ED25519 verification in the ScalarUtil class","org.bouncycastle:bcprov-jdk15on","1.70","1.78",https://avd.aquasec.com/nvd/cve-2024-30172 -CVE-2024-30172 +## CVE-2025-53864,MEDIUM,,"com.nimbusds/nimbus-jose-jwt: Uncontrolled recursion in Connect2id Nimbus JOSE + JWT","com.nimbusds:nimbus-jose-jwt","9.37.2","10.0.2",https://avd.aquasec.com/nvd/cve-2025-53864 +CVE-2025-53864 -## Ignored io.netty:netty-buffer:4.1.115.Final and hadoop-common:3.4.0 CVE -## CVE-2025-25193,MEDIUM,,"Netty, an asynchronous, event-driven network application framework, ha ...","io.netty:netty-common","4.1.115.Final","",https://avd.aquasec.com/nvd/cve-2025-25193 -CVE-2025-25193 -## Ignored hadoop 3.4.0 related CVE -## CVE-2025-24970,HIGH,,"io.netty:netty-handler: SslHandler doesn't correctly validate packets which can lead to native crash when using native SSLEngine","io.netty:netty-handler","4.1.100.Final","4.1.118.Final",https://avd.aquasec.com/nvd/cve-2025-24970 -CVE-2025-24970 +## CVE-2025-48924,MEDIUM,,"commons-lang/commons-lang: org.apache.commons/commons-lang3: Uncontrolled Recursion vulnerability in Apache Commons Lang","org.apache.commons:commons-lang3","3.12.0","3.18.0",https://avd.aquasec.com/nvd/cve-2025-48924 +CVE-2025-48924 + +## CVE-2025-58057,MEDIUM,7.5,"netty-codec: netty-codec-compression: Netty's BrotliDecoder is vulnerable to DoS via zip bomb style attack","io.netty:netty-codec","4.1.100.Final","4.1.125.Final",https://avd.aquasec.com/nvd/cve-2025-58057 +CVE-2025-58057 diff --git a/utils/trivy/trivy.yaml b/utils/trivy/trivy.yaml index c6d9974456d..5ac0b5a86c7 100644 --- a/utils/trivy/trivy.yaml +++ b/utils/trivy/trivy.yaml @@ -19,7 +19,7 @@ db: no-progress: false repository: ghcr.io/aquasecurity/trivy-db skip-update: false -debug: true +debug: false dependency-tree: true exit-code: 0 generate-default-config: false @@ -228,6 +228,7 @@ scan: # ignore all hadoop dependencies skip-dirs: ./src/client/java/hadoop-daos + ./build skip-files: [] show-suppressed: true secret: