Home | History | Annotate | Line # | Download | only in putter
putter.c revision 1.4
      1 /*	$NetBSD: putter.c,v 1.4 2007/11/13 13:11:51 pooka 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.4 2007/11/13 13:11:51 pooka 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 
     48 #include <dev/putter/putter_sys.h>
     49 
     50 /*
     51  * putter instance structures.  these are always allocated and freed
     52  * from the context of the transport user.
     53  */
     54 struct putter_instance {
     55 	pid_t			pi_pid;
     56 	int			pi_idx;
     57 	int			pi_fd;
     58 	struct selinfo		pi_sel;
     59 
     60 	void			*pi_private;
     61 	struct putter_ops	*pi_pop;
     62 
     63 	uint8_t			*pi_curput;
     64 	size_t			pi_curres;
     65 	void			*pi_curopaq;
     66 
     67 	TAILQ_ENTRY(putter_instance) pi_entries;
     68 };
     69 #define PUTTER_EMBRYO ((void *)-1)	/* before attach	*/
     70 #define PUTTER_DEAD ((void *)-2)	/* after detach		*/
     71 
     72 static TAILQ_HEAD(, putter_instance) putter_ilist
     73     = TAILQ_HEAD_INITIALIZER(putter_ilist);
     74 
     75 static int get_pi_idx(struct putter_instance *);
     76 
     77 #ifdef DEBUG
     78 #ifndef PUTTERDEBUG
     79 #define PUTTERDEBUG
     80 #endif
     81 #endif
     82 
     83 #ifdef PUTTERDEBUG
     84 static int putterdebug = 0;
     85 #define DPRINTF(x) if (putterdebug > 0) printf x
     86 #define DPRINTF_VERBOSE(x) if (putterdebug > 1) printf x
     87 #else
     88 #define DPRINTF(x)
     89 #define DPRINTF_VERBOSE(x)
     90 #endif
     91 
     92 /*
     93  * public init / deinit
     94  */
     95 
     96 /* protects both the list and the contents of the list elements */
     97 static kmutex_t pi_mtx;
     98 
     99 void putterattach(void);
    100 
    101 void
    102 putterattach()
    103 {
    104 
    105 	mutex_init(&pi_mtx, MUTEX_DEFAULT, IPL_NONE);
    106 }
    107 
    108 #if 0
    109 void
    110 putter_destroy()
    111 {
    112 
    113 	mutex_destroy(&pi_mtx);
    114 }
    115 #endif
    116 
    117 /*
    118  * fd routines, for cloner
    119  */
    120 static int putter_fop_read(struct file *, off_t *, struct uio *,
    121 			   kauth_cred_t, int);
    122 static int putter_fop_write(struct file *, off_t *, struct uio *,
    123 			    kauth_cred_t, int);
    124 static int putter_fop_ioctl(struct file*, u_long, void *, struct lwp *);
    125 static int putter_fop_poll(struct file *, int, struct lwp *);
    126 static int putter_fop_close(struct file *, struct lwp *);
    127 static int putter_fop_kqfilter(struct file *, struct knote *);
    128 
    129 
    130 static const struct fileops putter_fileops = {
    131 	putter_fop_read,
    132 	putter_fop_write,
    133 	putter_fop_ioctl,
    134 	fnullop_fcntl,
    135 	putter_fop_poll,
    136 	fbadop_stat,
    137 	putter_fop_close,
    138 	putter_fop_kqfilter
    139 };
    140 
    141 static int
    142 putter_fop_read(struct file *fp, off_t *off, struct uio *uio,
    143 	kauth_cred_t cred, int flags)
    144 {
    145 	struct putter_instance *pi = fp->f_data;
    146 	size_t origres, moved;
    147 	int error;
    148 
    149 	if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) {
    150 		printf("putter_fop_read: private %d not inited\n", pi->pi_idx);
    151 		return ENOENT;
    152 	}
    153 
    154 	if (pi->pi_curput == NULL) {
    155 		error = pi->pi_pop->pop_getout(pi->pi_private, uio->uio_resid,
    156 		    fp->f_flag & O_NONBLOCK, &pi->pi_curput,
    157 		    &pi->pi_curres, &pi->pi_curopaq);
    158 		if (error)
    159 			return error;
    160 	}
    161 
    162 	origres = uio->uio_resid;
    163 	error = uiomove(pi->pi_curput, pi->pi_curres, uio);
    164 	moved = origres - uio->uio_resid;
    165 	DPRINTF(("putter_fop_read (%p): moved %zu bytes from %p, error %d\n",
    166 	    pi, moved, pi->pi_curput, error));
    167 
    168 	KASSERT(pi->pi_curres >= moved);
    169 	pi->pi_curres -= moved;
    170 	pi->pi_curput += moved;
    171 
    172 	if (pi->pi_curres == 0) {
    173 		pi->pi_pop->pop_releaseout(pi->pi_private,
    174 		    pi->pi_curopaq, error);
    175 		pi->pi_curput = NULL;
    176 	}
    177 
    178 	return error;
    179 }
    180 
    181 static int
    182 putter_fop_write(struct file *fp, off_t *off, struct uio *uio,
    183 	kauth_cred_t cred, int flags)
    184 {
    185 	struct putter_instance *pi = fp->f_data;
    186 	struct putter_hdr pth;
    187 	uint8_t *buf;
    188 	size_t frsize;
    189 	int error;
    190 
    191 	DPRINTF(("putter_fop_write (%p): writing response, resid %zu\n",
    192 	    pi->pi_private, uio->uio_resid));
    193 
    194 	if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) {
    195 		printf("putter_fop_write: putter %d not inited\n", pi->pi_idx);
    196 		return ENOENT;
    197 	}
    198 
    199 	error = uiomove(&pth, sizeof(struct putter_hdr), uio);
    200 	if (error)
    201 		return error;
    202 
    203 	/* Sorry mate, the kernel doesn't buffer. */
    204 	frsize = pth.pth_framelen - sizeof(struct putter_hdr);
    205 	if (uio->uio_resid < frsize)
    206 		return EINVAL;
    207 
    208 	buf = kmem_alloc(frsize + sizeof(struct putter_hdr), KM_SLEEP);
    209 	memcpy(buf, &pth, sizeof(pth));
    210 	error = uiomove(buf+sizeof(struct putter_hdr), frsize, uio);
    211 	if (error == 0) {
    212 		pi->pi_pop->pop_dispatch(pi->pi_private,
    213 		    (struct putter_hdr *)buf);
    214 	}
    215 	kmem_free(buf, frsize + sizeof(struct putter_hdr));
    216 
    217 	return error;
    218 }
    219 
    220 /*
    221  * Poll query interface.  The question is only if an event
    222  * can be read from us.
    223  */
    224 #define PUTTERPOLL_EVSET (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
    225 static int
    226 putter_fop_poll(struct file *fp, int events, struct lwp *l)
    227 {
    228 	struct putter_instance *pi = fp->f_data;
    229 	int revents;
    230 
    231 	if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD) {
    232 		printf("putter_fop_ioctl: putter %d not inited\n", pi->pi_idx);
    233 		return ENOENT;
    234 	}
    235 
    236 	revents = events & (POLLOUT | POLLWRNORM | POLLWRBAND);
    237 	if ((events & PUTTERPOLL_EVSET) == 0)
    238 		return revents;
    239 
    240 	/* check queue */
    241 	if (pi->pi_pop->pop_waitcount(pi->pi_private))
    242 		revents |= PUTTERPOLL_EVSET;
    243 	else
    244 		selrecord(l, &pi->pi_sel);
    245 
    246 	return revents;
    247 }
    248 
    249 /*
    250  * device close = forced unmount.
    251  *
    252  * unmounting is a frightfully complex operation to avoid races
    253  */
    254 static int
    255 putter_fop_close(struct file *fp, struct lwp *l)
    256 {
    257 	struct putter_instance *pi = fp->f_data;
    258 	int rv;
    259 
    260 	DPRINTF(("putter_fop_close: device closed\n"));
    261 
    262  restart:
    263 	mutex_enter(&pi_mtx);
    264 	/*
    265 	 * First check if the fs was never mounted.  In that case
    266 	 * remove the instance from the list.  If mount is attempted later,
    267 	 * it will simply fail.
    268 	 */
    269 	if (pi->pi_private == PUTTER_EMBRYO) {
    270 		TAILQ_REMOVE(&putter_ilist, pi, pi_entries);
    271 		mutex_exit(&pi_mtx);
    272 
    273 		DPRINTF(("putter_fop_close: data associated with fp %p was "
    274 		    "embryonic\n", fp));
    275 
    276 		goto out;
    277 	}
    278 
    279 	/*
    280 	 * Next, analyze if unmount was called and the instance is dead.
    281 	 * In this case we can just free the structure and go home, it
    282 	 * was removed from the list by putter_rmprivate().
    283 	 */
    284 	if (pi->pi_private == PUTTER_DEAD) {
    285 		mutex_exit(&pi_mtx);
    286 
    287 		DPRINTF(("putter_fop_close: putter associated with fp %p (%d) "
    288 		    "dead, freeing\n", fp, pi->pi_idx));
    289 
    290 		goto out;
    291 	}
    292 
    293 	/*
    294 	 * So we have a reference.  Proceed to unwrap the file system.
    295 	 */
    296 	mutex_exit(&pi_mtx);
    297 
    298 	/* hmm?  suspicious locking? */
    299 	while ((rv = pi->pi_pop->pop_close(pi->pi_private)) == ERESTART)
    300 		goto restart;
    301 
    302  out:
    303 	/*
    304 	 * Finally, release the instance information.  It was already
    305 	 * removed from the list by putter_rmprivate() and we know it's
    306 	 * dead, so no need to lock.
    307 	 */
    308 	kmem_free(pi, sizeof(struct putter_instance));
    309 
    310 	return 0;
    311 }
    312 
    313 static int
    314 putter_fop_ioctl(struct file *fp, u_long cmd, void *data, struct lwp *l)
    315 {
    316 
    317 	/*
    318 	 * work already done in sys_ioctl().  skip sanity checks to enable
    319 	 * setting non-blocking fd without yet having mounted the fs
    320 	 */
    321 	if (cmd == FIONBIO)
    322 		return 0;
    323 
    324 	return EINVAL;
    325 }
    326 
    327 /* kqueue stuff */
    328 
    329 static void
    330 filt_putterdetach(struct knote *kn)
    331 {
    332 	struct putter_instance *pi = kn->kn_hook;
    333 
    334 	mutex_enter(&pi_mtx);
    335 	SLIST_REMOVE(&pi->pi_sel.sel_klist, kn, knote, kn_selnext);
    336 	mutex_exit(&pi_mtx);
    337 }
    338 
    339 static int
    340 filt_putterioctl(struct knote *kn, long hint)
    341 {
    342 	struct putter_instance *pi = kn->kn_hook;
    343 	int error;
    344 
    345 	error = 0;
    346 	mutex_enter(&pi_mtx);
    347 	if (pi->pi_private == PUTTER_EMBRYO || pi->pi_private == PUTTER_DEAD)
    348 		error = 1;
    349 	mutex_exit(&pi_mtx);
    350 	if (error)
    351 		return 0;
    352 
    353 	kn->kn_data = pi->pi_pop->pop_waitcount(pi->pi_private);
    354 
    355 	return kn->kn_data != 0;
    356 }
    357 
    358 static const struct filterops putterioctl_filtops =
    359 	{ 1, NULL, filt_putterdetach, filt_putterioctl };
    360 
    361 static int
    362 putter_fop_kqfilter(struct file *fp, struct knote *kn)
    363 {
    364 	struct putter_instance *pi = fp->f_data;
    365 	struct klist *klist;
    366 
    367 	if (kn->kn_filter != EVFILT_READ)
    368 		return 1;
    369 
    370 	klist = &pi->pi_sel.sel_klist;
    371 	kn->kn_fop = &putterioctl_filtops;
    372 	kn->kn_hook = pi;
    373 
    374 	mutex_enter(&pi_mtx);
    375 	SLIST_INSERT_HEAD(klist, kn, kn_selnext);
    376 	mutex_exit(&pi_mtx);
    377 
    378 	return 0;
    379 }
    380 
    381 
    382 /*
    383  * Device routines.  These are for when /dev/puffs is initially
    384  * opened before it has been cloned.
    385  */
    386 
    387 dev_type_open(puttercdopen);
    388 dev_type_close(puttercdclose);
    389 dev_type_ioctl(puttercdioctl);
    390 
    391 /* dev */
    392 const struct cdevsw putter_cdevsw = {
    393 	puttercdopen,	puttercdclose,	noread,		nowrite,
    394 	noioctl,	nostop,		notty,		nopoll,
    395 	nommap,		nokqfilter,	D_OTHER
    396 };
    397 int
    398 puttercdopen(dev_t dev, int flags, int fmt, struct lwp *l)
    399 {
    400 	struct putter_instance *pi;
    401 	struct file *fp;
    402 	int error, fd, idx;
    403 
    404 	if ((error = falloc(l, &fp, &fd)) != 0)
    405 		return error;
    406 
    407 	pi = kmem_alloc(sizeof(struct putter_instance), KM_SLEEP);
    408 
    409 	mutex_enter(&pi_mtx);
    410 	idx = get_pi_idx(pi);
    411 
    412 	pi->pi_pid = l->l_proc->p_pid;
    413 	pi->pi_idx = idx;
    414 	pi->pi_curput = NULL;
    415 	pi->pi_curres = 0;
    416 	pi->pi_curopaq = NULL;
    417 	selinit(&pi->pi_sel);
    418 	mutex_exit(&pi_mtx);
    419 
    420 	DPRINTF(("puttercdopen: registered embryonic pmp for pid: %d\n",
    421 	    pi->pi_pid));
    422 
    423 	return fdclone(l, fp, fd, FREAD|FWRITE, &putter_fileops, pi);
    424 }
    425 
    426 int
    427 puttercdclose(dev_t dev, int flags, int fmt, struct lwp *l)
    428 {
    429 
    430 	panic("puttercdclose impossible\n");
    431 
    432 	return 0;
    433 }
    434 
    435 
    436 /*
    437  * Set the private structure for the file descriptor.  This is
    438  * typically done immediately when the counterpart has knowledge
    439  * about the private structure's address and the file descriptor
    440  * (e.g. vfs mount routine).
    441  *
    442  * We only want to make sure that the caller had the right to open the
    443  * device, we don't so much care about which context it gets in case
    444  * the same process opened multiple (since they are equal at this point).
    445  */
    446 struct putter_instance *
    447 putter_attach(pid_t pid, int fd, void *ppriv, struct putter_ops *pop)
    448 {
    449 	struct putter_instance *pi = NULL;
    450 
    451 	mutex_enter(&pi_mtx);
    452 	TAILQ_FOREACH(pi, &putter_ilist, pi_entries) {
    453 		if (pi->pi_pid == pid && pi->pi_private == PUTTER_EMBRYO) {
    454 			pi->pi_private = ppriv;
    455 			pi->pi_fd = fd;
    456 			pi->pi_pop = pop;
    457 			break;
    458 		    }
    459 	}
    460 	mutex_exit(&pi_mtx);
    461 
    462 	DPRINTF(("putter_setprivate: pi at %p (%d/%d)\n", pi,
    463 	    pi ? pi->pi_pid : 0, pi ? pi->pi_fd : 0));
    464 
    465 	return pi;
    466 }
    467 
    468 /*
    469  * Remove fp <-> private mapping.
    470  */
    471 void
    472 putter_detach(struct putter_instance *pi)
    473 {
    474 
    475 	mutex_enter(&pi_mtx);
    476 	TAILQ_REMOVE(&putter_ilist, pi, pi_entries);
    477 	pi->pi_private = PUTTER_DEAD;
    478 	mutex_exit(&pi_mtx);
    479 
    480 	DPRINTF(("putter_nukebypmp: nuked %p\n", pi));
    481 }
    482 
    483 void
    484 putter_notify(struct putter_instance *pi)
    485 {
    486 
    487 	selnotify(&pi->pi_sel, 0);
    488 }
    489 
    490 /* search sorted list of instances for free minor, sorted insert arg */
    491 static int
    492 get_pi_idx(struct putter_instance *pi_i)
    493 {
    494 	struct putter_instance *pi;
    495 	int i;
    496 
    497 	KASSERT(mutex_owned(&pi_mtx));
    498 
    499 	i = 0;
    500 	TAILQ_FOREACH(pi, &putter_ilist, pi_entries) {
    501 		if (i != pi->pi_idx)
    502 			break;
    503 		i++;
    504 	}
    505 
    506 	pi_i->pi_private = PUTTER_EMBRYO;
    507 
    508 	if (pi == NULL)
    509 		TAILQ_INSERT_TAIL(&putter_ilist, pi_i, pi_entries);
    510 	else
    511 		TAILQ_INSERT_BEFORE(pi, pi_i, pi_entries);
    512 
    513 	return i;
    514 }
    515