Source-Changes-HG archive

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

[src/trunk]: src/lib/librumpuser Support proper rw_downgrade() semantics.



details:   https://anonhg.NetBSD.org/src/rev/36b4faaa3d44
branches:  trunk
changeset: 786598:36b4faaa3d44
user:      pooka <pooka%NetBSD.org@localhost>
date:      Fri May 03 00:23:49 2013 +0000

description:
Support proper rw_downgrade() semantics.

diffstat:

 lib/librumpuser/rumpuser_pth.c |  161 +++++++++++++++++++++++++++-------------
 1 files changed, 109 insertions(+), 52 deletions(-)

diffs (247 lines):

diff -r c25cb012e89c -r 36b4faaa3d44 lib/librumpuser/rumpuser_pth.c
--- a/lib/librumpuser/rumpuser_pth.c    Fri May 03 00:01:15 2013 +0000
+++ b/lib/librumpuser/rumpuser_pth.c    Fri May 03 00:23:49 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: rumpuser_pth.c,v 1.26 2013/05/02 22:07:57 pooka Exp $  */
+/*     $NetBSD: rumpuser_pth.c,v 1.27 2013/05/03 00:23:49 pooka Exp $  */
 
 /*
  * Copyright (c) 2007-2010 Antti Kantee.  All Rights Reserved.
@@ -28,7 +28,7 @@
 #include "rumpuser_port.h"
 
 #if !defined(lint)
-__RCSID("$NetBSD: rumpuser_pth.c,v 1.26 2013/05/02 22:07:57 pooka Exp $");
+__RCSID("$NetBSD: rumpuser_pth.c,v 1.27 2013/05/03 00:23:49 pooka Exp $");
 #endif /* !lint */
 
 #include <sys/queue.h>
@@ -223,7 +223,12 @@
 }
 
 /*
- * rwlocks
+ * rwlocks.  these are mostly simple, except that NetBSD wants to
+ * support something called downgrade, which means we need to swap
+ * our exclusive lock for a shared lock.  to accommodate this,
+ * we need to check *after* acquiring a lock in case someone was
+ * downgrading it.  if so, we couldn't actually have it and maybe
+ * need to retry later.
  */
 
 struct rumpuser_rw {
@@ -231,38 +236,77 @@
        pthread_spinlock_t spin;
        int readers;
        struct lwp *writer;
+       int downgrade; /* someone is downgrading (hopefully lock holder ;) */
 };
 
-#define RURW_AMWRITER(rw) (rw->writer == rumpuser_curlwp()             \
-                               && rw->readers == -1)
-#define RURW_HASREAD(rw)  (rw->readers > 0)
+static int
+rw_amwriter(struct rumpuser_rw *rw)
+{
+
+       return rw->writer == rumpuser_curlwp() && rw->readers == -1;
+}
+
+static int
+rw_nreaders(struct rumpuser_rw *rw)
+{
+
+       return rw->readers > 0 ? rw->readers : 0;
+}
+
+static int
+rw_setwriter(struct rumpuser_rw *rw, int retry)
+{
+
+       /*
+        * Don't need the spinlock here, we already have an
+        * exclusive lock and "downgrade" is stable until complete.
+        */
+       if (rw->downgrade) {
+               pthread_rwlock_unlock(&rw->pthrw);
+               if (retry) {
+                       struct timespec ts;
 
-#define RURW_SETWRITE(rw)                                              \
-do {                                                                   \
-       assert(rw->readers == 0);                                       \
-       rw->writer = rumpuser_curlwp();                                 \
-       rw->readers = -1;                                               \
-} while (/*CONSTCOND*/0)
-#define RURW_CLRWRITE(rw)                                              \
-do {                                                                   \
-       assert(RURW_AMWRITER(rw));                                      \
-       rw->readers = 0;                                                \
-       rw->writer = NULL;                                              \
-} while (/*CONSTCOND*/0)
-#define RURW_INCREAD(rw)                                               \
-do {                                                                   \
-       pthread_spin_lock(&rw->spin);                                   \
-       assert(rw->readers >= 0);                                       \
-       ++(rw)->readers;                                                \
-       pthread_spin_unlock(&rw->spin);                                 \
-} while (/*CONSTCOND*/0)
-#define RURW_DECREAD(rw)                                               \
-do {                                                                   \
-       pthread_spin_lock(&rw->spin);                                   \
-       assert(rw->readers > 0);                                        \
-       --(rw)->readers;                                                \
-       pthread_spin_unlock(&rw->spin);                                 \
-} while (/*CONSTCOND*/0)
+                       /* portable yield, essentially */
+                       ts.tv_sec = 0;
+                       ts.tv_nsec = 1;
+                       KLOCK_WRAP(nanosleep(&ts, NULL));
+               }
+               return EBUSY;
+       }
+       assert(rw->readers == 0);
+       rw->writer = rumpuser_curlwp();
+       rw->readers = -1;
+       return 0;
+}
+
+static void
+rw_clearwriter(struct rumpuser_rw *rw)
+{
+
+       assert(rw_amwriter(rw));
+       rw->readers = 0;
+       rw->writer = NULL;
+}
+
+static void
+rw_readup(struct rumpuser_rw *rw)
+{
+
+       pthread_spin_lock(&rw->spin);
+       assert(rw->readers >= 0);
+       ++rw->readers;
+       pthread_spin_unlock(&rw->spin);
+}
+
+static void
+rw_readdown(struct rumpuser_rw *rw)
+{
+
+       pthread_spin_lock(&rw->spin);
+       assert(rw->readers > 0);
+       --rw->readers;
+       pthread_spin_unlock(&rw->spin);
+}
 
 void
 rumpuser_rw_init(struct rumpuser_rw **rw)
