Print this page
6265 speed up mount/umount

@@ -83,10 +83,11 @@
 #include <sys/attr.h>
 #include <sys/zio.h>
 #include <sys/spa.h>
 #include <sys/lofi.h>
 #include <sys/bootprops.h>
+#include <sys/avl.h>
 
 #include <vm/page.h>
 
 #include <fs/fs_subr.h>
 /* Private interfaces to create vopstats-related data structures */

@@ -135,10 +136,15 @@
 
 static struct vfs root;
 static struct vfs devices;
 static struct vfs dev;
 struct vfs *rootvfs = &root;    /* pointer to root vfs; head of VFS list. */
+avl_tree_t vfs_by_dev;          /* avl tree to index mounted VFSs by dev */
+avl_tree_t vfs_by_mntpnt;       /* avl tree to index mounted VFSs by mntpnt */
+uint64_t vfs_curr_mntix;        /* counter to provide a unique mntix for
+                                 * entries in the above avl trees.
+                                 * protected by vfslist lock */
 rvfs_t *rvfs_list;              /* array of vfs ptrs for vfs hash list */
 int vfshsz = 512;               /* # of heads/locks in vfs hash arrays */
                                 /* must be power of 2!  */
 timespec_t vfs_mnttab_ctime;    /* mnttab created time */
 timespec_t vfs_mnttab_mtime;    /* mnttab last modified time */

@@ -606,10 +612,52 @@
 {
         vfs_sync(0);
 }
 
 /*
+ * compare function for vfs_by_dev avl tree. compare dev first, then mntix
+ */
+static int
+vfs_cmp_dev(const void *aa, const void *bb)
+{
+        const vfs_t *a = aa;
+        const vfs_t *b = bb;
+
+        if (a->vfs_dev < b->vfs_dev)
+                return (-1);
+        if (a->vfs_dev > b->vfs_dev)
+                return (1);
+        if (a->vfs_mntix < b->vfs_mntix)
+                return (-1);
+        if (a->vfs_mntix > b->vfs_mntix)
+                return (1);
+        return (0);
+}
+
+/*
+ * compare function for vfs_by_mntpnt avl tree. compare mntpnt first, then mntix
+ */
+static int
+vfs_cmp_mntpnt(const void *aa, const void *bb)
+{
+        const vfs_t *a = aa;
+        const vfs_t *b = bb;
+        int ret;
+
+        ret = strcmp(refstr_value(a->vfs_mntpt), refstr_value(b->vfs_mntpt));
+        if (ret < 0)
+                return (-1);
+        if (ret > 0)
+                return (1);
+        if (a->vfs_mntix < b->vfs_mntix)
+                return (-1);
+        if (a->vfs_mntix > b->vfs_mntix)
+                return (1);
+        return (0);
+}
+
+/*
  * External routines.
  */
 
 krwlock_t vfssw_lock;   /* lock accesses to vfssw */
 

@@ -816,10 +864,18 @@
 
         rw_init(&vfssw_lock, NULL, RW_DEFAULT, NULL);
         rw_init(&vfslist, NULL, RW_DEFAULT, NULL);
 
         /*
+         * Alloc the avl trees for quick indexing via dev and mountpoint
+         */
+        avl_create(&vfs_by_dev, vfs_cmp_dev, sizeof(vfs_t),
+            offsetof(vfs_t, vfs_avldev));
+        avl_create(&vfs_by_mntpnt, vfs_cmp_mntpnt, sizeof(vfs_t),
+            offsetof(vfs_t, vfs_avlmntpnt));
+
+        /*
          * Alloc the vfs hash bucket array and locks
          */
         rvfs_list = kmem_zalloc(vfshsz * sizeof (rvfs_t), KM_SLEEP);
 
         /*

@@ -3609,10 +3665,22 @@
          * the UFS.
          */
         vfs_hash_add(vfsp, 0);
 
         /*
+         * Link into tree indexed by mntpoint, for vfs_mntpoint2vfsp
+         * mntix discerns entries with the same key
+         */
+        vfsp->vfs_mntix = ++vfs_curr_mntix;
+        avl_add(&vfs_by_dev, vfsp);
+
+        /*
+         * Link into tree indexed by dev, for vfs_devismounted
+         */
+        avl_add(&vfs_by_mntpnt, vfsp);
+
+        /*
          * update the mnttab modification time
          */
         vfs_mnttab_modtimeupd();
         vfs_list_unlock();
         zone_rele(zone);

