diff options
author | davidxu <davidxu@FreeBSD.org> | 2008-09-05 07:32:57 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2008-09-05 07:32:57 +0000 |
commit | 40259861b6c771d63243844c5a977af17c3fb727 (patch) | |
tree | ccb483409af9e9c9465fd7f410651673320fb15d /sys/kern/uipc_mqueue.c | |
parent | 686ff3ee25fc6bea21e81985922455f1b70977ec (diff) | |
download | FreeBSD-src-40259861b6c771d63243844c5a977af17c3fb727.zip FreeBSD-src-40259861b6c771d63243844c5a977af17c3fb727.tar.gz |
Fix LOR between vnode lock and internal mqueue locks.
Diffstat (limited to 'sys/kern/uipc_mqueue.c')
-rw-r--r-- | sys/kern/uipc_mqueue.c | 113 |
1 files changed, 72 insertions, 41 deletions
diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c index bce381f..3b73f40 100644 --- a/sys/kern/uipc_mqueue.c +++ b/sys/kern/uipc_mqueue.c @@ -373,16 +373,24 @@ mqnode_addref(struct mqfs_node *node) static __inline void mqnode_release(struct mqfs_node *node) { + struct mqfs_info *mqfs; int old, exp; + mqfs = node->mn_info; old = atomic_fetchadd_int(&node->mn_refcount, -1); if (node->mn_type == mqfstype_dir || node->mn_type == mqfstype_root) exp = 3; /* include . and .. */ else exp = 1; - if (old == exp) + if (old == exp) { + int locked = sx_xlocked(&mqfs->mi_lock); + if (!locked) + sx_xlock(&mqfs->mi_lock); mqfs_destroy(node); + if (!locked) + sx_xunlock(&mqfs->mi_lock); + } } /* @@ -417,7 +425,7 @@ mqfs_create_node(const char *name, int namelen, struct ucred *cred, int mode, strncpy(node->mn_name, name, namelen); node->mn_type = nodetype; node->mn_refcount = 1; - getnanotime(&node->mn_birth); + vfs_timestamp(&node->mn_birth); node->mn_ctime = node->mn_atime = node->mn_mtime = node->mn_birth; node->mn_uid = cred->cr_uid; @@ -601,9 +609,7 @@ mqfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) int ret; mqfs = VFSTOMQFS(mp); - sx_xlock(&mqfs->mi_lock); ret = mqfs_allocv(mp, vpp, mqfs->mi_root); - sx_xunlock(&mqfs->mi_lock); return (ret); } @@ -696,28 +702,56 @@ static int mqfs_allocv(struct mount *mp, struct vnode **vpp, struct mqfs_node *pn) { struct mqfs_vdata *vd; + struct mqfs_info *mqfs; + struct vnode *newvpp; int error; + mqfs = pn->mn_info; + *vpp = NULL; + sx_xlock(&mqfs->mi_lock); LIST_FOREACH(vd, &pn->mn_vnodes, mv_link) { - if (vd->mv_vnode->v_mount == mp) + if (vd->mv_vnode->v_mount == mp) { + vhold(vd->mv_vnode); break; + } } if (vd != NULL) { +found: *vpp = vd->mv_vnode; - vget(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); - return (0); + sx_xunlock(&mqfs->mi_lock); + error = vget(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); + vdrop(*vpp); + return (error); } + sx_xunlock(&mqfs->mi_lock); - error = getnewvnode("mqueue", mp, &mqfs_vnodeops, vpp); + error = getnewvnode("mqueue", mp, &mqfs_vnodeops, &newvpp); if (error) return (error); - vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); - error = insmntque(*vpp, mp); - if (error != 0) { - *vpp = NULLVP; + vn_lock(newvpp, LK_EXCLUSIVE | LK_RETRY); + error = insmntque(newvpp, mp); + if (error != 0) return (error); + + sx_xlock(&mqfs->mi_lock); + /* + * Check if it has already been allocated + * while we were blocked. + */ + LIST_FOREACH(vd, &pn->mn_vnodes, mv_link) { + if (vd->mv_vnode->v_mount == mp) { + vhold(vd->mv_vnode); + sx_xunlock(&mqfs->mi_lock); + + vgone(newvpp); + vput(newvpp); + goto found; + } } + + *vpp = newvpp; + vd = uma_zalloc(mvdata_zone, M_WAITOK); (*vpp)->v_data = vd; vd->mv_vnode = *vpp; @@ -745,6 +779,7 @@ mqfs_allocv(struct mount *mp, struct vnode **vpp, struct mqfs_node *pn) default: panic("%s has unexpected type: %d", pn->mn_name, pn->mn_type); } + sx_xunlock(&mqfs->mi_lock); return (0); } @@ -756,6 +791,7 @@ mqfs_search(struct mqfs_node *pd, const char *name, int len) { struct mqfs_node *pn; + sx_assert(&pd->mn_info->mi_lock, SX_LOCKED); LIST_FOREACH(pn, &pd->mn_children, mn_sibling) { if (strncmp(pn->mn_name, name, len) == 0) return (pn); @@ -773,6 +809,7 @@ mqfs_lookupx(struct vop_cachedlookup_args *ap) struct vnode *dvp, **vpp; struct mqfs_node *pd; struct mqfs_node *pn; + struct mqfs_info *mqfs; int nameiop, flags, error, namelen; char *pname; struct thread *td; @@ -787,6 +824,7 @@ mqfs_lookupx(struct vop_cachedlookup_args *ap) nameiop = cnp->cn_nameiop; pd = VTON(dvp); pn = NULL; + mqfs = pd->mn_info; *vpp = NULLVP; if (dvp->v_type != VDIR) @@ -825,24 +863,32 @@ mqfs_lookupx(struct vop_cachedlookup_args *ap) } /* named node */ + sx_xlock(&mqfs->mi_lock); pn = mqfs_search(pd, pname, namelen); + if (pn != NULL) + mqnode_addref(pn); + sx_xunlock(&mqfs->mi_lock); /* found */ if (pn != NULL) { /* DELETE */ if (nameiop == DELETE && (flags & ISLASTCN)) { error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, td); - if (error) + if (error) { + mqnode_release(pn); return (error); + } if (*vpp == dvp) { VREF(dvp); *vpp = dvp; + mqnode_release(pn); return (0); } } /* allocate vnode */ error = mqfs_allocv(dvp->v_mount, vpp, pn); + mqnode_release(pn); if (error == 0 && cnp->cn_flags & MAKEENTRY) cache_enter(dvp, *vpp, cnp); return (error); @@ -877,12 +923,9 @@ struct vop_lookup_args { static int mqfs_lookup(struct vop_cachedlookup_args *ap) { - struct mqfs_info *mqfs = VFSTOMQFS(ap->a_dvp->v_mount); int rc; - sx_xlock(&mqfs->mi_lock); rc = mqfs_lookupx(ap); - sx_xunlock(&mqfs->mi_lock); return (rc); } @@ -915,30 +958,23 @@ mqfs_create(struct vop_create_args *ap) if (mq == NULL) return (EAGAIN); sx_xlock(&mqfs->mi_lock); -#if 0 - /* named node */ - pn = mqfs_search(pd, cnp->cn_nameptr, cnp->cn_namelen); - if (pn != NULL) { - mqueue_free(mq); - sx_xunlock(&mqfs->mi_lock); - return (EEXIST); - } -#else if ((cnp->cn_flags & HASBUF) == 0) panic("%s: no name", __func__); -#endif pn = mqfs_create_file(pd, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_cred, ap->a_vap->va_mode); - if (pn == NULL) + if (pn == NULL) { + sx_xunlock(&mqfs->mi_lock); error = ENOSPC; - else { + } else { + mqnode_addref(pn); + sx_xunlock(&mqfs->mi_lock); error = mqfs_allocv(ap->a_dvp->v_mount, ap->a_vpp, pn); + mqnode_release(pn); if (error) mqfs_destroy(pn); else pn->mn_data = mq; } - sx_xunlock(&mqfs->mi_lock); if (error) mqueue_free(mq); return (error); @@ -1412,24 +1448,19 @@ mqfs_mkdir(struct vop_mkdir_args *ap) if (pd->mn_type != mqfstype_root && pd->mn_type != mqfstype_dir) return (ENOTDIR); sx_xlock(&mqfs->mi_lock); -#if 0 - /* named node */ - pn = mqfs_search(pd, cnp->cn_nameptr, cnp->cn_namelen); - if (pn != NULL) { - sx_xunlock(&mqfs->mi_lock); - return (EEXIST); - } -#else if ((cnp->cn_flags & HASBUF) == 0) panic("%s: no name", __func__); -#endif pn = mqfs_create_dir(pd, cnp->cn_nameptr, cnp->cn_namelen, ap->a_vap->cn_cred, ap->a_vap->va_mode); - if (pn == NULL) + if (pn != NULL) + mqnode_addref(pn); + sx_xunlock(&mqfs->mi_lock); + if (pn == NULL) { error = ENOSPC; - else + } else { error = mqfs_allocv(ap->a_dvp->v_mount, ap->a_vpp, pn); - sx_xunlock(&mqfs->mi_lock); + mqnode_release(pn); + } return (error); } |