No empty .Rs/.Re
[netbsd-mini2440.git] / sys / fs / smbfs / smbfs_kq.c
blobcc02286d055e7a055c696f76e899481ac3ed6f63
1 /* $NetBSD: smbfs_kq.c,v 1.21 2008/04/28 20:24:02 martin Exp $ */
3 /*-
4 * Copyright (c) 2003, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jaromir Dolecek.
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: smbfs_kq.c,v 1.21 2008/04/28 20:24:02 martin Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/namei.h>
38 #include <sys/kernel.h>
39 #include <sys/proc.h>
40 #include <sys/buf.h>
41 #include <sys/fcntl.h>
42 #include <sys/mount.h>
43 #include <sys/unistd.h>
44 #include <sys/vnode.h>
45 #include <sys/lockf.h>
46 #include <sys/kmem.h>
47 #include <sys/kthread.h>
48 #include <sys/file.h>
49 #include <sys/dirent.h>
51 #include <machine/limits.h>
53 #include <uvm/uvm.h>
54 #include <uvm/uvm_extern.h>
56 #include <netsmb/smb.h>
57 #include <netsmb/smb_conn.h>
58 #include <netsmb/smb_subr.h>
59 #include <netsmb/smb_rq.h>
61 #include <fs/smbfs/smbfs.h>
62 #include <fs/smbfs/smbfs_node.h>
63 #include <fs/smbfs/smbfs_subr.h>
65 #include <miscfs/genfs/genfs.h>
68 * The maximum of outstanding SMB requests is 65536, since the
69 * message id is 16bit. Don't consume all. If there is more
70 * than 30k directory notify requests, fall back to polling mode.
72 #define DNOTIFY_MAX 30000
74 struct kevq {
75 SLIST_ENTRY(kevq) kev_link; /* link on kevlist */
76 SLIST_ENTRY(kevq) k_link; /* link on poll/dn list */
78 struct vnode *vp;
79 u_int usecount;
80 u_int flags;
81 #define KEVQ_BUSY 0x01 /* currently being processed */
82 #define KEVQ_WANT 0x02 /* want to change this entry */
83 #define KEVQ_DNOT 0x04 /* kevent using NT directory change notify */
84 struct timespec omtime; /* old modification time */
85 struct timespec octime; /* old change time */
86 nlink_t onlink; /* old number of references to file */
87 struct smb_rq *rq; /* request structure */
90 static struct lwp *smbkql; /* the kevent handler */
91 static struct smb_cred smbkq_scred;
93 static kmutex_t smbkq_lock;
94 /* guard access to k*evlist */
95 static SLIST_HEAD(, kevq) kevlist = SLIST_HEAD_INITIALIZER(kevlist);
96 static SLIST_HEAD(, kevq) kplist = SLIST_HEAD_INITIALIZER(kplist);
97 static SLIST_HEAD(, kevq) kdnlist = SLIST_HEAD_INITIALIZER(kdnlist);
99 static int dnot_num = 0; /* number of active dir notifications */
100 static u_int32_t kevs;
102 static void smbfskq_dirnotify(void *);
105 * This routine periodically checks server for change
106 * of any of the watched files every SMBFS_MINATTRTIME/2 seconds.
107 * Only changes in size, modification time, change time and nlinks
108 * are being checked, everything else is ignored.
109 * Directory events are watched via NT DIRECTORY CHANGE NOTIFY
110 * if the server supports it.
112 * The routine only calls VOP_GETATTR() when it's likely it would get
113 * some new data, i.e. when the vnode expires from attrcache. This
114 * should give same result as periodically running stat(2) from userland,
115 * while keeping CPU/network usage low, and still provide proper kevent
116 * semantics.
117 * The poller thread is created when first vnode is added to watch list,
118 * and exits when the watch list is empty. The overhead of thread creation
119 * isn't really important, neither speed of attach and detach of knote.
121 /* ARGSUSED */
122 static void
123 smbfs_kqpoll(void *arg)
125 struct kevq *ke;
126 struct vattr attr;
127 int error = 0;
128 struct lwp *l;
129 u_quad_t osize;
130 int needwake;
132 l = curlwp;
134 mutex_enter(&smbkq_lock);
135 for(;;) {
136 /* check all entries on poll list for changes */
137 SLIST_FOREACH(ke, &kplist, k_link) {
138 /* skip if still in attrcache */
139 if (smbfs_attr_cachelookup(ke->vp, &attr) != ENOENT)
140 continue;
143 * Mark entry busy, release lock and check
144 * for changes.
146 ke->flags |= KEVQ_BUSY;
147 mutex_exit(&smbkq_lock);
149 /* save v_size, smbfs_getattr() updates it */
150 osize = ke->vp->v_size;
152 error = VOP_GETATTR(ke->vp, &attr, l->l_cred);
153 if (error) {
154 /* relock and proceed with next */
155 mutex_enter(&smbkq_lock);
156 continue;
159 /* following is a bit fragile, but about best
160 * we can get */
161 if (ke->vp->v_type != VDIR && attr.va_size != osize) {
162 int extended = (attr.va_size > osize);
163 VN_KNOTE(ke->vp, NOTE_WRITE
164 | (extended ? NOTE_EXTEND : 0));
165 ke->omtime = attr.va_mtime;
166 } else if (attr.va_mtime.tv_sec != ke->omtime.tv_sec
167 || attr.va_mtime.tv_nsec != ke->omtime.tv_nsec) {
168 VN_KNOTE(ke->vp, NOTE_WRITE);
169 ke->omtime = attr.va_mtime;
172 if (attr.va_ctime.tv_sec != ke->octime.tv_sec
173 || attr.va_ctime.tv_nsec != ke->octime.tv_nsec) {
174 VN_KNOTE(ke->vp, NOTE_ATTRIB);
175 ke->octime = attr.va_ctime;
178 if (attr.va_nlink != ke->onlink) {
179 VN_KNOTE(ke->vp, NOTE_LINK);
180 ke->onlink = attr.va_nlink;
183 mutex_enter(&smbkq_lock);
184 ke->flags &= ~KEVQ_BUSY;
185 if (ke->flags & KEVQ_WANT) {
186 ke->flags &= ~KEVQ_WANT;
187 wakeup(ke);
191 /* Exit if there are no more kevents to watch for */
192 if (kevs == 0) {
193 smbkql = NULL;
194 break;
197 /* only wake periodically if poll list is nonempty */
198 needwake = !SLIST_EMPTY(&kplist);
200 /* wait a while before checking for changes again */
201 if (SLIST_EMPTY(&kdnlist)) {
202 error = mtsleep(smbkql, PSOCK, "smbkqidl",
203 needwake ? (SMBFS_ATTRTIMO * hz / 2) : 0,
204 &smbkq_lock);
207 if (!error) {
208 /* woken up, check if any pending notifications */
209 while (!SLIST_EMPTY(&kdnlist)) {
210 int s, hint;
212 s = splnet();
213 ke = SLIST_FIRST(&kdnlist);
214 SLIST_REMOVE_HEAD(&kdnlist, k_link);
215 SLIST_NEXT(ke, k_link) = NULL;
216 splx(s);
218 /* drop lock while processing */
219 mutex_exit(&smbkq_lock);
222 * Skip fetch if not yet setup.
224 if (__predict_false(ke->rq == NULL))
225 goto notifyrq;
227 error = smbfs_smb_nt_dirnotify_fetch(ke->rq,
228 &hint);
229 ke->rq = NULL; /* rq deallocated by now */
230 if (error) {
232 * if there is error, switch to
233 * polling for this one
235 ke->flags &= KEVQ_DNOT;
236 SLIST_INSERT_HEAD(&kplist, ke, k_link);
237 continue;
240 VN_KNOTE(ke->vp, hint);
242 notifyrq:
243 /* reissue the notify request */
244 (void) smbfs_smb_nt_dirnotify_setup(
245 VTOSMB(ke->vp),
246 &ke->rq, &smbkq_scred,
247 smbfskq_dirnotify, ke);
249 /* reacquire the lock */
250 mutex_enter(&smbkq_lock);
254 mutex_exit(&smbkq_lock);
256 kthread_exit(0);
259 static void
260 smbfskq_dirnotify(void *arg)
262 struct kevq *ke = arg;
264 if (SLIST_NEXT(ke, k_link)) {
265 /* already on notify list */
266 return;
269 SLIST_INSERT_HEAD(&kdnlist, ke, k_link);
270 wakeup(smbkql);
273 static void
274 filt_smbfsdetach(struct knote *kn)
276 struct kevq *ke = (struct kevq *)kn->kn_hook;
277 struct vnode *vp = ke->vp;
278 struct smb_rq *rq = NULL;
280 mutex_enter(&vp->v_interlock);
281 SLIST_REMOVE(&ke->vp->v_klist, kn, knote, kn_selnext);
282 mutex_exit(&vp->v_interlock);
284 /* Remove the vnode from watch list */
285 mutex_enter(&smbkq_lock);
287 /* the handler does something to it, wait */
288 while (ke->flags & KEVQ_BUSY) {
289 ke->flags |= KEVQ_WANT;
290 mtsleep(ke, PSOCK, "smbkqdw", 0, &smbkq_lock);
293 if (ke->usecount > 1) {
294 /* keep, other kevents need this */
295 ke->usecount--;
296 } else {
297 /* last user, g/c */
298 if (ke->flags & KEVQ_DNOT) {
299 dnot_num--;
300 rq = ke->rq;
302 /* If on dirnotify list, remove */
303 if (SLIST_NEXT(ke, k_link))
304 SLIST_REMOVE(&kdnlist, ke, kevq, k_link);
305 } else
306 SLIST_REMOVE(&kplist, ke, kevq, k_link);
307 SLIST_REMOVE(&kevlist, ke, kevq, kev_link);
308 kmem_free(ke, sizeof(*ke));
310 kevs--;
312 mutex_exit(&smbkq_lock);
314 /* If there was request still pending, cancel it now */
315 if (rq) {
316 smb_iod_removerq(rq);
319 * Explicitly cancel the request, so that server can
320 * free directory change notify resources.
322 smbfs_smb_ntcancel(SSTOCP(rq->sr_share), rq->sr_mid,
323 &smbkq_scred);
325 /* Free */
326 smb_rq_done(rq);
330 static int
331 filt_smbfsread(struct knote *kn, long hint)
333 struct kevq *ke = (struct kevq *)kn->kn_hook;
334 struct vnode *vp = ke->vp;
335 int rv;
337 if (hint == NOTE_REVOKE) {
339 * filesystem is gone, so set the EOF flag and schedule
340 * the knote for deletion.
342 KASSERT(mutex_owned(&vp->v_interlock));
343 kn->kn_flags |= (EV_EOF | EV_ONESHOT);
344 return (1);
347 /* There is no size info for directories */
348 if (hint == 0) {
349 mutex_enter(&vp->v_interlock);
350 } else {
351 KASSERT(mutex_owned(&vp->v_interlock));
353 if (vp->v_type == VDIR) {
355 * This is kind of hackish, since we need to
356 * set the flag when we are called with the hint
357 * to make confirming call from kern_event.c
358 * succeed too, but need to unset it afterwards
359 * so that the directory wouldn't stay flagged
360 * as changed.
361 * XXX perhaps just fail for directories?
363 if (hint & NOTE_WRITE) {
364 kn->kn_fflags |= NOTE_WRITE;
365 rv = (1 * sizeof(struct dirent));
366 } else if (hint == 0 && (kn->kn_fflags & NOTE_WRITE)) {
367 kn->kn_fflags &= ~NOTE_WRITE;
368 rv = (1 * sizeof(struct dirent));
369 } else
370 rv = 0;
371 } else {
372 kn->kn_data = vp->v_size - ((file_t *)kn->kn_obj)->f_offset;
373 rv = (kn->kn_data != 0);
375 if (hint == 0) {
376 mutex_enter(&vp->v_interlock);
379 return rv;
382 static int
383 filt_smbfsvnode(struct knote *kn, long hint)
385 struct kevq *ke = (struct kevq *)kn->kn_hook;
386 struct vnode *vp = ke->vp;
387 int fflags;
389 switch (hint) {
390 case NOTE_REVOKE:
391 KASSERT(mutex_owned(&vp->v_interlock));
392 kn->kn_flags |= EV_EOF;
393 if ((kn->kn_sfflags & hint) != 0)
394 kn->kn_fflags |= hint;
395 return (1);
396 case 0:
397 mutex_enter(&vp->v_interlock);
398 fflags = kn->kn_fflags;
399 mutex_exit(&vp->v_interlock);
400 break;
401 default:
402 KASSERT(mutex_owned(&vp->v_interlock));
403 if ((kn->kn_sfflags & hint) != 0)
404 kn->kn_fflags |= hint;
405 fflags = kn->kn_fflags;
406 break;
409 return (kn->kn_fflags != 0);
412 static const struct filterops smbfsread_filtops =
413 { 1, NULL, filt_smbfsdetach, filt_smbfsread };
414 static const struct filterops smbfsvnode_filtops =
415 { 1, NULL, filt_smbfsdetach, filt_smbfsvnode };
418 smbfs_kqfilter(void *v)
420 struct vop_kqfilter_args /* {
421 struct vnode *a_vp;
422 struct knote *a_kn;
423 } */ *ap = v;
424 struct vnode *vp = ap->a_vp;
425 struct knote *kn = ap->a_kn;
426 struct kevq *ke, *ken;
427 int error = 0;
428 struct vattr attr;
429 struct lwp *l = curlwp; /* XXX */
430 int dnot;
431 struct smb_vc *vcp = SSTOVC(VTOSMB(vp)->n_mount->sm_share);
432 static bool again;
434 switch (kn->kn_filter) {
435 case EVFILT_READ:
436 kn->kn_fop = &smbfsread_filtops;
437 break;
438 case EVFILT_VNODE:
439 kn->kn_fop = &smbfsvnode_filtops;
440 break;
441 default:
442 return (EINVAL);
445 /* Find out if we can use directory change notify for this file */
446 dnot = (vp->v_type == VDIR
447 && (SMB_CAPS(vcp) & SMB_CAP_NT_SMBS)
448 && dnot_num < DNOTIFY_MAX);
451 * Put the vnode to watched list.
453 kevs++;
456 * Fetch current attributes. It's only needed when the vnode
457 * is not watched yet, but we need to do this without lock
458 * held. This is likely cheap due to attrcache, so do it now.
460 memset(&attr, 0, sizeof(attr));
461 (void) VOP_GETATTR(vp, &attr, l->l_cred);
463 /* ensure the handler is running */
464 /* XXX this is unreliable. */
465 if (!again) {
466 mutex_init(&smbkq_lock, MUTEX_DEFAULT, IPL_NONE);
468 if (!smbkql) {
469 /* XXX very fishy */
470 error = kthread_create(PRI_NONE, 0, NULL, smbfs_kqpoll,
471 NULL, &smbkql, "smbkq");
472 smb_makescred(&smbkq_scred, smbkql, smbkql->l_cred);
473 if (error) {
474 kevs--;
475 return (error);
480 * Allocate new kev. It's more probable it will be needed,
481 * and the malloc is cheaper than scanning possibly
482 * large kevlist list second time after malloc.
484 ken = kmem_alloc(sizeof(*ken), KM_SLEEP);
486 /* Check the list and insert new entry */
487 mutex_enter(&smbkq_lock);
488 SLIST_FOREACH(ke, &kevlist, kev_link) {
489 if (ke->vp == vp)
490 break;
493 if (ke) {
494 /* already watched, so just bump usecount */
495 ke->usecount++;
496 kmem_free(ken, sizeof(*ken));
497 } else {
498 /* need a new one */
499 memset(ken, 0, sizeof(*ken));
500 ke = ken;
501 ke->vp = vp;
502 ke->usecount = 1;
503 ke->flags = (dnot) ? KEVQ_DNOT : 0;
504 ke->omtime = attr.va_mtime;
505 ke->octime = attr.va_ctime;
506 ke->onlink = attr.va_nlink;
508 if (dnot) {
509 int s;
512 * Add kevent to list of 'need attend' kevnets.
513 * The handler will pick it up and setup request
514 * appropriately.
516 s = splnet();
517 SLIST_INSERT_HEAD(&kdnlist, ke, k_link);
518 splx(s);
519 dnot_num++;
520 } else {
521 /* add to poll list */
522 SLIST_INSERT_HEAD(&kplist, ke, k_link);
525 SLIST_INSERT_HEAD(&kevlist, ke, kev_link);
527 /* kick the handler */
528 wakeup(smbkql);
531 mutex_enter(&vp->v_interlock);
532 SLIST_INSERT_HEAD(&vp->v_klist, kn, kn_selnext);
533 kn->kn_hook = ke;
534 mutex_exit(&vp->v_interlock);
536 mutex_exit(&smbkq_lock);
538 return (0);