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