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