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 2008 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 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/thread.h> 34 #include <sys/t_lock.h> 35 #include <sys/time.h> 36 #include <sys/vnode.h> 37 #include <sys/vfs.h> 38 #include <sys/errno.h> 39 #include <sys/buf.h> 40 #include <sys/stat.h> 41 #include <sys/cred.h> 42 #include <sys/kmem.h> 43 #include <sys/debug.h> 44 #include <sys/dnlc.h> 45 #include <sys/vmsystm.h> 46 #include <sys/flock.h> 47 #include <sys/share.h> 48 #include <sys/cmn_err.h> 49 #include <sys/tiuser.h> 50 #include <sys/sysmacros.h> 51 #include <sys/callb.h> 52 #include <sys/acl.h> 53 #include <sys/kstat.h> 54 #include <sys/signal.h> 55 #include <sys/list.h> 56 #include <sys/zone.h> 57 58 #include <netsmb/smb_conn.h> 59 60 #include <smbfs/smbfs.h> 61 #include <smbfs/smbfs_node.h> 62 #include <smbfs/smbfs_subr.h> 63 64 #include <vm/hat.h> 65 #include <vm/as.h> 66 #include <vm/page.h> 67 #include <vm/pvn.h> 68 #include <vm/seg.h> 69 #include <vm/seg_map.h> 70 #include <vm/seg_vn.h> 71 72 /* 73 * The following code provide zone support in order to perform an action 74 * for each smbfs mount in a zone. This is also where we would add 75 * per-zone globals and kernel threads for the smbfs module (since 76 * they must be terminated by the shutdown callback). 77 */ 78 79 struct smi_globals { 80 kmutex_t smg_lock; /* lock protecting smg_list */ 81 list_t smg_list; /* list of SMBFS mounts in zone */ 82 boolean_t smg_destructor_called; 83 }; 84 typedef struct smi_globals smi_globals_t; 85 86 static zone_key_t smi_list_key; 87 88 /* ARGSUSED */ 89 static void * 90 smbfs_zone_init(zoneid_t zoneid) 91 { 92 smi_globals_t *smg; 93 94 smg = kmem_alloc(sizeof (*smg), KM_SLEEP); 95 mutex_init(&smg->smg_lock, NULL, MUTEX_DEFAULT, NULL); 96 list_create(&smg->smg_list, sizeof (smbmntinfo_t), 97 offsetof(smbmntinfo_t, smi_zone_node)); 98 smg->smg_destructor_called = B_FALSE; 99 return (smg); 100 } 101 102 /* 103 * Callback routine to tell all SMBFS mounts in the zone to stop creating new 104 * threads. Existing threads should exit. 105 */ 106 /* ARGSUSED */ 107 static void 108 smbfs_zone_shutdown(zoneid_t zoneid, void *data) 109 { 110 smi_globals_t *smg = data; 111 smbmntinfo_t *smi; 112 113 ASSERT(smg != NULL); 114 again: 115 mutex_enter(&smg->smg_lock); 116 for (smi = list_head(&smg->smg_list); smi != NULL; 117 smi = list_next(&smg->smg_list, smi)) { 118 119 /* 120 * If we've done the shutdown work for this FS, skip. 121 * Once we go off the end of the list, we're done. 122 */ 123 if (smi->smi_flags & SMI_DEAD) 124 continue; 125 126 /* 127 * We will do work, so not done. Get a hold on the FS. 128 */ 129 VFS_HOLD(smi->smi_vfsp); 130 131 /* 132 * purge the DNLC for this filesystem 133 */ 134 (void) dnlc_purge_vfsp(smi->smi_vfsp, 0); 135 136 mutex_enter(&smi->smi_lock); 137 smi->smi_flags |= SMI_DEAD; 138 mutex_exit(&smi->smi_lock); 139 140 /* 141 * Drop lock and release FS, which may change list, then repeat. 142 * We're done when every mi has been done or the list is empty. 143 */ 144 mutex_exit(&smg->smg_lock); 145 VFS_RELE(smi->smi_vfsp); 146 goto again; 147 } 148 mutex_exit(&smg->smg_lock); 149 } 150 151 static void 152 smbfs_zone_free_globals(smi_globals_t *smg) 153 { 154 list_destroy(&smg->smg_list); /* makes sure the list is empty */ 155 mutex_destroy(&smg->smg_lock); 156 kmem_free(smg, sizeof (*smg)); 157 158 } 159 160 /* ARGSUSED */ 161 static void 162 smbfs_zone_destroy(zoneid_t zoneid, void *data) 163 { 164 smi_globals_t *smg = data; 165 166 ASSERT(smg != NULL); 167 mutex_enter(&smg->smg_lock); 168 if (list_head(&smg->smg_list) != NULL) { 169 /* Still waiting for VFS_FREEVFS() */ 170 smg->smg_destructor_called = B_TRUE; 171 mutex_exit(&smg->smg_lock); 172 return; 173 } 174 smbfs_zone_free_globals(smg); 175 } 176 177 /* 178 * Add an SMBFS mount to the per-zone list of SMBFS mounts. 179 */ 180 void 181 smbfs_zonelist_add(smbmntinfo_t *smi) 182 { 183 smi_globals_t *smg; 184 185 smg = zone_getspecific(smi_list_key, smi->smi_zone); 186 mutex_enter(&smg->smg_lock); 187 list_insert_head(&smg->smg_list, smi); 188 mutex_exit(&smg->smg_lock); 189 } 190 191 /* 192 * Remove an SMBFS mount from the per-zone list of SMBFS mounts. 193 */ 194 void 195 smbfs_zonelist_remove(smbmntinfo_t *smi) 196 { 197 smi_globals_t *smg; 198 199 smg = zone_getspecific(smi_list_key, smi->smi_zone); 200 mutex_enter(&smg->smg_lock); 201 list_remove(&smg->smg_list, smi); 202 /* 203 * We can be called asynchronously by VFS_FREEVFS() after the zone 204 * shutdown/destroy callbacks have executed; if so, clean up the zone's 205 * smi_globals. 206 */ 207 if (list_head(&smg->smg_list) == NULL && 208 smg->smg_destructor_called == B_TRUE) { 209 smbfs_zone_free_globals(smg); 210 return; 211 } 212 mutex_exit(&smg->smg_lock); 213 } 214 215 216 #ifdef NEED_SMBFS_CALLBACKS 217 /* 218 * Call-back hooks for netsmb, in case we want them. 219 * Apple's VFS wants them. We may not need them. 220 * 221 * I thought I could use the "dead" callback from netsmb 222 * to set the SMI_DEAD flag, but that looks like it will 223 * interfere with the zone shutdown mechanisms. 224 */ 225 static void smbfs_dead(smb_share_t *ssp) 226 { 227 #if 0 /* see above */ 228 smbmntinfo_t *smi = ssp->ss_mount; 229 if (smi) { 230 mutex_enter(&smi->smi_lock); 231 smi->smi_flags |= SMI_DEAD; 232 mutex_exit(&smi->smi_lock); 233 } 234 #endif 235 } 236 237 static void smbfs_down(smb_share_t *ss) 238 { 239 /* no-op */ 240 } 241 242 static void smbfs_up(smb_share_t *ss) 243 { 244 /* no-op */ 245 } 246 247 smb_fscb_t smbfs_cb = { 248 .fscb_dead = smbfs_dead, 249 .fscb_down = smbfs_down, 250 .fscb_up = smbfs_up }; 251 252 #endif /* NEED_SMBFS_CALLBACKS */ 253 254 /* 255 * SMBFS Client initialization routine. This routine should only be called 256 * once. It performs the following tasks: 257 * - Initalize all global locks 258 * - Call sub-initialization routines (localize access to variables) 259 */ 260 int 261 smbfs_clntinit(void) 262 { 263 int error; 264 265 error = smbfs_subrinit(); 266 if (error) 267 return (error); 268 zone_key_create(&smi_list_key, smbfs_zone_init, smbfs_zone_shutdown, 269 smbfs_zone_destroy); 270 #ifdef NEED_SMBFS_CALLBACKS 271 smb_fscb_set(&smbfs_cb); 272 #endif /* NEED_SMBFS_CALLBACKS */ 273 return (0); 274 } 275 276 /* 277 * This routine is called when the modunload is called. This will cleanup 278 * the previously allocated/initialized nodes. 279 */ 280 void 281 smbfs_clntfini(void) 282 { 283 #ifdef NEED_SMBFS_CALLBACKS 284 smb_fscb_set(NULL); 285 #endif /* NEED_SMBFS_CALLBACKS */ 286 (void) zone_key_delete(smi_list_key); 287 smbfs_subrfini(); 288 } 289