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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25#include <sys/vfs.h>
26#include <smbsrv/smb_ktypes.h>
27#include <smbsrv/smb_kproto.h>
28
29static smb_vfs_t *smb_vfs_find(smb_export_t *, vfs_t *);
30static void smb_vfs_destroy(smb_export_t *, smb_vfs_t *);
31
32/*
33 * If a hold on the specified VFS has already been taken
34 * then only increment the reference count of the corresponding
35 * smb_vfs_t structure. If no smb_vfs_t structure has been created
36 * yet for the specified VFS then create one and take a hold on
37 * the VFS.
38 */
39int
40smb_vfs_hold(smb_export_t *se, vfs_t *vfsp)
41{
42	smb_vfs_t	*smb_vfs;
43	vnode_t 	*rootvp;
44	int		rc;
45
46	if (se == NULL || vfsp == NULL)
47		return (EINVAL);
48
49	smb_llist_enter(&se->e_vfs_list, RW_WRITER);
50
51	if ((smb_vfs = smb_vfs_find(se, vfsp)) != NULL) {
52		smb_vfs->sv_refcnt++;
53		DTRACE_PROBE1(smb_vfs_hold_hit, smb_vfs_t *, smb_vfs);
54		smb_llist_exit(&se->e_vfs_list);
55		return (0);
56	}
57
58	if ((rc = VFS_ROOT(vfsp, &rootvp)) != 0) {
59		smb_llist_exit(&se->e_vfs_list);
60		return (rc);
61	}
62
63	smb_vfs = kmem_cache_alloc(se->e_cache_vfs, KM_SLEEP);
64
65	bzero(smb_vfs, sizeof (smb_vfs_t));
66
67	smb_vfs->sv_magic = SMB_VFS_MAGIC;
68	smb_vfs->sv_refcnt = 1;
69	smb_vfs->sv_vfsp = vfsp;
70	/*
71	 * We have a hold on the root vnode of the file system
72	 * from the VFS_ROOT call above.
73	 */
74	smb_vfs->sv_rootvp = rootvp;
75
76	smb_llist_insert_head(&se->e_vfs_list, smb_vfs);
77	DTRACE_PROBE1(smb_vfs_hold_miss, smb_vfs_t *, smb_vfs);
78	smb_llist_exit(&se->e_vfs_list);
79
80	return (0);
81}
82
83/*
84 * smb_vfs_rele
85 *
86 * Decrements the reference count of the fs passed in. If the reference count
87 * drops to zero the smb_vfs_t structure associated with the fs is freed.
88 */
89void
90smb_vfs_rele(smb_export_t *se, vfs_t *vfsp)
91{
92	smb_vfs_t	*smb_vfs;
93
94	ASSERT(vfsp);
95
96	smb_llist_enter(&se->e_vfs_list, RW_WRITER);
97	smb_vfs = smb_vfs_find(se, vfsp);
98	DTRACE_PROBE1(smb_vfs_release, smb_vfs_t *, smb_vfs)
99	if (smb_vfs) {
100		ASSERT(smb_vfs->sv_refcnt);
101		if (--smb_vfs->sv_refcnt == 0) {
102			smb_llist_remove(&se->e_vfs_list, smb_vfs);
103			smb_llist_exit(&se->e_vfs_list);
104			smb_vfs_destroy(se, smb_vfs);
105			return;
106		}
107	}
108	smb_llist_exit(&se->e_vfs_list);
109}
110
111/*
112 * smb_vfs_rele_all()
113 *
114 * Release all holds on root vnodes of file systems which were taken
115 * due to the existence of at least one enabled share on the file system.
116 * Called at driver close time.
117 */
118void
119smb_vfs_rele_all(smb_export_t *se)
120{
121	smb_vfs_t	*smb_vfs;
122
123	smb_llist_enter(&se->e_vfs_list, RW_WRITER);
124	while ((smb_vfs = smb_llist_head(&se->e_vfs_list)) != NULL) {
125
126		ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC);
127		DTRACE_PROBE1(smb_vfs_rele_all_hit, smb_vfs_t *, smb_vfs);
128		smb_llist_remove(&se->e_vfs_list, smb_vfs);
129		smb_vfs_destroy(se, smb_vfs);
130	}
131	smb_llist_exit(&se->e_vfs_list);
132}
133
134/*
135 * Goes through the list of smb_vfs_t structure and returns the one matching
136 * the vnode passed in. If no match is found a NULL pointer is returned.
137 *
138 * The list of smb_vfs_t structures has to have been entered prior calling
139 * this function.
140 */
141static smb_vfs_t *
142smb_vfs_find(smb_export_t *se, vfs_t *vfsp)
143{
144	smb_vfs_t *smb_vfs;
145
146	smb_vfs = smb_llist_head(&se->e_vfs_list);
147	while (smb_vfs) {
148		ASSERT(smb_vfs->sv_magic == SMB_VFS_MAGIC);
149		if (smb_vfs->sv_vfsp == vfsp)
150			return (smb_vfs);
151		smb_vfs = smb_llist_next(&se->e_vfs_list, smb_vfs);
152	}
153
154	return (NULL);
155}
156
157static void
158smb_vfs_destroy(smb_export_t *se, smb_vfs_t *smb_vfs)
159{
160	VN_RELE(smb_vfs->sv_rootvp);
161	smb_vfs->sv_magic = (uint32_t)~SMB_VFS_MAGIC;
162	kmem_cache_free(se->e_cache_vfs, smb_vfs);
163}
164