1 /* SPDX-License-Identifier: Apache-2.0
2 * Copyright (c) 2025 Cisco Systems, Inc.
6 #include <vlib/unix/unix.h>
9 #include <sys/eventfd.h>
12 VLIB_REGISTER_LOG_CLASS (vlib_file_log, static) = {
14 .subclass_name = "file",
17 #define log_debug(fmt, ...) \
18 vlib_log_debug (vlib_file_log.class, fmt, __VA_ARGS__)
19 #define log_warn(fmt, ...) \
20 vlib_log_warn (vlib_file_log.class, fmt, __VA_ARGS__)
21 #define log_err(fmt, ...) vlib_log_err (vlib_file_log.class, fmt, __VA_ARGS__)
23 clib_file_main_t file_main;
26 vlib_file_update (clib_file_t *f, clib_file_update_type_t update_type)
28 vlib_main_t *vm = vlib_get_main_by_index (f->polling_thread_index);
29 int op = -1, add_del = 0;
31 struct epoll_event e = {
36 if (f->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE)
38 if (f->flags & UNIX_FILE_EVENT_EDGE_TRIGGERED)
43 case UNIX_FILE_UPDATE_ADD:
48 case UNIX_FILE_UPDATE_MODIFY:
52 case UNIX_FILE_UPDATE_DELETE:
58 log_err ("%s: unknown update_type %d", __func__, update_type);
62 if (epoll_ctl (vm->epoll_fd, op, (int) f->file_descriptor, &e) < 0)
64 log_err ("%s: epoll_ctl() failed, errno %d", __func__, errno);
68 vm->n_epoll_fds += add_del;
72 wake_read_fn (struct clib_file *f)
74 u64 val, __clib_unused rv;
75 rv = read ((int) f->file_descriptor, &val, sizeof (u64));
80 vlib_file_poll_init (vlib_main_t *vm)
82 vm->epoll_fd = epoll_create (1);
85 clib_panic ("failed to initialize epoll for thread %u", vm->thread_index);
87 vm->wakeup_fd = eventfd (0, EFD_NONBLOCK);
89 if (vm->wakeup_fd < 0)
90 clib_panic ("failed to initialize wakeup event for thread %u",
93 if (!file_main.file_update)
94 file_main.file_update = vlib_file_update;
96 clib_file_add (&file_main, &(clib_file_t){
97 .polling_thread_index = vm->thread_index,
98 .file_descriptor = vm->wakeup_fd,
99 .description = format (0, "wakeup thread %u",
101 .read_function = wake_read_fn,
106 vlib_file_poll (vlib_main_t *vm)
108 vlib_node_main_t *nm = &vm->node_main;
109 unix_main_t *um = &unix_main;
110 struct epoll_event *e, epoll_events[16];
112 int is_main = (vm->thread_index == 0);
113 int timeout_ms = 0, max_timeout_ms = 10;
117 * If we've been asked for a fixed-sleep between main loop polls,
120 if (PREDICT_FALSE (is_main && um->poll_sleep_usec))
122 struct timespec ts, tsrem;
124 ts.tv_nsec = 1000L * um->poll_sleep_usec;
126 while (nanosleep (&ts, &tsrem) < 0)
132 /* we are busy, skip some loops before polling again */
133 if (vlib_last_vectors_per_main_loop (vm) >= 2)
136 /* at least one node is polling */
137 if (nm->input_node_counts_by_state[VLIB_NODE_STATE_POLLING])
140 /* pending APIs in the queue */
141 if (is_main && vm->api_queue_nonempty)
146 if (*vlib_worker_threads->wait_at_barrier)
149 if (vlib_get_first_main ()->time_last_barrier_release + 0.5 >=
154 /* check for pending interrupts */
155 for (int nt = 0; nt < VLIB_N_NODE_TYPE; nt++)
156 if (nm->node_interrupts[nt] &&
157 clib_interrupt_is_any_pending (nm->node_interrupts[nt]))
160 /* at this point we know that thread is going to sleep, so let's annonce
161 * to other threads that they need to wakeup us if they need our attention */
162 __atomic_store_n (&vm->thread_sleeps, 1, __ATOMIC_RELAXED);
164 ticks = vlib_tw_timer_first_expires_in_ticks (vm);
166 if (ticks != TW_SLOTS_PER_RING)
168 timeout_ms = (int) (ticks / ((u32) VLIB_TW_TICKS_PER_SECOND / 1000));
169 timeout_ms = clib_min (timeout_ms, max_timeout_ms);
172 timeout_ms = max_timeout_ms;
177 /* Don't come back for a respectable number of dispatch cycles */
178 vm->file_poll_skip_loops = 1024;
181 n_fds_ready = epoll_wait (vm->epoll_fd, epoll_events,
182 ARRAY_LEN (epoll_events), timeout_ms);
184 __atomic_store_n (&vm->thread_sleeps, 0, __ATOMIC_RELAXED);
185 __atomic_store_n (&vm->wakeup_pending, 0, __ATOMIC_RELAXED);
189 if (unix_error_is_fatal (errno))
190 vlib_panic_with_error (vm, clib_error_return_unix (0, "epoll_wait"));
192 /* non fatal error (e.g. EINTR). */
196 vm->epoll_waits += 1;
197 vm->epoll_files_ready += n_fds_ready;
199 for (e = epoll_events; e < epoll_events + n_fds_ready; e++)
201 clib_file_t *f = e->data.ptr;
204 if (PREDICT_FALSE (!f->active))
206 foreach_int (flag, EPOLLIN, EPOLLOUT, EPOLLERR)
207 if (e->events & flag)
209 const char *str[] = {
210 [EPOLLIN] = "EPOLLIN",
211 [EPOLLOUT] = "EPOLLOUT",
212 [EPOLLERR] = "EPOLLERR",
214 log_debug ("epoll event %s dropped due to inactive file",
219 else if (PREDICT_TRUE (!(e->events & EPOLLERR)))
221 if (e->events & EPOLLIN)
224 err = f->read_function (f);
227 log_err ("file read error: %U", format_clib_error, err);
228 clib_error_free (err);
231 if (e->events & EPOLLOUT)
234 err = f->write_function (f);
237 log_err ("file write error: %U", format_clib_error, err);
238 clib_error_free (err);
244 if (f->error_function)
247 err = f->error_function (f);
250 log_err ("file error: %U", format_clib_error, err);
251 clib_error_free (err);
254 else if (f->dont_close == 0)
255 close ((int) f->file_descriptor);
259 /* maximum epoll events received, there may be more ... */
260 if (n_fds_ready == ARRAY_LEN (epoll_events))
266 /* removing fd from epoll instance doesn't remove event from epoll queue
267 * so we need to be sure epoll queue is empty before freeing */
268 clib_file_free_deleted (&file_main, vm->thread_index);
271 static clib_error_t *
272 show_files (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
274 clib_error_t *error = 0;
275 clib_file_main_t *fm = &file_main;
279 vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread",
280 "Read", "Write", "Error", "File Name", "Description");
282 pool_foreach_pointer (f, fm->file_pool)
285 s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0);
286 rv = readlink ((char *) s, path, PATH_MAX - 1);
288 path[rv < 0 ? 0 : rv] = 0;
290 vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v",
291 f->file_descriptor, f->polling_thread_index,
292 f->read_events, f->write_events, f->error_events, path,
294 vec_reset_length (s);
301 VLIB_CLI_COMMAND (cli_show_files, static) = {
302 .path = "show files",
303 .short_help = "Show files in use",
304 .function = show_files,