@@ -3632,10 +3700,16 @@
         ASSERT(vfsp != rootvfs);
 
         vfs_list_lock();
 
         /*
+         * Remove from avl trees
+         */
+        avl_remove(&vfs_by_mntpnt, vfsp);
+        avl_remove(&vfs_by_dev, vfsp);
+
+        /*
          * Remove from hash.
          */
         vfs_hash_remove(vfsp);
 
         /*

@@ -3725,22 +3799,30 @@
 
 int
 vfs_devismounted(dev_t dev)
 {
         struct vfs *vfsp;
-        int found;
+        int found = 0;
+        struct vfs search;
+        avl_index_t index;
+
+        search.vfs_dev = dev;
+        search.vfs_mntix = 0;
 
         vfs_list_read_lock();
-        vfsp = rootvfs;
-        found = 0;
-        do {
-                if (vfsp->vfs_dev == dev) {
+
+        /*
+         * there might be several entries with the same dev in the tree,
+         * only discerned by mntix. To find the first, we start with a mntix
+         * of 0. The search will fail. The following avl_nearest will give
+         * us the actual first entry.
+         */
+        VERIFY(avl_find(&vfs_by_dev, &search, &index) == NULL);
+        vfsp = avl_nearest(&vfs_by_dev, index, AVL_AFTER);
+
+        if (vfsp != NULL && vfsp->vfs_dev == dev)
                         found = 1;
-                        break;
-                }
-                vfsp = vfsp->vfs_next;
-        } while (vfsp != rootvfs);
 
         vfs_list_unlock();
         return (found);
 }
 

@@ -3752,33 +3834,46 @@
 struct vfs *
 vfs_dev2vfsp(dev_t dev)
 {
         struct vfs *vfsp;
         int found;
+        struct vfs search;
+        avl_index_t index;
+
+        search.vfs_dev = dev;
+        search.vfs_mntix = 0;
 
         vfs_list_read_lock();
-        vfsp = rootvfs;
+
+        /*
+         * there might be several entries with the same dev in the tree,
+         * only discerned by mntix. To find the first, we start with a mntix
+         * of 0. The search will fail. The following avl_nearest will give
+         * us the actual first entry.
+         */
+        VERIFY(avl_find(&vfs_by_dev, &search, &index) == NULL);
+        vfsp = avl_nearest(&vfs_by_dev, index, AVL_AFTER);
+
         found = 0;
-        do {
+        while (vfsp != NULL && vfsp->vfs_dev == dev) {
                 /*
                  * The following could be made more efficient by making
                  * the entire loop use vfs_zone_next if the call is from
                  * a zone.  The only callers, however, ustat(2) and
                  * umount2(2), don't seem to justify the added
                  * complexity at present.
                  */
-                if (vfsp->vfs_dev == dev &&
-                    ZONE_PATH_VISIBLE(refstr_value(vfsp->vfs_mntpt),
+                if (ZONE_PATH_VISIBLE(refstr_value(vfsp->vfs_mntpt),
                     curproc->p_zone)) {
                         VFS_HOLD(vfsp);
                         found = 1;
                         break;
                 }
-                vfsp = vfsp->vfs_next;
-        } while (vfsp != rootvfs);
+                vfsp = AVL_NEXT(&vfs_by_dev, vfsp);
+        }
         vfs_list_unlock();
-        return (found ? vfsp: NULL);
+        return (found ? vfsp : NULL);
 }
 
 /*
  * Search the vfs list for a specified mntpoint.  Returns a pointer to it
  * or NULL if no suitable entry is found. The caller of this routine

@@ -3801,18 +3896,30 @@
         vfs_list_read_lock();
         if (getzoneid() == GLOBAL_ZONEID) {
                 /*
                  * The global zone may see filesystems in any zone.
                  */
-                vfsp = rootvfs->vfs_prev;
-                do {
-                        if (strcmp(refstr_value(vfsp->vfs_mntpt), mp) == 0) {
+                struct vfs search;
+                search.vfs_mntpt = refstr_alloc(mp);
+                search.vfs_mntix = UINT64_MAX;
+                avl_index_t index;
+
+                /*
+                 * there might be several entries with the same mntpnt in the
+                 * tree, only discerned by mntix. To find the last, we start
+                 * with a mntix of UINT64_MAX. The search will fail. The
+                 * following avl_nearest will give  us the actual last entry
+                 * matching the mntpnt.
+                 */
+                VERIFY(avl_find(&vfs_by_mntpnt, &search, &index) == 0);
+                vfsp = avl_nearest(&vfs_by_mntpnt, index, AVL_BEFORE);
+
+                refstr_rele(search.vfs_mntpt);
+
+                if (vfsp != NULL &&
+                    strcmp(refstr_value(vfsp->vfs_mntpt), mp) == 0)
                                 retvfsp = vfsp;
-                                break;
-                        }
-                        vfsp = vfsp->vfs_prev;
-                } while (vfsp != rootvfs->vfs_prev);
         } else if ((list = zone->zone_vfslist) != NULL) {
                 const char *mntpt;
 
                 vfsp = list->vfs_zone_prev;
                 do {