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 }