bf7edd7c645eb760263c8947bd3992fdfa8feda7
[lunaix-os.git] / lunaix-os / kernel / device / poll.c
1 #include <lunaix/clock.h>
2 #include <lunaix/device.h>
3 #include <lunaix/fs.h>
4 #include <lunaix/mm/valloc.h>
5 #include <lunaix/process.h>
6 #include <lunaix/sched.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/syscall.h>
9 #include <lunaix/syscall_utils.h>
10
11 #define MAX_POLLER_COUNT 16
12
13 static inline void
14 current_rmiopoll(int pld)
15 {
16     iopoll_remove(__current->pid, &__current->pollctx, pld);
17 }
18
19 static struct iopoller*
20 iopoll_getpoller(struct iopoll* ctx, int pld)
21 {
22     if (pld < 0 || pld >= MAX_POLLER_COUNT) {
23         return NULL;
24     }
25
26     return ctx->pollers[pld];
27 }
28
29 static inline int
30 __do_poll(struct poll_info* pinfo, int pld)
31 {
32     struct iopoller* poller = iopoll_getpoller(&__current->pollctx, pld);
33     if (!poller) {
34         return 0;
35     }
36
37     struct device* dev;
38     int evt = 0;
39
40     if ((dev = device_cast(poller->file_ref->inode->data))) {
41         dev->ops.poll(dev);
42     } else {
43         // TODO handle generic file
44         /*
45             N.B. In Linux, polling on any of the non-device mapped file cause
46            immediate return of poller, in other words, the I/O signal on file is
47            always active. Which make it no use on monitoring any file
48            modifications. However, polling for such modifications
49            must go through inotify_* API. Which is not that elegant as it breaks
50            the nice consistency that *poll(2) should have. Let see want can we
51            do in Lunaix.
52         */
53     }
54
55     if (evt < 0) {
56         poll_setrevt(pinfo, _POLLERR);
57         goto has_err;
58     }
59
60     if ((evt = poll_checkevt(pinfo, evt))) {
61         poll_setrevt(pinfo, evt);
62         goto has_event;
63     }
64
65     return 0;
66
67 has_err:
68     if ((pinfo->flags & _POLLEE_RM_ON_ERR)) {
69         current_rmiopoll(pld);
70         return 1;
71     }
72
73 has_event:
74     if (!(pinfo->flags & _POLLEE_ALWAYS)) {
75         current_rmiopoll(pld);
76     }
77
78     return 1;
79 }
80
81 static int
82 __do_poll_round(struct poll_info* pinfos, int ninfo)
83 {
84     int nc = 0;
85     struct v_fd* fd_s;
86     struct device* dev;
87     for (int i = 0; i < ninfo; i++) {
88         struct poll_info* pinfo = &pinfos[i];
89         int pld = pinfo->pld;
90
91         if (__do_poll(pinfo, pld)) {
92             nc++;
93         }
94     }
95
96     return nc;
97 }
98
99 static int
100 __do_poll_all(struct poll_info* pinfo)
101 {
102     for (int i = 0; i < MAX_POLLER_COUNT; i++) {
103         if (!__do_poll(pinfo, i)) {
104             continue;
105         }
106
107         pinfo->pld = i;
108         return 1;
109     }
110
111     return 0;
112 }
113
114 #define fd2dev(fd) device_cast((fd)->file->inode->data)
115
116 static int
117 __alloc_pld()
118 {
119     for (size_t i = 0; i < MAX_POLLER_COUNT; i++) {
120         if (!__current->pollctx.pollers[i]) {
121             return i;
122         }
123     }
124
125     return -1;
126 }
127
128 static int
129 __append_pollers(int* ds, int npoller)
130 {
131     int err = 0, nc = 0;
132     struct v_fd* fd_s;
133     for (int i = 0; i < npoller; i++) {
134         int* fd = &ds[i];
135         if ((err = vfs_getfd(*fd, &fd_s))) {
136             *fd = err;
137             nc++;
138             continue;
139         }
140
141         int pld = iopoll_install(__current->pid, &__current->pollctx, fd_s);
142         if (pld < 0) {
143             nc++;
144         }
145
146         *fd = pld;
147     }
148
149     return nc;
150 }
151
152 static void
153 __wait_until_event()
154 {
155     block_current();
156     sched_yieldk();
157 }
158
159 void
160 iopoll_init(struct iopoll* ctx)
161 {
162     ctx->pollers = vzalloc(sizeof(ptr_t) * MAX_POLLER_COUNT);
163     ctx->n_poller = 0;
164 }
165
166 void
167 iopoll_free(pid_t pid, struct iopoll* ctx)
168 {
169     for (int i = 0; i < MAX_POLLER_COUNT; i++) {
170         struct iopoller* poller = ctx->pollers[i];
171         if (poller) {
172             vfs_pclose(poller->file_ref, pid);
173             llist_delete(&poller->evt_listener);
174             vfree(poller);
175         }
176     }
177     
178     vfree(ctx->pollers);
179 }
180
181 void
182 iopoll_wake_pollers(poll_evt_q* pollers_q)
183 {
184     struct iopoller *pos, *n;
185     llist_for_each(pos, n, pollers_q, evt_listener)
186     {
187         struct proc_info* proc = get_process(pos->pid);
188         if (proc_hanged(proc)) {
189             resume_process(proc);
190         }
191
192         assert(!proc_terminated(proc));
193     }
194 }
195
196 int
197 iopoll_remove(pid_t pid, struct iopoll* ctx, int pld)
198 {
199     struct iopoller* poller = ctx->pollers[pld];
200     if (!poller) {
201         return ENOENT;
202     }
203
204     vfs_pclose(poller->file_ref, pid);
205     vfree(poller);
206     ctx->pollers[pld] = NULL;
207     ctx->n_poller--;
208
209     return 0;
210 }
211
212 int
213 iopoll_install(pid_t pid, struct iopoll* pollctx, struct v_fd* fd)
214 {
215     int pld = __alloc_pld();
216     if (pld < 0) {
217         return EMFILE;
218     }
219
220     struct iopoller* iop = valloc(sizeof(struct iopoller));
221     *iop = (struct iopoller){
222         .file_ref = fd->file,
223         .pid = pid,
224     };
225
226     vfs_ref_file(fd->file);
227     __current->pollctx.pollers[pld] = iop;
228     __current->pollctx.n_poller++;
229
230     struct device* dev;
231     if ((dev = fd2dev(fd))) {
232         iopoll_listen_on(iop, &dev->pollers);
233     } else {
234         // TODO handle generic file
235     }
236
237     return pld;
238 }
239
240 __DEFINE_LXSYSCALL2(int, pollctl, int, action, va_list, va)
241 {
242     int retcode = 0;
243     switch (action) {
244         case _SPOLL_ADD: {
245             int* ds = va_arg(va, int*);
246             int nds = va_arg(va, int);
247             retcode = __append_pollers(ds, nds);
248         } break;
249         case _SPOLL_RM: {
250             int pld = va_arg(va, int);
251             retcode = iopoll_remove(__current->pid, &__current->pollctx, pld);
252         } break;
253         case _SPOLL_WAIT: {
254             struct poll_info* pinfos = va_arg(va, struct poll_info*);
255             int npinfos = va_arg(va, int);
256             int timeout = va_arg(va, int);
257
258             time_t t1 = clock_systime() + timeout;
259             while (!(retcode == __do_poll_round(pinfos, npinfos))) {
260                 if (timeout >= 0 && t1 < clock_systime()) {
261                     break;
262                 }
263                 __wait_until_event();
264             }
265         } break;
266         case _SPOLL_WAIT_ANY: {
267             struct poll_info* pinfo = va_arg(va, struct poll_info*);
268             int timeout = va_arg(va, int);
269
270             time_t t1 = clock_systime() + timeout;
271             while (!(retcode == __do_poll_all(pinfo))) {
272                 if (timeout >= 0 && t1 < clock_systime()) {
273                     break;
274                 }
275                 __wait_until_event();
276             }
277         } break;
278         default:
279             retcode = EINVAL;
280             break;
281     }
282
283     return DO_STATUS(retcode);
284 }