@@ -281,16 +325,17 @@
 
        switch (lk) {
        case RUMPUSER_RW_WRITER:
-               if (pthread_rwlock_trywrlock(&rw->pthrw) != 0)
-                       KLOCK_WRAP(NOFAIL_ERRNO(
-                           pthread_rwlock_wrlock(&rw->pthrw)));
-               RURW_SETWRITE(rw);
+               do {
+                       if (pthread_rwlock_trywrlock(&rw->pthrw) != 0)
+                               KLOCK_WRAP(NOFAIL_ERRNO(
+                                   pthread_rwlock_wrlock(&rw->pthrw)));
+               } while (rw_setwriter(rw, 1) != 0);
                break;
        case RUMPUSER_RW_READER:
                if (pthread_rwlock_tryrdlock(&rw->pthrw) != 0)
                        KLOCK_WRAP(NOFAIL_ERRNO(
                            pthread_rwlock_rdlock(&rw->pthrw)));
-               RURW_INCREAD(rw);
+               rw_readup(rw);
                break;
        }
 }
@@ -304,12 +349,12 @@
        case RUMPUSER_RW_WRITER:
                rv = pthread_rwlock_trywrlock(&rw->pthrw);
                if (rv == 0)
-                       RURW_SETWRITE(rw);
+                       rv = rw_setwriter(rw, 0);
                break;
        case RUMPUSER_RW_READER:
                rv = pthread_rwlock_tryrdlock(&rw->pthrw);
                if (rv == 0)
-                       RURW_INCREAD(rw);
+                       rw_readup(rw);
                break;
        default:
                rv = EINVAL;
@@ -323,33 +368,45 @@
 rumpuser_rw_tryupgrade(struct rumpuser_rw *rw)
 {
 
-       /* not supported by pthreads */
+       /*
+        * Not supported by pthreads.  Since the caller needs to
+        * back off anyway to avoid deadlock, always failing
+        * is correct.
+        */
        ET(EBUSY);
 }
 
+/*
+ * convert from exclusive to shared lock without allowing anyone to
+ * obtain an exclusive lock in between.  actually, might allow
+ * someone to obtain the lock, we just don't allow that thread to
+ * return from the hypercall with it.
+ */
 void
 rumpuser_rw_downgrade(struct rumpuser_rw *rw)
 {
 
+       assert(rw->downgrade == 0);
+       rw->downgrade = 1;
+       rumpuser_rw_exit(rw);
        /*
-        * I guess this is not strictly speaking correct,
-        * but the option is to provide a complete implementation
-        * of rwlocks here, or at least wrap acquiry in 1) lock
-        * 2) check if someone is downgrading. if not, we're done
-        * 3) unlock 4) yield 5) goto 1.
+        * though the competition can't get out of the hypervisor, it
+        * might have rescheduled itself after we released the lock.
+        * so need a wrap here.
         */
-       rumpuser_rw_exit(rw);
-       rumpuser_rw_enter(rw, RUMPUSER_RW_READER);
+       KLOCK_WRAP(NOFAIL_ERRNO(pthread_rwlock_rdlock(&rw->pthrw)));
+       rw->downgrade = 0;
+       rw_readup(rw);
 }
 
 void
 rumpuser_rw_exit(struct rumpuser_rw *rw)
 {
 
-       if (RURW_HASREAD(rw))
-               RURW_DECREAD(rw);
+       if (rw_nreaders(rw))
+               rw_readdown(rw);
        else
-               RURW_CLRWRITE(rw);
+               rw_clearwriter(rw);
        NOFAIL_ERRNO(pthread_rwlock_unlock(&rw->pthrw));
 }
 
@@ -368,10 +425,10 @@
 
        switch (lk) {
        case RUMPUSER_RW_WRITER:
-               *rv = RURW_AMWRITER(rw);
+               *rv = rw_amwriter(rw);
                break;
        case RUMPUSER_RW_READER:
-               *rv = RURW_HASREAD(rw);
+               *rv = rw_nreaders(rw);
                break;
        }
 }



Home | Main Index | Thread Index | Old Index