Coverage Report

Created: 2025-10-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/open5gs/lib/core/ogs-epoll.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2019-2025 by Sukchan Lee <acetcom@gmail.com>
3
 *
4
 * This file is part of Open5GS.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#include "core-config-private.h"
21
22
#if HAVE_UNISTD_H
23
#include <unistd.h>
24
#endif
25
26
#include <sys/epoll.h>
27
28
#include "ogs-core.h"
29
#include "ogs-poll-private.h"
30
31
static void epoll_init(ogs_pollset_t *pollset);
32
static void epoll_cleanup(ogs_pollset_t *pollset);
33
static int epoll_add(ogs_poll_t *poll);
34
static int epoll_remove(ogs_poll_t *poll);
35
static int epoll_process(ogs_pollset_t *pollset, ogs_time_t timeout);
36
37
const ogs_pollset_actions_t ogs_epoll_actions = {
38
    epoll_init,
39
    epoll_cleanup,
40
41
    epoll_add,
42
    epoll_remove,
43
    epoll_process,
44
45
    ogs_notify_pollset,
46
};
47
48
struct epoll_map_s {
49
    ogs_poll_t *read;
50
    ogs_poll_t *write;
51
};
52
53
struct epoll_context_s {
54
    int epfd;
55
56
    ogs_hash_t *map_hash;
57
    struct epoll_event *event_list;
58
};
59
60
static void epoll_init(ogs_pollset_t *pollset)
61
0
{
62
0
    struct epoll_context_s *context = NULL;
63
0
    ogs_assert(pollset);
64
65
0
    context = ogs_calloc(1, sizeof *context);
66
0
    ogs_assert(context);
67
0
    pollset->context = context;
68
69
0
    context->event_list = ogs_calloc(
70
0
            pollset->capacity, sizeof(struct epoll_event));
71
0
    ogs_assert(context->event_list);
72
73
0
    context->map_hash = ogs_hash_make();
74
0
    ogs_assert(context->map_hash);
75
76
0
    context->epfd = epoll_create(pollset->capacity);
77
0
    if (context->epfd < 0) {
78
0
        ogs_log_message(OGS_LOG_FATAL, ogs_errno,
79
0
                "epoll_create() failed [%d]", pollset->capacity);
80
0
        ogs_assert_if_reached();
81
0
        return;
82
0
    }
83
84
0
    ogs_notify_init(pollset);
85
0
}
86
87
static void epoll_cleanup(ogs_pollset_t *pollset)
88
0
{
89
0
    struct epoll_context_s *context = NULL;
90
91
0
    ogs_assert(pollset);
92
0
    context = pollset->context;
93
0
    ogs_assert(context);
94
95
0
    ogs_notify_final(pollset);
96
0
    close(context->epfd);
97
0
    ogs_free(context->event_list);
98
0
    ogs_hash_destroy(context->map_hash);
99
100
0
    ogs_free(context);
101
0
}
102
103
static int epoll_add(ogs_poll_t *poll)
104
0
{
105
0
    int rv, op;
106
0
    ogs_pollset_t *pollset = NULL;
107
0
    struct epoll_context_s *context = NULL;
108
0
    struct epoll_map_s *map = NULL;
109
0
    struct epoll_event ee;
110
111
0
    ogs_assert(poll);
112
0
    pollset = poll->pollset;
113
0
    ogs_assert(pollset);
114
0
    context = pollset->context;
115
0
    ogs_assert(context);
116
117
0
    map = ogs_hash_get(context->map_hash, &poll->fd, sizeof(poll->fd));
118
0
    if (!map) {
119
0
        map = ogs_calloc(1, sizeof(*map));
120
0
        if (!map) {
121
0
            ogs_error("ogs_calloc() failed");
122
0
            return OGS_ERROR;
123
0
        }
124
125
0
        op = EPOLL_CTL_ADD;
126
0
        ogs_hash_set(context->map_hash, &poll->fd, sizeof(poll->fd), map);
127
0
    } else {
128
0
        op = EPOLL_CTL_MOD;
129
0
    }
130
131
0
    if (poll->when & OGS_POLLIN)
132
0
        map->read = poll;
133
0
    if (poll->when & OGS_POLLOUT)
134
0
        map->write = poll;
135
136
0
    memset(&ee, 0, sizeof ee);
137
138
0
    ee.events = 0;
139
0
    if (map->read)
140
0
        ee.events |= (EPOLLIN|EPOLLRDHUP);
141
0
    if (map->write)
142
0
        ee.events |= EPOLLOUT;
143
0
    ee.data.fd = poll->fd;
144
145
0
    rv = epoll_ctl(context->epfd, op, poll->fd, &ee);
146
0
    if (rv < 0) {
147
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
148
0
                "epoll_ctl[%d] failed", op);
149
0
        return OGS_ERROR;
150
0
    }
151
152
0
    return OGS_OK;
