[BACK]Return to nfs_kq.c CVS log [TXT][DIR] Up to [local] / sys / nfs

Annotation of sys/nfs/nfs_kq.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: nfs_kq.c,v 1.9 2007/06/01 23:47:57 deraadt Exp $ */
                      2: /*     $NetBSD: nfs_kq.c,v 1.7 2003/10/30 01:43:10 simonb Exp $        */
                      3:
                      4: /*-
                      5:  * Copyright (c) 2002 The NetBSD Foundation, Inc.
                      6:  * All rights reserved.
                      7:  *
                      8:  * This code is derived from software contributed to The NetBSD Foundation
                      9:  * by Jaromir Dolecek.
                     10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  * 3. All advertising materials mentioning features or use of this software
                     20:  *    must display the following acknowledgement:
                     21:  *        This product includes software developed by the NetBSD
                     22:  *        Foundation, Inc. and its contributors.
                     23:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     24:  *    contributors may be used to endorse or promote products derived
                     25:  *    from this software without specific prior written permission.
                     26:  *
                     27:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     28:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     29:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     30:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     31:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     32:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     33:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     34:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     35:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     36:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     37:  * POSSIBILITY OF SUCH DAMAGE.
                     38:  */
                     39:
                     40: #include <sys/cdefs.h>
                     41:
                     42: #include <sys/param.h>
                     43: #include <sys/systm.h>
                     44: #include <sys/kernel.h>
                     45: #include <sys/proc.h>
                     46: #include <sys/mount.h>
                     47: #include <sys/malloc.h>
                     48: #include <sys/vnode.h>
                     49: #include <sys/unistd.h>
                     50: #include <sys/file.h>
                     51: #include <sys/kthread.h>
                     52: #include <sys/rwlock.h>
                     53:
                     54: #include <uvm/uvm_extern.h>
                     55: #include <uvm/uvm.h>
                     56:
                     57: #include <nfs/rpcv2.h>
                     58: #include <nfs/nfsproto.h>
                     59: #include <nfs/nfs.h>
                     60: #include <nfs/nfsnode.h>
                     61: #include <nfs/nfs_var.h>
                     62:
                     63: void   nfs_kqpoll(void *);
                     64:
                     65: void   filt_nfsdetach(struct knote *);
                     66: int    filt_nfsread(struct knote *, long);
                     67: int    filt_nfsvnode(struct knote *, long);
                     68:
                     69: struct kevq {
                     70:        SLIST_ENTRY(kevq)       kev_link;
                     71:        struct vnode            *vp;
                     72:        u_int                   usecount;
                     73:        u_int                   flags;
                     74: #define KEVQ_BUSY      0x01    /* currently being processed */
                     75: #define KEVQ_WANT      0x02    /* want to change this entry */
                     76:        struct timespec         omtime; /* old modification time */
                     77:        struct timespec         octime; /* old change time */
                     78:        nlink_t                 onlink; /* old number of references to file */
                     79: };
                     80: SLIST_HEAD(kevqlist, kevq);
                     81:
                     82: struct rwlock nfskevq_lock = RWLOCK_INITIALIZER("nfskqlk");
                     83: struct proc *pnfskq;
                     84: struct kevqlist kevlist = SLIST_HEAD_INITIALIZER(kevlist);
                     85:
                     86: /*
                     87:  * This quite simplistic routine periodically checks for server changes
                     88:  * of any of the watched files every NFS_MINATTRTIMO/2 seconds.
                     89:  * Only changes in size, modification time, change time and nlinks
                     90:  * are being checked, everything else is ignored.
                     91:  * The routine only calls VOP_GETATTR() when it's likely it would get
                     92:  * some new data, i.e. when the vnode expires from attrcache. This
                     93:  * should give same result as periodically running stat(2) from userland,
                     94:  * while keeping CPU/network usage low, and still provide proper kevent
                     95:  * semantics.
                     96:  * The poller thread is created when first vnode is added to watch list,
                     97:  * and exits when the watch list is empty. The overhead of thread creation
                     98:  * isn't really important, neither speed of attach and detach of knote.
                     99:  */
                    100: /* ARGSUSED */
                    101: void
                    102: nfs_kqpoll(void *arg)
                    103: {
                    104:        struct kevq *ke;
                    105:        struct vattr attr;
                    106:        struct proc *p = pnfskq;
                    107:        u_quad_t osize;
                    108:        int error;
                    109:
                    110:        for(;;) {
                    111:                rw_enter_write(&nfskevq_lock);
                    112:                SLIST_FOREACH(ke, &kevlist, kev_link) {
                    113:                        struct nfsnode *np = VTONFS(ke->vp);
                    114:
                    115: #ifdef DEBUG
                    116:                        printf("nfs_kqpoll on: ");
                    117:                        VOP_PRINT(ke->vp);
                    118: #endif
                    119:                        /* skip if still in attrcache */
                    120:                        if (nfs_getattrcache(ke->vp, &attr) != ENOENT)
                    121:                                continue;
                    122:
                    123:                        /*
                    124:                         * Mark entry busy, release lock and check
                    125:                         * for changes.
                    126:                         */
                    127:                        ke->flags |= KEVQ_BUSY;
                    128:                        rw_exit_write(&nfskevq_lock);
                    129:
                    130:                        /* save v_size, nfs_getattr() updates it */
                    131:                        osize = np->n_size;
                    132:
                    133:                        error = VOP_GETATTR(ke->vp, &attr, p->p_ucred, p);
                    134:                        if (error == ESTALE) {
                    135:                                np->n_attrstamp = 0;
                    136:                                VN_KNOTE(ke->vp, NOTE_DELETE);
                    137:                                goto next;
                    138:                        }
                    139:
                    140:                        /* following is a bit fragile, but about best
                    141:                         * we can get */
                    142:                        if (attr.va_size != osize) {
                    143:                                int extended = (attr.va_size > osize);
                    144:                                VN_KNOTE(ke->vp, NOTE_WRITE
                    145:                                    | (extended ? NOTE_EXTEND : 0));
                    146:                                ke->omtime = attr.va_mtime;
                    147:                        } else if (attr.va_mtime.tv_sec != ke->omtime.tv_sec
                    148:                            || attr.va_mtime.tv_nsec != ke->omtime.tv_nsec) {
                    149:                                VN_KNOTE(ke->vp, NOTE_WRITE);
                    150:                                ke->omtime = attr.va_mtime;
                    151:                        }
                    152:
                    153:                        if (attr.va_ctime.tv_sec != ke->octime.tv_sec
                    154:                            || attr.va_ctime.tv_nsec != ke->octime.tv_nsec) {
                    155:                                VN_KNOTE(ke->vp, NOTE_ATTRIB);
                    156:                                ke->octime = attr.va_ctime;
                    157:                        }
                    158:
                    159:                        if (attr.va_nlink != ke->onlink) {
                    160:                                VN_KNOTE(ke->vp, NOTE_LINK);
                    161:                                ke->onlink = attr.va_nlink;
                    162:                        }
                    163:
                    164: next:
                    165:                        rw_enter_write(&nfskevq_lock);
                    166:                        ke->flags &= ~KEVQ_BUSY;
                    167:                        if (ke->flags & KEVQ_WANT) {
                    168:                                ke->flags &= ~KEVQ_WANT;
                    169:                                wakeup(ke);
                    170:                        }
                    171:                }
                    172:
                    173:                if (SLIST_EMPTY(&kevlist)) {
                    174:                        /* Nothing more to watch, exit */
                    175:                        pnfskq = NULL;
                    176:                        rw_exit_write(&nfskevq_lock);
                    177:                        kthread_exit(0);
                    178:                }
                    179:                rw_exit_write(&nfskevq_lock);
                    180:
                    181:                /* wait a while before checking for changes again */
                    182:                tsleep(pnfskq, PSOCK, "nfskqpw", NFS_MINATTRTIMO * hz / 2);
                    183:
                    184:        }
                    185: }
                    186:
                    187: void
                    188: filt_nfsdetach(struct knote *kn)
                    189: {
                    190:        struct vnode *vp = (struct vnode *)kn->kn_hook;
                    191:        struct kevq *ke;
                    192:
                    193:        SLIST_REMOVE(&vp->v_selectinfo.si_note, kn, knote, kn_selnext);
                    194:
                    195:        /* Remove the vnode from watch list */
                    196:        rw_enter_write(&nfskevq_lock);
                    197:        SLIST_FOREACH(ke, &kevlist, kev_link) {
                    198:                if (ke->vp == vp) {
                    199:                        while (ke->flags & KEVQ_BUSY) {
                    200:                                ke->flags |= KEVQ_WANT;
                    201:                                rw_exit_write(&nfskevq_lock);
                    202:                                (void) tsleep(ke, PSOCK, "nfskqdet", 0);
                    203:                                rw_enter_write(&nfskevq_lock);
                    204:                        }
                    205:
                    206:                        if (ke->usecount > 1) {
                    207:                                /* keep, other kevents need this */
                    208:                                ke->usecount--;
                    209:                        } else {
                    210:                                /* last user, g/c */
                    211:                                SLIST_REMOVE(&kevlist, ke, kevq, kev_link);
                    212:                                FREE(ke, M_KEVENT);
                    213:                        }
                    214:                        break;
                    215:                }
                    216:        }
                    217:        rw_exit_write(&nfskevq_lock);
                    218: }
                    219:
                    220: int
                    221: filt_nfsread(struct knote *kn, long hint)
                    222: {
                    223:        struct vnode *vp = (struct vnode *)kn->kn_hook;
                    224:        struct nfsnode *np = VTONFS(vp);
                    225:
                    226:        /*
                    227:         * filesystem is gone, so set the EOF flag and schedule
                    228:         * the knote for deletion.
                    229:         */
                    230:        if (hint == NOTE_REVOKE) {
                    231:                kn->kn_flags |= (EV_EOF | EV_ONESHOT);
                    232:                return (1);
                    233:        }
                    234:
                    235:        kn->kn_data = np->n_size - kn->kn_fp->f_offset;
                    236: #ifdef DEBUG
                    237:        printf("nfsread event. %d\n", kn->kn_data);
                    238: #endif
                    239:         return (kn->kn_data != 0);
                    240: }
                    241:
                    242: int
                    243: filt_nfsvnode(struct knote *kn, long hint)
                    244: {
                    245:        if (kn->kn_sfflags & hint)
                    246:                kn->kn_fflags |= hint;
                    247:        if (hint == NOTE_REVOKE) {
                    248:                kn->kn_flags |= EV_EOF;
                    249:                return (1);
                    250:        }
                    251:        return (kn->kn_fflags != 0);
                    252: }
                    253:
                    254: static const struct filterops nfsread_filtops =
                    255:        { 1, NULL, filt_nfsdetach, filt_nfsread };
                    256: static const struct filterops nfsvnode_filtops =
                    257:        { 1, NULL, filt_nfsdetach, filt_nfsvnode };
                    258:
                    259: int
                    260: nfs_kqfilter(void *v)
                    261: {
                    262:        struct vop_kqfilter_args *ap = v;
                    263:        struct vnode *vp;
                    264:        struct knote *kn;
                    265:        struct kevq *ke;
                    266:        int error = 0;
                    267:        struct vattr attr;
                    268:        struct proc *p = curproc;       /* XXX */
                    269:
                    270:        vp = ap->a_vp;
                    271:        kn = ap->a_kn;
                    272:
                    273: #ifdef DEBUG
                    274:        printf("nfs_kqfilter(%d) on: ", kn->kn_filter);
                    275:        VOP_PRINT(vp);
                    276: #endif
                    277:
                    278:        switch (kn->kn_filter) {
                    279:        case EVFILT_READ:
                    280:                kn->kn_fop = &nfsread_filtops;
                    281:                break;
                    282:        case EVFILT_VNODE:
                    283:                kn->kn_fop = &nfsvnode_filtops;
                    284:                break;
                    285:        default:
                    286:                return (1);
                    287:        }
                    288:
                    289:        kn->kn_hook = vp;
                    290:
                    291:        /*
                    292:         * Put the vnode to watched list.
                    293:         */
                    294:
                    295:        /*
                    296:         * Fetch current attributes. It's only needed when the vnode
                    297:         * is not watched yet, but we need to do this without lock
                    298:         * held. This is likely cheap due to attrcache, so do it now.
                    299:         */
                    300:        memset(&attr, 0, sizeof(attr));
                    301:        (void) VOP_GETATTR(vp, &attr, p->p_ucred, p);
                    302:
                    303:        rw_enter_write(&nfskevq_lock);
                    304:
                    305:        /* ensure the poller is running */
                    306:        if (!pnfskq) {
                    307:                error = kthread_create(nfs_kqpoll, NULL, &pnfskq,
                    308:                                "nfskqpoll");
                    309:                if (error)
                    310:                        goto out;
                    311:        }
                    312:
                    313:        SLIST_FOREACH(ke, &kevlist, kev_link)
                    314:                if (ke->vp == vp)
                    315:                        break;
                    316:
                    317:        if (ke) {
                    318:                /* already watched, so just bump usecount */
                    319:                ke->usecount++;
                    320:        } else {
                    321:                /* need a new one */
                    322:                MALLOC(ke, struct kevq *,
                    323:                    sizeof(struct kevq), M_KEVENT, M_WAITOK);
                    324:                ke->vp = vp;
                    325:                ke->usecount = 1;
                    326:                ke->flags = 0;
                    327:                ke->omtime = attr.va_mtime;
                    328:                ke->octime = attr.va_ctime;
                    329:                ke->onlink = attr.va_nlink;
                    330:                SLIST_INSERT_HEAD(&kevlist, ke, kev_link);
                    331:        }
                    332:
                    333:        /* kick the poller */
                    334:        wakeup(pnfskq);
                    335:
                    336:        SLIST_INSERT_HEAD(&vp->v_selectinfo.si_note, kn, kn_selnext);
                    337:
                    338: out:
                    339:        rw_exit_write(&nfskevq_lock);
                    340:        return (error);
                    341: }

CVSweb