putter.c revision 1.10 1 /* $NetBSD: putter.c,v 1.10 2008/05/19 16:59:55 jmcneill Exp $ */
2
3 /*
4 * Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Ulla Tuominen Foundation and the Finnish Cultural Foundation and the
8 * Research Foundation of Helsinki University of Technology
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Pass-to-Userspace TransporTER: generic kernel-user request-response
34 * transport interface.
35 */
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: putter.c,v 1.10 2008/05/19 16:59:55 jmcneill Exp $");
39
40 #include <sys/param.h>
41 #include <sys/conf.h>
42 #include <sys/file.h>
43 #include <sys/filedesc.h>
44 #include <sys/kmem.h>
45 #include <sys/poll.h>
46 #include <sys/socketvar.h>
47 #include <sys/module.h>
48
49 #include <dev/putter/putter_sys.h>
50
51 /*
52 * Configuration data.
53 *
54 * This is static-size for now. Will be redone for devfs.
55 */
56
57 #define PUTTER_CONFSIZE 16
58
59 static struct putter_config {
60 int pc_minor;
61 int (*pc_config)(int, int, int);
62 } putterconf[PUTTER_CONFSIZE];
63
64 static int
65 putter_configure(dev_t dev, int flags, int fmt, int fd)
66 {
67 struct putter_config *pc;
68
69 /* are we the catch-all node? */
70 if (minor(dev) == PUTTER_MINOR_WILDCARD
71 || minor(dev) == PUTTER_MINOR_COMPAT)
72 return 0;
73
74 /* nopes? try to configure us */
75 for (pc = putterconf; pc->pc_config; pc++)
76 if (minor(dev) == pc->pc_minor)
77 return pc->pc_config(fd, flags, fmt);
78 return ENXIO;
79 }
80
81 int
82 putter_register(putter_config_fn pcfn, int minor)
83 {
84 int i;
85
86 for (i = 0; i < PUTTER_CONFSIZE; i++)
87 if (putterconf[i].pc_config == NULL)
88 break;
89 if (i == PUTTER_CONFSIZE)
90 return EBUSY;
91
92 putterconf[i].pc_minor = minor;
93 putterconf[i].pc_config = pcfn;
94 return 0;
95 }
96
97 /*
98 * putter instance structures. these are always allocated and freed
99 * from the context of the transport user.
100 */
101 struct putter_instance {
102 pid_t pi_pid;
103 int pi_idx;
104 int pi_fd;
105 struct selinfo pi_sel;
106
107 void *pi_private;
108 struct putter_ops *pi_pop;
109
110 uint8_t *pi_curput;
111 size_t pi_curres;
112 void *pi_curopaq;
113
114 TAILQ_ENTRY(putter_instance) pi_entries;
115 };
116 #define PUTTER_EMBRYO ((void *)-1) /* before attach */
117 #define PUTTER_DEAD ((void *)-2) /* after detach */
118
119 static TAILQ_HEAD(, putter_instance) putter_ilist
120 = TAILQ_HEAD_INITIALIZER(putter_ilist);
121
122 static int get_pi_idx(struct putter_instance *);
123
124 #ifdef DEBUG
125 #ifndef PUTTERDEBUG
126 #define PUTTERDEBUG
127 #endif
128 #endif
129
130 #ifdef PUTTERDEBUG
131 int putterdebug = 0;
132 #define DPRINTF(x) if (putterdebug > 0) printf x
133 #define DPRINTF_VERBOSE(x) if (putterdebug > 1) printf x
134 #else
135 #define DPRINTF(x)
136 #define DPRINTF_VERBOSE(x)
137 #endif
138
139 /*
140 * public init / deinit
141 */
142
143 /* protects both the list and the contents of the list elements */
144 static kmutex_t pi_mtx;
145
146 void putterattach(void);
147
148 void
149 putterattach()
150 {
151
152 mutex_init(&pi_mtx, MUTEX_DEFAULT, IPL_NONE);
153 }
154
155 #if 0
156 void
157 putter_destroy()
158 {
159
160 mutex_destroy(&pi_mtx);
161 }
162 #endif
163
164 /*
165 * fd routines, for cloner
166 */
167 static int putter_fop_read(file_t *, off_t *, struct uio *,
168 kauth_cred_t, int);
169 static int putter_fop_write(file_t *, off_t *, struct uio *,
170 kauth_cred_t, int);
171 static int putter_fop_ioctl(file_t*, u_long, void *);
172 static int putter_fop_poll(file_t *, int);
173 static int putter_fop_close(file_t *);
174 static int putter_fop_kqfilter(file_t *, struct knote *);
175
176
177 static const struct fileops putter_fileops = {
178 putter_fop_read,
179 putter_fop_write,
180 putter_fop_ioctl,
181 fnullop_fcntl,
182 putter_fop_poll,
183 fbadop_stat,
184 putter_fop_close,
185 putter_fop_kqfilter
186 };
187
188 static int
189 putter_fop_read(file_t *fp, off_t *off, struct uio *uio,
190 kauth_cred_t cred, int flags)
191 {
192 struct putter_instance *pi = fp->f_data;
193 size_t origres, moved;
194 int error;
195
196 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) {
197 printf("putter_fop_read: private %d not inited\n", pi->pi_idx);
198 return ENOENT;
199 }
200
201 if (pi->pi_curput == NULL) {
202 error = pi->pi_pop->pop_getout(pi->pi_private, uio->uio_resid,
203 fp->f_flag & O_NONBLOCK, &pi->pi_curput,
204 &pi->pi_curres, &pi->pi_curopaq);
205 if (error)
206 return error;
207 }
208
209 origres = uio->uio_resid;
210 error = uiomove(pi->pi_curput, pi->pi_curres, uio);
211 moved = origres - uio->uio_resid;
212 DPRINTF(("putter_fop_read (%p): moved %zu bytes from %p, error %d\n",
213 pi, moved, pi->pi_curput, error));
214
215 KASSERT(pi->pi_curres >= moved);
216 pi->pi_curres -= moved;
217 pi->pi_curput += moved;
218
219 if (pi->pi_curres == 0) {
220 pi->pi_pop->pop_releaseout(pi->pi_private,
221 pi->pi_curopaq, error);
222 pi->pi_curput = NULL;
223 }
224
225 return error;
226 }
227
228 static int
229 putter_fop_write(file_t *fp, off_t *off, struct uio *uio,
230 kauth_cred_t cred, int flags)
231 {
232 struct putter_instance *pi = fp->f_data;
233 struct putter_hdr pth;
234 uint8_t *buf;
235 size_t frsize;
236 int error;
237
238 DPRINTF(("putter_fop_write (%p): writing response, resid %zu\n",
239 pi->pi_private, uio->uio_resid));
240
241 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) {
242 printf("putter_fop_write: putter %d not inited\n", pi->pi_idx);
243 return ENOENT;
244 }
245
246 error = uiomove(&pth, sizeof(struct putter_hdr), uio);
247 if (error)
248 return error;
249
250 /* Sorry mate, the kernel doesn't buffer. */
251 frsize = pth.pth_framelen - sizeof(struct putter_hdr);
252 if (uio->uio_resid < frsize)
253 return EINVAL;
254
255 buf = kmem_alloc(frsize + sizeof(struct putter_hdr), KM_SLEEP);
256 memcpy(buf, &pth, sizeof(pth));
257 error = uiomove(buf+sizeof(struct putter_hdr), frsize, uio);
258 if (error == 0) {
259 pi->pi_pop->pop_dispatch(pi->pi_private,
260 (struct putter_hdr *)buf);
261 }
262 kmem_free(buf, frsize + sizeof(struct putter_hdr));
263
264 return error;
265 }
266
267 /*
268 * Poll query interface. The question is only if an event
269 * can be read from us.
270 */
271 #define PUTTERPOLL_EVSET (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
272 static int
273 putter_fop_poll(file_t *fp, int events)
274 {
275 struct putter_instance *pi = fp->f_data;
276 int revents;
277
278 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) {
279 printf("putter_fop_ioctl: putter %d not inited\n", pi->pi_idx);
280 return ENOENT;
281 }
282
283 revents = events & (POLLOUT | POLLWRNORM | POLLWRBAND);
284 if ((events & PUTTERPOLL_EVSET) == 0)
285 return revents;
286
287 /* check queue */
288 if (pi->pi_pop->pop_waitcount(pi->pi_private))
289 revents |= PUTTERPOLL_EVSET;
290 else
291 selrecord(curlwp, &pi->pi_sel);
292
293 return revents;
294 }
295
296 /*
297 * device close = forced unmount.
298 *
299 * unmounting is a frightfully complex operation to avoid races
300 */
301 static int
302 putter_fop_close(file_t *fp)
303 {
304 struct putter_instance *pi = fp->f_data;
305 int rv;
306
307 DPRINTF(("putter_fop_close: device closed\n"));
308
309 restart:
310 mutex_enter(&pi_mtx);
311 /*
312 * First check if the fs was never mounted. In that case
313 * remove the instance from the list. If mount is attempted later,
314 * it will simply fail.
315 */
316 if (pi->pi_private == PUTTER_EMBRYO) {
317 TAILQ_REMOVE(&putter_ilist, pi, pi_entries);
318 mutex_exit(&pi_mtx);
319
320 DPRINTF(("putter_fop_close: data associated with fp %p was "
321 "embryonic\n", fp));
322
323 goto out;
324 }
325
326 /*
327 * Next, analyze if unmount was called and the instance is dead.
328 * In this case we can just free the structure and go home, it
329 * was removed from the list by putter_rmprivate().
330 */
331 if (pi->pi_private == PUTTER_DEAD) {
332 mutex_exit(&pi_mtx);
333
334 DPRINTF(("putter_fop_close: putter associated with fp %p (%d) "
335 "dead, freeing\n", fp, pi->pi_idx));
336
337 goto out;
338 }
339
340 /*
341 * So we have a reference. Proceed to unwrap the file system.
342 */
343 mutex_exit(&pi_mtx);
344
345 /* hmm? suspicious locking? */
346 while ((rv = pi->pi_pop->pop_close(pi->pi_private)) == ERESTART)
347 goto restart;
348
349 out:
350 /*
351 * Finally, release the instance information. It was already
352 * removed from the list by putter_rmprivate() and we know it's
353 * dead, so no need to lock.
354 */
355 kmem_free(pi, sizeof(struct putter_instance));
356
357 return 0;
358 }
359
360 static int
361 putter_fop_ioctl(file_t *fp, u_long cmd, void *data)
362 {
363
364 /*
365 * work already done in sys_ioctl(). skip sanity checks to enable
366 * setting non-blocking fd without yet having mounted the fs
367 */
368 if (cmd == FIONBIO)
369 return 0;
370
371 return EINVAL;
372 }
373
374 /* kqueue stuff */
375
376 static void
377 filt_putterdetach(struct knote *kn)
378 {
379 struct putter_instance *pi = kn->kn_hook;
380
381 mutex_enter(&pi_mtx);
382 SLIST_REMOVE(&pi->pi_sel.sel_klist, kn, knote, kn_selnext);
383 mutex_exit(&pi_mtx);
384 }
385
386 static int
387 filt_putter(struct knote *kn, long hint)
388 {
389 struct putter_instance *pi = kn->kn_hook;
390 int error;
391
392 error = 0;
393 mutex_enter(&pi_mtx);
394 if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD)
395 error = 1;
396 mutex_exit(&pi_mtx);
397 if (error)
398 return 0;
399
400 kn->kn_data = pi->pi_pop->pop_waitcount(pi->pi_private);
401
402 return kn->kn_data != 0;
403 }
404
405 static const struct filterops putter_filtops =
406 { 1, NULL, filt_putterdetach, filt_putter };
407
408 static int
409 putter_fop_kqfilter(file_t *fp, struct knote *kn)
410 {
411 struct putter_instance *pi = fp->f_data;
412 struct klist *klist;
413
414 switch (kn->kn_filter) {
415 case EVFILT_READ:
416 klist = &pi->pi_sel.sel_klist;
417 kn->kn_fop = &putter_filtops;
418 kn->kn_hook = pi;
419
420 mutex_enter(&pi_mtx);
421 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
422 mutex_exit(&pi_mtx);
423
424 break;
425 case EVFILT_WRITE:
426 kn->kn_fop = &seltrue_filtops;
427 break;
428 default:
429 return EINVAL;
430 }
431
432 return 0;
433 }
434
435 /*
436 * Device routines. These are for when /dev/puffs is initially
437 * opened before it has been cloned.
438 */
439
440 dev_type_open(puttercdopen);
441 dev_type_close(puttercdclose);
442 dev_type_ioctl(puttercdioctl);
443
444 /* dev */
445 const struct cdevsw putter_cdevsw = {
446 puttercdopen, puttercdclose, noread, nowrite,
447 noioctl, nostop, notty, nopoll,
448 nommap, nokqfilter, D_OTHER
449 };
450 int
451 puttercdopen(dev_t dev, int flags, int fmt, struct lwp *l)
452 {
453 struct putter_instance *pi;
454 file_t *fp;
455 int error, fd, idx;
456 proc_t *p;
457
458 p = curproc;
459 pi = kmem_alloc(sizeof(struct putter_instance), KM_SLEEP);
460 mutex_enter(&pi_mtx);
461 idx = get_pi_idx(pi);
462
463 pi->pi_pid = p->p_pid;
464 pi->pi_idx = idx;
465 pi->pi_curput = NULL;
466 pi->pi_curres = 0;
467 pi->pi_curopaq = NULL;
468 selinit(&pi->pi_sel);
469 mutex_exit(&pi_mtx);
470
471 if ((error = fd_allocfile(&fp, &fd)) != 0)
472 goto bad1;
473
474 if ((error = putter_configure(dev, flags, fmt, fd)) != 0)
475 goto bad2;
476
477 DPRINTF(("puttercdopen: registered embryonic pmp for pid: %d\n",
478 pi->pi_pid));
479
480 error = fd_clone(fp, fd, FREAD|FWRITE, &putter_fileops, pi);
481 KASSERT(error = EMOVEFD);
482 return error;
483
484 bad2:
485 fd_abort(p, fp, fd);
486 bad1:
487 putter_detach(pi);
488 kmem_free(pi, sizeof(struct putter_instance));
489 return error;
490 }
491
492 int
493 puttercdclose(dev_t dev, int flags, int fmt, struct lwp *l)
494 {
495
496 panic("puttercdclose impossible\n");
497
498 return 0;
499 }
500
501
502 /*
503 * Set the private structure for the file descriptor. This is
504 * typically done immediately when the counterpart has knowledge
505 * about the private structure's address and the file descriptor
506 * (e.g. vfs mount routine).
507 *
508 * We only want to make sure that the caller had the right to open the
509 * device, we don't so much care about which context it gets in case
510 * the same process opened multiple (since they are equal at this point).
511 */
512 struct putter_instance *
513 putter_attach(pid_t pid, int fd, void *ppriv, struct putter_ops *pop)
514 {
515 struct putter_instance *pi = NULL;
516
517 mutex_enter(&pi_mtx);
518 TAILQ_FOREACH(pi, &putter_ilist, pi_entries) {
519 if (pi->pi_pid == pid && pi->pi_private == PUTTER_EMBRYO) {
520 pi->pi_private = ppriv;
521 pi->pi_fd = fd;
522 pi->pi_pop = pop;
523 break;
524 }
525 }
526 mutex_exit(&pi_mtx);
527
528 DPRINTF(("putter_setprivate: pi at %p (%d/%d)\n", pi,
529 pi ? pi->pi_pid : 0, pi ? pi->pi_fd : 0));
530
531 return pi;
532 }
533
534 /*
535 * Remove fp <-> private mapping.
536 */
537 void
538 putter_detach(struct putter_instance *pi)
539 {
540
541 mutex_enter(&pi_mtx);
542 TAILQ_REMOVE(&putter_ilist, pi, pi_entries);
543 pi->pi_private = PUTTER_DEAD;
544 mutex_exit(&pi_mtx);
545
546 DPRINTF(("putter_nukebypmp: nuked %p\n", pi));
547 }
548
549 void
550 putter_notify(struct putter_instance *pi)
551 {
552
553 selnotify(&pi->pi_sel, 0, 0);
554 }
555
556 /* search sorted list of instances for free minor, sorted insert arg */
557 static int
558 get_pi_idx(struct putter_instance *pi_i)
559 {
560 struct putter_instance *pi;
561 int i;
562
563 KASSERT(mutex_owned(&pi_mtx));
564
565 i = 0;
566 TAILQ_FOREACH(pi, &putter_ilist, pi_entries) {
567 if (i != pi->pi_idx)
568 break;
569 i++;
570 }
571
572 pi_i->pi_private = PUTTER_EMBRYO;
573
574 if (pi == NULL)
575 TAILQ_INSERT_TAIL(&putter_ilist, pi_i, pi_entries);
576 else
577 TAILQ_INSERT_BEFORE(pi, pi_i, pi_entries);
578
579 return i;
580 }
581
582 MODULE(MODULE_CLASS_MISC, putter, NULL);
583
584 static int
585 putter_modcmd(modcmd_t cmd, void *arg)
586 {
587 int bmajor = -1, cmajor = -1;
588
589 switch (cmd) {
590 case MODULE_CMD_INIT:
591 return devsw_attach("putter", NULL, &bmajor,
592 &putter_cdevsw, &cmajor);
593 case MODULE_CMD_FINI:
594 devsw_detach(NULL, &putter_cdevsw);
595 return 0;
596 default:
597 return ENOTTY;
598 }
599 }
600