197870c34SAlex Dewar // SPDX-License-Identifier: GPL-2.0
263ae2a94SJeff Dike /*
3ff6a1798SAnton Ivanov * Copyright (C) 2017 - Cambridge Greys Ltd
4ff6a1798SAnton Ivanov * Copyright (C) 2011 - 2014 Cisco Systems Inc
5fee64d3cSJeff Dike * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
663ae2a94SJeff Dike */
763ae2a94SJeff Dike
863ae2a94SJeff Dike #include <stdlib.h>
963ae2a94SJeff Dike #include <errno.h>
10ff6a1798SAnton Ivanov #include <sys/epoll.h>
1163ae2a94SJeff Dike #include <signal.h>
1263ae2a94SJeff Dike #include <string.h>
1337185b33SAl Viro #include <irq_user.h>
1437185b33SAl Viro #include <os.h>
1537185b33SAl Viro #include <um_malloc.h>
1663ae2a94SJeff Dike
17ff6a1798SAnton Ivanov /* Epoll support */
18ff6a1798SAnton Ivanov
19ff6a1798SAnton Ivanov static int epollfd = -1;
20ff6a1798SAnton Ivanov
21ff6a1798SAnton Ivanov #define MAX_EPOLL_EVENTS 64
22ff6a1798SAnton Ivanov
23ff6a1798SAnton Ivanov static struct epoll_event epoll_events[MAX_EPOLL_EVENTS];
24ff6a1798SAnton Ivanov
25ff6a1798SAnton Ivanov /* Helper to return an Epoll data pointer from an epoll event structure.
26ff6a1798SAnton Ivanov * We need to keep this one on the userspace side to keep includes separate
27f2e62992SJeff Dike */
2863ae2a94SJeff Dike
os_epoll_get_data_pointer(int index)29ff6a1798SAnton Ivanov void *os_epoll_get_data_pointer(int index)
3063ae2a94SJeff Dike {
31ff6a1798SAnton Ivanov return epoll_events[index].data.ptr;
32ff6a1798SAnton Ivanov }
3363ae2a94SJeff Dike
34ff6a1798SAnton Ivanov /* Helper to compare events versus the events in the epoll structure.
35ff6a1798SAnton Ivanov * Same as above - needs to be on the userspace side
36ff6a1798SAnton Ivanov */
37ff6a1798SAnton Ivanov
38ff6a1798SAnton Ivanov
os_epoll_triggered(int index,int events)39ff6a1798SAnton Ivanov int os_epoll_triggered(int index, int events)
40ff6a1798SAnton Ivanov {
41ff6a1798SAnton Ivanov return epoll_events[index].events & events;
42ff6a1798SAnton Ivanov }
43ff6a1798SAnton Ivanov /* Helper to set the event mask.
44ff6a1798SAnton Ivanov * The event mask is opaque to the kernel side, because it does not have
45ff6a1798SAnton Ivanov * access to the right includes/defines for EPOLL constants.
46ff6a1798SAnton Ivanov */
47ff6a1798SAnton Ivanov
os_event_mask(enum um_irq_type irq_type)482fccfcc0SJohannes Berg int os_event_mask(enum um_irq_type irq_type)
49ff6a1798SAnton Ivanov {
50ff6a1798SAnton Ivanov if (irq_type == IRQ_READ)
51e3a01cbeSAnton Ivanov return EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP | EPOLLRDHUP;
52ff6a1798SAnton Ivanov if (irq_type == IRQ_WRITE)
53ff6a1798SAnton Ivanov return EPOLLOUT;
54ff6a1798SAnton Ivanov return 0;
55ff6a1798SAnton Ivanov }
56ff6a1798SAnton Ivanov
57ff6a1798SAnton Ivanov /*
58ff6a1798SAnton Ivanov * Initial Epoll Setup
59ff6a1798SAnton Ivanov */
os_setup_epoll(void)60ff6a1798SAnton Ivanov int os_setup_epoll(void)
61ff6a1798SAnton Ivanov {
62ff6a1798SAnton Ivanov epollfd = epoll_create(MAX_EPOLL_EVENTS);
63ff6a1798SAnton Ivanov return epollfd;
64ff6a1798SAnton Ivanov }
65ff6a1798SAnton Ivanov
66ff6a1798SAnton Ivanov /*
67ff6a1798SAnton Ivanov * Helper to run the actual epoll_wait
68ff6a1798SAnton Ivanov */
os_waiting_for_events_epoll(void)69ff6a1798SAnton Ivanov int os_waiting_for_events_epoll(void)
70ff6a1798SAnton Ivanov {
71ff6a1798SAnton Ivanov int n, err;
72ff6a1798SAnton Ivanov
73ff6a1798SAnton Ivanov n = epoll_wait(epollfd,
74ff6a1798SAnton Ivanov (struct epoll_event *) &epoll_events, MAX_EPOLL_EVENTS, 0);
7563ae2a94SJeff Dike if (n < 0) {
7663ae2a94SJeff Dike err = -errno;
7763ae2a94SJeff Dike if (errno != EINTR)
78ff6a1798SAnton Ivanov printk(
79ff6a1798SAnton Ivanov UM_KERN_ERR "os_waiting_for_events:"
80ff6a1798SAnton Ivanov " epoll returned %d, error = %s\n", n,
81ff6a1798SAnton Ivanov strerror(errno)
82ff6a1798SAnton Ivanov );
8363ae2a94SJeff Dike return err;
8463ae2a94SJeff Dike }
8563ae2a94SJeff Dike return n;
8663ae2a94SJeff Dike }
8763ae2a94SJeff Dike
8863ae2a94SJeff Dike
89fee64d3cSJeff Dike /*
90ff6a1798SAnton Ivanov * Helper to add a fd to epoll
9163ae2a94SJeff Dike */
os_add_epoll_fd(int events,int fd,void * data)92ff6a1798SAnton Ivanov int os_add_epoll_fd(int events, int fd, void *data)
9363ae2a94SJeff Dike {
94ff6a1798SAnton Ivanov struct epoll_event event;
95ff6a1798SAnton Ivanov int result;
96ff6a1798SAnton Ivanov
97ff6a1798SAnton Ivanov event.data.ptr = data;
98ff6a1798SAnton Ivanov event.events = events | EPOLLET;
99ff6a1798SAnton Ivanov result = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
100ff6a1798SAnton Ivanov if ((result) && (errno == EEXIST))
101ff6a1798SAnton Ivanov result = os_mod_epoll_fd(events, fd, data);
102ff6a1798SAnton Ivanov if (result)
103ff6a1798SAnton Ivanov printk("epollctl add err fd %d, %s\n", fd, strerror(errno));
104ff6a1798SAnton Ivanov return result;
10563ae2a94SJeff Dike }
10663ae2a94SJeff Dike
107ff6a1798SAnton Ivanov /*
108ff6a1798SAnton Ivanov * Helper to mod the fd event mask and/or data backreference
109ff6a1798SAnton Ivanov */
os_mod_epoll_fd(int events,int fd,void * data)110ff6a1798SAnton Ivanov int os_mod_epoll_fd(int events, int fd, void *data)
11163ae2a94SJeff Dike {
112ff6a1798SAnton Ivanov struct epoll_event event;
113ff6a1798SAnton Ivanov int result;
114ff6a1798SAnton Ivanov
115ff6a1798SAnton Ivanov event.data.ptr = data;
116ff6a1798SAnton Ivanov event.events = events;
117ff6a1798SAnton Ivanov result = epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
118ff6a1798SAnton Ivanov if (result)
119ff6a1798SAnton Ivanov printk(UM_KERN_ERR
120ff6a1798SAnton Ivanov "epollctl mod err fd %d, %s\n", fd, strerror(errno));
121ff6a1798SAnton Ivanov return result;
122ff6a1798SAnton Ivanov }
123ff6a1798SAnton Ivanov
124ff6a1798SAnton Ivanov /*
125ff6a1798SAnton Ivanov * Helper to delete the epoll fd
126ff6a1798SAnton Ivanov */
os_del_epoll_fd(int fd)127ff6a1798SAnton Ivanov int os_del_epoll_fd(int fd)
128ff6a1798SAnton Ivanov {
129ff6a1798SAnton Ivanov struct epoll_event event;
130ff6a1798SAnton Ivanov /* This is quiet as we use this as IO ON/OFF - so it is often
131ff6a1798SAnton Ivanov * invoked on a non-existent fd
132ff6a1798SAnton Ivanov */
133*a0a9ad95Sye xingchen return epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &event);
13463ae2a94SJeff Dike }
13563ae2a94SJeff Dike
os_set_ioignore(void)13663ae2a94SJeff Dike void os_set_ioignore(void)
13763ae2a94SJeff Dike {
1384b84c69bSJeff Dike signal(SIGIO, SIG_IGN);
13963ae2a94SJeff Dike }
140ff6a1798SAnton Ivanov
os_close_epoll_fd(void)141ff6a1798SAnton Ivanov void os_close_epoll_fd(void)
142ff6a1798SAnton Ivanov {
143ff6a1798SAnton Ivanov /* Needed so we do not leak an fd when rebooting */
144ff6a1798SAnton Ivanov os_close_file(epollfd);
145ff6a1798SAnton Ivanov }
146