153
0
}
154
155
static int epoll_remove(ogs_poll_t *poll)
156
0
{
157
0
    int rv, op;
158
0
    ogs_pollset_t *pollset = NULL;
159
0
    struct epoll_context_s *context = NULL;
160
0
    struct epoll_map_s *map = NULL;
161
0
    struct epoll_event ee;
162
163
0
    ogs_assert(poll);
164
0
    pollset = poll->pollset;
165
0
    ogs_assert(pollset);
166
0
    context = pollset->context;
167
0
    ogs_assert(context);
168
169
0
    map = ogs_hash_get(context->map_hash, &poll->fd, sizeof(poll->fd));
170
0
    ogs_assert(map);
171
172
0
    if (poll->when & OGS_POLLIN)
173
0
        map->read = NULL;
174
0
    if (poll->when & OGS_POLLOUT)
175
0
        map->write = NULL;
176
177
0
    memset(&ee, 0, sizeof ee);
178
179
0
    ee.events = 0;
180
0
    if (map->read)
181
0
        ee.events |= (EPOLLIN|EPOLLRDHUP);
182
0
    if (map->write)
183
0
        ee.events |= EPOLLOUT;
184
185
0
    if (map->read || map->write) {
186
0
        op = EPOLL_CTL_MOD;
187
0
        ee.data.fd = poll->fd;
188
0
    } else {
189
0
        op = EPOLL_CTL_DEL;
190
0
        ee.data.fd = INVALID_SOCKET;
191
192
0
        ogs_hash_set(context->map_hash, &poll->fd, sizeof(poll->fd), NULL);
193
0
        ogs_free(map);
194
0
    }
195
196
0
    rv = epoll_ctl(context->epfd, op, poll->fd, &ee);
197
0
    if (rv < 0) {
198
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
199
0
                "epoll_remove[%d] failed", op);
200
0
        return OGS_ERROR;
201
0
    }
202
203
0
    return OGS_OK;
204
0
}
205
206
static int epoll_process(ogs_pollset_t *pollset, ogs_time_t timeout)
207
0
{
208
0
    struct epoll_context_s *context = NULL;
209
0
    int num_of_poll;
210
0
    int i;
211
212
0
    ogs_assert(pollset);
213
0
    context = pollset->context;
214
0
    ogs_assert(context);
215
216
0
    num_of_poll = epoll_wait(context->epfd, context->event_list,
217
0
            pollset->capacity,
218
0
            timeout == OGS_INFINITE_TIME ? OGS_INFINITE_TIME :
219
0
                ogs_time_to_msec(timeout));
220
0
    if (num_of_poll < 0) {
221
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "epoll failed");
222
0
        return OGS_ERROR;
223
0
    } else if (num_of_poll == 0) {
224
0
        return OGS_TIMEUP;
225
0
    }
226
227
0
    for (i = 0; i < num_of_poll; i++) {
228
0
        struct epoll_map_s *map = NULL;
229
0
        uint32_t received;
230
0
        short when = 0;
231
0
        ogs_socket_t fd;
232
233
0
        received = context->event_list[i].events;
234
0
        if (received & EPOLLERR) {
235
0
            when = OGS_POLLIN|OGS_POLLOUT;
236
0
        } else if ((received & EPOLLHUP) && !(received & EPOLLRDHUP)) {
237
0
            when = OGS_POLLIN|OGS_POLLOUT;
238
0
        } else {
239
0
            if (received & EPOLLIN) {
240
0
                when |= OGS_POLLIN;
241
0
            }
242
0
            if (received & EPOLLOUT) {
243
0
                when |= OGS_POLLOUT;
244
0
            }
245
0
            if (received & EPOLLRDHUP) {
246
0
                when |= OGS_POLLIN;
247
0
                when &= ~OGS_POLLOUT;
248
0
            }
249
0
        }
250
251
0
        if (!when)
252
0
            continue;
253
254
0
        fd = context->event_list[i].data.fd;
255
0
        ogs_assert(fd != INVALID_SOCKET);
256
257
0
        map = ogs_hash_get(context->map_hash, &fd, sizeof(fd));
258
0
        if (!map) continue;
259
260
0
        if (map->read && map->write && map->read == map->write) {
261
0
            map->read->handler(when, map->read->fd, map->read->data);
262
0
        } else {
263
0
            if ((when & OGS_POLLIN) && map->read)
264
0
                map->read->handler(when, map->read->fd, map->read->data);
265
266
            /*
267
             * map->read->handler() can call ogs_remove_epoll()
268
             * So, we need to check map instance
269
             */
270
0
            map = ogs_hash_get(context->map_hash, &fd, sizeof(fd));
271
0
            if (!map) continue;
272
273
0
            if ((when & OGS_POLLOUT) && map->write)
274
0
                map->write->handler(when, map->write->fd, map->write->data);
275
0
        }
276
0
    }
277
    
278
0
    return OGS_OK;
279
0
}