1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
  24  */
  25 
  26 /*
  27  * A Zero Reference Lock (ZRL) is a reference count that can lock out new
  28  * references only when the count is zero and only without waiting if the count
  29  * is not already zero. It is similar to a read-write lock in that it allows
  30  * multiple readers and only a single writer, but it does not allow a writer to
  31  * block while waiting for readers to exit, and therefore the question of
  32  * reader/writer priority is moot (no WRWANT bit). Since the equivalent of
  33  * rw_enter(&lock, RW_WRITER) is disallowed and only tryenter() is allowed, it
  34  * is perfectly safe for the same reader to acquire the same lock multiple
  35  * times. The fact that a ZRL is reentrant for readers (through multiple calls
  36  * to zrl_add()) makes it convenient for determining whether something is
  37  * actively referenced without the fuss of flagging lock ownership across
  38  * function calls.
  39  */
  40 #include <sys/zrlock.h>
  41 
  42 /*
  43  * A ZRL can be locked only while there are zero references, so ZRL_LOCKED is
  44  * treated as zero references.
  45  */
  46 #define ZRL_LOCKED      -1
  47 #define ZRL_DESTROYED   -2
  48 
  49 void
  50 zrl_init(zrlock_t *zrl)
  51 {
  52         mutex_init(&zrl->zr_mtx, NULL, MUTEX_DEFAULT, NULL);
  53         zrl->zr_refcount = 0;
  54         cv_init(&zrl->zr_cv, NULL, CV_DEFAULT, NULL);
  55 #ifdef  ZFS_DEBUG
  56         zrl->zr_owner = NULL;
  57         zrl->zr_caller = NULL;
  58 #endif
  59 }
  60 
  61 void
  62 zrl_destroy(zrlock_t *zrl)
  63 {
  64         ASSERT0(zrl->zr_refcount);
  65 
  66         mutex_destroy(&zrl->zr_mtx);
  67         zrl->zr_refcount = ZRL_DESTROYED;
  68         cv_destroy(&zrl->zr_cv);
  69 }
  70 
  71 void
  72 zrl_add_impl(zrlock_t *zrl, const char *zc)
  73 {
  74         uint32_t n = (uint32_t)zrl->zr_refcount;
  75 
  76         while (n != ZRL_LOCKED) {
  77                 uint32_t cas = atomic_cas_32(
  78                     (uint32_t *)&zrl->zr_refcount, n, n + 1);
  79                 if (cas == n) {
  80                         ASSERT3S((int32_t)n, >=, 0);
  81 #ifdef  ZFS_DEBUG
  82                         if (zrl->zr_owner == curthread) {
  83                                 DTRACE_PROBE2(zrlock__reentry,
  84                                     zrlock_t *, zrl, uint32_t, n);
  85                         }
  86                         zrl->zr_owner = curthread;
  87                         zrl->zr_caller = zc;
  88 #endif
  89                         return;
  90                 }
  91                 n = cas;
  92         }
  93 
  94         mutex_enter(&zrl->zr_mtx);
  95         while (zrl->zr_refcount == ZRL_LOCKED) {
  96                 cv_wait(&zrl->zr_cv, &zrl->zr_mtx);
  97         }
  98         ASSERT3S(zrl->zr_refcount, >=, 0);
  99         zrl->zr_refcount++;
 100 #ifdef  ZFS_DEBUG
 101         zrl->zr_owner = curthread;
 102         zrl->zr_caller = zc;
 103 #endif
 104         mutex_exit(&zrl->zr_mtx);
 105 }
 106 
 107 void
 108 zrl_remove(zrlock_t *zrl)
 109 {
 110         uint32_t n;
 111 
 112 #ifdef  ZFS_DEBUG
 113         if (zrl->zr_owner == curthread) {
 114                 zrl->zr_owner = NULL;
 115                 zrl->zr_caller = NULL;
 116         }
 117 #endif
 118         n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount);
 119         ASSERT3S((int32_t)n, >=, 0);
 120 }
 121 
 122 int
 123 zrl_tryenter(zrlock_t *zrl)
 124 {
 125         uint32_t n = (uint32_t)zrl->zr_refcount;
 126 
 127         if (n == 0) {
 128                 uint32_t cas = atomic_cas_32(
 129                     (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED);
 130                 if (cas == 0) {
 131 #ifdef  ZFS_DEBUG
 132                         ASSERT3P(zrl->zr_owner, ==, NULL);
 133                         zrl->zr_owner = curthread;
 134 #endif
 135                         return (1);
 136                 }
 137         }
 138 
 139         ASSERT3S((int32_t)n, >, ZRL_DESTROYED);
 140 
 141         return (0);
 142 }
 143 
 144 void
 145 zrl_exit(zrlock_t *zrl)
 146 {
 147         ASSERT3S(zrl->zr_refcount, ==, ZRL_LOCKED);
 148 
 149         mutex_enter(&zrl->zr_mtx);
 150 #ifdef  ZFS_DEBUG
 151         ASSERT3P(zrl->zr_owner, ==, curthread);
 152         zrl->zr_owner = NULL;
 153         membar_producer();      /* make sure the owner store happens first */
 154 #endif
 155         zrl->zr_refcount = 0;
 156         cv_broadcast(&zrl->zr_cv);
 157         mutex_exit(&zrl->zr_mtx);
 158 }
 159 
 160 int
 161 zrl_refcount(zrlock_t *zrl)
 162 {
 163         ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
 164 
 165         int n = (int)zrl->zr_refcount;
 166         return (n <= 0 ? 0 : n);
 167 }
 168 
 169 int
 170 zrl_is_zero(zrlock_t *zrl)
 171 {
 172         ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
 173 
 174         return (zrl->zr_refcount <= 0);
 175 }
 176 
 177 int
 178 zrl_is_locked(zrlock_t *zrl)
 179 {
 180         ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
 181 
 182         return (zrl->zr_refcount == ZRL_LOCKED);
 183 }
 184 
 185 #ifdef  ZFS_DEBUG
 186 kthread_t *
 187 zrl_owner(zrlock_t *zrl)
 188 {
 189         return (zrl->zr_owner);
 190 }
 191 #endif