NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/46997: ufs_extattr_uepm_lock() is broken
The following reply was made to PR kern/46997; it has been noted by GNATS.
From: David Holland <dholland-bugs%netbsd.org@localhost>
To: gnats-bugs%NetBSD.org@localhost
Cc:
Subject: Re: kern/46997: ufs_extattr_uepm_lock() is broken
Date: Wed, 9 Nov 2016 05:31:53 +0000
On Sun, Sep 23, 2012 at 03:05:01AM +0000, mrg%eterna.com.au@localhost wrote:
> static void
> ufs_extattr_uepm_lock(struct ufsmount *ump)
> {
>
> /* XXX Why does this need to be recursive? */
> if (mutex_owned(&ump->um_extattr.uepm_lock)) {
> ump->um_extattr.uepm_lockcnt++;
> return;
> }
> mutex_enter(&ump->um_extattr.uepm_lock);
> }
>
> mutex_owned() is not usable this way. it should never be used
> for anything except asserting a mutex is held. see mutex(9).
here is an untested candidate patch that makes it into a workable
recursive lock.
(How does one test this? Do we have atf test coverage for the ffsv1
extended attributes code?)
diff -r 91e47d073544 sys/ufs/ufs/extattr.h
--- a/sys/ufs/ufs/extattr.h Wed Nov 09 00:12:44 2016 -0500
+++ b/sys/ufs/ufs/extattr.h Wed Nov 09 00:26:57 2016 -0500
@@ -94,12 +94,21 @@ struct ufs_extattr_list_entry {
#define UELE_NEEDSWAP(uele) ((uele)->uele_flags & UELE_F_NEEDSWAP)
+/*
+ * XXX: this should not need to exist. See code in ufs_extattr.c.
+ */
+struct ufs_extattr_custom_lock {
+ kmutex_t uecl_innerlock;
+ kcondvar_t uecl_waithook;
+ struct lwp * uecl_locker;
+ unsigned uecl_lockcount;
+};
+
struct lock;
struct ufs_extattr_per_mount {
- kmutex_t uepm_lock;
+ struct ufs_extattr_custom_lock uepm_lock;
struct ufs_extattr_list_head uepm_list;
kauth_cred_t uepm_ucred;
- int uepm_lockcnt;
int uepm_flags;
};
diff -r 91e47d073544 sys/ufs/ufs/ufs_extattr.c
--- a/sys/ufs/ufs/ufs_extattr.c Wed Nov 09 00:12:44 2016 -0500
+++ b/sys/ufs/ufs/ufs_extattr.c Wed Nov 09 00:26:57 2016 -0500
@@ -160,6 +160,78 @@ internal_extattr_check_cred(vnode_t *vp,
}
/*
+ * Custom recursive lock because this file's locking model is crap.
+ *
+ * A recursive lock is needed for the following reasons:
+ * - it is taken in ufs_extattr_vnode_inactive
+ * - which is called from VOP_INACTIVE
+ * - which can be triggered by any vrele, vput, or vn_close
+ * - several of these can happen while it's held
+ *
+ * XXX: this should be removed.
+ */
+
+static void
+uecl_init(struct ufs_extattr_custom_lock *uecl)
+{
+ mutex_init(&uecl->uecl_innerlock, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&uecl->uecl_waithook, "ufsextattr");
+ uecl->uecl_locker = NULL;
+ uecl->uecl_lockcount = 0;
+}
+
+static void
+uecl_cleanup(struct ufs_extattr_custom_lock *uecl)
+{
+ KASSERT(uecl->uecl_locker == NULL);
+ KASSERT(uecl->uecl_lockcount == 0);
+
+ mutex_destroy(&uecl->uecl_innerlock);
+ cv_destroy(&uecl->uecl_waithook);
+}
+
+static void
+uecl_enter(struct ufs_extattr_custom_lock *uecl)
+{
+ /*int error;*/
+
+ mutex_enter(&uecl->uecl_innerlock);
+ if (uecl->uecl_locker == curlwp) {
+ uecl->uecl_lockcount++;
+ } else {
+ while (uecl->uecl_locker != NULL) {
+ KASSERT(uecl->uecl_locker != curlwp);
+ /*error =*/ cv_wait/*_sig*/(&uecl->uecl_waithook,
+ &uecl->uecl_innerlock);
+ /*if (error) {
+ mutex_exit(&uecl->uecl_innerlock);
+ return error;
+ }*/
+ }
+ KASSERT(uecl->uecl_lockcount == 0);
+ uecl->uecl_locker = curlwp;
+ uecl->uecl_lockcount = 1;
+ }
+ mutex_exit(&uecl->uecl_innerlock);
+}
+
+static void
+uecl_exit(struct ufs_extattr_custom_lock *uecl)
+{
+ mutex_enter(&uecl->uecl_innerlock);
+
+ KASSERT(uecl->uecl_locker == curlwp);
+ KASSERT(uecl->uecl_lockcount > 0);
+
+ uecl->uecl_lockcount--;
+ if (uecl->uecl_lockcount == 0) {
+ uecl->uecl_locker = NULL;
+ cv_signal(&uecl->uecl_waithook /*, &uecl->uecl_innerlock*/);
+ }
+ mutex_exit(&uecl->uecl_innerlock);
+}
+
+/*
* Per-FS attribute lock protecting attribute operations.
* XXX Right now there is a lot of lock contention due to having a single
* lock per-FS; really, this should be far more fine-grained.
@@ -167,31 +239,13 @@ internal_extattr_check_cred(vnode_t *vp,
static void
ufs_extattr_uepm_lock(struct ufsmount *ump)
{
-
- /*
- * XXX This needs to be recursive for the following reasons:
- * - it is taken in ufs_extattr_vnode_inactive
- * - which is called from VOP_INACTIVE
- * - which can be triggered by any vrele, vput, or vn_close
- * - several of these can happen while it's held
- */
- if (mutex_owned(&ump->um_extattr.uepm_lock)) {
- ump->um_extattr.uepm_lockcnt++;
- return;
- }
- mutex_enter(&ump->um_extattr.uepm_lock);
+ uecl_enter(&ump->um_extattr.uepm_lock);
}
static void
ufs_extattr_uepm_unlock(struct ufsmount *ump)
{
-
- if (ump->um_extattr.uepm_lockcnt != 0) {
- KASSERT(mutex_owned(&ump->um_extattr.uepm_lock));
- ump->um_extattr.uepm_lockcnt--;
- return;
- }
- mutex_exit(&ump->um_extattr.uepm_lock);
+ uecl_exit(&ump->um_extattr.uepm_lock);
}
/*-
@@ -390,10 +444,9 @@ ufs_extattr_uepm_init(struct ufs_extattr
{
uepm->uepm_flags = 0;
- uepm->uepm_lockcnt = 0;
LIST_INIT(&uepm->uepm_list);
- mutex_init(&uepm->uepm_lock, MUTEX_DEFAULT, IPL_NONE);
+ uecl_init(&uepm->uepm_lock);
uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
}
@@ -418,7 +471,7 @@ ufs_extattr_uepm_destroy(struct ufs_exta
*/
uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
- mutex_destroy(&uepm->uepm_lock);
+ uecl_cleanup(&uepm->uepm_lock);
}
/*
--
David A. Holland
dholland%netbsd.org@localhost
Home |
Main Index |
Thread Index |
Old Index