Coverage Report

Created: 2023-09-25 07:12

/src/open5gs/lib/core/ogs-epoll.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2019 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
    ogs_assert(context->epfd >= 0);
78
79
0
    ogs_notify_init(pollset);
80
0
}
81
82
static void epoll_cleanup(ogs_pollset_t *pollset)
83
0
{
84
0
    struct epoll_context_s *context = NULL;
85
86
0
    ogs_assert(pollset);
87
0
    context = pollset->context;
88
0
    ogs_assert(context);
89
90
0
    ogs_notify_final(pollset);
91
0
    close(context->epfd);
92
0
    ogs_free(context->event_list);
93
0
    ogs_hash_destroy(context->map_hash);
94
95
0
    ogs_free(context);
96
0
}
97
98
static int epoll_add(ogs_poll_t *poll)
99
0
{
100
0
    int rv, op;
101
0
    ogs_pollset_t *pollset = NULL;
102
0
    struct epoll_context_s *context = NULL;
103
0
    struct epoll_map_s *map = NULL;
104
0
    struct epoll_event ee;
105
106
0
    ogs_assert(poll);
107
0
    pollset = poll->pollset;
108
0
    ogs_assert(pollset);
109
0
    context = pollset->context;
110
0
    ogs_assert(context);
111
112
0
    map = ogs_hash_get(context->map_hash, &poll->fd, sizeof(poll->fd));
113
0
    if (!map) {
114
0
        map = ogs_calloc(1, sizeof(*map));
115
0
        if (!map) {
116
0
            ogs_error("ogs_calloc() failed");
117
0
            return OGS_ERROR;
118
0
        }
119
120
0
        op = EPOLL_CTL_ADD;
121
0
        ogs_hash_set(context->map_hash, &poll->fd, sizeof(poll->fd), map);
122
0
    } else {
123
0
        op = EPOLL_CTL_MOD;
124
0
    }
125
126
0
    if (poll->when & OGS_POLLIN)
127
0
        map->read = poll;
128
0
    if (poll->when & OGS_POLLOUT)
129
0
        map->write = poll;
130
131
0
    memset(&ee, 0, sizeof ee);
132
133
0
    ee.events = 0;
134
0
    if (map->read)
135
0
        ee.events |= (EPOLLIN|EPOLLRDHUP);
136
0
    if (map->write)
137
0
        ee.events |= EPOLLOUT;
138
0
    ee.data.fd = poll->fd;
139
140
0
    rv = epoll_ctl(context->epfd, op, poll->fd, &ee);
141
0
    if (rv < 0) {
142
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
143
0
                "epoll_ctl[%d] failed", op);
144
0
        return OGS_ERROR;
145
0
    }
146
147
0
    return OGS_OK;
148
0
}
149
150
static int epoll_remove(ogs_poll_t *poll)
151
0
{
152
0
    int rv, op;
153
0
    ogs_pollset_t *pollset = NULL;
154
0
    struct epoll_context_s *context = NULL;
155
0
    struct epoll_map_s *map = NULL;
156
0
    struct epoll_event ee;
157
158
0
    ogs_assert(poll);
159
0
    pollset = poll->pollset;
160
0
    ogs_assert(pollset);
161
0
    context = pollset->context;
162
0
    ogs_assert(context);
163
164
0
    map = ogs_hash_get(context->map_hash, &poll->fd, sizeof(poll->fd));
165
0
    ogs_assert(map);
166
167
0
    if (poll->when & OGS_POLLIN)
168
0
        map->read = NULL;
169
0
    if (poll->when & OGS_POLLOUT)
170
0
        map->write = NULL;
171
172
0
    memset(&ee, 0, sizeof ee);
173
174
0
    ee.events = 0;
175
0
    if (map->read)
176
0
        ee.events |= (EPOLLIN|EPOLLRDHUP);
177
0
    if (map->write)
178
0
        ee.events |= EPOLLOUT;
179
180
0
    if (map->read || map->write) {
181
0
        op = EPOLL_CTL_MOD;
182
0
        ee.data.fd = poll->fd;
183
0
    } else {
184
0
        op = EPOLL_CTL_DEL;
185
0
        ee.data.fd = INVALID_SOCKET;
186
187
0
        ogs_hash_set(context->map_hash, &poll->fd, sizeof(poll->fd), NULL);
188
0
        ogs_free(map);
189
0
    }
190
191
0
    rv = epoll_ctl(context->epfd, op, poll->fd, &ee);
192
0
    if (rv < 0) {
193
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno,
194
0
                "epoll_remove[%d] failed", op);
195
0
        return OGS_ERROR;
196
0
    }
