Module Name: src
Committed By: snj
Date: Thu Dec 4 05:43:55 UTC 2014
Modified Files:
src/sys/ufs/ufs [netbsd-6]: ufs_extattr.c
Log Message:
Pull up following revision(s) (requested by manu in ticket #1197):
sys/ufs/ufs/ufs_extattr.c: revision 1.41, 1.45
Remove always-true condition and note that the current code is
suboptimal.
--
Fix UFS1 extended attribute backend autocreation deadlock
UFS1 extended attribute backend autocration goes through a vn_open()
to create the backend file, and this forces us to release the lock
on the target node, in case the target is within the parents of the
backend file. That created a window within which another thread could
acquire a lock on the target vnode and deadlock awaiting for the
mount extended attribute lock.
We fix the problem by also releasing the mount extended attribute lock
when calling vn_open(), but that lets another thread race us for backend
creation. We just detect this using O_EXCL for vn_open() and by checking
for EEXIST return code. If we are raced, we fail backend creation but
this is not a problem since another thread succeeded on it: we just have
to use the result.
To generate a diff of this commit:
cvs rdiff -u -r1.36.2.3 -r1.36.2.4 src/sys/ufs/ufs/ufs_extattr.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/ufs/ufs/ufs_extattr.c
diff -u src/sys/ufs/ufs/ufs_extattr.c:1.36.2.3 src/sys/ufs/ufs/ufs_extattr.c:1.36.2.4
--- src/sys/ufs/ufs/ufs_extattr.c:1.36.2.3 Thu Dec 4 05:38:54 2014
+++ src/sys/ufs/ufs/ufs_extattr.c Thu Dec 4 05:43:55 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: ufs_extattr.c,v 1.36.2.3 2014/12/04 05:38:54 snj Exp $ */
+/* $NetBSD: ufs_extattr.c,v 1.36.2.4 2014/12/04 05:43:55 snj Exp $ */
/*-
* Copyright (c) 1999-2002 Robert N. M. Watson
@@ -48,7 +48,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.36.2.3 2014/12/04 05:38:54 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.36.2.4 2014/12/04 05:43:55 snj Exp $");
#ifdef _KERNEL_OPT
#include "opt_ffs.h"
@@ -158,9 +158,9 @@ ufs_extattr_valid_attrname(int attrnames
/*
* Autocreate an attribute storage
*/
-static struct ufs_extattr_list_entry *
+static int
ufs_extattr_autocreate_attr(struct vnode *vp, int attrnamespace,
- const char *attrname, struct lwp *l)
+ const char *attrname, struct lwp *l, struct ufs_extattr_list_entry **uelep)
{
struct mount *mp = vp->v_mount;
struct ufsmount *ump = VFSTOUFS(mp);
@@ -194,38 +194,51 @@ ufs_extattr_autocreate_attr(struct vnode
break;
default:
PNBUF_PUT(path);
- return NULL;
+ *uelep = NULL;
+ return EINVAL;
break;
}
/*
- * When setting attribute on the root vnode, we get it
- * already locked, and vn_open/namei/VFS_ROOT will try to
- * look it, causing a panic. Unlock it first.
+ * Release extended attribute mount lock, otherwise
+ * we can deadlock with another thread that would lock
+ * vp after we unlock it below, and call
+ * ufs_extattr_uepm_lock(ump), for instance
+ * in ufs_getextattr().
+ */
+ ufs_extattr_uepm_unlock(ump);
+
+ /*
+ * XXX unlock/lock should only be done when setting extattr
+ * on backing store or one of its parent directory
+ * including root, but we always do it for now.
*/
- if (vp->v_vflag && VV_ROOT) {
- KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
- VOP_UNLOCK(vp);
- }
- KASSERT(VOP_ISLOCKED(vp) == 0);
+ KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+ VOP_UNLOCK(vp);
pb = pathbuf_create(path);
NDINIT(&nd, CREATE, LOCKPARENT, pb);
- error = vn_open(&nd, O_CREAT|O_RDWR, 0600);
+ /*
+ * Since we do not hold ufs_extattr_uepm_lock anymore,
+ * another thread may race with us for backend creation,
+ * but only one can succeed here thanks to O_EXCL
+ */
+ error = vn_open(&nd, O_CREAT|O_EXCL|O_RDWR, 0600);
/*
- * Reacquire the lock on the vnode if it was root.
+ * Reacquire the lock on the vnode
*/
KASSERT(VOP_ISLOCKED(vp) == 0);
- if (vp->v_vflag && VV_ROOT)
- vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
- KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+
+ ufs_extattr_uepm_lock(ump);
if (error != 0) {
pathbuf_destroy(pb);
PNBUF_PUT(path);
- return NULL;
+ *uelep = NULL;
+ return error;
}
KASSERT(nd.ni_vp != NULL);
@@ -253,7 +266,8 @@ ufs_extattr_autocreate_attr(struct vnode
printf("%s: write uef header failed for %s, error = %d\n",
__func__, attrname, error);
vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
- return NULL;
+ *uelep = NULL;
+ return error;
}
/*
@@ -266,7 +280,8 @@ ufs_extattr_autocreate_attr(struct vnode
printf("%s: enable %s failed, error %d\n",
__func__, attrname, error);
vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
- return NULL;
+ *uelep = NULL;
+ return error;
}
uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
@@ -274,13 +289,15 @@ ufs_extattr_autocreate_attr(struct vnode
printf("%s: atttribute %s created but not found!\n",
__func__, attrname);
vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
- return NULL;
+ *uelep = NULL;
+ return ESRCH; /* really internal error */
}
printf("%s: EA backing store autocreated for %s\n",
mp->mnt_stat.f_mntonname, attrname);
- return uele;
+ *uelep = uele;
+ return 0;
}
/*
@@ -1343,10 +1360,17 @@ ufs_extattr_set(struct vnode *vp, int at
attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
if (!attribute) {
- attribute = ufs_extattr_autocreate_attr(vp, attrnamespace,
- name, l);
- if (!attribute)
- return (ENODATA);
+ error = ufs_extattr_autocreate_attr(vp, attrnamespace,
+ name, l, &attribute);
+ if (error == EEXIST) {
+ /* Another thread raced us for backend creation */
+ error = 0;
+ attribute =
+ ufs_extattr_find_attr(ump, attrnamespace, name);
+ }
+
+ if (error || !attribute)
+ return ENODATA;
}
/*