1/*
2 * Copyright (c) 2023 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#include "kswapd_observer.h"
16
17#include <cerrno>
18#include <fcntl.h>
19#include <sstream>
20#include <string>
21#include <sys/epoll.h>
22#include <unistd.h>
23
24#include "memmgr_log.h"
25#include "memmgr_ptr_util.h"
26#include "memory_level_constants.h"
27#ifdef USE_PURGEABLE_MEMORY
28#include "purgeable_mem_manager.h"
29#endif
30
31namespace OHOS {
32namespace Memory {
33namespace {
34const std::string TAG = "KswapdObserver";
35}
36
37KswapdObserver::KswapdObserver()
38{
39    MAKE_POINTER(handler_, shared, AppExecFwk::EventHandler, "failed to create event handler", return,
40                 AppExecFwk::EventRunner::Create());
41    HILOGI("handler init success!");
42}
43
44void KswapdObserver::Init()
45{
46    epollfd_ = epoll_create(6); // 6 : max epoll events
47    if (epollfd_ == -1) {
48        HILOGE("epoll_create failed (errno=%{public}d)", errno);
49        return;
50    }
51    HILOGI("epoll_create success epollfd_=<%{public}d>", epollfd_);
52
53    if (!RegisterKswapdListener()) {
54        HILOGE("register kswapd pressure failed!");
55        return;
56    }
57    // call MainLoop at handler thread
58    if (handler_ != nullptr) {
59        handler_->PostImmediateTask([this] { this->MainLoop(); });
60        HILOGD("call MainLoop at handler thread");
61    }
62}
63
64bool KswapdObserver::RegisterKswapdListener()
65{
66    struct epoll_event epollEvent;
67
68    // open file
69    do {
70        kswapdMonitorFd_ = open(KSWAPD_PRESSURE_FILE, O_WRONLY | O_CLOEXEC);
71    } while (kswapdMonitorFd_ == -1 && errno == EINTR);
72    if (kswapdMonitorFd_ < 0) {
73        HILOGE("invalid fd (errno=%{public}d)", errno);
74        return false;
75    }
76
77    epollEvent.events = EPOLLPRI;
78    epollEvent.data.ptr = nullptr;
79    epollEvent.data.fd = kswapdMonitorFd_;
80    if (epoll_ctl(epollfd_, EPOLL_CTL_ADD, kswapdMonitorFd_, &epollEvent) < 0) {
81        close(kswapdMonitorFd_);
82        kswapdMonitorFd_ = -1;
83        HILOGE("failed to add file fd to epoll : errno=%{public}d", errno);
84        return false;
85    }
86    HILOGI("fd for kswapd monitor = %{public}d", kswapdMonitorFd_);
87    return true;
88}
89
90void KswapdObserver::MainLoop(void)
91{
92    struct epoll_event *curEpollEvent;
93
94    while (1) {
95        HILOGD("waiting for epoll event ...");
96        struct epoll_event events[1];
97        int i;
98        int nevents = epoll_wait(epollfd_, events, 1, -1);
99        HILOGD("receive events, num=%{public}d!", nevents);
100        if (nevents == -1) {
101            if (errno == EINTR) {
102                continue;
103            }
104            HILOGE("failed to wait epoll event(errno=%{public}d)", errno);
105            continue;
106        }
107
108        for (i = 0, curEpollEvent = &events[0]; i < nevents; ++i, curEpollEvent++) {
109            if (curEpollEvent->events & EPOLLHUP) {
110                HILOGE("EPOLLHUP in events[%{public}d]", i);
111                HandleEventEpollHup(curEpollEvent);
112                continue;
113            }
114            if (curEpollEvent->events & EPOLLERR) {
115                HILOGE("epoll err in events[%{public}d]", i);
116                continue;
117            }
118            if ((curEpollEvent->events & EPOLLPRI) && curEpollEvent->data.fd == kswapdMonitorFd_) {
119                HandleKswapdReport();
120            }
121        } // end of for
122    } // end of while
123}
124
125void KswapdObserver::HandleEventEpollHup(struct epoll_event *curEpollEvent)
126{
127    if (epoll_ctl(epollfd_, EPOLL_CTL_DEL, curEpollEvent->data.fd, NULL) < 0) {
128        HILOGE("Failed to unmonitor for kswapd, errno=%{public}d", errno);
129    }
130    if (curEpollEvent->data.fd >= 0) {
131        close(curEpollEvent->data.fd);
132    }
133    if (curEpollEvent->data.fd == kswapdMonitorFd_) {
134        kswapdMonitorFd_ = -1;
135    }
136}
137
138void HandleKswapdReport()
139{
140    HILOGD("called");
141#ifdef USE_PURGEABLE_MEMORY
142    SystemMemoryInfo info = {MemorySource::KSWAPD, SystemMemoryLevel::MEMORY_LEVEL_LOW};
143    PurgeableMemManager::GetInstance().NotifyMemoryLevel(info);
144#endif
145}
146
147KswapdObserver::~KswapdObserver()
148{
149    if (kswapdMonitorFd_ >= 0) {
150        epoll_ctl(epollfd_, EPOLL_CTL_DEL, kswapdMonitorFd_, NULL);
151        close(kswapdMonitorFd_);
152    }
153    if (epollfd_ >= 0) {
154        close(epollfd_);
155    }
156}
157} // namespace Memory
158} // namespace OHOS
159