Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/fs/union Always take the hash list lock before removing ...



details:   https://anonhg.NetBSD.org/src/rev/3e7a47526880
branches:  trunk
changeset: 756393:3e7a47526880
user:      hannken <hannken%NetBSD.org@localhost>
date:      Fri Jul 16 08:23:28 2010 +0000

description:
Always take the hash list lock before removing a node from the hash chain.

Release the hash list lock before calling getnewvnode() and check the
hash list again like other file systems do.

Take v_interlock before calling vget().

diffstat:

 sys/fs/union/union_subr.c |  55 +++++++++++++++++++++++++++++++++-------------
 1 files changed, 39 insertions(+), 16 deletions(-)

diffs (128 lines):

diff -r fb1eb190121e -r 3e7a47526880 sys/fs/union/union_subr.c
--- a/sys/fs/union/union_subr.c Thu Jul 15 23:46:55 2010 +0000
+++ b/sys/fs/union/union_subr.c Fri Jul 16 08:23:28 2010 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: union_subr.c,v 1.37 2010/06/24 13:03:11 hannken Exp $  */
+/*     $NetBSD: union_subr.c,v 1.38 2010/07/16 08:23:28 hannken Exp $  */
 
 /*
  * Copyright (c) 1994
@@ -72,7 +72,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.37 2010/06/24 13:03:11 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.38 2010/07/16 08:23:28 hannken Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -330,8 +330,8 @@
 {
        int error;
        struct vattr va;
-       struct union_node *un = NULL;
-       struct vnode *xlowervp = NULLVP;
+       struct union_node *un = NULL, *un1;
+       struct vnode *vp, *xlowervp = NULLVP;
        struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
        voff_t uppersz, lowersz;
        int hash = 0;
@@ -394,7 +394,9 @@
                            (un->un_uppervp == uppervp ||
                             un->un_uppervp == NULLVP) &&
                            (UNIONTOV(un)->v_mount == mp)) {
-                               if (vget(UNIONTOV(un), 0)) {
+                               vp = UNIONTOV(un);
+                               mutex_enter(&vp->v_interlock);
+                               if (vget(vp, LK_INTERLOCK)) {
                                        union_list_unlock(hash);
                                        goto loop;
                                }
@@ -502,17 +504,7 @@
        if (lowervp != NULLVP)
                if (VOP_GETATTR(lowervp, &va, FSCRED) == 0)
                        lowersz = va.va_size;
-
-       if (docache) {
-               /*
-                * otherwise lock the vp list while we call getnewvnode
-                * since that can block.
-                */
-               hash = UNION_HASH(uppervp, lowervp);
-
-               if (union_list_lock(hash))
-                       goto loop;
-       }
+       hash = UNION_HASH(uppervp, lowervp);
 
        error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
        if (error) {
@@ -528,6 +520,24 @@
                goto out;
        }
 
+       if (docache) {
+               while (union_list_lock(hash))
+                       continue;
+               LIST_FOREACH(un1, &unhead[hash], un_cache) {
+                       if (un1->un_lowervp == lowervp &&
+                           un1->un_uppervp == uppervp &&
+                           UNIONTOV(un1)->v_mount == mp) {
+                               /*
+                                * Another thread beat us, push back freshly
+                                * allocated vnode and retry.
+                                */
+                               union_list_unlock(hash);
+                               ungetnewvnode(*vpp);
+                               goto loop;
+                       }
+               }
+       }
+
        (*vpp)->v_data = malloc(sizeof(struct union_node), M_TEMP, M_WAITOK);
 
        (*vpp)->v_vflag |= vflag;
@@ -590,12 +600,18 @@
 int
 union_freevp(struct vnode *vp)
 {
+       int hash;
        struct union_node *un = VTOUNION(vp);
 
+       hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+
+       while (union_list_lock(hash))
+               continue;
        if (un->un_flags & UN_CACHED) {
                un->un_flags &= ~UN_CACHED;
                LIST_REMOVE(un, un_cache);
        }
+       union_list_unlock(hash);
 
        if (un->un_pvp != NULLVP)
                vrele(un->un_pvp);
@@ -998,6 +1014,8 @@
 void
 union_removed_upper(struct union_node *un)
 {
+       int hash;
+
 #if 1
        /*
         * We do not set the uppervp to NULLVP here, because lowervp
@@ -1013,10 +1031,15 @@
        union_newupper(un, NULLVP);
 #endif
 
+       hash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+
+       while (union_list_lock(hash))
+               continue;
        if (un->un_flags & UN_CACHED) {
                un->un_flags &= ~UN_CACHED;
                LIST_REMOVE(un, un_cache);
        }
+       union_list_unlock(hash);
 
        if (un->un_flags & UN_ULOCK) {
                un->un_flags &= ~UN_ULOCK;



Home | Main Index | Thread Index | Old Index