1 /* $NetBSD: putter.c,v 1.25 2009/12/09 21:32:59 dsl Exp $ */
4 * Copyright (c) 2006, 2007 Antti Kantee. All Rights Reserved.
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
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
33 * Pass-to-Userspace TransporTER: generic kernel-user request-response
34 * transport interface.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: putter.c,v 1.25 2009/12/09 21:32:59 dsl Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
44 #include <sys/filedesc.h>
48 #include <sys/socketvar.h>
49 #include <sys/module.h>
50 #include <sys/kauth.h>
52 #include <dev/putter/putter_sys.h>
55 * Device routines. These are for when /dev/puffs is initially
56 * opened before it has been cloned.
59 dev_type_open(puttercdopen
);
60 dev_type_close(puttercdclose
);
61 dev_type_ioctl(puttercdioctl
);
64 const struct cdevsw putter_cdevsw
= {
65 puttercdopen
, puttercdclose
, noread
, nowrite
,
66 noioctl
, nostop
, notty
, nopoll
,
67 nommap
, nokqfilter
, D_OTHER
73 * This is static-size for now. Will be redone for devfs.
76 #define PUTTER_CONFSIZE 16
78 static struct putter_config
{
80 int (*pc_config
)(int, int, int);
81 } putterconf
[PUTTER_CONFSIZE
];
84 putter_configure(dev_t dev
, int flags
, int fmt
, int fd
)
86 struct putter_config
*pc
;
88 /* are we the catch-all node? */
89 if (minor(dev
) == PUTTER_MINOR_WILDCARD
90 || minor(dev
) == PUTTER_MINOR_COMPAT
)
93 /* nopes? try to configure us */
94 for (pc
= putterconf
; pc
->pc_config
; pc
++)
95 if (minor(dev
) == pc
->pc_minor
)
96 return pc
->pc_config(fd
, flags
, fmt
);
101 putter_register(putter_config_fn pcfn
, int minor
)
105 for (i
= 0; i
< PUTTER_CONFSIZE
; i
++)
106 if (putterconf
[i
].pc_config
== NULL
)
108 if (i
== PUTTER_CONFSIZE
)
111 putterconf
[i
].pc_minor
= minor
;
112 putterconf
[i
].pc_config
= pcfn
;
117 * putter instance structures. these are always allocated and freed
118 * from the context of the transport user.
120 struct putter_instance
{
124 struct selinfo pi_sel
;
127 struct putter_ops
*pi_pop
;
132 struct timespec pi_atime
;
133 struct timespec pi_mtime
;
134 struct timespec pi_btime
;
136 TAILQ_ENTRY(putter_instance
) pi_entries
;
138 #define PUTTER_EMBRYO ((void *)-1) /* before attach */
139 #define PUTTER_DEAD ((void *)-2) /* after detach */
141 static TAILQ_HEAD(, putter_instance
) putter_ilist
142 = TAILQ_HEAD_INITIALIZER(putter_ilist
);
144 static int get_pi_idx(struct putter_instance
*);
154 #define DPRINTF(x) if (putterdebug > 0) printf x
155 #define DPRINTF_VERBOSE(x) if (putterdebug > 1) printf x
158 #define DPRINTF_VERBOSE(x)
162 * public init / deinit
165 /* protects both the list and the contents of the list elements */
166 static kmutex_t pi_mtx
;
168 void putterattach(void);
174 mutex_init(&pi_mtx
, MUTEX_DEFAULT
, IPL_NONE
);
182 mutex_destroy(&pi_mtx
);
187 * fd routines, for cloner
189 static int putter_fop_read(file_t
*, off_t
*, struct uio
*,
191 static int putter_fop_write(file_t
*, off_t
*, struct uio
*,
193 static int putter_fop_ioctl(file_t
*, u_long
, void *);
194 static int putter_fop_poll(file_t
*, int);
195 static int putter_fop_stat(file_t
*, struct stat
*);
196 static int putter_fop_close(file_t
*);
197 static int putter_fop_kqfilter(file_t
*, struct knote
*);
200 static const struct fileops putter_fileops
= {
201 .fo_read
= putter_fop_read
,
202 .fo_write
= putter_fop_write
,
203 .fo_ioctl
= putter_fop_ioctl
,
204 .fo_fcntl
= fnullop_fcntl
,
205 .fo_poll
= putter_fop_poll
,
206 .fo_stat
= putter_fop_stat
,
207 .fo_close
= putter_fop_close
,
208 .fo_kqfilter
= putter_fop_kqfilter
,
209 .fo_restart
= fnullop_restart
,
213 putter_fop_read(file_t
*fp
, off_t
*off
, struct uio
*uio
,
214 kauth_cred_t cred
, int flags
)
216 struct putter_instance
*pi
= fp
->f_data
;
217 size_t origres
, moved
;
220 KERNEL_LOCK(1, NULL
);
221 getnanotime(&pi
->pi_atime
);
223 if (pi
->pi_private
== PUTTER_EMBRYO
|| pi
->pi_private
== PUTTER_DEAD
) {
224 printf("putter_fop_read: private %d not inited\n", pi
->pi_idx
);
225 KERNEL_UNLOCK_ONE(NULL
);
229 if (pi
->pi_curput
== NULL
) {
230 error
= pi
->pi_pop
->pop_getout(pi
->pi_private
, uio
->uio_resid
,
231 fp
->f_flag
& O_NONBLOCK
, &pi
->pi_curput
,
232 &pi
->pi_curres
, &pi
->pi_curopaq
);
234 KERNEL_UNLOCK_ONE(NULL
);
239 origres
= uio
->uio_resid
;
240 error
= uiomove(pi
->pi_curput
, pi
->pi_curres
, uio
);
241 moved
= origres
- uio
->uio_resid
;
242 DPRINTF(("putter_fop_read (%p): moved %zu bytes from %p, error %d\n",
243 pi
, moved
, pi
->pi_curput
, error
));
245 KASSERT(pi
->pi_curres
>= moved
);
246 pi
->pi_curres
-= moved
;
247 pi
->pi_curput
+= moved
;
249 if (pi
->pi_curres
== 0) {
250 pi
->pi_pop
->pop_releaseout(pi
->pi_private
,
251 pi
->pi_curopaq
, error
);
252 pi
->pi_curput
= NULL
;
255 KERNEL_UNLOCK_ONE(NULL
);
260 putter_fop_write(file_t
*fp
, off_t
*off
, struct uio
*uio
,
261 kauth_cred_t cred
, int flags
)
263 struct putter_instance
*pi
= fp
->f_data
;
264 struct putter_hdr pth
;
269 KERNEL_LOCK(1, NULL
);
270 getnanotime(&pi
->pi_mtime
);
272 DPRINTF(("putter_fop_write (%p): writing response, resid %zu\n",
273 pi
->pi_private
, uio
->uio_resid
));
275 if (pi
->pi_private
== PUTTER_EMBRYO
|| pi
->pi_private
== PUTTER_DEAD
) {
276 printf("putter_fop_write: putter %d not inited\n", pi
->pi_idx
);
277 KERNEL_UNLOCK_ONE(NULL
);
281 error
= uiomove(&pth
, sizeof(struct putter_hdr
), uio
);
283 KERNEL_UNLOCK_ONE(NULL
);
287 /* Sorry mate, the kernel doesn't buffer. */
288 frsize
= pth
.pth_framelen
- sizeof(struct putter_hdr
);
289 if (uio
->uio_resid
< frsize
) {
290 KERNEL_UNLOCK_ONE(NULL
);
294 buf
= kmem_alloc(frsize
+ sizeof(struct putter_hdr
), KM_SLEEP
);
295 memcpy(buf
, &pth
, sizeof(pth
));
296 error
= uiomove(buf
+sizeof(struct putter_hdr
), frsize
, uio
);
298 pi
->pi_pop
->pop_dispatch(pi
->pi_private
,
299 (struct putter_hdr
*)buf
);
301 kmem_free(buf
, frsize
+ sizeof(struct putter_hdr
));
303 KERNEL_UNLOCK_ONE(NULL
);
308 * Poll query interface. The question is only if an event
309 * can be read from us.
311 #define PUTTERPOLL_EVSET (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
313 putter_fop_poll(file_t
*fp
, int events
)
315 struct putter_instance
*pi
= fp
->f_data
;
318 KERNEL_LOCK(1, NULL
);
320 if (pi
->pi_private
== PUTTER_EMBRYO
|| pi
->pi_private
== PUTTER_DEAD
) {
321 printf("putter_fop_ioctl: putter %d not inited\n", pi
->pi_idx
);
322 KERNEL_UNLOCK_ONE(NULL
);
326 revents
= events
& (POLLOUT
| POLLWRNORM
| POLLWRBAND
);
327 if ((events
& PUTTERPOLL_EVSET
) == 0) {
328 KERNEL_UNLOCK_ONE(NULL
);
333 if (pi
->pi_pop
->pop_waitcount(pi
->pi_private
))
334 revents
|= PUTTERPOLL_EVSET
;
336 selrecord(curlwp
, &pi
->pi_sel
);
338 KERNEL_UNLOCK_ONE(NULL
);
343 * device close = forced unmount.
345 * unmounting is a frightfully complex operation to avoid races
348 putter_fop_close(file_t
*fp
)
350 struct putter_instance
*pi
= fp
->f_data
;
353 DPRINTF(("putter_fop_close: device closed\n"));
355 KERNEL_LOCK(1, NULL
);
358 mutex_enter(&pi_mtx
);
360 * First check if the fs was never mounted. In that case
361 * remove the instance from the list. If mount is attempted later,
362 * it will simply fail.
364 if (pi
->pi_private
== PUTTER_EMBRYO
) {
365 TAILQ_REMOVE(&putter_ilist
, pi
, pi_entries
);
368 DPRINTF(("putter_fop_close: data associated with fp %p was "
375 * Next, analyze if unmount was called and the instance is dead.
376 * In this case we can just free the structure and go home, it
377 * was removed from the list by putter_rmprivate().
379 if (pi
->pi_private
== PUTTER_DEAD
) {
382 DPRINTF(("putter_fop_close: putter associated with fp %p (%d) "
383 "dead, freeing\n", fp
, pi
->pi_idx
));
389 * So we have a reference. Proceed to unwrap the file system.
393 /* hmm? suspicious locking? */
394 while ((rv
= pi
->pi_pop
->pop_close(pi
->pi_private
)) == ERESTART
)
398 KERNEL_UNLOCK_ONE(NULL
);
400 * Finally, release the instance information. It was already
401 * removed from the list by putter_rmprivate() and we know it's
402 * dead, so no need to lock.
404 kmem_free(pi
, sizeof(struct putter_instance
));
410 putter_fop_stat(file_t
*fp
, struct stat
*st
)
412 struct putter_instance
*pi
= fp
->f_data
;
414 (void)memset(st
, 0, sizeof(*st
));
415 KERNEL_LOCK(1, NULL
);
416 st
->st_dev
= makedev(cdevsw_lookup_major(&putter_cdevsw
), pi
->pi_idx
);
417 st
->st_atimespec
= pi
->pi_atime
;
418 st
->st_mtimespec
= pi
->pi_mtime
;
419 st
->st_ctimespec
= st
->st_birthtimespec
= pi
->pi_btime
;
420 st
->st_uid
= kauth_cred_geteuid(fp
->f_cred
);
421 st
->st_gid
= kauth_cred_getegid(fp
->f_cred
);
422 KERNEL_UNLOCK_ONE(NULL
);
427 putter_fop_ioctl(file_t
*fp
, u_long cmd
, void *data
)
431 * work already done in sys_ioctl(). skip sanity checks to enable
432 * setting non-blocking fd without yet having mounted the fs
443 filt_putterdetach(struct knote
*kn
)
445 struct putter_instance
*pi
= kn
->kn_hook
;
447 KERNEL_LOCK(1, NULL
);
448 mutex_enter(&pi_mtx
);
449 SLIST_REMOVE(&pi
->pi_sel
.sel_klist
, kn
, knote
, kn_selnext
);
451 KERNEL_UNLOCK_ONE(NULL
);
455 filt_putter(struct knote
*kn
, long hint
)
457 struct putter_instance
*pi
= kn
->kn_hook
;
460 KERNEL_LOCK(1, NULL
);
462 mutex_enter(&pi_mtx
);
463 if (pi
->pi_private
== PUTTER_EMBRYO
|| pi
->pi_private
== PUTTER_DEAD
)
467 KERNEL_UNLOCK_ONE(NULL
);
471 kn
->kn_data
= pi
->pi_pop
->pop_waitcount(pi
->pi_private
);
472 rv
= kn
->kn_data
!= 0;
473 KERNEL_UNLOCK_ONE(NULL
);
477 static const struct filterops putter_filtops
=
478 { 1, NULL
, filt_putterdetach
, filt_putter
};
481 putter_fop_kqfilter(file_t
*fp
, struct knote
*kn
)
483 struct putter_instance
*pi
= fp
->f_data
;
486 KERNEL_LOCK(1, NULL
);
488 switch (kn
->kn_filter
) {
490 klist
= &pi
->pi_sel
.sel_klist
;
491 kn
->kn_fop
= &putter_filtops
;
494 mutex_enter(&pi_mtx
);
495 SLIST_INSERT_HEAD(klist
, kn
, kn_selnext
);
500 kn
->kn_fop
= &seltrue_filtops
;
503 KERNEL_UNLOCK_ONE(NULL
);
507 KERNEL_UNLOCK_ONE(NULL
);
512 puttercdopen(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
514 struct putter_instance
*pi
;
520 pi
= kmem_alloc(sizeof(struct putter_instance
), KM_SLEEP
);
521 mutex_enter(&pi_mtx
);
522 idx
= get_pi_idx(pi
);
524 pi
->pi_pid
= p
->p_pid
;
526 pi
->pi_curput
= NULL
;
528 pi
->pi_curopaq
= NULL
;
529 getnanotime(&pi
->pi_btime
);
530 pi
->pi_atime
= pi
->pi_mtime
= pi
->pi_btime
;
531 selinit(&pi
->pi_sel
);
534 if ((error
= fd_allocfile(&fp
, &fd
)) != 0)
537 if ((error
= putter_configure(dev
, flags
, fmt
, fd
)) != 0)
540 DPRINTF(("puttercdopen: registered embryonic pmp for pid: %d\n",
543 error
= fd_clone(fp
, fd
, FREAD
|FWRITE
, &putter_fileops
, pi
);
544 KASSERT(error
== EMOVEFD
);
551 kmem_free(pi
, sizeof(struct putter_instance
));
556 puttercdclose(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
559 panic("puttercdclose impossible\n");
566 * Set the private structure for the file descriptor. This is
567 * typically done immediately when the counterpart has knowledge
568 * about the private structure's address and the file descriptor
569 * (e.g. vfs mount routine).
571 * We only want to make sure that the caller had the right to open the
572 * device, we don't so much care about which context it gets in case
573 * the same process opened multiple (since they are equal at this point).
575 struct putter_instance
*
576 putter_attach(pid_t pid
, int fd
, void *ppriv
, struct putter_ops
*pop
)
578 struct putter_instance
*pi
= NULL
;
580 mutex_enter(&pi_mtx
);
581 TAILQ_FOREACH(pi
, &putter_ilist
, pi_entries
) {
582 if (pi
->pi_pid
== pid
&& pi
->pi_private
== PUTTER_EMBRYO
) {
583 pi
->pi_private
= ppriv
;
591 DPRINTF(("putter_setprivate: pi at %p (%d/%d)\n", pi
,
592 pi
? pi
->pi_pid
: 0, pi
? pi
->pi_fd
: 0));
598 * Remove fp <-> private mapping.
601 putter_detach(struct putter_instance
*pi
)
604 mutex_enter(&pi_mtx
);
605 TAILQ_REMOVE(&putter_ilist
, pi
, pi_entries
);
606 pi
->pi_private
= PUTTER_DEAD
;
609 DPRINTF(("putter_nukebypmp: nuked %p\n", pi
));
613 putter_notify(struct putter_instance
*pi
)
616 selnotify(&pi
->pi_sel
, 0, 0);
619 /* search sorted list of instances for free minor, sorted insert arg */
621 get_pi_idx(struct putter_instance
*pi_i
)
623 struct putter_instance
*pi
;
626 KASSERT(mutex_owned(&pi_mtx
));
629 TAILQ_FOREACH(pi
, &putter_ilist
, pi_entries
) {
635 pi_i
->pi_private
= PUTTER_EMBRYO
;
638 TAILQ_INSERT_TAIL(&putter_ilist
, pi_i
, pi_entries
);
640 TAILQ_INSERT_BEFORE(pi
, pi_i
, pi_entries
);
645 MODULE(MODULE_CLASS_DRIVER
, putter
, NULL
);
648 putter_modcmd(modcmd_t cmd
, void *arg
)
651 devmajor_t bmajor
= NODEVMAJOR
, cmajor
= NODEVMAJOR
;
654 case MODULE_CMD_INIT
:
655 return devsw_attach("putter", NULL
, &bmajor
,
656 &putter_cdevsw
, &cmajor
);
657 case MODULE_CMD_FINI
:
658 return devsw_detach(NULL
, &putter_cdevsw
);
663 if (cmd
== MODULE_CMD_INIT
)