9ns: Fix devtab function pointer signatures
[akaros.git] / kern / drivers / dev / eventfd.c
1 /* Copyright (c) 2015 Google Inc
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * #eventfd device, the kernel-side implementation of man 2 eventfd.
6  *
7  * Unlike the Linux interface, which takes host-endian u64s, we read and write
8  * strings.  It's a little slower, but it maintains the distributed-system
9  * nature of Plan 9 devices. */
10
11 #include <ns.h>
12 #include <kmalloc.h>
13 #include <kref.h>
14 #include <atomic.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <sys/queue.h>
20 #include <fdtap.h>
21 #include <syscall.h>
22
23 struct dev efd_devtab;
24
25 static char *devname(void)
26 {
27         return efd_devtab.name;
28 }
29
30 enum {
31         Qdir,
32         Qctl,
33         Qefd,
34 };
35
36 static struct dirtab efd_dir[] = {
37         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0555},
38         {"ctl", {Qctl, 0, QTFILE}, 0, 0666},
39         {"efd", {Qefd, 0, QTFILE}, 8, 0666},
40 };
41
42 enum {
43         EFD_SEMAPHORE =                         1 << 0,
44         EFD_MAX_VAL =                           (unsigned long)(-2), // i.e. 0xfffffffffffffffe
45 };
46
47
48 struct eventfd {
49         int                                             flags;
50         atomic_t                                        counter;
51         struct fdtap_slist                      fd_taps;
52         spinlock_t                                      tap_lock;
53         struct rendez                           rv_readers;
54         struct rendez                           rv_writers;
55         struct kref                                     refcnt;
56 };
57
58
59 static void efd_release(struct kref *kref)
60 {
61         struct eventfd *efd = container_of(kref, struct eventfd, refcnt);
62         /* All FDs with taps should be closed before we decreffed all the chans */
63         assert(SLIST_EMPTY(&efd->fd_taps));
64         kfree(efd);
65 }
66
67 static struct chan *efd_attach(char *spec)
68 {
69         struct chan *c;
70         struct eventfd *efd;
71
72         c = devattach(devname(), spec);
73         efd = kzmalloc(sizeof(struct eventfd), MEM_WAIT);
74         SLIST_INIT(&efd->fd_taps);
75         spinlock_init(&efd->tap_lock);
76         rendez_init(&efd->rv_readers);
77         rendez_init(&efd->rv_writers);
78         /* Attach and walk are the two sources of chans.  Each returns a refcnt'd
79          * object, for the most part. */
80         kref_init(&efd->refcnt, efd_release, 1);
81         /* nothing special in the qid to ID this eventfd.  the main thing is the
82          * aux.  we could put a debugging ID in the path like pipe. */
83         mkqid(&c->qid, Qdir, 0, QTDIR);
84         c->aux = efd;
85         /* just to be fancy and remove a syscall, if they pass spec == "sem", then
86          * we'll treat them as being in semaphore mode. */
87         if (!strcmp(spec, "sem"))
88                 efd->flags |= EFD_SEMAPHORE;
89         return c;
90 }
91
92 static struct walkqid *efd_walk(struct chan *c, struct chan *nc, char **name,
93                                                                 unsigned int nname)
94 {
95         struct walkqid *wq;
96         struct eventfd *efd = c->aux;
97
98         wq = devwalk(c, nc, name, nname, efd_dir, ARRAY_SIZE(efd_dir), devgen);
99         /* Walk is a source of a distinct chan from this device.  The other source
100          * is attach.  Once created, these chans will eventually be closed, and when
101          * they close, they will decref their aux, efd.  All chans within this
102          * *instance* of eventfd share the same efd.  Each one will have one refcnt.
103          * Each chan may also have several copies of its pointer out there (e.g. FD
104          * dup), all of which have their own *chan* refcnt.
105          *
106          * All of the above applies on successful walks that found all nname parts
107          * of the path.  A mid-success is wq: we got something.  wq->clone means we
108          * got to the end and the "big walk" considers this a success.
109          *
110          * There is a slight chance the new chan is the same as our original chan
111          * (if nc == c when we're called).  In which case, there's only one chan.
112          * The number of refs on efd == the number of distinct chans within this
113          * instance of #eventfd. */
114         if (wq != NULL && wq->clone != NULL && wq->clone != c)
115                 kref_get(&efd->refcnt, 1);
116         return wq;
117 }
118
119 /* In the future, we could use stat / wstat to get and set O_NONBLOCK */
120 static size_t efd_stat(struct chan *c, uint8_t *db, size_t n)
121 {
122         return devstat(c, db, n, efd_dir, ARRAY_SIZE(efd_dir), devgen);
123 }
124
125 static struct chan *efd_open(struct chan *c, int omode)
126 {
127         return devopen(c, omode, efd_dir, ARRAY_SIZE(efd_dir), devgen);
128 }
129
130 static void efd_close(struct chan *c)
131 {
132         struct eventfd *efd = c->aux;
133         /* Here's where we put the ref from attach and successful walks */
134         kref_put(&efd->refcnt);
135 }
136
137 static void efd_fire_taps(struct eventfd *efd, int filter)
138 {
139         struct fd_tap *tap_i;
140         if (SLIST_EMPTY(&efd->fd_taps))
141                 return;
142         /* We're not expecting many FD taps, so it's not worth splitting readers
143          * from writers or anything like that.
144          * TODO: (RCU) Locking to protect the list and the tap's existence. */
145         spin_lock(&efd->tap_lock);
146         SLIST_FOREACH(tap_i, &efd->fd_taps, link)
147                 fire_tap(tap_i, filter);
148         spin_unlock(&efd->tap_lock);
149 }
150
151 static int has_counts(void *arg)
152 {
153         struct eventfd *efd = arg;
154         return atomic_read(&efd->counter) != 0;
155 }
156
157 /* The heart of reading an eventfd */
158 static unsigned long efd_read_efd(struct eventfd *efd, struct chan *c)
159 {
160         unsigned long old_count, new_count, ret;
161         while (1) {
162                 old_count = atomic_read(&efd->counter);
163                 if (!old_count) {
164                         if (c->flag & O_NONBLOCK)
165                                 error(EAGAIN, "Would block on #%s read", devname());
166                         rendez_sleep(&efd->rv_readers, has_counts, efd);
167                 } else {
168                         if (efd->flags & EFD_SEMAPHORE) {
169                                 new_count = old_count - 1;
170                                 ret = 1;
171                         } else {
172                                 new_count = 0;
173                                 ret = old_count;
174                         }
175                         if (atomic_cas(&efd->counter, old_count, new_count))
176                                 goto success;
177                 }
178         }
179 success:
180         rendez_wakeup(&efd->rv_writers);
181         efd_fire_taps(efd, FDTAP_FILT_WRITABLE);
182         return ret;
183 }
184
185 static size_t efd_read(struct chan *c, void *ubuf, size_t n, off64_t offset)
186 {
187         struct eventfd *efd = c->aux;
188
189         switch (c->qid.path) {
190                 case Qdir:
191                         return devdirread(c, ubuf, n, efd_dir, ARRAY_SIZE(efd_dir),
192                                                           devgen);
193                 case Qctl:
194                         return readnum(offset, ubuf, n, efd->flags, NUMSIZE32);
195                 case Qefd:
196                         /* ignoring the chan offset for Qefd */
197                         return readnum(0, ubuf, n, efd_read_efd(efd, c),
198                                                    NUMSIZE64);
199                 default:
200                         panic("Bad Qid %p!", c->qid.path);
201         }
202         return -1;
203 }
204
205 static int has_room(void *arg)
206 {
207         struct eventfd *efd = arg;
208         return atomic_read(&efd->counter) != EFD_MAX_VAL;
209 }
210
211 /* The heart of writing an eventfd */
212 static void efd_write_efd(struct eventfd *efd, unsigned long add_to,
213                           struct chan *c)
214 {
215         unsigned long old_count, new_count;
216         while (1) {
217                 old_count = atomic_read(&efd->counter);
218                 new_count = old_count + add_to;
219                 if (new_count > EFD_MAX_VAL) {
220                         if (c->flag & O_NONBLOCK)
221                                 error(EAGAIN, "Would block on #%s write", devname());
222                         rendez_sleep(&efd->rv_writers, has_room, efd);
223                 } else {
224                         if (atomic_cas(&efd->counter, old_count, new_count))
225                                 goto success;
226                 }
227         }
228 success:
229         rendez_wakeup(&efd->rv_readers);
230         efd_fire_taps(efd, FDTAP_FILT_READABLE);
231 }
232
233 static size_t efd_write(struct chan *c, void *ubuf, size_t n, off64_t offset)
234 {
235         struct eventfd *efd = c->aux;
236         unsigned long write_val;
237         char num64[NUMSIZE64];
238
239         switch (c->qid.path) {
240                 case Qctl:
241                         /* If we want to allow runtime changing of settings, we can do it
242                          * here. */
243                         error(EFAIL, "No #%s ctl commands supported", devname());
244                         break;
245                 case Qefd:
246                         /* We want to give strtoul a null-terminated buf (can't handle
247                          * arbitrary user strings).  Ignoring the chan offset too. */
248                         if (n > sizeof(num64))
249                                 error(EAGAIN, "attempted to write %d chars, max %d", n,
250                                           sizeof(num64));
251                         memcpy(num64, ubuf, n);
252                         num64[n] = 0;   /* enforce trailing 0 */
253                         write_val = strtoul(num64, 0, 0);
254                         if (write_val == (unsigned long)(-1))
255                                 error(EFAIL, "Eventfd write must not be -1");
256                         efd_write_efd(efd, write_val, c);
257                         break;
258                 default:
259                         panic("Bad Qid %p!", c->qid.path);
260         }
261         return n;
262 }
263
264 static char *efd_chaninfo(struct chan *c, char *ret, size_t ret_l)
265 {
266         struct eventfd *efd = c->aux;
267
268         snprintf(ret, ret_l, "QID type %s, flags %p, counter %p",
269                  efd_dir[c->qid.path].name, efd->flags, atomic_read(&efd->counter));
270         return ret;
271 }
272
273 static int efd_tapfd(struct chan *c, struct fd_tap *tap, int cmd)
274 {
275         struct eventfd *efd = c->aux;
276         int ret;
277
278         /* HANGUP, ERROR, and PRIORITY will never fire, but people can ask for them.
279          * We don't actually support HANGUP, but epoll implies it.  Linux's eventfd
280          * cand have ERROR, so apps can ask for it.  Likewise, priority is
281          * meaningless for us, but sometimes people ask for it. */
282         #define EFD_LEGAL_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_WRITABLE |        \
283                                 FDTAP_FILT_HANGUP | FDTAP_FILT_PRIORITY |          \
284                                 FDTAP_FILT_ERROR)
285
286         switch (c->qid.path) {
287                 case Qefd:
288                         if (tap->filter & ~EFD_LEGAL_TAPS) {
289                                 set_error(ENOSYS, "Unsupported #%s tap, must be %p", devname(),
290                                                   EFD_LEGAL_TAPS);
291                                 return -1;
292                         }
293                         spin_lock(&efd->tap_lock);
294                         switch (cmd) {
295                                 case (FDTAP_CMD_ADD):
296                                         SLIST_INSERT_HEAD(&efd->fd_taps, tap, link);
297                                         ret = 0;
298                                         break;
299                                 case (FDTAP_CMD_REM):
300                                         SLIST_REMOVE(&efd->fd_taps, tap, fd_tap, link);
301                                         ret = 0;
302                                         break;
303                                 default:
304                                         set_error(ENOSYS, "Unsupported #%s tap command %p",
305                                                           devname(), cmd);
306                                         ret = -1;
307                         }
308                         spin_unlock(&efd->tap_lock);
309                         return ret;
310                 default:
311                         set_error(ENOSYS, "Can't tap #%s file type %d", devname(),
312                                   c->qid.path);
313                         return -1;
314         }
315 }
316
317 struct dev efd_devtab __devtab = {
318         .name = "eventfd",
319         .reset = devreset,
320         .init = devinit,
321         .shutdown = devshutdown,
322         .attach = efd_attach,
323         .walk = efd_walk,
324         .stat = efd_stat,
325         .open = efd_open,
326         .create = devcreate,
327         .close = efd_close,
328         .read = efd_read,
329         .bread = devbread,
330         .write = efd_write,
331         .bwrite = devbwrite,
332         .remove = devremove,
333         .wstat = devwstat,
334         .power = devpower,
335         .chaninfo = efd_chaninfo,
336         .tapfd = efd_tapfd,
337 };