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