framebuf.c revision 1.13 1 /* $NetBSD: framebuf.c,v 1.13 2007/05/20 19:56:56 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Finnish Cultural Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 #if !defined(lint)
33 __RCSID("$NetBSD: framebuf.c,v 1.13 2007/05/20 19:56:56 pooka Exp $");
34 #endif /* !lint */
35
36 #include <sys/types.h>
37 #include <sys/queue.h>
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <poll.h>
42 #include <puffs.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45
46 #include "puffs_priv.h"
47
48 struct puffs_framebuf {
49 struct puffs_cc *pcc; /* pcc to continue with */
50 /* OR */
51 puffs_framev_cb fcb; /* non-blocking callback */
52 void *fcb_arg; /* argument for previous */
53
54 uint8_t *buf; /* buffer base */
55 size_t len; /* total length */
56
57 size_t offset; /* cursor, telloff() */
58 size_t maxoff; /* maximum offset for data, tellsize() */
59
60 volatile int rv; /* errno value */
61
62 int istat;
63
64 TAILQ_ENTRY(puffs_framebuf) pfb_entries;
65 };
66 #define ISTAT_NODESTROY 0x01 /* indestructible by framebuf_destroy() */
67 #define ISTAT_INTERNAL 0x02 /* never leaves library */
68 #define ISTAT_NOREPLY 0x04 /* nuke after sending */
69
70 #define ISTAT_ONQUEUE ISTAT_NODESTROY /* alias */
71
72 #define PUFBUF_INCRALLOC 4096
73 #define PUFBUF_REMAIN(p) (p->len - p->offset)
74
75 static struct puffs_fctrl_io *
76 getfiobyfd(struct puffs_usermount *pu, int fd)
77 {
78 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
79 struct puffs_fctrl_io *fio;
80
81 LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries)
82 if (fio->io_fd == fd)
83 return fio;
84 return NULL;
85 }
86
87 struct puffs_framebuf *
88 puffs_framebuf_make()
89 {
90 struct puffs_framebuf *pufbuf;
91
92 pufbuf = malloc(sizeof(struct puffs_framebuf));
93 if (pufbuf == NULL)
94 return NULL;
95 memset(pufbuf, 0, sizeof(struct puffs_framebuf));
96
97 pufbuf->buf = malloc(PUFBUF_INCRALLOC);
98 if (pufbuf->buf == NULL) {
99 free(pufbuf);
100 return NULL;
101 }
102 pufbuf->len = PUFBUF_INCRALLOC;
103
104 puffs_framebuf_recycle(pufbuf);
105 return pufbuf;
106 }
107
108 void
109 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
110 {
111
112 assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
113
114 free(pufbuf->buf);
115 free(pufbuf);
116 }
117
118 void
119 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
120 {
121
122 assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
123
124 pufbuf->offset = 0;
125 pufbuf->maxoff = 0;
126 pufbuf->istat = 0;
127 }
128
129 static int
130 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
131 {
132 size_t incr;
133 void *nd;
134
135 if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
136 return 0;
137
138 for (incr = PUFBUF_INCRALLOC;
139 pufbuf->len + incr < off + wantsize;
140 incr += PUFBUF_INCRALLOC)
141 continue;
142
143 nd = realloc(pufbuf->buf, pufbuf->offset + incr);
144 if (nd == NULL)
145 return -1;
146
147 pufbuf->buf = nd;
148 pufbuf->len += incr;
149
150 return 0;
151 }
152
153 int
154 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
155 {
156
157 return reservespace(pufbuf, pufbuf->offset, wantsize);
158 }
159
160 int
161 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
162 const void *data, size_t dlen)
163 {
164
165 if (PUFBUF_REMAIN(pufbuf) < dlen)
166 if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
167 return -1;
168
169 memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
170 pufbuf->offset += dlen;
171
172 if (pufbuf->offset > pufbuf->maxoff)
173 pufbuf->maxoff = pufbuf->offset;
174
175 return 0;
176 }
177
178 int
179 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
180 const void *data, size_t dlen)
181 {
182
183 if (reservespace(pufbuf, offset, dlen) == -1)
184 return -1;
185
186 memcpy(pufbuf->buf + offset, data, dlen);
187
188 if (offset + dlen > pufbuf->maxoff)
189 pufbuf->maxoff = offset + dlen;
190
191 return 0;
192 }
193
194 int
195 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
196 {
197
198 if (pufbuf->maxoff < pufbuf->offset + dlen) {
199 errno = ENOBUFS;
200 return -1;
201 }
202
203 memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
204 pufbuf->offset += dlen;
205
206 return 0;
207 }
208
209 int
210 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
211 void *data, size_t dlen)
212 {
213
214 if (pufbuf->maxoff < offset + dlen) {
215 errno = ENOBUFS;
216 return -1;
217 }
218
219 memcpy(data, pufbuf->buf + offset, dlen);
220 return 0;
221 }
222
223 size_t
224 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
225 {
226
227 return pufbuf->offset;
228 }
229
230 size_t
231 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
232 {
233
234 return pufbuf->maxoff;
235 }
236
237 size_t
238 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
239 {
240
241 return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
242 }
243
244 int
245 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
246 {
247
248 if (reservespace(pufbuf, newoff, 0) == -1)
249 return -1;
250
251 pufbuf->offset = newoff;
252 return 0;
253 }
254
255 int
256 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
257 void **data, size_t *dlen)
258 {
259 size_t winlen;
260
261 #ifdef WINTESTING
262 winlen = MIN(*dlen, 32);
263 #else
264 winlen = *dlen;
265 #endif
266
267 if (reservespace(pufbuf, winoff, winlen) == -1)
268 return -1;
269
270 *data = pufbuf->buf + winoff;
271 if (pufbuf->maxoff < winoff + winlen)
272 pufbuf->maxoff = winoff + winlen;
273
274 return 0;
275 }
276
277 static void
278 errnotify(struct puffs_framebuf *pufbuf, int error)
279 {
280
281 pufbuf->rv = error;
282 if (pufbuf->pcc) {
283 puffs_goto(pufbuf->pcc);
284 } else if (pufbuf->fcb) {
285 pufbuf->istat &= ~ISTAT_NODESTROY;
286 pufbuf->fcb(puffs_cc_getusermount(pufbuf->pcc),
287 pufbuf, pufbuf->fcb_arg, error);
288 } else {
289 pufbuf->istat &= ~ISTAT_NODESTROY;
290 puffs_framebuf_destroy(pufbuf);
291 }
292 }
293
294 #define GETFIO(fd) \
295 do { \
296 fio = getfiobyfd(pu, fd); \
297 if (fio == NULL) { \
298 errno = EINVAL; \
299 return -1; \
300 } \
301 if (fio->stat & FIO_WRGONE) { \
302 errno = ESHUTDOWN; \
303 return -1; \
304 } \
305 } while (/*CONSTCOND*/0)
306
307 int
308 puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
309 struct puffs_framebuf *pufbuf)
310 {
311 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
312 struct puffs_fctrl_io *fio;
313
314 /*
315 * Technically we shouldn't allow this is RDGONE, but it's
316 * difficult to trap write close without allowing writes.
317 * And besides, there's probably a disconnect sequence in
318 * the protocol, so unexpectedly getting a closed fd is
319 * most likely an error condition.
320 */
321 GETFIO(fd);
322
323 pufbuf->pcc = pcc;
324 pufbuf->fcb = NULL;
325 pufbuf->fcb_arg = NULL;
326
327 pufbuf->offset = 0;
328 pufbuf->istat |= ISTAT_NODESTROY;
329
330 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
331
332 puffs_cc_yield(pcc);
333 if (pufbuf->rv) {
334 pufbuf->istat &= ~ISTAT_NODESTROY;
335 errno = pufbuf->rv;
336 return -1;
337 }
338
339 return 0;
340 }
341
342 int
343 puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
344 struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg)
345 {
346 struct puffs_fctrl_io *fio;
347
348 /* see enqueue_cc */
349 GETFIO(fd);
350
351 pufbuf->pcc = NULL;
352 pufbuf->fcb = fcb;
353 pufbuf->fcb_arg = arg;
354
355 pufbuf->offset = 0;
356 pufbuf->istat |= ISTAT_NODESTROY;
357
358 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
359
360 return 0;
361 }
362
363 int
364 puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
365 struct puffs_framebuf *pufbuf, int reply)
366 {
367 struct puffs_fctrl_io *fio;
368
369 GETFIO(fd);
370
371 pufbuf->pcc = NULL;
372 pufbuf->fcb = NULL;
373 pufbuf->fcb_arg = NULL;
374
375 pufbuf->offset = 0;
376 pufbuf->istat |= ISTAT_NODESTROY;
377 if (!reply)
378 pufbuf->istat |= ISTAT_NOREPLY;
379
380 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
381
382 return 0;
383 }
384
385 /*
386 * this beauty shall remain undocumented for now
387 */
388 int
389 puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
390 struct puffs_cc *pcc)
391 {
392
393 if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
394 errno = EBUSY;
395 return -1;
396 }
397
398 pufbuf->pcc = pcc;
399 pufbuf->fcb = NULL;
400 pufbuf->fcb_arg = NULL;
401 pufbuf->istat &= ~ISTAT_NOREPLY;
402
403 puffs_cc_yield(pcc);
404
405 return 0;
406 }
407
408 static struct puffs_framebuf *
409 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
410 struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
411 {
412 struct puffs_framebuf *cand;
413
414 TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
415 if (fctrl->cmpfb(pu, findme, cand) == 0)
416 break;
417
418 if (cand == NULL)
419 return NULL;
420
421 TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
422 return cand;
423 }
424
425 static void
426 moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
427 {
428
429 assert(from->istat & ISTAT_INTERNAL);
430
431 /* migrate buffer */
432 free(to->buf);
433 to->buf = from->buf;
434 from->buf = NULL;
435
436 /* migrate buffer info */
437 to->len = from->len;
438 to->offset = from->offset;
439 to->maxoff = from->maxoff;
440 }
441
442 void
443 puffs_framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
444 struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
445 {
446 struct puffs_framebuf *pufbuf, *appbuf;
447 int rv, complete;
448
449 if (fio->stat & FIO_DEAD)
450 return;
451
452 for (;;) {
453 if ((pufbuf = fio->cur_in) == NULL) {
454 pufbuf = puffs_framebuf_make();
455 if (pufbuf == NULL)
456 return;
457 pufbuf->istat |= ISTAT_INTERNAL;
458 fio->cur_in = pufbuf;
459 }
460
461 complete = 0;
462 rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
463
464 /* error */
465 if (rv) {
466 puffs_framev_readclose(pu, fio, rv);
467 return;
468 }
469
470 /* partial read, come back to fight another day */
471 if (complete == 0)
472 break;
473
474 /* else: full read, process */
475
476 appbuf = findbuf(pu, fctrl, fio, pufbuf);
477
478 /* XXX: error delivery? */
479 if (appbuf == NULL) {
480 /* errno = ENOMSG; */
481 return;
482 }
483
484 appbuf->istat &= ~ISTAT_NODESTROY;
485 moveinfo(pufbuf, appbuf);
486 if (appbuf->pcc) {
487 puffs_docc(appbuf->pcc, ppr);
488 } else if (appbuf->fcb) {
489 appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
490 } else {
491 puffs_framebuf_destroy(appbuf);
492 }
493 puffs_framebuf_destroy(pufbuf);
494
495 /* hopeless romantics, here we go again */
496 fio->cur_in = NULL;
497 }
498 }
499
500 int
501 puffs_framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
502 struct puffs_fctrl_io *fio)
503 {
504 struct puffs_framebuf *pufbuf, *pufbuf_next;
505 int rv, complete, error;
506
507 if (fio->stat & FIO_DEAD)
508 return 0;
509
510 for (pufbuf = TAILQ_FIRST(&fio->snd_qing), error = 0;
511 pufbuf;
512 pufbuf = pufbuf_next) {
513 complete = 0;
514 pufbuf_next = TAILQ_NEXT(pufbuf, pfb_entries);
515 rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
516
517 if (rv) {
518 puffs_framev_writeclose(pu, fio, rv);
519 error = 1;
520 break;
521 }
522
523 /* partial write */
524 if (complete == 0)
525 return error;
526
527 /* else, complete write */
528 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
529
530 /* can't wait for result if we can't read */
531 if (fio->stat & FIO_RDGONE) {
532 errnotify(pufbuf, ENXIO);
533 error = 1;
534
535 } else if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
536 TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
537 pfb_entries);
538 } else {
539 pufbuf->istat &= ~ISTAT_NODESTROY;
540 puffs_framebuf_destroy(pufbuf);
541 }
542
543 /* omstart! */
544 }
545
546 return error;
547 }
548
549 int
550 puffs_framev_addfd(struct puffs_usermount *pu, int fd)
551 {
552 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
553 struct puffs_fctrl_io *fio;
554 struct kevent kev[2];
555 struct kevent *newevs;
556 size_t nfds;
557 int rv;
558
559 nfds = pfctrl->nfds+1;
560 newevs = realloc(pfctrl->evs, (2*nfds+1) * sizeof(struct kevent));
561 if (newevs == NULL)
562 return -1;
563 pfctrl->evs = newevs;
564
565 fio = malloc(sizeof(struct puffs_fctrl_io));
566 if (fio == NULL)
567 return -1;
568
569 if (pu->pu_state & PU_INLOOP) {
570 EV_SET(&kev[0], fd, EVFILT_READ, EV_ADD, 0, 0, (intptr_t)fio);
571 EV_SET(&kev[1], fd, EVFILT_WRITE, EV_ADD|EV_DISABLE,
572 0, 0, (intptr_t)fio);
573 rv = kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
574 if (rv == -1) {
575 free(fio);
576 return -1;
577 }
578 }
579
580 fio->io_fd = fd;
581 fio->cur_in = NULL;
582 fio->stat = 0;
583 TAILQ_INIT(&fio->snd_qing);
584 TAILQ_INIT(&fio->res_qing);
585
586 LIST_INSERT_HEAD(&pfctrl->fb_ios, fio, fio_entries);
587 pfctrl->nfds = nfds;
588
589 return 0;
590 }
591
592 void
593 puffs_framev_readclose(struct puffs_usermount *pu,
594 struct puffs_fctrl_io *fio, int error)
595 {
596 struct puffs_framebuf *pufbuf;
597 struct kevent kev;
598 int notflag;
599
600 if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
601 return;
602 fio->stat |= FIO_RDGONE;
603
604 if (fio->cur_in) {
605 puffs_framebuf_destroy(fio->cur_in);
606 fio->cur_in = NULL;
607 }
608
609 while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
610 TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
611 errnotify(pufbuf, error);
612 }
613
614 EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
615 (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
616
617 notflag = PUFFS_FBGONE_READ;
618 if (fio->stat & FIO_WRGONE)
619 notflag |= PUFFS_FBGONE_WRITE;
620
621 pu->pu_framectrl.fdnotfn(pu, fio->io_fd, notflag);
622 }
623
624 void
625 puffs_framev_writeclose(struct puffs_usermount *pu,
626 struct puffs_fctrl_io *fio, int error)
627 {
628 struct puffs_framebuf *pufbuf;
629 struct kevent kev;
630 int notflag;
631
632 if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
633 return;
634 fio->stat |= FIO_WRGONE;
635
636 while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
637 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
638 errnotify(pufbuf, error);
639 }
640
641 EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
642 (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
643
644 notflag = PUFFS_FBGONE_WRITE;
645 if (fio->stat & FIO_RDGONE)
646 notflag |= PUFFS_FBGONE_READ;
647
648 pu->pu_framectrl.fdnotfn(pu, fio->io_fd, notflag);
649 }
650
651 static int
652 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
653 {
654 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
655
656 LIST_REMOVE(fio, fio_entries);
657 if (pu->pu_state & PU_INLOOP) {
658 puffs_framev_readclose(pu, fio, error);
659 puffs_framev_writeclose(pu, fio, error);
660 }
661
662 /* don't bother with realloc */
663 pfctrl->nfds--;
664
665 /* don't free us yet, might have some references in event arrays */
666 fio->stat |= FIO_DEAD;
667 LIST_INSERT_HEAD(&pfctrl->fb_ios_rmlist, fio, fio_entries);
668
669 return 0;
670
671 }
672
673 int
674 puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
675 {
676 struct puffs_fctrl_io *fio;
677
678 fio = getfiobyfd(pu, fd);
679 if (fio == NULL) {
680 errno = ENXIO;
681 return -1;
682 }
683
684 return removefio(pu, fio, error ? error : ECONNRESET);
685 }
686
687 static void
688 defaultnot(struct puffs_usermount *pu, int fd, int what)
689 {
690
691 if (PUFFS_FBGONE_BOTH(what))
692 (void) puffs_framev_removefd(pu, fd, ECONNRESET);
693 }
694
695 void
696 puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
697 {
698
699 /* XXX & X: unmount is non-sensible */
700 defaultnot(pu, fd, what);
701 if (PUFFS_FBGONE_BOTH(what))
702 PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
703 }
704
705 void
706 puffs_framev_init(struct puffs_usermount *pu,
707 puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
708 puffs_framev_cmpframe_fn cmpfb, puffs_framev_fdnotify_fn fdnotfn)
709 {
710 struct puffs_framectrl *pfctrl;
711
712 pfctrl = &pu->pu_framectrl;
713 pfctrl->rfb = rfb;
714 pfctrl->wfb = wfb;
715 pfctrl->cmpfb = cmpfb;
716 if (fdnotfn)
717 pfctrl->fdnotfn = fdnotfn;
718 else
719 pfctrl->fdnotfn = defaultnot;
720 }
721
722 void
723 puffs_framev_exit(struct puffs_usermount *pu)
724 {
725 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
726 struct puffs_fctrl_io *fio;
727
728 while ((fio = LIST_FIRST(&pfctrl->fb_ios)) != NULL)
729 removefio(pu, fio, ENXIO);
730 free(pfctrl->evs);
731
732 /* closing pu->pu_kq takes care of puffsfd */
733 }
734