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