Annotation of sys/kern/kern_event.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: kern_event.c,v 1.31 2007/05/30 02:24:59 tedu Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
! 5: * All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: *
! 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 17: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 18: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 19: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 20: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 21: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 22: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 23: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 24: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 25: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 26: * SUCH DAMAGE.
! 27: *
! 28: * $FreeBSD: src/sys/kern/kern_event.c,v 1.22 2001/02/23 20:32:42 jlemon Exp $
! 29: */
! 30:
! 31: #include <sys/param.h>
! 32: #include <sys/systm.h>
! 33: #include <sys/kernel.h>
! 34: #include <sys/proc.h>
! 35: #include <sys/malloc.h>
! 36: #include <sys/unistd.h>
! 37: #include <sys/file.h>
! 38: #include <sys/filedesc.h>
! 39: #include <sys/fcntl.h>
! 40: #include <sys/selinfo.h>
! 41: #include <sys/queue.h>
! 42: #include <sys/event.h>
! 43: #include <sys/eventvar.h>
! 44: #include <sys/pool.h>
! 45: #include <sys/protosw.h>
! 46: #include <sys/socket.h>
! 47: #include <sys/socketvar.h>
! 48: #include <sys/stat.h>
! 49: #include <sys/uio.h>
! 50: #include <sys/mount.h>
! 51: #include <sys/poll.h>
! 52: #include <sys/syscallargs.h>
! 53: #include <sys/timeout.h>
! 54:
! 55: int kqueue_scan(struct file *fp, int maxevents,
! 56: struct kevent *ulistp, const struct timespec *timeout,
! 57: struct proc *p, int *retval);
! 58:
! 59: int kqueue_read(struct file *fp, off_t *poff, struct uio *uio,
! 60: struct ucred *cred);
! 61: int kqueue_write(struct file *fp, off_t *poff, struct uio *uio,
! 62: struct ucred *cred);
! 63: int kqueue_ioctl(struct file *fp, u_long com, caddr_t data,
! 64: struct proc *p);
! 65: int kqueue_poll(struct file *fp, int events, struct proc *p);
! 66: int kqueue_kqfilter(struct file *fp, struct knote *kn);
! 67: int kqueue_stat(struct file *fp, struct stat *st, struct proc *p);
! 68: int kqueue_close(struct file *fp, struct proc *p);
! 69: void kqueue_wakeup(struct kqueue *kq);
! 70:
! 71: struct fileops kqueueops = {
! 72: kqueue_read,
! 73: kqueue_write,
! 74: kqueue_ioctl,
! 75: kqueue_poll,
! 76: kqueue_kqfilter,
! 77: kqueue_stat,
! 78: kqueue_close
! 79: };
! 80:
! 81: void knote_attach(struct knote *kn, struct filedesc *fdp);
! 82: void knote_drop(struct knote *kn, struct proc *p, struct filedesc *fdp);
! 83: void knote_enqueue(struct knote *kn);
! 84: void knote_dequeue(struct knote *kn);
! 85: #define knote_alloc() ((struct knote *)pool_get(&knote_pool, PR_WAITOK))
! 86: #define knote_free(kn) pool_put(&knote_pool, (kn))
! 87:
! 88: void filt_kqdetach(struct knote *kn);
! 89: int filt_kqueue(struct knote *kn, long hint);
! 90: int filt_procattach(struct knote *kn);
! 91: void filt_procdetach(struct knote *kn);
! 92: int filt_proc(struct knote *kn, long hint);
! 93: int filt_fileattach(struct knote *kn);
! 94: void filt_timerexpire(void *knx);
! 95: int filt_timerattach(struct knote *kn);
! 96: void filt_timerdetach(struct knote *kn);
! 97: int filt_timer(struct knote *kn, long hint);
! 98:
! 99: struct filterops kqread_filtops =
! 100: { 1, NULL, filt_kqdetach, filt_kqueue };
! 101: struct filterops proc_filtops =
! 102: { 0, filt_procattach, filt_procdetach, filt_proc };
! 103: struct filterops file_filtops =
! 104: { 1, filt_fileattach, NULL, NULL };
! 105: struct filterops timer_filtops =
! 106: { 0, filt_timerattach, filt_timerdetach, filt_timer };
! 107:
! 108: struct pool knote_pool;
! 109: struct pool kqueue_pool;
! 110: int kq_ntimeouts = 0;
! 111: int kq_timeoutmax = (4 * 1024);
! 112:
! 113: #define KNOTE_ACTIVATE(kn) do { \
! 114: kn->kn_status |= KN_ACTIVE; \
! 115: if ((kn->kn_status & (KN_QUEUED | KN_DISABLED)) == 0) \
! 116: knote_enqueue(kn); \
! 117: } while(0)
! 118:
! 119: #define KN_HASHSIZE 64 /* XXX should be tunable */
! 120: #define KN_HASH(val, mask) (((val) ^ (val >> 8)) & (mask))
! 121:
! 122: extern struct filterops sig_filtops;
! 123: #ifdef notyet
! 124: extern struct filterops aio_filtops;
! 125: #endif
! 126:
! 127: /*
! 128: * Table for for all system-defined filters.
! 129: */
! 130: struct filterops *sysfilt_ops[] = {
! 131: &file_filtops, /* EVFILT_READ */
! 132: &file_filtops, /* EVFILT_WRITE */
! 133: NULL, /*&aio_filtops,*/ /* EVFILT_AIO */
! 134: &file_filtops, /* EVFILT_VNODE */
! 135: &proc_filtops, /* EVFILT_PROC */
! 136: &sig_filtops, /* EVFILT_SIGNAL */
! 137: &timer_filtops, /* EVFILT_TIMER */
! 138: };
! 139:
! 140: void kqueue_init(void);
! 141:
! 142: void
! 143: kqueue_init(void)
! 144: {
! 145:
! 146: pool_init(&kqueue_pool, sizeof(struct kqueue), 0, 0, 0, "kqueuepl",
! 147: &pool_allocator_nointr);
! 148: pool_init(&knote_pool, sizeof(struct knote), 0, 0, 0, "knotepl",
! 149: &pool_allocator_nointr);
! 150: }
! 151:
! 152: int
! 153: filt_fileattach(struct knote *kn)
! 154: {
! 155: struct file *fp = kn->kn_fp;
! 156:
! 157: return ((*fp->f_ops->fo_kqfilter)(fp, kn));
! 158: }
! 159:
! 160: int
! 161: kqueue_kqfilter(struct file *fp, struct knote *kn)
! 162: {
! 163: struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data;
! 164:
! 165: if (kn->kn_filter != EVFILT_READ)
! 166: return (1);
! 167:
! 168: kn->kn_fop = &kqread_filtops;
! 169: SLIST_INSERT_HEAD(&kq->kq_sel.si_note, kn, kn_selnext);
! 170: return (0);
! 171: }
! 172:
! 173: void
! 174: filt_kqdetach(struct knote *kn)
! 175: {
! 176: struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data;
! 177:
! 178: SLIST_REMOVE(&kq->kq_sel.si_note, kn, knote, kn_selnext);
! 179: }
! 180:
! 181: /*ARGSUSED*/
! 182: int
! 183: filt_kqueue(struct knote *kn, long hint)
! 184: {
! 185: struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data;
! 186:
! 187: kn->kn_data = kq->kq_count;
! 188: return (kn->kn_data > 0);
! 189: }
! 190:
! 191: int
! 192: filt_procattach(struct knote *kn)
! 193: {
! 194: struct proc *p;
! 195:
! 196: p = pfind(kn->kn_id);
! 197: if (p == NULL)
! 198: return (ESRCH);
! 199:
! 200: /*
! 201: * Fail if it's not owned by you, or the last exec gave us
! 202: * setuid/setgid privs (unless you're root).
! 203: */
! 204: if ((p->p_cred->p_ruid != curproc->p_cred->p_ruid ||
! 205: (p->p_flag & P_SUGID)) && suser(curproc, 0) != 0)
! 206: return (EACCES);
! 207:
! 208: kn->kn_ptr.p_proc = p;
! 209: kn->kn_flags |= EV_CLEAR; /* automatically set */
! 210:
! 211: /*
! 212: * internal flag indicating registration done by kernel
! 213: */
! 214: if (kn->kn_flags & EV_FLAG1) {
! 215: kn->kn_data = kn->kn_sdata; /* ppid */
! 216: kn->kn_fflags = NOTE_CHILD;
! 217: kn->kn_flags &= ~EV_FLAG1;
! 218: }
! 219:
! 220: /* XXX lock the proc here while adding to the list? */
! 221: SLIST_INSERT_HEAD(&p->p_klist, kn, kn_selnext);
! 222:
! 223: return (0);
! 224: }
! 225:
! 226: /*
! 227: * The knote may be attached to a different process, which may exit,
! 228: * leaving nothing for the knote to be attached to. So when the process
! 229: * exits, the knote is marked as DETACHED and also flagged as ONESHOT so
! 230: * it will be deleted when read out. However, as part of the knote deletion,
! 231: * this routine is called, so a check is needed to avoid actually performing
! 232: * a detach, because the original process does not exist any more.
! 233: */
! 234: void
! 235: filt_procdetach(struct knote *kn)
! 236: {
! 237: struct proc *p = kn->kn_ptr.p_proc;
! 238:
! 239: if (kn->kn_status & KN_DETACHED)
! 240: return;
! 241:
! 242: /* XXX locking? this might modify another process. */
! 243: SLIST_REMOVE(&p->p_klist, kn, knote, kn_selnext);
! 244: }
! 245:
! 246: int
! 247: filt_proc(struct knote *kn, long hint)
! 248: {
! 249: u_int event;
! 250:
! 251: /*
! 252: * mask off extra data
! 253: */
! 254: event = (u_int)hint & NOTE_PCTRLMASK;
! 255:
! 256: /*
! 257: * if the user is interested in this event, record it.
! 258: */
! 259: if (kn->kn_sfflags & event)
! 260: kn->kn_fflags |= event;
! 261:
! 262: /*
! 263: * process is gone, so flag the event as finished.
! 264: */
! 265: if (event == NOTE_EXIT) {
! 266: kn->kn_status |= KN_DETACHED;
! 267: kn->kn_flags |= (EV_EOF | EV_ONESHOT);
! 268: return (1);
! 269: }
! 270:
! 271: /*
! 272: * process forked, and user wants to track the new process,
! 273: * so attach a new knote to it, and immediately report an
! 274: * event with the parent's pid.
! 275: */
! 276: if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) {
! 277: struct kevent kev;
! 278: int error;
! 279:
! 280: /*
! 281: * register knote with new process.
! 282: */
! 283: kev.ident = hint & NOTE_PDATAMASK; /* pid */
! 284: kev.filter = kn->kn_filter;
! 285: kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1;
! 286: kev.fflags = kn->kn_sfflags;
! 287: kev.data = kn->kn_id; /* parent */
! 288: kev.udata = kn->kn_kevent.udata; /* preserve udata */
! 289: error = kqueue_register(kn->kn_kq, &kev, NULL);
! 290: if (error)
! 291: kn->kn_fflags |= NOTE_TRACKERR;
! 292: }
! 293:
! 294: return (kn->kn_fflags != 0);
! 295: }
! 296:
! 297: void
! 298: filt_timerexpire(void *knx)
! 299: {
! 300: struct knote *kn = knx;
! 301: struct timeval tv;
! 302: int tticks;
! 303:
! 304: kn->kn_data++;
! 305: KNOTE_ACTIVATE(kn);
! 306:
! 307: if ((kn->kn_flags & EV_ONESHOT) == 0) {
! 308: tv.tv_sec = kn->kn_sdata / 1000;
! 309: tv.tv_usec = (kn->kn_sdata % 1000) * 1000;
! 310: tticks = tvtohz(&tv);
! 311: timeout_add((struct timeout *)kn->kn_hook, tticks);
! 312: }
! 313: }
! 314:
! 315:
! 316: /*
! 317: * data contains amount of time to sleep, in milliseconds
! 318: */
! 319: int
! 320: filt_timerattach(struct knote *kn)
! 321: {
! 322: struct timeout *to;
! 323: struct timeval tv;
! 324: int tticks;
! 325:
! 326: if (kq_ntimeouts > kq_timeoutmax)
! 327: return (ENOMEM);
! 328: kq_ntimeouts++;
! 329:
! 330: tv.tv_sec = kn->kn_sdata / 1000;
! 331: tv.tv_usec = (kn->kn_sdata % 1000) * 1000;
! 332: tticks = tvtohz(&tv);
! 333:
! 334: kn->kn_flags |= EV_CLEAR; /* automatically set */
! 335: MALLOC(to, struct timeout *, sizeof(*to), M_KEVENT, 0);
! 336: timeout_set(to, filt_timerexpire, kn);
! 337: timeout_add(to, tticks);
! 338: kn->kn_hook = to;
! 339:
! 340: return (0);
! 341: }
! 342:
! 343: void
! 344: filt_timerdetach(struct knote *kn)
! 345: {
! 346: struct timeout *to;
! 347:
! 348: to = (struct timeout *)kn->kn_hook;
! 349: timeout_del(to);
! 350: FREE(to, M_KEVENT);
! 351: kq_ntimeouts--;
! 352: }
! 353:
! 354: int
! 355: filt_timer(struct knote *kn, long hint)
! 356: {
! 357: return (kn->kn_data != 0);
! 358: }
! 359:
! 360:
! 361: /*
! 362: * filt_seltrue:
! 363: *
! 364: * This filter "event" routine simulates seltrue().
! 365: */
! 366: int
! 367: filt_seltrue(struct knote *kn, long hint)
! 368: {
! 369:
! 370: /*
! 371: * We don't know how much data can be read/written,
! 372: * but we know that it *can* be. This is about as
! 373: * good as select/poll does as well.
! 374: */
! 375: kn->kn_data = 0;
! 376: return (1);
! 377: }
! 378:
! 379: int
! 380: sys_kqueue(struct proc *p, void *v, register_t *retval)
! 381: {
! 382: struct filedesc *fdp = p->p_fd;
! 383: struct kqueue *kq;
! 384: struct file *fp;
! 385: int fd, error;
! 386:
! 387: error = falloc(p, &fp, &fd);
! 388: if (error)
! 389: return (error);
! 390: fp->f_flag = FREAD | FWRITE;
! 391: fp->f_type = DTYPE_KQUEUE;
! 392: fp->f_ops = &kqueueops;
! 393: kq = pool_get(&kqueue_pool, PR_WAITOK);
! 394: bzero(kq, sizeof(*kq));
! 395: TAILQ_INIT(&kq->kq_head);
! 396: fp->f_data = (caddr_t)kq;
! 397: *retval = fd;
! 398: if (fdp->fd_knlistsize < 0)
! 399: fdp->fd_knlistsize = 0; /* this process has a kq */
! 400: kq->kq_fdp = fdp;
! 401: FILE_SET_MATURE(fp);
! 402: return (0);
! 403: }
! 404:
! 405: int
! 406: sys_kevent(struct proc *p, void *v, register_t *retval)
! 407: {
! 408: struct filedesc* fdp = p->p_fd;
! 409: struct sys_kevent_args /* {
! 410: syscallarg(int) fd;
! 411: syscallarg(const struct kevent *) changelist;
! 412: syscallarg(int) nchanges;
! 413: syscallarg(struct kevent *) eventlist;
! 414: syscallarg(int) nevents;
! 415: syscallarg(const struct timespec *) timeout;
! 416: } */ *uap = v;
! 417: struct kevent *kevp;
! 418: struct kqueue *kq;
! 419: struct file *fp;
! 420: struct timespec ts;
! 421: int i, n, nerrors, error;
! 422:
! 423: if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL ||
! 424: (fp->f_type != DTYPE_KQUEUE))
! 425: return (EBADF);
! 426:
! 427: FREF(fp);
! 428:
! 429: if (SCARG(uap, timeout) != NULL) {
! 430: error = copyin(SCARG(uap, timeout), &ts, sizeof(ts));
! 431: if (error)
! 432: goto done;
! 433: SCARG(uap, timeout) = &ts;
! 434: }
! 435:
! 436: kq = (struct kqueue *)fp->f_data;
! 437: nerrors = 0;
! 438:
! 439: while (SCARG(uap, nchanges) > 0) {
! 440: n = SCARG(uap, nchanges) > KQ_NEVENTS
! 441: ? KQ_NEVENTS : SCARG(uap, nchanges);
! 442: error = copyin(SCARG(uap, changelist), kq->kq_kev,
! 443: n * sizeof(struct kevent));
! 444: if (error)
! 445: goto done;
! 446: for (i = 0; i < n; i++) {
! 447: kevp = &kq->kq_kev[i];
! 448: kevp->flags &= ~EV_SYSFLAGS;
! 449: error = kqueue_register(kq, kevp, p);
! 450: if (error) {
! 451: if (SCARG(uap, nevents) != 0) {
! 452: kevp->flags = EV_ERROR;
! 453: kevp->data = error;
! 454: (void) copyout((caddr_t)kevp,
! 455: (caddr_t)SCARG(uap, eventlist),
! 456: sizeof(*kevp));
! 457: SCARG(uap, eventlist)++;
! 458: SCARG(uap, nevents)--;
! 459: nerrors++;
! 460: } else {
! 461: goto done;
! 462: }
! 463: }
! 464: }
! 465: SCARG(uap, nchanges) -= n;
! 466: SCARG(uap, changelist) += n;
! 467: }
! 468: if (nerrors) {
! 469: *retval = nerrors;
! 470: error = 0;
! 471: goto done;
! 472: }
! 473:
! 474: error = kqueue_scan(fp, SCARG(uap, nevents), SCARG(uap, eventlist),
! 475: SCARG(uap, timeout), p, &n);
! 476: *retval = n;
! 477: done:
! 478: FRELE(fp);
! 479: return (error);
! 480: }
! 481:
! 482: int
! 483: kqueue_register(struct kqueue *kq, struct kevent *kev, struct proc *p)
! 484: {
! 485: struct filedesc *fdp = kq->kq_fdp;
! 486: struct filterops *fops = NULL;
! 487: struct file *fp = NULL;
! 488: struct knote *kn = NULL;
! 489: int s, error = 0;
! 490:
! 491: if (kev->filter < 0) {
! 492: if (kev->filter + EVFILT_SYSCOUNT < 0)
! 493: return (EINVAL);
! 494: fops = sysfilt_ops[~kev->filter]; /* to 0-base index */
! 495: }
! 496:
! 497: if (fops == NULL) {
! 498: /*
! 499: * XXX
! 500: * filter attach routine is responsible for ensuring that
! 501: * the identifier can be attached to it.
! 502: */
! 503: return (EINVAL);
! 504: }
! 505:
! 506: if (fops->f_isfd) {
! 507: /* validate descriptor */
! 508: if ((fp = fd_getfile(fdp, kev->ident)) == NULL)
! 509: return (EBADF);
! 510: FREF(fp);
! 511: fp->f_count++;
! 512:
! 513: if (kev->ident < fdp->fd_knlistsize) {
! 514: SLIST_FOREACH(kn, &fdp->fd_knlist[kev->ident], kn_link)
! 515: if (kq == kn->kn_kq &&
! 516: kev->filter == kn->kn_filter)
! 517: break;
! 518: }
! 519: } else {
! 520: if (fdp->fd_knhashmask != 0) {
! 521: struct klist *list;
! 522:
! 523: list = &fdp->fd_knhash[
! 524: KN_HASH((u_long)kev->ident, fdp->fd_knhashmask)];
! 525: SLIST_FOREACH(kn, list, kn_link)
! 526: if (kev->ident == kn->kn_id &&
! 527: kq == kn->kn_kq &&
! 528: kev->filter == kn->kn_filter)
! 529: break;
! 530: }
! 531: }
! 532:
! 533: if (kn == NULL && ((kev->flags & EV_ADD) == 0)) {
! 534: error = ENOENT;
! 535: goto done;
! 536: }
! 537:
! 538: /*
! 539: * kn now contains the matching knote, or NULL if no match
! 540: */
! 541: if (kev->flags & EV_ADD) {
! 542:
! 543: if (kn == NULL) {
! 544: kn = knote_alloc();
! 545: if (kn == NULL) {
! 546: error = ENOMEM;
! 547: goto done;
! 548: }
! 549: kn->kn_fp = fp;
! 550: kn->kn_kq = kq;
! 551: kn->kn_fop = fops;
! 552:
! 553: /*
! 554: * apply reference count to knote structure, and
! 555: * do not release it at the end of this routine.
! 556: */
! 557: if (fp != NULL)
! 558: FRELE(fp);
! 559: fp = NULL;
! 560:
! 561: kn->kn_sfflags = kev->fflags;
! 562: kn->kn_sdata = kev->data;
! 563: kev->fflags = 0;
! 564: kev->data = 0;
! 565: kn->kn_kevent = *kev;
! 566:
! 567: knote_attach(kn, fdp);
! 568: if ((error = fops->f_attach(kn)) != 0) {
! 569: knote_drop(kn, p, fdp);
! 570: goto done;
! 571: }
! 572: } else {
! 573: /*
! 574: * The user may change some filter values after the
! 575: * initial EV_ADD, but doing so will not reset any
! 576: * filters which have already been triggered.
! 577: */
! 578: kn->kn_sfflags = kev->fflags;
! 579: kn->kn_sdata = kev->data;
! 580: kn->kn_kevent.udata = kev->udata;
! 581: }
! 582:
! 583: s = splhigh();
! 584: if (kn->kn_fop->f_event(kn, 0))
! 585: KNOTE_ACTIVATE(kn);
! 586: splx(s);
! 587:
! 588: } else if (kev->flags & EV_DELETE) {
! 589: kn->kn_fop->f_detach(kn);
! 590: knote_drop(kn, p, p->p_fd);
! 591: goto done;
! 592: }
! 593:
! 594: if ((kev->flags & EV_DISABLE) &&
! 595: ((kn->kn_status & KN_DISABLED) == 0)) {
! 596: s = splhigh();
! 597: kn->kn_status |= KN_DISABLED;
! 598: splx(s);
! 599: }
! 600:
! 601: if ((kev->flags & EV_ENABLE) && (kn->kn_status & KN_DISABLED)) {
! 602: s = splhigh();
! 603: kn->kn_status &= ~KN_DISABLED;
! 604: if ((kn->kn_status & KN_ACTIVE) &&
! 605: ((kn->kn_status & KN_QUEUED) == 0))
! 606: knote_enqueue(kn);
! 607: splx(s);
! 608: }
! 609:
! 610: done:
! 611: if (fp != NULL)
! 612: closef(fp, p);
! 613: return (error);
! 614: }
! 615:
! 616: int
! 617: kqueue_scan(struct file *fp, int maxevents, struct kevent *ulistp,
! 618: const struct timespec *tsp, struct proc *p, int *retval)
! 619: {
! 620: struct kqueue *kq = (struct kqueue *)fp->f_data;
! 621: struct kevent *kevp;
! 622: struct timeval atv, rtv, ttv;
! 623: struct knote *kn, marker;
! 624: int s, count, timeout, nkev = 0, error = 0;
! 625:
! 626: count = maxevents;
! 627: if (count == 0)
! 628: goto done;
! 629:
! 630: if (tsp != NULL) {
! 631: TIMESPEC_TO_TIMEVAL(&atv, tsp);
! 632: if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) {
! 633: /* No timeout, just poll */
! 634: timeout = -1;
! 635: goto start;
! 636: }
! 637: if (itimerfix(&atv)) {
! 638: error = EINVAL;
! 639: goto done;
! 640: }
! 641:
! 642: timeout = atv.tv_sec > 24 * 60 * 60 ?
! 643: 24 * 60 * 60 * hz : tvtohz(&atv);
! 644:
! 645: getmicrouptime(&rtv);
! 646: timeradd(&atv, &rtv, &atv);
! 647: } else {
! 648: atv.tv_sec = 0;
! 649: atv.tv_usec = 0;
! 650: timeout = 0;
! 651: }
! 652: goto start;
! 653:
! 654: retry:
! 655: if (atv.tv_sec || atv.tv_usec) {
! 656: getmicrouptime(&rtv);
! 657: if (timercmp(&rtv, &atv, >=))
! 658: goto done;
! 659: ttv = atv;
! 660: timersub(&ttv, &rtv, &ttv);
! 661: timeout = ttv.tv_sec > 24 * 60 * 60 ?
! 662: 24 * 60 * 60 * hz : tvtohz(&ttv);
! 663: }
! 664:
! 665: start:
! 666: kevp = kq->kq_kev;
! 667: s = splhigh();
! 668: if (kq->kq_count == 0) {
! 669: if (timeout < 0) {
! 670: error = EWOULDBLOCK;
! 671: } else {
! 672: kq->kq_state |= KQ_SLEEP;
! 673: error = tsleep(kq, PSOCK | PCATCH, "kqread", timeout);
! 674: }
! 675: splx(s);
! 676: if (error == 0)
! 677: goto retry;
! 678: /* don't restart after signals... */
! 679: if (error == ERESTART)
! 680: error = EINTR;
! 681: else if (error == EWOULDBLOCK)
! 682: error = 0;
! 683: goto done;
! 684: }
! 685:
! 686: TAILQ_INSERT_TAIL(&kq->kq_head, &marker, kn_tqe);
! 687: while (count) {
! 688: kn = TAILQ_FIRST(&kq->kq_head);
! 689: TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
! 690: if (kn == &marker) {
! 691: splx(s);
! 692: if (count == maxevents)
! 693: goto retry;
! 694: goto done;
! 695: }
! 696: if (kn->kn_status & KN_DISABLED) {
! 697: kn->kn_status &= ~KN_QUEUED;
! 698: kq->kq_count--;
! 699: continue;
! 700: }
! 701: if ((kn->kn_flags & EV_ONESHOT) == 0 &&
! 702: kn->kn_fop->f_event(kn, 0) == 0) {
! 703: kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
! 704: kq->kq_count--;
! 705: continue;
! 706: }
! 707: *kevp = kn->kn_kevent;
! 708: kevp++;
! 709: nkev++;
! 710: if (kn->kn_flags & EV_ONESHOT) {
! 711: kn->kn_status &= ~KN_QUEUED;
! 712: kq->kq_count--;
! 713: splx(s);
! 714: kn->kn_fop->f_detach(kn);
! 715: knote_drop(kn, p, p->p_fd);
! 716: s = splhigh();
! 717: } else if (kn->kn_flags & EV_CLEAR) {
! 718: kn->kn_data = 0;
! 719: kn->kn_fflags = 0;
! 720: kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
! 721: kq->kq_count--;
! 722: } else {
! 723: TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
! 724: }
! 725: count--;
! 726: if (nkev == KQ_NEVENTS) {
! 727: splx(s);
! 728: error = copyout((caddr_t)&kq->kq_kev, (caddr_t)ulistp,
! 729: sizeof(struct kevent) * nkev);
! 730: ulistp += nkev;
! 731: nkev = 0;
! 732: kevp = kq->kq_kev;
! 733: s = splhigh();
! 734: if (error)
! 735: break;
! 736: }
! 737: }
! 738: TAILQ_REMOVE(&kq->kq_head, &marker, kn_tqe);
! 739: splx(s);
! 740: done:
! 741: if (nkev != 0)
! 742: error = copyout((caddr_t)&kq->kq_kev, (caddr_t)ulistp,
! 743: sizeof(struct kevent) * nkev);
! 744: *retval = maxevents - count;
! 745: return (error);
! 746: }
! 747:
! 748: /*
! 749: * XXX
! 750: * This could be expanded to call kqueue_scan, if desired.
! 751: */
! 752: /*ARGSUSED*/
! 753: int
! 754: kqueue_read(struct file *fp, off_t *poff, struct uio *uio, struct ucred *cred)
! 755: {
! 756: return (ENXIO);
! 757: }
! 758:
! 759: /*ARGSUSED*/
! 760: int
! 761: kqueue_write(struct file *fp, off_t *poff, struct uio *uio, struct ucred *cred)
! 762:
! 763: {
! 764: return (ENXIO);
! 765: }
! 766:
! 767: /*ARGSUSED*/
! 768: int
! 769: kqueue_ioctl(struct file *fp, u_long com, caddr_t data, struct proc *p)
! 770: {
! 771: return (ENOTTY);
! 772: }
! 773:
! 774: /*ARGSUSED*/
! 775: int
! 776: kqueue_poll(struct file *fp, int events, struct proc *p)
! 777: {
! 778: struct kqueue *kq = (struct kqueue *)fp->f_data;
! 779: int revents = 0;
! 780: int s = splhigh();
! 781:
! 782: if (events & (POLLIN | POLLRDNORM)) {
! 783: if (kq->kq_count) {
! 784: revents |= events & (POLLIN | POLLRDNORM);
! 785: } else {
! 786: selrecord(p, &kq->kq_sel);
! 787: kq->kq_state |= KQ_SEL;
! 788: }
! 789: }
! 790: splx(s);
! 791: return (revents);
! 792: }
! 793:
! 794: /*ARGSUSED*/
! 795: int
! 796: kqueue_stat(struct file *fp, struct stat *st, struct proc *p)
! 797: {
! 798: struct kqueue *kq = (struct kqueue *)fp->f_data;
! 799:
! 800: bzero((void *)st, sizeof(*st));
! 801: st->st_size = kq->kq_count;
! 802: st->st_blksize = sizeof(struct kevent);
! 803: st->st_mode = S_IFIFO;
! 804: return (0);
! 805: }
! 806:
! 807: /*ARGSUSED*/
! 808: int
! 809: kqueue_close(struct file *fp, struct proc *p)
! 810: {
! 811: struct kqueue *kq = (struct kqueue *)fp->f_data;
! 812: struct filedesc *fdp = p->p_fd;
! 813: struct knote **knp, *kn, *kn0;
! 814: int i;
! 815:
! 816: for (i = 0; i < fdp->fd_knlistsize; i++) {
! 817: knp = &SLIST_FIRST(&fdp->fd_knlist[i]);
! 818: kn = *knp;
! 819: while (kn != NULL) {
! 820: kn0 = SLIST_NEXT(kn, kn_link);
! 821: if (kq == kn->kn_kq) {
! 822: FREF(kn->kn_fp);
! 823: kn->kn_fop->f_detach(kn);
! 824: closef(kn->kn_fp, p);
! 825: knote_free(kn);
! 826: *knp = kn0;
! 827: } else {
! 828: knp = &SLIST_NEXT(kn, kn_link);
! 829: }
! 830: kn = kn0;
! 831: }
! 832: }
! 833: if (fdp->fd_knhashmask != 0) {
! 834: for (i = 0; i < fdp->fd_knhashmask + 1; i++) {
! 835: knp = &SLIST_FIRST(&fdp->fd_knhash[i]);
! 836: kn = *knp;
! 837: while (kn != NULL) {
! 838: kn0 = SLIST_NEXT(kn, kn_link);
! 839: if (kq == kn->kn_kq) {
! 840: kn->kn_fop->f_detach(kn);
! 841: /* XXX non-fd release of kn->kn_ptr */
! 842: knote_free(kn);
! 843: *knp = kn0;
! 844: } else {
! 845: knp = &SLIST_NEXT(kn, kn_link);
! 846: }
! 847: kn = kn0;
! 848: }
! 849: }
! 850: }
! 851: pool_put(&kqueue_pool, kq);
! 852: fp->f_data = NULL;
! 853:
! 854: return (0);
! 855: }
! 856:
! 857: void
! 858: kqueue_wakeup(struct kqueue *kq)
! 859: {
! 860:
! 861: if (kq->kq_state & KQ_SLEEP) {
! 862: kq->kq_state &= ~KQ_SLEEP;
! 863: wakeup(kq);
! 864: }
! 865: if (kq->kq_state & KQ_SEL) {
! 866: kq->kq_state &= ~KQ_SEL;
! 867: selwakeup(&kq->kq_sel);
! 868: }
! 869: KNOTE(&kq->kq_sel.si_note, 0);
! 870: }
! 871:
! 872: /*
! 873: * walk down a list of knotes, activating them if their event has triggered.
! 874: */
! 875: void
! 876: knote(struct klist *list, long hint)
! 877: {
! 878: struct knote *kn;
! 879:
! 880: SLIST_FOREACH(kn, list, kn_selnext)
! 881: if (kn->kn_fop->f_event(kn, hint))
! 882: KNOTE_ACTIVATE(kn);
! 883: }
! 884:
! 885: /*
! 886: * remove all knotes from a specified klist
! 887: */
! 888: void
! 889: knote_remove(struct proc *p, struct klist *list)
! 890: {
! 891: struct knote *kn;
! 892:
! 893: while ((kn = SLIST_FIRST(list)) != NULL) {
! 894: kn->kn_fop->f_detach(kn);
! 895: knote_drop(kn, p, p->p_fd);
! 896: }
! 897: }
! 898:
! 899: /*
! 900: * remove all knotes referencing a specified fd
! 901: */
! 902: void
! 903: knote_fdclose(struct proc *p, int fd)
! 904: {
! 905: struct filedesc *fdp = p->p_fd;
! 906: struct klist *list = &fdp->fd_knlist[fd];
! 907:
! 908: knote_remove(p, list);
! 909: }
! 910:
! 911: void
! 912: knote_attach(struct knote *kn, struct filedesc *fdp)
! 913: {
! 914: struct klist *list;
! 915: int size;
! 916:
! 917: if (! kn->kn_fop->f_isfd) {
! 918: if (fdp->fd_knhashmask == 0)
! 919: fdp->fd_knhash = hashinit(KN_HASHSIZE, M_TEMP,
! 920: M_WAITOK, &fdp->fd_knhashmask);
! 921: list = &fdp->fd_knhash[KN_HASH(kn->kn_id, fdp->fd_knhashmask)];
! 922: goto done;
! 923: }
! 924:
! 925: if (fdp->fd_knlistsize <= kn->kn_id) {
! 926: size = fdp->fd_knlistsize;
! 927: while (size <= kn->kn_id)
! 928: size += KQEXTENT;
! 929: list = malloc(size * sizeof(struct klist *), M_TEMP, M_WAITOK);
! 930: bcopy((caddr_t)fdp->fd_knlist, (caddr_t)list,
! 931: fdp->fd_knlistsize * sizeof(struct klist *));
! 932: bzero((caddr_t)list +
! 933: fdp->fd_knlistsize * sizeof(struct klist *),
! 934: (size - fdp->fd_knlistsize) * sizeof(struct klist *));
! 935: if (fdp->fd_knlist != NULL)
! 936: free(fdp->fd_knlist, M_TEMP);
! 937: fdp->fd_knlistsize = size;
! 938: fdp->fd_knlist = list;
! 939: }
! 940: list = &fdp->fd_knlist[kn->kn_id];
! 941: done:
! 942: SLIST_INSERT_HEAD(list, kn, kn_link);
! 943: kn->kn_status = 0;
! 944: }
! 945:
! 946: /*
! 947: * should be called at spl == 0, since we don't want to hold spl
! 948: * while calling closef and free.
! 949: */
! 950: void
! 951: knote_drop(struct knote *kn, struct proc *p, struct filedesc *fdp)
! 952: {
! 953: struct klist *list;
! 954:
! 955: if (kn->kn_fop->f_isfd)
! 956: list = &fdp->fd_knlist[kn->kn_id];
! 957: else
! 958: list = &fdp->fd_knhash[KN_HASH(kn->kn_id, fdp->fd_knhashmask)];
! 959:
! 960: SLIST_REMOVE(list, kn, knote, kn_link);
! 961: if (kn->kn_status & KN_QUEUED)
! 962: knote_dequeue(kn);
! 963: if (kn->kn_fop->f_isfd) {
! 964: FREF(kn->kn_fp);
! 965: closef(kn->kn_fp, p);
! 966: }
! 967: knote_free(kn);
! 968: }
! 969:
! 970:
! 971: void
! 972: knote_enqueue(struct knote *kn)
! 973: {
! 974: struct kqueue *kq = kn->kn_kq;
! 975: int s = splhigh();
! 976:
! 977: KASSERT((kn->kn_status & KN_QUEUED) == 0);
! 978:
! 979: TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
! 980: kn->kn_status |= KN_QUEUED;
! 981: kq->kq_count++;
! 982: splx(s);
! 983: kqueue_wakeup(kq);
! 984: }
! 985:
! 986: void
! 987: knote_dequeue(struct knote *kn)
! 988: {
! 989: struct kqueue *kq = kn->kn_kq;
! 990: int s = splhigh();
! 991:
! 992: KASSERT(kn->kn_status & KN_QUEUED);
! 993:
! 994: TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
! 995: kn->kn_status &= ~KN_QUEUED;
! 996: kq->kq_count--;
! 997: splx(s);
! 998: }
! 999:
! 1000: void
! 1001: klist_invalidate(struct klist *list)
! 1002: {
! 1003: struct knote *kn;
! 1004:
! 1005: SLIST_FOREACH(kn, list, kn_selnext) {
! 1006: kn->kn_status |= KN_DETACHED;
! 1007: kn->kn_flags |= EV_EOF | EV_ONESHOT;
! 1008: }
! 1009: }
CVSweb