Add period and count to #alarm
[akaros.git] / kern / drivers / dev / pipe.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #include <vfs.h>
30 #include <kfs.h>
31 #include <slab.h>
32 #include <kmalloc.h>
33 #include <kref.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <assert.h>
37 #include <error.h>
38 #include <cpio.h>
39 #include <pmap.h>
40 #include <smp.h>
41 #include <ip.h>
42
43 struct dev pipedevtab;
44
45 static char *devname(void)
46 {
47         return pipedevtab.name;
48 }
49
50 typedef struct Pipe Pipe;
51 struct Pipe {
52         qlock_t qlock;
53         Pipe *next;
54         struct kref ref;
55         uint32_t path;
56         struct queue *q[2];
57         int qref[2];
58         struct dirtab *pipedir;
59         char *user;
60         struct fdtap_slist data_taps[2];
61         spinlock_t tap_lock;
62 };
63
64 static struct {
65         spinlock_t lock;
66         uint32_t path;
67         int pipeqsize;
68 } pipealloc;
69
70 enum {
71         Qdir,
72         Qdata0,
73         Qdata1,
74 };
75
76 static
77 struct dirtab pipedir[] = {
78         {".", {Qdir, 0, QTDIR}, 0, DMDIR | 0500},
79         {"data", {Qdata0}, 0, 0660},
80         {"data1", {Qdata1}, 0, 0660},
81 };
82
83 static void freepipe(Pipe * p)
84 {
85         if (p != NULL) {
86                 kfree(p->user);
87                 kfree(p->q[0]);
88                 kfree(p->q[1]);
89                 kfree(p->pipedir);
90                 kfree(p);
91         }
92 }
93
94 static void pipe_release(struct kref *kref)
95 {
96         Pipe *pipe = container_of(kref, Pipe, ref);
97         freepipe(pipe);
98 }
99
100 static void pipeinit(void)
101 {
102         pipealloc.pipeqsize = 32 * 1024;
103 }
104
105 /*
106  *  create a pipe, no streams are created until an open
107  */
108 static struct chan *pipeattach(char *spec)
109 {
110         ERRSTACK(2);
111         Pipe *p;
112         struct chan *c;
113
114         c = devattach(devname(), spec);
115         p = kzmalloc(sizeof(Pipe), 0);
116         if (p == 0)
117                 error(ENOMEM, ERROR_FIXME);
118         if (waserror()) {
119                 freepipe(p);
120                 nexterror();
121         }
122         p->pipedir = kzmalloc(sizeof(pipedir), 0);
123         if (p->pipedir == 0)
124                 error(ENOMEM, ERROR_FIXME);
125         memmove(p->pipedir, pipedir, sizeof(pipedir));
126         kstrdup(&p->user, current->user);
127         kref_init(&p->ref, pipe_release, 1);
128         qlock_init(&p->qlock);
129
130         p->q[0] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0);
131         if (p->q[0] == 0)
132                 error(ENOMEM, ERROR_FIXME);
133         p->q[1] = qopen(pipealloc.pipeqsize, Qcoalesce, 0, 0);
134         if (p->q[1] == 0)
135                 error(ENOMEM, ERROR_FIXME);
136         poperror();
137
138         spin_lock(&(&pipealloc)->lock);
139         p->path = ++pipealloc.path;
140         spin_unlock(&(&pipealloc)->lock);
141
142         c->qid.path = NETQID(2 * p->path, Qdir);
143         c->qid.vers = 0;
144         c->qid.type = QTDIR;
145         c->aux = p;
146         c->dev = 0;
147
148         /* taps. */
149         SLIST_INIT(&p->data_taps[0]);   /* already = 0; set to be futureproof */
150         SLIST_INIT(&p->data_taps[1]);
151         spinlock_init(&p->tap_lock);
152         return c;
153 }
154
155 static int
156 pipegen(struct chan *c, char *unused,
157                 struct dirtab *tab, int ntab, int i, struct dir *dp)
158 {
159         int id, len;
160         struct qid qid;
161         Pipe *p;
162
163         if (i == DEVDOTDOT) {
164                 devdir(c, c->qid, devname(), 0, eve, 0555, dp);
165                 return 1;
166         }
167         i++;    /* skip . */
168         if (tab == 0 || i >= ntab)
169                 return -1;
170         tab += i;
171         p = c->aux;
172         switch (NETTYPE(tab->qid.path)) {
173                 case Qdata0:
174                         len = qlen(p->q[0]);
175                         break;
176                 case Qdata1:
177                         len = qlen(p->q[1]);
178                         break;
179                 default:
180                         len = tab->length;
181                         break;
182         }
183         id = NETID(c->qid.path);
184         qid.path = NETQID(id, tab->qid.path);
185         qid.vers = 0;
186         qid.type = QTFILE;
187         devdir(c, qid, tab->name, len, eve, tab->perm, dp);
188         return 1;
189 }
190
191 static struct walkqid *pipewalk(struct chan *c, struct chan *nc, char **name,
192                                                                 int nname)
193 {
194         struct walkqid *wq;
195         Pipe *p;
196
197         p = c->aux;
198         wq = devwalk(c, nc, name, nname, p->pipedir, ARRAY_SIZE(pipedir), pipegen);
199         if (wq != NULL && wq->clone != NULL && wq->clone != c) {
200                 qlock(&p->qlock);
201                 kref_get(&p->ref, 1);
202                 if (c->flag & COPEN) {
203                         switch (NETTYPE(c->qid.path)) {
204                                 case Qdata0:
205                                         p->qref[0]++;
206                                         break;
207                                 case Qdata1:
208                                         p->qref[1]++;
209                                         break;
210                         }
211                 }
212                 qunlock(&p->qlock);
213         }
214         return wq;
215 }
216
217 static int pipestat(struct chan *c, uint8_t * db, int n)
218 {
219         Pipe *p;
220         struct dir dir;
221         struct dirtab *tab;
222
223         p = c->aux;
224         tab = p->pipedir;
225
226         switch (NETTYPE(c->qid.path)) {
227                 case Qdir:
228                         devdir(c, c->qid, ".", 0, eve, DMDIR | 0555, &dir);
229                         break;
230                 case Qdata0:
231                         devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm,
232                                    &dir);
233                         break;
234                 case Qdata1:
235                         devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm,
236                                    &dir);
237                         break;
238                 default:
239                         panic("pipestat");
240         }
241         n = convD2M(&dir, db, n);
242         if (n < BIT16SZ)
243                 error(ENODATA, ERROR_FIXME);
244         return n;
245 }
246
247 /*
248  *  if the stream doesn't exist, create it
249  */
250 static struct chan *pipeopen(struct chan *c, int omode)
251 {
252         ERRSTACK(2);
253         Pipe *p;
254
255         if (c->qid.type & QTDIR) {
256                 if (omode & O_WRITE)
257                         error(EINVAL, "Can only open directories O_READ, mode is %o oct",
258                                   omode);
259                 c->mode = openmode(omode);
260                 c->flag |= COPEN;
261                 c->offset = 0;
262                 return c;
263         }
264
265         openmode(omode);        /* check it */
266
267         p = c->aux;
268         qlock(&p->qlock);
269         if (waserror()) {
270                 qunlock(&p->qlock);
271                 nexterror();
272         }
273         switch (NETTYPE(c->qid.path)) {
274                 case Qdata0:
275                         devpermcheck(p->user, p->pipedir[1].perm, omode);
276                         p->qref[0]++;
277                         break;
278                 case Qdata1:
279                         devpermcheck(p->user, p->pipedir[2].perm, omode);
280                         p->qref[1]++;
281                         break;
282         }
283         poperror();
284         qunlock(&p->qlock);
285
286         c->mode = openmode(omode);
287         c->flag |= COPEN;
288         c->offset = 0;
289         c->iounit = qiomaxatomic;
290         return c;
291 }
292
293 static void pipeclose(struct chan *c)
294 {
295         Pipe *p;
296
297         p = c->aux;
298         qlock(&p->qlock);
299
300         if (c->flag & COPEN) {
301                 /*
302                  *  closing either side hangs up the stream
303                  */
304                 switch (NETTYPE(c->qid.path)) {
305                         case Qdata0:
306                                 p->qref[0]--;
307                                 if (p->qref[0] == 0) {
308                                         qhangup(p->q[1], 0);
309                                         qclose(p->q[0]);
310                                 }
311                                 break;
312                         case Qdata1:
313                                 p->qref[1]--;
314                                 if (p->qref[1] == 0) {
315                                         qhangup(p->q[0], 0);
316                                         qclose(p->q[1]);
317                                 }
318                                 break;
319                 }
320         }
321
322         /*
323          *  if both sides are closed, they are reusable
324          */
325         if (p->qref[0] == 0 && p->qref[1] == 0) {
326                 qreopen(p->q[0]);
327                 qreopen(p->q[1]);
328         }
329
330         qunlock(&p->qlock);
331         /*
332          *  free the structure on last close
333          */
334         kref_put(&p->ref);
335 }
336
337 static long piperead(struct chan *c, void *va, long n, int64_t ignored)
338 {
339         Pipe *p;
340
341         p = c->aux;
342
343         switch (NETTYPE(c->qid.path)) {
344                 case Qdir:
345                         return devdirread(c, va, n, p->pipedir, ARRAY_SIZE(pipedir),
346                                                           pipegen);
347                 case Qdata0:
348                         if (c->flag & O_NONBLOCK)
349                                 return qread_nonblock(p->q[0], va, n);
350                         else
351                                 return qread(p->q[0], va, n);
352                 case Qdata1:
353                         if (c->flag & O_NONBLOCK)
354                                 return qread_nonblock(p->q[1], va, n);
355                         else
356                                 return qread(p->q[1], va, n);
357                 default:
358                         panic("piperead");
359         }
360         return -1;      /* not reached */
361 }
362
363 static struct block *pipebread(struct chan *c, long n, uint32_t offset)
364 {
365         Pipe *p;
366
367         p = c->aux;
368
369         switch (NETTYPE(c->qid.path)) {
370                 case Qdata0:
371                         if (c->flag & O_NONBLOCK)
372                                 return qbread_nonblock(p->q[0], n);
373                         else
374                                 return qbread(p->q[0], n);
375                 case Qdata1:
376                         if (c->flag & O_NONBLOCK)
377                                 return qbread_nonblock(p->q[1], n);
378                         else
379                                 return qbread(p->q[1], n);
380         }
381
382         return devbread(c, n, offset);
383 }
384
385 /*
386  *  A write to a closed pipe causes an EPIPE error to be thrown.
387  */
388 static long pipewrite(struct chan *c, void *va, long n, int64_t ignored)
389 {
390         ERRSTACK(2);
391         Pipe *p;
392         //Prog *r;
393
394         if (waserror()) {
395                 /* avoid exceptions when pipe is a mounted queue */
396                 if ((c->flag & CMSG) == 0) {
397 /*
398                         r = up->iprog;
399                         if(r != NULL && r->kill == NULL)
400                                 r->kill = "write on closed pipe";
401 */
402                 }
403                 set_errno(EPIPE);
404                 nexterror();
405         }
406
407         p = c->aux;
408
409         switch (NETTYPE(c->qid.path)) {
410                 case Qdata0:
411                         if (c->flag & O_NONBLOCK)
412                                 n = qwrite_nonblock(p->q[1], va, n);
413                         else
414                                 n = qwrite(p->q[1], va, n);
415                         break;
416
417                 case Qdata1:
418                         if (c->flag & O_NONBLOCK)
419                                 n = qwrite_nonblock(p->q[0], va, n);
420                         else
421                                 n = qwrite(p->q[0], va, n);
422                         break;
423
424                 default:
425                         panic("pipewrite");
426         }
427
428         poperror();
429         return n;
430 }
431
432 static long pipebwrite(struct chan *c, struct block *bp, uint32_t junk)
433 {
434         ERRSTACK(2);
435         long n;
436         Pipe *p;
437         //Prog *r;
438
439         if (waserror()) {
440                 /* avoid exceptions when pipe is a mounted queue */
441 /*
442                 if((c->flag & CMSG) == 0) {
443                         r = up->iprog;
444                         if(r != NULL && r->kill == NULL)
445                                 r->kill = "write on closed pipe";
446                 }
447 */
448                 set_errno(EPIPE);
449                 nexterror();
450         }
451
452         p = c->aux;
453         switch (NETTYPE(c->qid.path)) {
454                 case Qdata0:
455                         if (c->flag & O_NONBLOCK)
456                                 n = qbwrite_nonblock(p->q[1], bp);
457                         else
458                                 n = qbwrite(p->q[1], bp);
459                         break;
460
461                 case Qdata1:
462                         if (c->flag & O_NONBLOCK)
463                                 n = qbwrite_nonblock(p->q[0], bp);
464                         else
465                                 n = qbwrite(p->q[0], bp);
466                         break;
467
468                 default:
469                         n = 0;
470                         panic("pipebwrite");
471         }
472
473         poperror();
474         return n;
475 }
476
477 static int pipewstat(struct chan *c, uint8_t *dp, int n)
478 {
479         ERRSTACK(2);
480         struct dir *d;
481         Pipe *p;
482         int d1;
483
484         if (c->qid.type & QTDIR)
485                 error(EPERM, ERROR_FIXME);
486         p = c->aux;
487         if (strcmp(current->user, p->user) != 0)
488                 error(EPERM, ERROR_FIXME);
489         d = kzmalloc(sizeof(*d) + n, 0);
490         if (waserror()) {
491                 kfree(d);
492                 nexterror();
493         }
494         n = convM2D(dp, n, d, (char *)&d[1]);
495         if (n == 0)
496                 error(ENODATA, ERROR_FIXME);
497         d1 = NETTYPE(c->qid.path) == Qdata1;
498         if (!emptystr(d->name)) {
499                 validwstatname(d->name);
500                 if (strlen(d->name) >= KNAMELEN)
501                         error(ENAMETOOLONG, ERROR_FIXME);
502                 if (strncmp(p->pipedir[1 + !d1].name, d->name, KNAMELEN) == 0)
503                         error(EEXIST, ERROR_FIXME);
504                 strncpy(p->pipedir[1 + d1].name, d->name, KNAMELEN);
505         }
506         if (d->mode != ~0UL)
507                 p->pipedir[d1 + 1].perm = d->mode & 0777;
508         poperror();
509         kfree(d);
510         return n;
511 }
512
513 static void pipe_wake_cb(struct queue *q, void *data, int filter)
514 {
515         /* If you allocate structs like this on odd byte boundaries, you
516          * deserve what you get.  */
517         uintptr_t kludge = (uintptr_t) data;
518         int which = kludge & 1;
519         Pipe *p = (Pipe*)(kludge & ~1ULL);
520         struct fd_tap *tap_i;
521
522         spin_lock(&p->tap_lock);
523         SLIST_FOREACH(tap_i, &p->data_taps[which], link)
524                 fire_tap(tap_i, filter);
525         spin_unlock(&p->tap_lock);
526 }
527
528 static int pipetapfd(struct chan *chan, struct fd_tap *tap, int cmd)
529 {
530         int ret;
531         Pipe *p;
532         int which = 1;
533         uint64_t kludge;
534
535         p = chan->aux;
536         kludge = (uint64_t)p;
537 #define DEVPIPE_LEGAL_DATA_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_WRITABLE | \
538                                  FDTAP_FILT_HANGUP | FDTAP_FILT_ERROR)
539
540         switch (NETTYPE(chan->qid.path)) {
541         case Qdata0:
542                 which = 0;
543                 /* fall through */
544         case Qdata1:
545                 kludge |= which;
546
547                 if (tap->filter & ~DEVPIPE_LEGAL_DATA_TAPS) {
548                         set_errno(ENOSYS);
549                         set_errstr("Unsupported #%s data tap %p, must be %p", devname(),
550                                    tap->filter, DEVPIPE_LEGAL_DATA_TAPS);
551                         return -1;
552                 }
553                 spin_lock(&p->tap_lock);
554                 switch (cmd) {
555                 case (FDTAP_CMD_ADD):
556                         if (SLIST_EMPTY(&p->data_taps[which]))
557                                 qio_set_wake_cb(p->q[which], pipe_wake_cb, (void *)kludge);
558                         SLIST_INSERT_HEAD(&p->data_taps[which], tap, link);
559                         ret = 0;
560                         break;
561                 case (FDTAP_CMD_REM):
562                         SLIST_REMOVE(&p->data_taps[which], tap, fd_tap, link);
563                         if (SLIST_EMPTY(&p->data_taps[which]))
564                                 qio_set_wake_cb(p->q[which], 0, (void *)kludge);
565                         ret = 0;
566                         break;
567                 default:
568                         set_errno(ENOSYS);
569                         set_errstr("Unsupported #%s data tap command %p", devname(), cmd);
570                         ret = -1;
571                 }
572                 spin_unlock(&p->tap_lock);
573                 return ret;
574         default:
575                 set_errno(ENOSYS);
576                 set_errstr("Can't tap #%s file type %d", devname(),
577                            NETTYPE(chan->qid.path));
578                 return -1;
579         }
580 }
581
582 struct dev pipedevtab __devtab = {
583         .name = "pipe",
584
585         .reset = devreset,
586         .init = pipeinit,
587         .shutdown = devshutdown,
588         .attach = pipeattach,
589         .walk = pipewalk,
590         .stat = pipestat,
591         .open = pipeopen,
592         .create = devcreate,
593         .close = pipeclose,
594         .read = piperead,
595         .bread = pipebread,
596         .write = pipewrite,
597         .bwrite = pipebwrite,
598         .remove = devremove,
599         .wstat = pipewstat,
600         .power = devpower,
601         .chaninfo = devchaninfo,
602         .tapfd = pipetapfd,
603 };