devpipe from inferno.
[akaros.git] / kern / drivers / dev / pipe.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 typedef struct Pipe     Pipe;
17 struct Pipe
18 {
19         qlock_t qlock;
20         Pipe*   next;
21         struct kref     ref;
22         uint32_t        path;
23         struct queue*   q[2];
24         int     qref[2];
25         struct dirtab   *pipedir;
26         char*   user;
27 };
28
29 static struct
30 {
31         spinlock_t lock;
32         uint32_t        path;
33         int     pipeqsize;      
34 } pipealloc;
35
36 enum
37 {
38         Qdir,
39         Qdata0,
40         Qdata1,
41 };
42
43 static 
44 struct dirtab pipedir[] =
45 {
46         {".",           {Qdir,0,QTDIR}, 0,              DMDIR|0500},
47         {"data",                {Qdata0},       0,                      0660},
48         {"data1",       {Qdata1},       0,                      0660},
49 };
50
51 static void
52 freepipe(Pipe *p)
53 {
54         if(p != NULL){
55                 kfree(p->user);
56                 kfree(p->q[0]);
57                 kfree(p->q[1]);
58                 kfree(p->pipedir);
59                 kfree(p);
60         }
61 }
62
63 static void pipe_release(struct kref *kref)
64 {
65         Pipe *pipe = container_of(kref, Pipe, ref);
66         qunlock(&pipe->qlock);
67         freepipe(pipe);
68 }
69
70 static void
71 pipeinit(void)
72 {
73         pipealloc.pipeqsize = 32*1024;
74 }
75
76 /*
77  *  create a pipe, no streams are created until an open
78  */
79 static struct chan*
80 pipeattach(char *spec)
81 {
82         ERRSTACK(2);
83         Pipe *p;
84         struct chan *c;
85
86         c = devattach('|', spec);
87         p = kzmalloc(sizeof(Pipe), 0);
88         if(p == 0)
89                 error(Enomem);
90         if(waserror()){
91                 freepipe(p);
92                 nexterror();
93         }
94         p->pipedir = kzmalloc(sizeof(pipedir), 0);
95         if (p->pipedir == 0)
96                 error(Enomem);
97         memmove(p->pipedir, pipedir, sizeof(pipedir));
98         kstrdup(&p->user, current->user);
99         kref_init(&p->ref, pipe_release, 1);
100
101         p->q[0] = qopen(pipealloc.pipeqsize, 0, 0, 0);
102         if(p->q[0] == 0)
103                 error(Enomem);
104         p->q[1] = qopen(pipealloc.pipeqsize, 0, 0, 0);
105         if(p->q[1] == 0)
106                 error(Enomem);
107         poperror();
108
109         spin_lock(&(&pipealloc)->lock);
110         p->path = ++pipealloc.path;
111         spin_unlock(&(&pipealloc)->lock);
112
113         c->qid.path = NETQID(2*p->path, Qdir);
114         c->qid.vers = 0;
115         c->qid.type = QTDIR;
116         c->aux = p;
117         c->dev = 0;
118         return c;
119 }
120
121 static int
122 pipegen(struct chan *c, char *unused,
123         struct dirtab *tab, int ntab, int i,
124         struct dir *dp)
125 {
126         int id, len;
127         struct qid qid;
128         Pipe *p;
129
130         if(i == DEVDOTDOT){
131                 devdir(c, c->qid, "#|", 0, eve, 0555, dp);
132                 return 1;
133         }
134         i++;    /* skip . */
135         if(tab==0 || i>=ntab)
136                 return -1;
137         tab += i;
138         p = c->aux;
139         switch(NETTYPE(tab->qid.path)){
140         case Qdata0:
141                 len = qlen(p->q[0]);
142                 break;
143         case Qdata1:
144                 len = qlen(p->q[1]);
145                 break;
146         default:
147                 len = tab->length;
148                 break;
149         }
150         id = NETID(c->qid.path);
151         qid.path = NETQID(id, tab->qid.path);
152         qid.vers = 0;
153         qid.type = QTFILE;
154         devdir(c, qid, tab->name, len, eve, tab->perm, dp);
155         return 1;
156 }
157
158
159 static struct walkqid*
160 pipewalk(struct chan *c, struct chan *nc, char **name, int nname)
161 {
162         struct walkqid *wq;
163         Pipe *p;
164
165         p = c->aux;
166         wq = devwalk(c, nc, name, nname, p->pipedir, ARRAY_SIZE(pipedir), pipegen);
167         if(wq != NULL && wq->clone != NULL && wq->clone != c){
168                 qlock(&p->qlock);
169                 kref_get(&p->ref, 1);
170                 if(c->flag & COPEN){
171                         switch(NETTYPE(c->qid.path)){
172                         case Qdata0:
173                                 p->qref[0]++;
174                                 break;
175                         case Qdata1:
176                                 p->qref[1]++;
177                                 break;
178                         }
179                 }
180                 qunlock(&p->qlock);
181         }
182         return wq;
183 }
184
185 static int
186 pipestat(struct chan *c, uint8_t *db, int n)
187 {
188         Pipe *p;
189         struct dir dir;
190         struct dirtab *tab;
191
192         p = c->aux;
193         tab = p->pipedir;
194
195         switch(NETTYPE(c->qid.path)){
196         case Qdir:
197                 devdir(c, c->qid, ".", 0, eve, DMDIR|0555, &dir);
198                 break;
199         case Qdata0:
200                 devdir(c, c->qid, tab[1].name, qlen(p->q[0]), eve, tab[1].perm, &dir);
201                 break;
202         case Qdata1:
203                 devdir(c, c->qid, tab[2].name, qlen(p->q[1]), eve, tab[2].perm, &dir);
204                 break;
205         default:
206                 panic("pipestat");
207         }
208         n = convD2M(&dir, db, n);
209         if(n < BIT16SZ)
210                 error(Eshortstat);
211         return n;
212 }
213
214 /*
215  *  if the stream doesn't exist, create it
216  */
217 static struct chan*
218 pipeopen(struct chan *c, int omode)
219 {
220         ERRSTACK(2);
221         Pipe *p;
222
223         if(c->qid.type & QTDIR){
224                 if(omode != OREAD)
225                         error(Ebadarg);
226                 c->mode = omode;
227                 c->flag |= COPEN;
228                 c->offset = 0;
229                 return c;
230         }
231
232         openmode(omode);        /* check it */
233
234         p = c->aux;
235         qlock(&p->qlock);
236         if(waserror()){
237                 qunlock(&p->qlock);
238                 nexterror();
239         }
240         switch(NETTYPE(c->qid.path)){
241         case Qdata0:
242                 devpermcheck(p->user, p->pipedir[1].perm, omode);
243                 p->qref[0]++;
244                 break;
245         case Qdata1:
246                 devpermcheck(p->user, p->pipedir[2].perm, omode);
247                 p->qref[1]++;
248                 break;
249         }
250         poperror();
251         qunlock(&p->qlock);
252
253         c->mode = openmode(omode);
254         c->flag |= COPEN;
255         c->offset = 0;
256         c->iounit = qiomaxatomic;
257         return c;
258 }
259
260 static void
261 pipeclose(struct chan *c)
262 {
263         Pipe *p;
264
265         p = c->aux;
266         qlock(&p->qlock);
267
268         if(c->flag & COPEN){
269                 /*
270                  *  closing either side hangs up the stream
271                  */
272                 switch(NETTYPE(c->qid.path)){
273                 case Qdata0:
274                         p->qref[0]--;
275                         if(p->qref[0] == 0){
276                                 qhangup(p->q[1], 0);
277                                 qclose(p->q[0]);
278                         }
279                         break;
280                 case Qdata1:
281                         p->qref[1]--;
282                         if(p->qref[1] == 0){
283                                 qhangup(p->q[0], 0);
284                                 qclose(p->q[1]);
285                         }
286                         break;
287                 }
288         }
289
290         
291         /*
292          *  if both sides are closed, they are reusable
293          */
294         if(p->qref[0] == 0 && p->qref[1] == 0){
295                 qreopen(p->q[0]);
296                 qreopen(p->q[1]);
297         }
298
299         /*
300          *  free the structure on last close
301          */
302         kref_put(&p->ref);
303         qunlock(&p->qlock);
304 }
305
306 static long
307 piperead(struct chan *c, void *va, long n, int64_t ignored)
308 {
309         Pipe *p;
310
311         p = c->aux;
312
313         switch(NETTYPE(c->qid.path)){
314         case Qdir:
315                 return devdirread(c, va, n, p->pipedir, ARRAY_SIZE(pipedir), pipegen);
316         case Qdata0:
317                 return qread(p->q[0], va, n);
318         case Qdata1:
319                 return qread(p->q[1], va, n);
320         default:
321                 panic("piperead");
322         }
323         return -1;      /* not reached */
324 }
325
326 static struct block*
327 pipebread(struct chan *c, long n, uint32_t offset)
328 {
329         Pipe *p;
330
331         p = c->aux;
332
333         switch(NETTYPE(c->qid.path)){
334         case Qdata0:
335                 return qbread(p->q[0], n);
336         case Qdata1:
337                 return qbread(p->q[1], n);
338         }
339
340         return devbread(c, n, offset);
341 }
342
343 /*
344  *  a write to a closed pipe causes an exception to be sent to
345  *  the prog.
346  */
347 static long
348 pipewrite(struct chan *c, void *va, long n, int64_t ignored)
349 {
350         ERRSTACK(2);
351         Pipe *p;
352         //Prog *r;
353
354         if(waserror()) {
355                 /* avoid exceptions when pipe is a mounted queue */
356                 if((c->flag & CMSG) == 0) {
357 /*
358                         r = up->iprog;
359                         if(r != NULL && r->kill == NULL)
360                                 r->kill = "write on closed pipe";
361 */
362                 }
363                 nexterror();
364         }
365
366         p = c->aux;
367
368         switch(NETTYPE(c->qid.path)){
369         case Qdata0:
370                 n = qwrite(p->q[1], va, n);
371                 break;
372
373         case Qdata1:
374                 n = qwrite(p->q[0], va, n);
375                 break;
376
377         default:
378                 panic("pipewrite");
379         }
380
381         poperror();
382         return n;
383 }
384
385 static long
386 pipebwrite(struct chan *c, struct block *bp, uint32_t junk)
387 {
388         ERRSTACK(2);
389         long n;
390         Pipe *p;
391         //Prog *r;
392
393         if(waserror()) {
394                 /* avoid exceptions when pipe is a mounted queue */
395 /*
396                 if((c->flag & CMSG) == 0) {
397                         r = up->iprog;
398                         if(r != NULL && r->kill == NULL)
399                                 r->kill = "write on closed pipe";
400                 }
401 */
402                 nexterror();
403         }
404
405         p = c->aux;
406         switch(NETTYPE(c->qid.path)){
407         case Qdata0:
408                 n = qbwrite(p->q[1], bp);
409                 break;
410
411         case Qdata1:
412                 n = qbwrite(p->q[0], bp);
413                 break;
414
415         default:
416                 n = 0;
417                 panic("pipebwrite");
418         }
419
420         poperror();
421         return n;
422 }
423
424 static int
425 pipewstat(struct chan *c, uint8_t *dp, int n)
426 {
427         ERRSTACK(2);
428         struct dir *d;
429         Pipe *p;
430         int d1;
431
432         if (c->qid.type&QTDIR)
433                 error(Eperm);
434         p = c->aux;
435         if(strcmp(current->user, p->user) != 0)
436                 error(Eperm);
437         d = kzmalloc(sizeof(*d) + n, 0);
438         if(waserror()){
439                 kfree(d);
440                 nexterror();
441         }
442         n = convM2D(dp, n, d, ( char *)&d[1]);
443         if(n == 0)
444                 error(Eshortstat);
445         d1 = NETTYPE(c->qid.path) == Qdata1;
446         if(!emptystr(d->name)){
447                 validwstatname(d->name);
448                 if(strlen(d->name) >= KNAMELEN)
449                         error(Efilename);
450                 if(strcmp(p->pipedir[1+!d1].name, d->name) == 0)
451                         error(Eexist);
452                 strncpy(p->pipedir[1+d1].name, d->name,  MIN(KNAMELEN, sizeof(p->pipedir[1+d1].name, d->name)));
453         }
454         if(d->mode != ~0UL)
455                 p->pipedir[d1 + 1].perm = d->mode & 0777;
456         poperror();
457         kfree(d);
458         return n;
459 }
460
461 struct dev pipedevtab = {
462         '|',
463         "pipe",
464
465         devreset,
466         pipeinit,
467         devshutdown,
468         pipeattach,
469         pipewalk,
470         pipestat,
471         pipeopen,
472         devcreate,
473         pipeclose,
474         piperead,
475         pipebread,
476         pipewrite,
477         pipebwrite,
478         devremove,
479         pipewstat,
480 };