diff --git a/bundle.json b/bundle.json index ded14f6a75e1871877a533a53154379aa192f90d..5fdf841c8772ed99bb04b4c5aeada36e9e1e01fb 100644 --- a/bundle.json +++ b/bundle.json @@ -45,7 +45,8 @@ "napi", "samgr", "app_file_service", - "os_account" + "os_account", + "liburing" ], "third_party": [ "e2fsprogs", @@ -59,7 +60,8 @@ "//foundation/filemanagement/file_api/interfaces/kits/js:build_kits_js", "//foundation/filemanagement/file_api/interfaces/kits/ts/streamrw:streamrw_packages", "//foundation/filemanagement/file_api/interfaces/kits/ts/streamhash:streamhash_packages", - "//foundation/filemanagement/file_api/interfaces/kits/cj:fs_ffi_packages" + "//foundation/filemanagement/file_api/interfaces/kits/cj:fs_ffi_packages", + "//foundation/filemanagement/file_api/interfaces/kits/hyperaio:group_hyperaio" ], "service_group": [] }, @@ -73,6 +75,15 @@ "header_base": "//foundation/filemanagement/file_api/interfaces/kits/native/remote_uri" } }, + { + "name": "//foundation/filemanagement/file_api/interfaces/kits/hyperaio:HyperAio", + "header": { + "header_files": [ + "hyperaio.h" + ], + "header_base": "//foundation/filemanagement/file_api/interfaces/kits/hyperaio/include" + } + }, { "name": "//foundation/filemanagement/file_api/interfaces/kits/native:environment_native", "header": { diff --git a/interfaces/kits/hyperaio/BUILD.gn b/interfaces/kits/hyperaio/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..ce621d125ce6a4457328d3465f9a74440d813f33 --- /dev/null +++ b/interfaces/kits/hyperaio/BUILD.gn @@ -0,0 +1,65 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("//foundation/filemanagement/file_api/file_api.gni") + +group("group_hyperaio") { + deps = [ + ":HyperAio", + ] +} + +config("hyperaio_config") { + visibility = [ ":*" ] + include_dirs = [ "include" ] +} + +ohos_shared_library("HyperAio") { + if (!use_mingw_win && !use_mac) { + cflags = [ + "-fdata-sections", + "-ffunction-sections", + "-Oz", + ] + cflags_cc = [ + "-fvisibility-inlines-hidden", + "-Oz", + ] + branch_protector_ret = "pac_ret" + sanitize = { + integer_overflow = true + ubsan = true + boundary_sanitize = true + cfi = true + cfi_cross_dso = true + debug = false + } + } + + sources = [ "src/hyperaio.cpp" ] + + public_configs = [ ":hyperaio_config" ] + + deps = [ "${utils_path}/filemgmt_libhilog:filemgmt_libhilog"] + + external_deps = [ + "c_utils:utils", + "liburing:liburing", + "hilog:libhilog", + ] + + innerapi_tags = [ "platformsdk" ] + part_name = "file_api" + subsystem_name = "filemanagement" +} \ No newline at end of file diff --git a/interfaces/kits/hyperaio/include/hyperaio.h b/interfaces/kits/hyperaio/include/hyperaio.h new file mode 100644 index 0000000000000000000000000000000000000000..cb3b84b09be1a5e7bfd80c3f29692b2dd912c73d --- /dev/null +++ b/interfaces/kits/hyperaio/include/hyperaio.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILEMANAGEMENT_FILE_API_INTERFACES_KITS_IOURING_HYPER_AIO_H +#define FILEMANAGEMENT_FILE_API_INTERFACES_KITS_IOURING_HYPER_AIO_H + +#include +#include +#include "liburing.h" + +namespace OHOS { +namespace HyperAio { +#define IOURING_APP_PREMISSION (1U << 0) +using ErrCode = int32_t; +enum HyperAioError : ErrCode { + SUCCESS = 0, + E_NOMEM = -1, + E_INVAL = -2, + E_INIT = -3, + E_DESTROY = -4, + E_CANCEL = -5, + E_OPEN = -6, + E_READ = -7, +}; + +struct ReadInfo { + int32_t fd; + uint32_t len; + uint64_t offset; + void *buf; + uint64_t userData; +}; + +struct ReadReqs { + uint32_t reqNum; + struct ReadInfo *reqs; +}; + +struct OpenInfo { + int32_t dfd; + int32_t flags; + mode_t mode; + void *path; + uint64_t userData; +}; + +struct OpenReqs { + uint32_t reqNum; + struct OpenInfo *reqs; +}; + +struct CancelInfo { + uint64_t userData; + uint64_t targetUserData; +}; + +struct CancelReqs { + uint32_t reqNum; + struct CancelInfo *reqs; +}; + +struct IoResponse { + uint64_t userData; + int32_t res; + uint32_t flags; + IoResponse(uint64_t userData, int32_t res, uint32_t flags) + : userData(userData), + res(res), + flags(flags) { + } +}; + +class HyperAio { +public: + using ProcessIoResultCallBack = std::function)>; + uint32_t SupportIouring(); + int32_t CtxInit(ProcessIoResultCallBack *callBack); + int32_t StartReadReqs(ReadReqs *req); + int32_t StartOpenReqs(OpenReqs *req); + int32_t StartCancelReqs(CancelReqs *req); + int32_t DestroyCtx(); +private: + io_uring uring_; + ProcessIoResultCallBack ioResultCallBack_ = nullptr; + std::thread harvestThread_; + std::atomic stopThread_; + std::atomic initialized_; + void HarvestRes(); + struct io_uring_sqe* GetSqeWithRetry(struct io_uring *ring); +}; +} +} +#endif \ No newline at end of file diff --git a/interfaces/kits/hyperaio/include/libhilog.h b/interfaces/kits/hyperaio/include/libhilog.h new file mode 100644 index 0000000000000000000000000000000000000000..9e882f46b4731ff0a5deaf7f7ea74405a50ac14f --- /dev/null +++ b/interfaces/kits/hyperaio/include/libhilog.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FILEMGMT_LIBHILOG_H +#define FILEMGMT_LIBHILOG_H + +#include "hilog/log.h" + +#include + +namespace OHOS { +#ifndef LOG_DOMAIN +#define LOG_DOMAIN 0xD001600 +#endif + +#ifndef LOG_TAG +#define LOG_TAG "FileManagement" +#endif + +#if defined __FILE_NAME__ +#define FILEMGMT_FILE_NAME __FILE_NAME__ +#else +#include +#define FILEMGMT_FILE_NAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +#define HILOGF(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_FATAL, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGE(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_ERROR, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGW(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_WARN, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGI(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_INFO, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +#define HILOGD(fmt, ...) \ + ((void)HILOG_IMPL(LOG_CORE, LOG_DEBUG, LOG_DOMAIN, LOG_TAG, \ + "[%{public}s:%{public}d->%{public}s] " fmt, FILEMGMT_FILE_NAME, __LINE__, __FUNCTION__, ##__VA_ARGS__)) +} // namespace OHOS + +#endif // FILEMGMT_LIBHILOG_H \ No newline at end of file diff --git a/interfaces/kits/hyperaio/src/hyperaio.cpp b/interfaces/kits/hyperaio/src/hyperaio.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a10b05bc8f99e1602e56116dc4ad6a99b5f5f248 --- /dev/null +++ b/interfaces/kits/hyperaio/src/hyperaio.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hyperaio.h" +#include "libhilog.h" +#include +#include +#include "ipc_skeleton.h" +#include "accesstoken_kit.h" + +namespace OHOS { +namespace HyperAio { +const uint32_t URING_QUEUE_SIZE = 512; +const uint32_t DELAY = 20; +const uint32_t BATCH_SIZE = 128; +const uint32_t RETRIES = 3; + +static bool HasAccessIouringPermission() +{ + Security::AccessToken::AccessTokenID tokenCaller = IPCSkeleton::GetCallingTokenID(); + const std::string permissionName = "ohos.permission.ALLOW_IOURING"; + int32_t res = Security::AccessToken::AccessTokenKit::VerifyAccessToken(tokenCaller, permissionName); + if (res != Security::AccessToken::PermissionState::PERMISSION_GRANTED) { + HILOG("have no ALLOW_IOURING permission, AccessTokenID = %{public}u", tokenCaller); + return false; + } + return true; +} + +uint32_t HyperAio::SupportIouring() +{ + uint32_t flags = 0; + if (HasAccessIouringPermission()) { + flags |= IOURING_APP_PERMISSION; + } + return flags; +} + +int32_t HyperAio::CtxInit(ProcessIoResultCallBack *callBack) +{ + int32_t ret = io_uring_queue_init(URING_QUEUE_SIZE, &uring_, 0); + if (ret < 0) { + HILOGE("init io_uring failed, ret = %{public}d", ret); + return ret; + } + ioResultCallBack_ = *callBack; + stopThread_.store(false); + harvestThread_ = std::thread(&HyperAio::HarvestRes, this); + initialized_.sotre(true); + return HyperAioError::SUCCESS; +} + +struct io_uring_sqe* HyperAio::GetSqeWithRetry(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + for (uint32_t i = 0; i < RETRIES; i++) { + sqe = io_uring_get_sqe(ring); + if (sqe != nullptr) { + return sqe; + } + io_uring_submit(ring); + std::this_thread::sleep_for(std::chrono::milliseconds(DELAY)); + } + return nullptr; +} + +int32_t HyperAio::StartOpenReqs(OpenReqs *req) +{ + if (req == nullptr || req->reqs == nullptr) { + return HyperAioError::E_INVAL; + } + if (!initialized_.load()) { + HILOGE("HyperAio is not initialized"); + return HyperAioError::E_INIT; + } + uint32_t totalReqs = req->reqNum; + for (uint32_t start = 0; start < totalReqs; start += BATCH_SIZE) { + uint32_t end = std::min(totalReqs, start + BATCH_SIZE); + if (end < start) { + HILOGE ("Overflow detected: start + BATCH_SIZE exceeds uint32_t max"); + return HyperAioError::E_INVAL; + } + for (uint32_t i = start; i < end; ++i) { + struct io_uring_sqe *sqe = GetSqeWithRetry(&uring_); + if (sqe == nullptr) { + HILOGE("get sqe failed"); + return HyperAioError::E_NOMEN; + } + struct OpenInfo *openInfo = &req->reqs[i]; + io_uring_sqe_set_data(sqe, reinterpret_cast(openInfo->userData)); + io_uring_prep_openat(sqe, openInfo->dfd, static_cast(openInfo->path), + openInfo->flags, openInfo->mode); + } + int32_t ret = io_uring_submit(&uring_); + if (ret < 0) { + HILOGE("submit open reqs failed, ret = %{public}d", ret); + return ret; + } + } + return HyperAioError::SUCCESS; +} + +int32_t HyperAio::StartReadReqs(ReadReqs *req) +{ + if (req == nullptr || req->reqs == nullptr) { + return HyperAioError::E_INVAL; + } + if (!initialized_.load()) { + HILOGE("HyperAio is not initialized"); + return HyperAioError::E_INIT; + } + uint32_t totalReqs = req->reqNum; + for (uint32_t start = 0; start < totalReqs; start += BATCH_SIZE) { + uint32_t end = std::min(totalReqs, start + BATCH_SIZE); + if (end < start) { + HILOGE ("Overflow detected: start + BATCH_SIZE exceeds uint32_t max"); + return HyperAioError::E_INVAL; + } + for (uint32_t i = start; i < end; ++i) { + struct io_uring_sqe *sqe = GetSqeWithRetry(&uring_); + if (sqe == nullptr) { + HILOGE("get sqe failed"); + return HyperAioError::E_NOMEN; + } + struct ReadInfo *readInfo = &req->reqs[i]; + io_uring_sqe_set_data(sqe, reinterpret_cast(readInfo->userData)); + io_uring_prep_read(sqe, readInfo->fd, readInfo->buf, + readInfo->len, readInfo->offset); + } + int32_t ret = io_uring_submit(&uring_); + if (ret < 0) { + HILOGE("submit open reqs failed, ret = %{public}d", ret); + return ret; + } + } + return HyperAioError::SUCCESS; +} + +int32_t HyperAio::StartCancelReqs(CancelReqs *req) +{ + if (req == nullptr || req->reqs == nullptr) { + return HyperAioError::E_INVAL; + } + if (!initialized_.load()) { + HILOGE("HyperAio is not initialized"); + return HyperAioError::E_INIT; + } + uint32_t totalReqs = req->reqNum; + for (uint32_t start = 0; start < totalReqs; start += BATCH_SIZE) { + uint32_t end = std::min(totalReqs, start + BATCH_SIZE); + if (end < start) { + HILOGE ("Overflow detected: start + BATCH_SIZE exceeds uint32_t max"); + return HyperAioError::E_INVAL; + } + for (uint32_t i = start; i < end; ++i) { + struct io_uring_sqe *sqe = GetSqeWithRetry(&uring_); + if (sqe == nullptr) { + HILOGE("get sqe failed"); + return HyperAioError::E_NOMEN; + } + struct CancelInfo *cancelInfo = &req->reqs[i]; + io_uring_sqe_set_data(sqe, reinterpret_cast(cancelInfo->userData)); + io_uring_prep_cancel(sqe, reinterpret_cast(cancelInfo->targetUserData), 0); + } + int32_t ret = io_uring_submit(&uring_); + if (ret < 0) { + HILOGE("submit open reqs failed, ret = %{public}d", ret); + return ret; + } + } + return HyperAioError::SUCCESS; +} + +void HyperAio::HarvestRes() +{ + while (!stopThread_.load()) { + struct io_uring_cqe *cqe; + int32_t ret = io_uring_wait_cqe(&uring_, &cqe); + if (ret < 0 || cqe == nullptr) { + HILOGI("wait cqe failed, ret = %{public}d", ret); + continue; + } + auto response = std::make_unique(cqe->user_data, cqe->res, cqe->flags); + io_uring_cqe_seen(&uring_, cqe); + if (ioResultCallBack_) { + ioResultCallBack_(std::move(response)); + } + } +} + +int32_t HyperAio::DestroyCtx() +{ + stopThread_.store(true); + if (harvestThread_.joinable()) { + harvestThread_.join(); + } + io_uring_queue_exit(&uring_); + return HyperAioError::SUCCESS; +} +} +} \ No newline at end of file