1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
26 * All rights reserved.
27 */
28
29 /*
30 * Node hash implementation initially borrowed from NFS (nfs_subr.c)
31 * but then heavily modified. It's no longer an array of hash lists,
32 * but an AVL tree per mount point. More on this below.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/time.h>
38 #include <sys/vnode.h>
39 #include <sys/bitmap.h>
40 #include <sys/dnlc.h>
41 #include <sys/kmem.h>
42 #include <sys/sunddi.h>
43 #include <sys/sysmacros.h>
44
45 #include <netsmb/smb_osdep.h>
46
47 #include <netsmb/smb.h>
48 #include <netsmb/smb_conn.h>
49 #include <netsmb/smb_subr.h>
50 #include <netsmb/smb_rq.h>
51
52 #include <smbfs/smbfs.h>
53 #include <smbfs/smbfs_node.h>
54 #include <smbfs/smbfs_subr.h>
55
56 /*
57 * The AVL trees (now per-mount) allow finding an smbfs node by its
58 * full remote path name. It also allows easy traversal of all nodes
59 * below (path wise) any given node. A reader/writer lock for each
60 * (per mount) AVL tree is used to control access and to synchronize
61 * lookups, additions, and deletions from that AVL tree.
62 *
63 * Previously, this code use a global array of hash chains, each with
64 * its own rwlock. A few struct members, functions, and comments may
65 * still refer to a "hash", and those should all now be considered to
66 * refer to the per-mount AVL tree that replaced the old hash chains.
67 * (i.e. member smi_hash_lk, function sn_hashfind, etc.)
68 *
69 * The smbnode freelist is organized as a doubly linked list with
70 * a head pointer. Additions and deletions are synchronized via
71 * a single mutex.
72 *
73 * In order to add an smbnode to the free list, it must be linked into
74 * the mount's AVL tree and the exclusive lock for the AVL must be held.
75 * If an smbnode is not linked into the AVL tree, then it is destroyed
76 * because it represents no valuable information that can be reused
77 * about the file. The exclusive lock for the AVL tree must be held
78 * in order to prevent a lookup in the AVL tree from finding the
79 * smbnode and using it and assuming that the smbnode is not on the
80 * freelist. The lookup in the AVL tree will have the AVL tree lock
81 * held, either exclusive or shared.
82 *
83 * The vnode reference count for each smbnode is not allowed to drop
84 * below 1. This prevents external entities, such as the VM
85 * subsystem, from acquiring references to vnodes already on the
86 * freelist and then trying to place them back on the freelist
87 * when their reference is released. This means that the when an
88 * smbnode is looked up in the AVL tree, then either the smbnode
89 * is removed from the freelist and that reference is tranfered to
90 * the new reference or the vnode reference count must be incremented
91 * accordingly. The mutex for the freelist must be held in order to
92 * accurately test to see if the smbnode is on the freelist or not.
93 * The AVL tree lock might be held shared and it is possible that
94 * two different threads may race to remove the smbnode from the
95 * freelist. This race can be resolved by holding the mutex for the
96 * freelist. Please note that the mutex for the freelist does not
97 * need to held if the smbnode is not on the freelist. It can not be
98 * placed on the freelist due to the requirement that the thread
99 * putting the smbnode on the freelist must hold the exclusive lock
100 * for the AVL tree and the thread doing the lookup in the AVL tree
101 * is holding either a shared or exclusive lock for the AVL tree.
102 *
103 * The lock ordering is:
104 *
105 * AVL tree lock -> vnode lock
106 * AVL tree lock -> freelist lock
107 */
108
109 static kmutex_t smbfreelist_lock;
110 static smbnode_t *smbfreelist = NULL;
111 static ulong_t smbnodenew = 0;
112 long nsmbnode = 0;
113
114 static struct kmem_cache *smbnode_cache;
115
116 static const vsecattr_t smbfs_vsa0 = { 0 };
117
118 /*
119 * Mutex to protect the following variables:
120 * smbfs_major
121 * smbfs_minor
122 */
123 kmutex_t smbfs_minor_lock;
124 int smbfs_major;
125 int smbfs_minor;
126
127 /* See smbfs_node_findcreate() */
128 struct smbfattr smbfs_fattr0;
129
130 /*
131 * Local functions.
132 * SN for Smb Node
133 */
134 static void sn_rmfree(smbnode_t *);
135 static void sn_inactive(smbnode_t *);
136 static void sn_addhash_locked(smbnode_t *, avl_index_t);
137 static void sn_rmhash_locked(smbnode_t *);
138 static void sn_destroy_node(smbnode_t *);
139 void smbfs_kmem_reclaim(void *cdrarg);
140
141 static smbnode_t *
142 sn_hashfind(smbmntinfo_t *, const char *, int, avl_index_t *);
143
144 static smbnode_t *
145 make_smbnode(smbmntinfo_t *, const char *, int, int *);
146
147 /*
148 * Free the resources associated with an smbnode.
149 * Note: This is different from smbfs_inactive
150 *
151 * NFS: nfs_subr.c:rinactive
152 */
153 static void
154 sn_inactive(smbnode_t *np)
155 {
156 vsecattr_t ovsa;
157 cred_t *oldcr;
158 char *orpath;
159 int orplen;
160 vnode_t *vp;
161
162 /*
163 * Flush and invalidate all pages
164 * Free any held credentials and caches...
165 * etc. (See NFS code)
166 */
167 mutex_enter(&np->r_statelock);
168
169 ovsa = np->r_secattr;
170 np->r_secattr = smbfs_vsa0;
171 np->r_sectime = 0;
172
173 oldcr = np->r_cred;
174 np->r_cred = NULL;
175
176 orpath = np->n_rpath;
177 orplen = np->n_rplen;
178 np->n_rpath = NULL;
179 np->n_rplen = 0;
180
181 mutex_exit(&np->r_statelock);
182
183 vp = SMBTOV(np);
184 if (vn_has_cached_data(vp)) {
185 smbfs_invalidate_pages(vp, (u_offset_t) 0, oldcr);
186 }
187
188 if (ovsa.vsa_aclentp != NULL)
189 kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
190
191 if (oldcr != NULL)
192 crfree(oldcr);
193
194 if (orpath != NULL)
195 kmem_free(orpath, orplen + 1);
196 }
197
198 /*
199 * Find and optionally create an smbnode for the passed
200 * mountinfo, directory, separator, and name. If the
201 * desired smbnode already exists, return a reference.
202 * If the file attributes pointer is non-null, the node
203 * is created if necessary and linked into the AVL tree.
204 *
205 * Callers that need a node created but don't have the
206 * real attributes pass smbfs_fattr0 to force creation.
207 *
208 * Note: make_smbnode() may upgrade the "hash" lock to exclusive.
209 *
210 * NFS: nfs_subr.c:makenfsnode
211 */
212 smbnode_t *
213 smbfs_node_findcreate(
214 smbmntinfo_t *mi,
215 const char *dirnm,
216 int dirlen,
217 const char *name,
218 int nmlen,
219 char sep,
220 struct smbfattr *fap)
221 {
222 char tmpbuf[256];
223 size_t rpalloc;
224 char *p, *rpath;
225 int rplen;
226 smbnode_t *np;
227 vnode_t *vp;
228 int newnode;
229
230 /*
231 * Build the search string, either in tmpbuf or
232 * in allocated memory if larger than tmpbuf.
233 */
234 rplen = dirlen;
235 if (sep != '\0')
236 rplen++;
237 rplen += nmlen;
238 if (rplen < sizeof (tmpbuf)) {
239 /* use tmpbuf */
240 rpalloc = 0;
241 rpath = tmpbuf;
242 } else {
243 rpalloc = rplen + 1;
244 rpath = kmem_alloc(rpalloc, KM_SLEEP);
245 }
246 p = rpath;
247 bcopy(dirnm, p, dirlen);
248 p += dirlen;
249 if (sep != '\0')
250 *p++ = sep;
251 if (name != NULL) {
252 bcopy(name, p, nmlen);
253 p += nmlen;
254 }
255 ASSERT(p == rpath + rplen);
256
257 /*
258 * Find or create a node with this path.
259 */
260 rw_enter(&mi->smi_hash_lk, RW_READER);
261 if (fap == NULL)
262 np = sn_hashfind(mi, rpath, rplen, NULL);
263 else
264 np = make_smbnode(mi, rpath, rplen, &newnode);
265 rw_exit(&mi->smi_hash_lk);
266
267 if (rpalloc)
268 kmem_free(rpath, rpalloc);
269
270 if (fap == NULL) {
271 /*
272 * Caller is "just looking" (no create)
273 * so np may or may not be NULL here.
274 * Either way, we're done.
275 */
276 return (np);
277 }
278
279 /*
280 * We should have a node, possibly created.
281 * Do we have (real) attributes to apply?
282 */
283 ASSERT(np != NULL);
284 if (fap == &smbfs_fattr0)
285 return (np);
286
287 /*
288 * Apply the given attributes to this node,
289 * dealing with any cache impact, etc.
290 */
291 vp = SMBTOV(np);
292 if (!newnode) {
293 /*
294 * Found an existing node.
295 * Maybe purge caches...
296 */
297 smbfs_cache_check(vp, fap);
298 }
299 smbfs_attrcache_fa(vp, fap);
300
301 /*
302 * Note NFS sets vp->v_type here, assuming it
303 * can never change for the life of a node.
304 * We allow v_type to change, and set it in
305 * smbfs_attrcache(). Also: mode, uid, gid
306 */
307 return (np);
308 }
309
310 /*
311 * NFS: nfs_subr.c:rtablehash
312 * We use smbfs_hash().
313 */
314
315 /*
316 * Find or create an smbnode.
317 * NFS: nfs_subr.c:make_rnode
318 */
319 static smbnode_t *
320 make_smbnode(
321 smbmntinfo_t *mi,
322 const char *rpath,
323 int rplen,
324 int *newnode)
325 {
326 smbnode_t *np;
327 smbnode_t *tnp;
328 vnode_t *vp;
329 vfs_t *vfsp;
330 avl_index_t where;
331 char *new_rpath = NULL;
332
333 ASSERT(RW_READ_HELD(&mi->smi_hash_lk));
334 vfsp = mi->smi_vfsp;
335
336 start:
337 np = sn_hashfind(mi, rpath, rplen, NULL);
338 if (np != NULL) {
339 *newnode = 0;
340 return (np);
341 }
342
343 /* Note: will retake this lock below. */
344 rw_exit(&mi->smi_hash_lk);
345
346 /*
347 * see if we can find something on the freelist
348 */
349 mutex_enter(&smbfreelist_lock);
350 if (smbfreelist != NULL && smbnodenew >= nsmbnode) {
351 np = smbfreelist;
352 sn_rmfree(np);
353 mutex_exit(&smbfreelist_lock);
354
355 vp = SMBTOV(np);
356
357 if (np->r_flags & RHASHED) {
358 smbmntinfo_t *tmp_mi = np->n_mount;
359 ASSERT(tmp_mi != NULL);
360 rw_enter(&tmp_mi->smi_hash_lk, RW_WRITER);
361 mutex_enter(&vp->v_lock);
362 if (vp->v_count > 1) {
363 vp->v_count--;
364 mutex_exit(&vp->v_lock);
365 rw_exit(&tmp_mi->smi_hash_lk);
366 /* start over */
367 rw_enter(&mi->smi_hash_lk, RW_READER);
368 goto start;
369 }
370 mutex_exit(&vp->v_lock);
371 sn_rmhash_locked(np);
372 rw_exit(&tmp_mi->smi_hash_lk);
373 }
374
375 sn_inactive(np);
376
377 mutex_enter(&vp->v_lock);
378 if (vp->v_count > 1) {
379 vp->v_count--;
380 mutex_exit(&vp->v_lock);
381 rw_enter(&mi->smi_hash_lk, RW_READER);
382 goto start;
383 }
384 mutex_exit(&vp->v_lock);
385 vn_invalid(vp);
386 /*
387 * destroy old locks before bzero'ing and
388 * recreating the locks below.
389 */
390 smbfs_rw_destroy(&np->r_rwlock);
391 smbfs_rw_destroy(&np->r_lkserlock);
392 mutex_destroy(&np->r_statelock);
393 cv_destroy(&np->r_cv);
394 /*
395 * Make sure that if smbnode is recycled then
396 * VFS count is decremented properly before
397 * reuse.
398 */
399 VFS_RELE(vp->v_vfsp);
400 vn_reinit(vp);
401 } else {
402 /*
403 * allocate and initialize a new smbnode
404 */
405 vnode_t *new_vp;
406
407 mutex_exit(&smbfreelist_lock);
408
409 np = kmem_cache_alloc(smbnode_cache, KM_SLEEP);
410 new_vp = vn_alloc(KM_SLEEP);
411
412 atomic_add_long((ulong_t *)&smbnodenew, 1);
413 vp = new_vp;
414 }
415
416 /*
417 * Allocate and copy the rpath we'll need below.
418 */
419 new_rpath = kmem_alloc(rplen + 1, KM_SLEEP);
420 bcopy(rpath, new_rpath, rplen);
421 new_rpath[rplen] = '\0';
422
423 /* Initialize smbnode_t */
424 bzero(np, sizeof (*np));
425
426 smbfs_rw_init(&np->r_rwlock, NULL, RW_DEFAULT, NULL);
427 smbfs_rw_init(&np->r_lkserlock, NULL, RW_DEFAULT, NULL);
428 mutex_init(&np->r_statelock, NULL, MUTEX_DEFAULT, NULL);
429 cv_init(&np->r_cv, NULL, CV_DEFAULT, NULL);
430 /* cv_init(&np->r_commit.c_cv, NULL, CV_DEFAULT, NULL); */
431
432 np->r_vnode = vp;
433 np->n_mount = mi;
434
435 np->n_fid = SMB_FID_UNUSED;
436 np->n_uid = mi->smi_uid;
437 np->n_gid = mi->smi_gid;
438 /* Leave attributes "stale." */
439
440 #if 0 /* XXX dircache */
441 /*
442 * We don't know if it's a directory yet.
443 * Let the caller do this? XXX
444 */
445 avl_create(&np->r_dir, compar, sizeof (rddir_cache),
446 offsetof(rddir_cache, tree));
447 #endif
448
449 /* Now fill in the vnode. */
450 vn_setops(vp, smbfs_vnodeops);
451 vp->v_data = (caddr_t)np;
452 VFS_HOLD(vfsp);
453 vp->v_vfsp = vfsp;
454 vp->v_type = VNON;
455
456 /*
457 * We entered with mi->smi_hash_lk held (reader).
458 * Retake it now, (as the writer).
459 * Will return with it held.
460 */
461 rw_enter(&mi->smi_hash_lk, RW_WRITER);
462
463 /*
464 * There is a race condition where someone else
465 * may alloc the smbnode while no locks are held,
466 * so check again and recover if found.
467 */
468 tnp = sn_hashfind(mi, rpath, rplen, &where);
469 if (tnp != NULL) {
470 /*
471 * Lost the race. Put the node we were building
472 * on the free list and return the one we found.
473 */
474 rw_exit(&mi->smi_hash_lk);
475 kmem_free(new_rpath, rplen + 1);
476 smbfs_addfree(np);
477 rw_enter(&mi->smi_hash_lk, RW_READER);
478 *newnode = 0;
479 return (tnp);
480 }
481
482 /*
483 * Hash search identifies nodes by the remote path
484 * (n_rpath) so fill that in now, before linking
485 * this node into the node cache (AVL tree).
486 */
487 np->n_rpath = new_rpath;
488 np->n_rplen = rplen;
489 np->n_ino = smbfs_gethash(new_rpath, rplen);
490
491 sn_addhash_locked(np, where);
492 *newnode = 1;
493 return (np);
494 }
495
496 /*
497 * smbfs_addfree
498 * Put an smbnode on the free list, or destroy it immediately
499 * if it offers no value were it to be reclaimed later. Also
500 * destroy immediately when we have too many smbnodes, etc.
501 *
502 * Normally called by smbfs_inactive, but also
503 * called in here during cleanup operations.
504 *
505 * NFS: nfs_subr.c:rp_addfree
506 */
507 void
508 smbfs_addfree(smbnode_t *np)
509 {
510 vnode_t *vp;
511 struct vfs *vfsp;
512 smbmntinfo_t *mi;
513
514 ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
515
516 vp = SMBTOV(np);
517 ASSERT(vp->v_count >= 1);
518
519 vfsp = vp->v_vfsp;
520 mi = VFTOSMI(vfsp);
521
522 /*
523 * If there are no more references to this smbnode and:
524 * we have too many smbnodes allocated, or if the node
525 * is no longer accessible via the AVL tree (!RHASHED),
526 * or an i/o error occurred while writing to the file,
527 * or it's part of an unmounted FS, then try to destroy
528 * it instead of putting it on the smbnode freelist.
529 */
530 if (np->r_count == 0 && (
531 (np->r_flags & RHASHED) == 0 ||
532 (np->r_error != 0) ||
533 (vfsp->vfs_flag & VFS_UNMOUNTED) ||
534 (smbnodenew > nsmbnode))) {
535
536 /* Try to destroy this node. */
537
538 if (np->r_flags & RHASHED) {
539 rw_enter(&mi->smi_hash_lk, RW_WRITER);
540 mutex_enter(&vp->v_lock);
541 if (vp->v_count > 1) {
542 vp->v_count--;
543 mutex_exit(&vp->v_lock);
544 rw_exit(&mi->smi_hash_lk);
545 return;
546 /*
547 * Will get another call later,
548 * via smbfs_inactive.
549 */
550 }
551 mutex_exit(&vp->v_lock);
552 sn_rmhash_locked(np);
553 rw_exit(&mi->smi_hash_lk);
554 }
555
556 sn_inactive(np);
557
558 /*
559 * Recheck the vnode reference count. We need to
560 * make sure that another reference has not been
561 * acquired while we were not holding v_lock. The
562 * smbnode is not in the smbnode "hash" AVL tree, so
563 * the only way for a reference to have been acquired
564 * is for a VOP_PUTPAGE because the smbnode was marked
565 * with RDIRTY or for a modified page. This vnode
566 * reference may have been acquired before our call
567 * to sn_inactive. The i/o may have been completed,
568 * thus allowing sn_inactive to complete, but the
569 * reference to the vnode may not have been released
570 * yet. In any case, the smbnode can not be destroyed
571 * until the other references to this vnode have been
572 * released. The other references will take care of
573 * either destroying the smbnode or placing it on the
574 * smbnode freelist. If there are no other references,
575 * then the smbnode may be safely destroyed.
576 */
577 mutex_enter(&vp->v_lock);
578 if (vp->v_count > 1) {
579 vp->v_count--;
580 mutex_exit(&vp->v_lock);
581 return;
582 }
583 mutex_exit(&vp->v_lock);
584
585 sn_destroy_node(np);
586 return;
587 }
588
589 /*
590 * Lock the AVL tree and then recheck the reference count
591 * to ensure that no other threads have acquired a reference
592 * to indicate that the smbnode should not be placed on the
593 * freelist. If another reference has been acquired, then
594 * just release this one and let the other thread complete
595 * the processing of adding this smbnode to the freelist.
596 */
597 rw_enter(&mi->smi_hash_lk, RW_WRITER);
598
599 mutex_enter(&vp->v_lock);
600 if (vp->v_count > 1) {
601 vp->v_count--;
602 mutex_exit(&vp->v_lock);
603 rw_exit(&mi->smi_hash_lk);
604 return;
605 }
606 mutex_exit(&vp->v_lock);
607
608 /*
609 * Put this node on the free list.
610 */
611 mutex_enter(&smbfreelist_lock);
612 if (smbfreelist == NULL) {
613 np->r_freef = np;
614 np->r_freeb = np;
615 smbfreelist = np;
616 } else {
617 np->r_freef = smbfreelist;
618 np->r_freeb = smbfreelist->r_freeb;
619 smbfreelist->r_freeb->r_freef = np;
620 smbfreelist->r_freeb = np;
621 }
622 mutex_exit(&smbfreelist_lock);
623
624 rw_exit(&mi->smi_hash_lk);
625 }
626
627 /*
628 * Remove an smbnode from the free list.
629 *
630 * The caller must be holding smbfreelist_lock and the smbnode
631 * must be on the freelist.
632 *
633 * NFS: nfs_subr.c:rp_rmfree
634 */
635 static void
636 sn_rmfree(smbnode_t *np)
637 {
638
639 ASSERT(MUTEX_HELD(&smbfreelist_lock));
640 ASSERT(np->r_freef != NULL && np->r_freeb != NULL);
641
642 if (np == smbfreelist) {
643 smbfreelist = np->r_freef;
644 if (np == smbfreelist)
645 smbfreelist = NULL;
646 }
647
648 np->r_freeb->r_freef = np->r_freef;
649 np->r_freef->r_freeb = np->r_freeb;
650
651 np->r_freef = np->r_freeb = NULL;
652 }
653
654 /*
655 * Put an smbnode in the "hash" AVL tree.
656 *
657 * The caller must be hold the rwlock as writer.
658 *
659 * NFS: nfs_subr.c:rp_addhash
660 */
661 static void
662 sn_addhash_locked(smbnode_t *np, avl_index_t where)
663 {
664 smbmntinfo_t *mi = np->n_mount;
665
666 ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
667 ASSERT(!(np->r_flags & RHASHED));
668
669 avl_insert(&mi->smi_hash_avl, np, where);
670
671 mutex_enter(&np->r_statelock);
672 np->r_flags |= RHASHED;
673 mutex_exit(&np->r_statelock);
674 }
675
676 /*
677 * Remove an smbnode from the "hash" AVL tree.
678 *
679 * The caller must hold the rwlock as writer.
680 *
681 * NFS: nfs_subr.c:rp_rmhash_locked
682 */
683 static void
684 sn_rmhash_locked(smbnode_t *np)
685 {
686 smbmntinfo_t *mi = np->n_mount;
687
688 ASSERT(RW_WRITE_HELD(&mi->smi_hash_lk));
689 ASSERT(np->r_flags & RHASHED);
690
691 avl_remove(&mi->smi_hash_avl, np);
692
693 mutex_enter(&np->r_statelock);
694 np->r_flags &= ~RHASHED;
695 mutex_exit(&np->r_statelock);
696 }
697
698 /*
699 * Remove an smbnode from the "hash" AVL tree.
700 *
701 * The caller must not be holding the rwlock.
702 */
703 void
704 smbfs_rmhash(smbnode_t *np)
705 {
706 smbmntinfo_t *mi = np->n_mount;
707
708 rw_enter(&mi->smi_hash_lk, RW_WRITER);
709 sn_rmhash_locked(np);
710 rw_exit(&mi->smi_hash_lk);
711 }
712
713 /*
714 * Lookup an smbnode by remote pathname
715 *
716 * The caller must be holding the AVL rwlock, either shared or exclusive.
717 *
718 * NFS: nfs_subr.c:rfind
719 */
720 static smbnode_t *
721 sn_hashfind(
722 smbmntinfo_t *mi,
723 const char *rpath,
724 int rplen,
725 avl_index_t *pwhere) /* optional */
726 {
727 smbfs_node_hdr_t nhdr;
728 smbnode_t *np;
729 vnode_t *vp;
730
731 ASSERT(RW_LOCK_HELD(&mi->smi_hash_lk));
732
733 bzero(&nhdr, sizeof (nhdr));
734 nhdr.hdr_n_rpath = (char *)rpath;
735 nhdr.hdr_n_rplen = rplen;
736
737 /* See smbfs_node_cmp below. */
738 np = avl_find(&mi->smi_hash_avl, &nhdr, pwhere);
739
740 if (np == NULL)
741 return (NULL);
742
743 /*
744 * Found it in the "hash" AVL tree.
745 * Remove from free list, if necessary.
746 */
747 vp = SMBTOV(np);
748 if (np->r_freef != NULL) {
749 mutex_enter(&smbfreelist_lock);
750 /*
751 * If the smbnode is on the freelist,
752 * then remove it and use that reference
753 * as the new reference. Otherwise,
754 * need to increment the reference count.
755 */
756 if (np->r_freef != NULL) {
757 sn_rmfree(np);
758 mutex_exit(&smbfreelist_lock);
759 } else {
760 mutex_exit(&smbfreelist_lock);
761 VN_HOLD(vp);
762 }
763 } else
764 VN_HOLD(vp);
765
766 return (np);
767 }
768
769 static int
770 smbfs_node_cmp(const void *va, const void *vb)
771 {
772 const smbfs_node_hdr_t *a = va;
773 const smbfs_node_hdr_t *b = vb;
774 int clen, diff;
775
776 /*
777 * Same semantics as strcmp, but does not
778 * assume the strings are null terminated.
779 */
780 clen = (a->hdr_n_rplen < b->hdr_n_rplen) ?
781 a->hdr_n_rplen : b->hdr_n_rplen;
782 diff = strncmp(a->hdr_n_rpath, b->hdr_n_rpath, clen);
783 if (diff < 0)
784 return (-1);
785 if (diff > 0)
786 return (1);
787 /* they match through clen */
788 if (b->hdr_n_rplen > clen)
789 return (-1);
790 if (a->hdr_n_rplen > clen)
791 return (1);
792 return (0);
793 }
794
795 /*
796 * Setup the "hash" AVL tree used for our node cache.
797 * See: smbfs_mount, smbfs_destroy_table.
798 */
799 void
800 smbfs_init_hash_avl(avl_tree_t *avl)
801 {
802 avl_create(avl, smbfs_node_cmp, sizeof (smbnode_t),
803 offsetof(smbnode_t, r_avl_node));
804 }
805
806 /*
807 * Invalidate the cached attributes for all nodes "under" the
808 * passed-in node. Note: the passed-in node is NOT affected by
809 * this call. This is used both for files under some directory
810 * after the directory is deleted or renamed, and for extended
811 * attribute files (named streams) under a plain file after that
812 * file is renamed or deleted.
813 *
814 * Do this by walking the AVL tree starting at the passed in node,
815 * and continuing while the visited nodes have a path prefix matching
816 * the entire path of the passed-in node, and a separator just after
817 * that matching path prefix. Watch out for cases where the AVL tree
818 * order may not exactly match the order of an FS walk, i.e.
819 * consider this sequence:
820 * "foo" (directory)
821 * "foo bar" (name containing a space)
822 * "foo/bar"
823 * The walk needs to skip "foo bar" and keep going until it finds
824 * something that doesn't match the "foo" name prefix.
825 */
826 void
827 smbfs_attrcache_prune(smbnode_t *top_np)
828 {
829 smbmntinfo_t *mi;
830 smbnode_t *np;
831 char *rpath;
832 int rplen;
833
834 mi = top_np->n_mount;
835 rw_enter(&mi->smi_hash_lk, RW_READER);
836
837 np = top_np;
838 rpath = top_np->n_rpath;
839 rplen = top_np->n_rplen;
840 for (;;) {
841 np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER);
842 if (np == NULL)
843 break;
844 if (np->n_rplen < rplen)
845 break;
846 if (0 != strncmp(np->n_rpath, rpath, rplen))
847 break;
848 if (np->n_rplen > rplen && (
849 np->n_rpath[rplen] == ':' ||
850 np->n_rpath[rplen] == '\\'))
851 smbfs_attrcache_remove(np);
852 }
853
854 rw_exit(&mi->smi_hash_lk);
855 }
856
857 #ifdef SMB_VNODE_DEBUG
858 int smbfs_check_table_debug = 1;
859 #else /* SMB_VNODE_DEBUG */
860 int smbfs_check_table_debug = 0;
861 #endif /* SMB_VNODE_DEBUG */
862
863
864 /*
865 * Return 1 if there is a active vnode belonging to this vfs in the
866 * smbnode cache.
867 *
868 * Several of these checks are done without holding the usual
869 * locks. This is safe because destroy_smbtable(), smbfs_addfree(),
870 * etc. will redo the necessary checks before actually destroying
871 * any smbnodes.
872 *
873 * NFS: nfs_subr.c:check_rtable
874 *
875 * Debugging changes here relative to NFS.
876 * Relatively harmless, so left 'em in.
877 */
878 int
879 smbfs_check_table(struct vfs *vfsp, smbnode_t *rtnp)
880 {
881 smbmntinfo_t *mi;
882 smbnode_t *np;
883 vnode_t *vp;
884 int busycnt = 0;
885
886 mi = VFTOSMI(vfsp);
887 rw_enter(&mi->smi_hash_lk, RW_READER);
888 for (np = avl_first(&mi->smi_hash_avl); np != NULL;
889 np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
890
891 if (np == rtnp)
892 continue; /* skip the root */
893 vp = SMBTOV(np);
894
895 /* Now the 'busy' checks: */
896 /* Not on the free list? */
897 if (np->r_freef == NULL) {
898 SMBVDEBUG("!r_freef: node=0x%p, rpath=%s\n",
899 (void *)np, np->n_rpath);
900 busycnt++;
901 }
902
903 /* Has dirty pages? */
904 if (vn_has_cached_data(vp) &&
905 (np->r_flags & RDIRTY)) {
906 SMBVDEBUG("is dirty: node=0x%p, rpath=%s\n",
907 (void *)np, np->n_rpath);
908 busycnt++;
909 }
910
911 /* Other refs? (not reflected in v_count) */
912 if (np->r_count > 0) {
913 SMBVDEBUG("+r_count: node=0x%p, rpath=%s\n",
914 (void *)np, np->n_rpath);
915 busycnt++;
916 }
917
918 if (busycnt && !smbfs_check_table_debug)
919 break;
920
921 }
922 rw_exit(&mi->smi_hash_lk);
923
924 return (busycnt);
925 }
926
927 /*
928 * Destroy inactive vnodes from the AVL tree which belong to this
929 * vfs. It is essential that we destroy all inactive vnodes during a
930 * forced unmount as well as during a normal unmount.
931 *
932 * NFS: nfs_subr.c:destroy_rtable
933 *
934 * In here, we're normally destrying all or most of the AVL tree,
935 * so the natural choice is to use avl_destroy_nodes. However,
936 * there may be a few busy nodes that should remain in the AVL
937 * tree when we're done. The solution: use a temporary tree to
938 * hold the busy nodes until we're done destroying the old tree,
939 * then copy the temporary tree over the (now emtpy) real tree.
940 */
941 void
942 smbfs_destroy_table(struct vfs *vfsp)
943 {
944 avl_tree_t tmp_avl;
945 smbmntinfo_t *mi;
946 smbnode_t *np;
947 smbnode_t *rlist;
948 void *v;
949
950 mi = VFTOSMI(vfsp);
951 rlist = NULL;
952 smbfs_init_hash_avl(&tmp_avl);
953
954 rw_enter(&mi->smi_hash_lk, RW_WRITER);
955 v = NULL;
956 while ((np = avl_destroy_nodes(&mi->smi_hash_avl, &v)) != NULL) {
957
958 mutex_enter(&smbfreelist_lock);
959 if (np->r_freef == NULL) {
960 /*
961 * Busy node (not on the free list).
962 * Will keep in the final AVL tree.
963 */
964 mutex_exit(&smbfreelist_lock);
965 avl_add(&tmp_avl, np);
966 } else {
967 /*
968 * It's on the free list. Remove and
969 * arrange for it to be destroyed.
970 */
971 sn_rmfree(np);
972 mutex_exit(&smbfreelist_lock);
973
974 /*
975 * Last part of sn_rmhash_locked().
976 * NB: avl_destroy_nodes has already
977 * removed this from the "hash" AVL.
978 */
979 mutex_enter(&np->r_statelock);
980 np->r_flags &= ~RHASHED;
981 mutex_exit(&np->r_statelock);
982
983 /*
984 * Add to the list of nodes to destroy.
985 * Borrowing avl_child[0] for this list.
986 */
987 np->r_avl_node.avl_child[0] =
988 (struct avl_node *)rlist;
989 rlist = np;
990 }
991 }
992 avl_destroy(&mi->smi_hash_avl);
993
994 /*
995 * Replace the (now destroyed) "hash" AVL with the
996 * temporary AVL, which restores the busy nodes.
997 */
998 mi->smi_hash_avl = tmp_avl;
999 rw_exit(&mi->smi_hash_lk);
1000
1001 /*
1002 * Now destroy the nodes on our temporary list (rlist).
1003 * This call to smbfs_addfree will end up destroying the
1004 * smbnode, but in a safe way with the appropriate set
1005 * of checks done.
1006 */
1007 while ((np = rlist) != NULL) {
1008 rlist = (smbnode_t *)np->r_avl_node.avl_child[0];
1009 smbfs_addfree(np);
1010 }
1011 }
1012
1013 /*
1014 * This routine destroys all the resources associated with the smbnode
1015 * and then the smbnode itself. Note: sn_inactive has been called.
1016 *
1017 * NFS: nfs_subr.c:destroy_rnode
1018 */
1019 static void
1020 sn_destroy_node(smbnode_t *np)
1021 {
1022 vnode_t *vp;
1023 vfs_t *vfsp;
1024
1025 vp = SMBTOV(np);
1026 vfsp = vp->v_vfsp;
1027
1028 ASSERT(vp->v_count == 1);
1029 ASSERT(np->r_count == 0);
1030 ASSERT(np->r_mapcnt == 0);
1031 ASSERT(np->r_secattr.vsa_aclentp == NULL);
1032 ASSERT(np->r_cred == NULL);
1033 ASSERT(np->n_rpath == NULL);
1034 ASSERT(!(np->r_flags & RHASHED));
1035 ASSERT(np->r_freef == NULL && np->r_freeb == NULL);
1036 atomic_add_long((ulong_t *)&smbnodenew, -1);
1037 vn_invalid(vp);
1038 vn_free(vp);
1039 kmem_cache_free(smbnode_cache, np);
1040 VFS_RELE(vfsp);
1041 }
1042
1043 /*
1044 * Correspond to rflush() in NFS.
1045 * Flush all vnodes in this (or every) vfs.
1046 * Used by smbfs_sync and by smbfs_unmount.
1047 */
1048 /*ARGSUSED*/
1049 void
1050 smbfs_rflush(struct vfs *vfsp, cred_t *cr) {
1051
1052 smbmntinfo_t *mi;
1053 smbnode_t *np;
1054 vnode_t *vp;
1055
1056 long num, cnt;
1057
1058 vnode_t **vplist;
1059
1060 if(vfsp == NULL)
1061 return;
1062
1063 mi = VFTOSMI(vfsp);
1064
1065 cnt = 0;
1066
1067 num = mi->smi_hash_avl.avl_numnodes;
1068
1069 vplist = kmem_alloc(num * sizeof (vnode_t*), KM_SLEEP);
1070
1071 rw_enter(&mi->smi_hash_lk, RW_READER);
1072 for (np = avl_first(&mi->smi_hash_avl); np != NULL;
1073 np = avl_walk(&mi->smi_hash_avl, np, AVL_AFTER)) {
1074 vp = SMBTOV(np);
1075 if (vn_is_readonly(vp))
1076 continue;
1077
1078 if (vn_has_cached_data(vp) && (np->r_flags & RDIRTY || np->r_mapcnt > 0)) {
1079 VN_HOLD(vp);
1080 vplist[cnt++] = vp;
1081 if (cnt == num)
1082 break;
1083 }
1084 }
1085 rw_exit(&mi->smi_hash_lk);
1086
1087 while (cnt-- > 0) {
1088 vp = vplist[cnt];
1089 (void) VOP_PUTPAGE(vp, 0, 0, 0, cr, NULL);
1090 VN_RELE(vp);
1091 }
1092
1093 kmem_free(vplist, num * sizeof (vnode_t*));
1094 }
1095
1096 /* access cache */
1097 /* client handles */
1098
1099 /*
1100 * initialize resources that are used by smbfs_subr.c
1101 * this is called from the _init() routine (by the way of smbfs_clntinit())
1102 *
1103 * NFS: nfs_subr.c:nfs_subrinit
1104 */
1105 int
1106 smbfs_subrinit(void)
1107 {
1108 ulong_t nsmbnode_max;
1109
1110 /*
1111 * Allocate and initialize the smbnode cache
1112 */
1113 if (nsmbnode <= 0)
1114 nsmbnode = ncsize; /* dnlc.h */
1115 nsmbnode_max = (ulong_t)((kmem_maxavail() >> 2) /
1116 sizeof (struct smbnode));
1117 if (nsmbnode > nsmbnode_max || (nsmbnode == 0 && ncsize == 0)) {
1118 zcmn_err(GLOBAL_ZONEID, CE_NOTE,
1119 "setting nsmbnode to max value of %ld", nsmbnode_max);
1120 nsmbnode = nsmbnode_max;
1121 }
1122
1123 smbnode_cache = kmem_cache_create("smbnode_cache", sizeof (smbnode_t),
1124 0, NULL, NULL, smbfs_kmem_reclaim, NULL, NULL, 0);
1125
1126 /*
1127 * Initialize the various mutexes and reader/writer locks
1128 */
1129 mutex_init(&smbfreelist_lock, NULL, MUTEX_DEFAULT, NULL);
1130 mutex_init(&smbfs_minor_lock, NULL, MUTEX_DEFAULT, NULL);
1131
1132 /*
1133 * Assign unique major number for all smbfs mounts
1134 */
1135 if ((smbfs_major = getudev()) == -1) {
1136 zcmn_err(GLOBAL_ZONEID, CE_WARN,
1137 "smbfs: init: can't get unique device number");
1138 smbfs_major = 0;
1139 }
1140 smbfs_minor = 0;
1141
1142 return (0);
1143 }
1144
1145 /*
1146 * free smbfs hash table, etc.
1147 * NFS: nfs_subr.c:nfs_subrfini
1148 */
1149 void
1150 smbfs_subrfini(void)
1151 {
1152
1153 /*
1154 * Destroy the smbnode cache
1155 */
1156 kmem_cache_destroy(smbnode_cache);
1157
1158 /*
1159 * Destroy the various mutexes and reader/writer locks
1160 */
1161 mutex_destroy(&smbfreelist_lock);
1162 mutex_destroy(&smbfs_minor_lock);
1163 }
1164
1165 /* rddir_cache ? */
1166
1167 /*
1168 * Support functions for smbfs_kmem_reclaim
1169 */
1170
1171 static void
1172 smbfs_node_reclaim(void)
1173 {
1174 smbmntinfo_t *mi;
1175 smbnode_t *np;
1176 vnode_t *vp;
1177
1178 mutex_enter(&smbfreelist_lock);
1179 while ((np = smbfreelist) != NULL) {
1180 sn_rmfree(np);
1181 mutex_exit(&smbfreelist_lock);
1182 if (np->r_flags & RHASHED) {
1183 vp = SMBTOV(np);
1184 mi = np->n_mount;
1185 rw_enter(&mi->smi_hash_lk, RW_WRITER);
1186 mutex_enter(&vp->v_lock);
1187 if (vp->v_count > 1) {
1188 vp->v_count--;
1189 mutex_exit(&vp->v_lock);
1190 rw_exit(&mi->smi_hash_lk);
1191 mutex_enter(&smbfreelist_lock);
1192 continue;
1193 }
1194 mutex_exit(&vp->v_lock);
1195 sn_rmhash_locked(np);
1196 rw_exit(&mi->smi_hash_lk);
1197 }
1198 /*
1199 * This call to smbfs_addfree will end up destroying the
1200 * smbnode, but in a safe way with the appropriate set
1201 * of checks done.
1202 */
1203 smbfs_addfree(np);
1204 mutex_enter(&smbfreelist_lock);
1205 }
1206 mutex_exit(&smbfreelist_lock);
1207 }
1208
1209 /*
1210 * Called by kmem_cache_alloc ask us if we could
1211 * "Please give back some memory!"
1212 *
1213 * Todo: dump nodes from the free list?
1214 */
1215 /*ARGSUSED*/
1216 void
1217 smbfs_kmem_reclaim(void *cdrarg)
1218 {
1219 smbfs_node_reclaim();
1220 }
1221
1222 /* nfs failover stuff */
1223 /* nfs_rw_xxx - see smbfs_rwlock.c */