493d5d3ae8b5c820fc67f8e85cd36c275edc2d73
[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_thread, 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 = resolve_device(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) resolve_device((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_thread, 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_thread();
156     sched_pass();
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(struct proc_info* proc)
168 {
169     pid_t pid = proc->pid;
170     struct iopoll* ctx = &proc->pollctx;
171     for (int i = 0; i < MAX_POLLER_COUNT; i++) {
172         struct iopoller* poller = ctx->pollers[i];
173         if (poller) {
174             vfs_pclose(poller->file_ref, pid);
175             llist_delete(&poller->evt_listener);
176             vfree(poller);
177         }
178     }
179     
180     vfree(ctx->pollers);
181 }
182
183 void
184 iopoll_wake_pollers(poll_evt_q* pollers_q)
185 {
186     struct iopoller *pos, *n;
187     llist_for_each(pos, n, pollers_q, evt_listener)
188     {
189         struct thread* thread = pos->thread;
190         assert(!proc_terminated(thread));
191         
192         if (proc_hanged(thread)) {
193             resume_thread(thread);
194         }
195     }
196 }
197
198 int
199 iopoll_remove(struct thread* thread, int pld)
200 {
201     struct proc_info* proc = thread->process;
202     struct iopoll* ctx = &proc->pollctx;
203     struct iopoller* poller = ctx->pollers[pld];
204     if (!poller) {
205         return ENOENT;
206     }
207
208     // FIXME vfs locking model need to rethink in the presence of threads
209     vfs_pclose(poller->file_ref, proc->pid);
210     vfree(poller);
211     ctx->pollers[pld] = NULL;
212     ctx->n_poller--;
213
214     return 0;
215 }
216
217 int
218 iopoll_install(struct thread* thread, struct v_fd* fd)
219 {
220     int pld = __alloc_pld();
221     if (pld < 0) {
222         return EMFILE;
223     }
224
225     struct iopoller* iop = valloc(sizeof(struct iopoller));
226     *iop = (struct iopoller){
227         .file_ref = fd->file,
228         .thread = thread,
229     };
230
231     vfs_ref_file(fd->file);
232
233     struct proc_info* proc = thread->process;
234     proc->pollctx.pollers[pld] = iop;
235     proc->pollctx.n_poller++;
236
237     struct device* dev;
238     if ((dev = fd2dev(fd))) {
239         iopoll_listen_on(iop, &dev->pollers);
240     } else {
241         // TODO handle generic file
242     }
243
244     return pld;
245 }
246
247 __DEFINE_LXSYSCALL2(int, pollctl, int, action, sc_va_list, _ap)
248 {
249     int retcode = 0;
250     va_list va;
251
252     convert_valist(&va, _ap);
253     
254     switch (action) {
255         case _SPOLL_ADD: {
256             int* ds = va_arg(va, int*);
257             int nds = va_arg(va, int);
258             retcode = __append_pollers(ds, nds);
259         } break;
260         case _SPOLL_RM: {
261             int pld = va_arg(va, int);
262             retcode = iopoll_remove(current_thread, pld);
263         } break;
264         case _SPOLL_WAIT: {
265             struct poll_info* pinfos = va_arg(va, struct poll_info*);
266             int npinfos = va_arg(va, int);
267             int timeout = va_arg(va, int);
268
269             time_t t1 = clock_systime() + timeout;
270             while (!(retcode == __do_poll_round(pinfos, npinfos))) {
271                 if (timeout >= 0 && t1 < clock_systime()) {
272                     break;
273                 }
274                 __wait_until_event();
275             }
276         } break;
277         case _SPOLL_WAIT_ANY: {
278             struct poll_info* pinfo = va_arg(va, struct poll_info*);
279             int timeout = va_arg(va, int);
280
281             time_t t1 = clock_systime() + timeout;
282             while (!(retcode == __do_poll_all(pinfo))) {
283                 if (timeout >= 0 && t1 < clock_systime()) {
284                     break;
285                 }
286                 __wait_until_event();
287             }
288         } break;
289         default:
290             retcode = EINVAL;
291             break;
292     }
293
294     return DO_STATUS(retcode);
295 }