1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <cstdint>
17
18 #include <chrono>
19 #include <memory>
20 #include <mutex>
21 #include <optional>
22 #include <string>
23 #include <utility>
24
25 #include <unistd.h>
26
27 #include "plugin_mgr.h"
28 #include "rme_log_domain.h"
29 #include "event_runner.h"
30 #include "ffrt_inner.h"
31
32 #include "latency_control/inetwork_latency_switcher.h"
33 #include "latency_control/network_latency_controller.h"
34 #include "latency_control/noop_network_latency_switcher.h"
35 #include "latency_control/pmqos_network_latency_switcher.h"
36
37 #undef LOG_TAG
38 #define LOG_TAG "ueaServer-NetworkLatencyController"
39
40 namespace OHOS::ResourceSchedule {
41 namespace {
42 const std::string NET_LATENCY_TIMER_NAME = "netLatTimer";
43 const std::chrono::duration TIMEOUT = std::chrono::seconds(60); // 1 minute timeout
44 }
45
Init()46 void NetworkLatencyController::Init()
47 {
48 // use PMQoS switch if available
49 int err = access(PmqosNetworkLatencySwitcher::PMQOS_PATH.data(), W_OK);
50 if (!err) {
51 RME_LOGI("%{public}s: using pmqos latency switcher", __func__);
52 Init(std::make_unique<PmqosNetworkLatencySwitcher>());
53 return;
54 }
55
56 // Another latency switchers can be implemented if required.
57 // If nothing matched, use default object, which is noop switcher.
58 RME_LOGI("%{public}s: using default latency switcher", __func__);
59 Init(std::make_unique<NoopNetworkLatencySwitcher>());
60 }
61
Init(std::unique_ptr<INetworkLatencySwitcher> sw)62 void NetworkLatencyController::Init(std::unique_ptr<INetworkLatencySwitcher> sw)
63 {
64 {
65 std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
66 ffrtQueue_ = std::make_shared<ffrt::queue>("network_manager_ffrtQueue",
67 ffrt::queue_attr().qos(ffrt::qos_user_interactive));
68 if (ffrtQueue_ == nullptr) {
69 RME_LOGE("%{public}s: failed: cannot allocate event handler", __func__);
70 return;
71 }
72 }
73 std::unique_lock<std::mutex> lk(mtx);
74 switcher = std::move(sw);
75 }
76
HandleRequest(long long value,const std::string & identity)77 void NetworkLatencyController::HandleRequest(long long value, const std::string &identity)
78 {
79 if (!switcher || ffrtQueue_ == nullptr) {
80 RME_LOGE("%{public}s: controller is not initialized", __func__);
81 return;
82 }
83
84 switch (value) {
85 case NETWORK_LATENCY_REQUEST_LOW:
86 HandleAddRequest(identity);
87 break;
88 case NETWORK_LATENCY_REQUEST_NORMAL:
89 HandleDelRequest(identity);
90 break;
91 default:
92 RME_LOGW("%{public}s: invalid value: %{public}lld", __func__, value);
93 return;
94 }
95 }
96
HandleAddRequest(const std::string & identity)97 void NetworkLatencyController::HandleAddRequest(const std::string &identity)
98 {
99 // cancel auto disable task first
100 {
101 std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
102 for (auto iter = taskHandlerMap_.begin(); iter != taskHandlerMap_.end();) {
103 if (iter->first == identity) {
104 ffrtQueue_->cancel(iter->second);
105 taskHandlerMap_.erase(iter++);
106 } else {
107 iter++;
108 }
109 }
110 }
111
112 RME_LOGD("%{public}s: add new request from %{public}s", __func__, identity.c_str());
113 AddRequest(identity);
114
115 // set up the auto disable timer
116 {
117 std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
118 taskHandlerMap_[identity] = ffrtQueue_->submit_h(
119 [this, identity] { AutoDisableTask(identity); },
120 ffrt::task_attr().delay(
121 std::chrono::duration_cast<std::chrono::milliseconds>(TIMEOUT).count() * switchToFfrt_
122 )
123 );
124 }
125 }
126
HandleDelRequest(const std::string & identity)127 void NetworkLatencyController::HandleDelRequest(const std::string &identity)
128 {
129 // cancel auto disable task first
130 {
131 std::unique_lock<ffrt::mutex> autoLock(latencyFfrtMutex_);
132 for (auto iter = taskHandlerMap_.begin(); iter != taskHandlerMap_.end();) {
133 if (iter->first == identity) {
134 ffrtQueue_->cancel(iter->second);
135 taskHandlerMap_.erase(iter++);
136 } else {
137 iter++;
138 }
139 }
140 }
141 RME_LOGD("%{public}s: delete request from %{public}s", __func__, identity.c_str());
142 DelRequest(identity);
143 }
144
AddRequest(const std::string & identity)145 void NetworkLatencyController::AddRequest(const std::string &identity)
146 {
147 std::unique_lock<std::mutex> lk(mtx);
148 bool wasEmpty = requests.empty();
149 requests.insert(identity);
150
151 // check whether it is the first request
152 if (wasEmpty) {
153 RME_LOGD("%{public}s: activating low latency", __func__);
154 switcher->LowLatencyOn();
155 }
156 }
157
DelRequest(const std::string & identity)158 void NetworkLatencyController::DelRequest(const std::string &identity)
159 {
160 std::unique_lock<std::mutex> lk(mtx);
161 bool wasEmpty = requests.empty();
162 requests.erase(identity);
163
164 // check whether is was the last request
165 if (!wasEmpty && requests.empty()) {
166 RME_LOGD("%{public}s: no callers left, restore normal latency", __func__);
167 switcher->LowLatencyOff();
168 }
169 }
170
AutoDisableTask(const std::string & identity)171 void NetworkLatencyController::AutoDisableTask(const std::string &identity)
172 {
173 RME_LOGD("%{public}s: identity %{public}s timed out", __func__, identity.c_str());
174 DelRequest(identity);
175 }
176 } // namespace OHOS::ResourceSchedule
177