197
198
0
    return OGS_OK;
199
0
}
200
201
static int epoll_process(ogs_pollset_t *pollset, ogs_time_t timeout)
202
0
{
203
0
    struct epoll_context_s *context = NULL;
204
0
    int num_of_poll;
205
0
    int i;
206
207
0
    ogs_assert(pollset);
208
0
    context = pollset->context;
209
0
    ogs_assert(context);
210
211
0
    num_of_poll = epoll_wait(context->epfd, context->event_list,
212
0
            pollset->capacity,
213
0
            timeout == OGS_INFINITE_TIME ? OGS_INFINITE_TIME :
214
0
                ogs_time_to_msec(timeout));
215
0
    if (num_of_poll < 0) {
216
0
        ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "epoll failed");
217
0
        return OGS_ERROR;
218
0
    } else if (num_of_poll == 0) {
219
0
        return OGS_TIMEUP;
220
0
    }
221
222
0
    for (i = 0; i < num_of_poll; i++) {
223
0
        struct epoll_map_s *map = NULL;
224
0
        uint32_t received;
225
0
        short when = 0;
226
0
        ogs_socket_t fd;
227
228
0
        received = context->event_list[i].events;
229
0
        if (received & EPOLLERR) {
230
        /*
231
         * The libevent library has OGS_POLLOUT turned on in EPOLLERR.
232
         *
233
         * However, SIGPIPE can occur if write() is called
234
         * when the peer connection is closed.
235
         *
236
         * Therefore, Open5GS turns off OGS_POLLOUT
237
         * so that write() cannot be called in case of EPOLLERR.
238
         *
239
         * See also #2411 and #2312
240
         */
241
#if 0
242
            when = OGS_POLLIN|OGS_POLLOUT;
243
#else
244
0
            when = OGS_POLLIN;
245
0
#endif
246
0
        } else if ((received & EPOLLHUP) && !(received & EPOLLRDHUP)) {
247
0
            when = OGS_POLLIN|OGS_POLLOUT;
248
0
        } else {
249
0
            if (received & EPOLLIN) {
250
0
                when |= OGS_POLLIN;
251
0
            }
252
0
            if (received & EPOLLOUT) {
253
0
                when |= OGS_POLLOUT;
254
0
            }
255
0
            if (received & EPOLLRDHUP) {
256
0
                when |= OGS_POLLIN;
257
0
            }
258
0
        }
259
260
0
        if (!when)
261
0
            continue;
262
263
0
        fd = context->event_list[i].data.fd;
264
0
        ogs_assert(fd != INVALID_SOCKET);
265
266
0
        map = ogs_hash_get(context->map_hash, &fd, sizeof(fd));
267
0
        if (!map) continue;
268
269
0
        if (map->read && map->write && map->read == map->write) {
270
0
            map->read->handler(when, map->read->fd, map->read->data);
271
0
        } else {
272
0
            if ((when & OGS_POLLIN) && map->read)
273
0
                map->read->handler(when, map->read->fd, map->read->data);
274
275
            /*
276
             * map->read->handler() can call ogs_remove_epoll()
277
             * So, we need to check map instance
278
             */
279
0
            map = ogs_hash_get(context->map_hash, &fd, sizeof(fd));
280
0
            if (!map) continue;
281
282
0
            if ((when & OGS_POLLOUT) && map->write)
283
0
                map->write->handler(when, map->write->fd, map->write->data);
284
0
        }
285
0
    }
286
    
287
0
    return OGS_OK;
288
0
}