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  * Copyright 2016 Spectra Logic Corporation. All rights reserved.
  25  * Copyright 2016 The MathWorks, Inc. All rights reserved.
  26  */
  27 
  28 /*
  29  * A Zero Reference Lock (ZRL) is a reference count that can lock out new
  30  * references only when the count is zero and only without waiting if the count
  31  * is not already zero. It is similar to a read-write lock in that it allows
  32  * multiple readers and only a single writer, but it does not allow a writer to
  33  * block while waiting for readers to exit, and therefore the question of
  34  * reader/writer priority is moot (no WRWANT bit). Since the equivalent of
  35  * rw_enter(&lock, RW_WRITER) is disallowed and only tryenter() is allowed, it
  36  * is perfectly safe for the same reader to acquire the same lock multiple
  37  * times. The fact that a ZRL is reentrant for readers (through multiple calls
  38  * to zrl_add()) makes it convenient for determining whether something is
  39  * actively referenced without the fuss of flagging lock ownership across
  40  * function calls.
  41  */
  42 #include <sys/zrlock.h>
  43 
  44 /*
  45  * A ZRL can be locked only while there are zero references, so ZRL_LOCKED is
  46  * treated as zero references.
  47  */
  48 #define ZRL_LOCKED      -1
  49 #define ZRL_DESTROYED   -2
  50 
  51 void
  52 zrl_init(zrlock_t *zrl)
  53 {
  54         mutex_init(&zrl->zr_mtx, NULL, MUTEX_DEFAULT, NULL);
  55         zrl->zr_refcount = 0;
  56         cv_init(&zrl->zr_cv, NULL, CV_DEFAULT, NULL);
  57 #ifdef  ZFS_DEBUG
  58         zrl->zr_owner = NULL;
  59         zrl->zr_caller = NULL;
  60 #endif
  61 }
  62 
  63 void
  64 zrl_destroy(zrlock_t *zrl)
  65 {
  66         ASSERT0(zrl->zr_refcount);
  67 
  68         mutex_destroy(&zrl->zr_mtx);
  69         zrl->zr_refcount = ZRL_DESTROYED;
  70         cv_destroy(&zrl->zr_cv);
  71 }
  72 
  73 void
  74 zrl_add_impl(zrlock_t *zrl, const char *zc)
  75 {
  76         for (;;) {
  77                 uint32_t n = (uint32_t)zrl->zr_refcount;
  78 
  79                 while (n != ZRL_LOCKED) {
  80                         uint32_t cas = atomic_cas_32(
  81                             (uint32_t *)&zrl->zr_refcount, n, n + 1);
  82 
  83                         if (cas == n) {
  84                                 ASSERT3S((int32_t)n, >=, 0);
  85 #ifdef  ZFS_DEBUG
  86                                 if (zrl->zr_owner == curthread) {
  87                                         DTRACE_PROBE2(zrlock__reentry,
  88                                             zrlock_t *, zrl, uint32_t, n);
  89                                 }
  90                                 zrl->zr_owner = curthread;
  91                                 zrl->zr_caller = zc;
  92 #endif
  93                                 return;
  94                         }
  95                         n = cas;
  96                 }
  97 
  98                 mutex_enter(&zrl->zr_mtx);
  99                 while (zrl->zr_refcount == ZRL_LOCKED) {
 100                         cv_wait(&zrl->zr_cv, &zrl->zr_mtx);
 101                 }
 102                 mutex_exit(&zrl->zr_mtx);
 103         }
 104 }
 105 
 106 void
 107 zrl_remove(zrlock_t *zrl)
 108 {
 109         uint32_t n;
 110 
 111 #ifdef  ZFS_DEBUG
 112         if (zrl->zr_owner == curthread) {
 113                 zrl->zr_owner = NULL;
 114                 zrl->zr_caller = NULL;
 115         }
 116 #endif
 117         n = atomic_dec_32_nv((uint32_t *)&zrl->zr_refcount);
 118         ASSERT3S((int32_t)n, >=, 0);
 119 }
 120 
 121 int
 122 zrl_tryenter(zrlock_t *zrl)
 123 {
 124         uint32_t n = (uint32_t)zrl->zr_refcount;
 125 
 126         if (n == 0) {
 127                 uint32_t cas = atomic_cas_32(
 128                     (uint32_t *)&zrl->zr_refcount, 0, ZRL_LOCKED);
 129                 if (cas == 0) {
 130 #ifdef  ZFS_DEBUG
 131                         ASSERT3P(zrl->zr_owner, ==, NULL);
 132                         zrl->zr_owner = curthread;
 133 #endif
 134                         return (1);
 135                 }
 136         }
 137 
 138         ASSERT3S((int32_t)n, >, ZRL_DESTROYED);
 139 
 140         return (0);
 141 }
 142 
 143 void
 144 zrl_exit(zrlock_t *zrl)
 145 {
 146         ASSERT3S(zrl->zr_refcount, ==, ZRL_LOCKED);
 147 
 148         mutex_enter(&zrl->zr_mtx);
 149 #ifdef  ZFS_DEBUG
 150         ASSERT3P(zrl->zr_owner, ==, curthread);
 151         zrl->zr_owner = NULL;
 152         membar_producer();      /* make sure the owner store happens first */
 153 #endif
 154         zrl->zr_refcount = 0;
 155         cv_broadcast(&zrl->zr_cv);
 156         mutex_exit(&zrl->zr_mtx);
 157 }
 158 
 159 int
 160 zrl_refcount(zrlock_t *zrl)
 161 {
 162         ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
 163 
 164         int n = (int)zrl->zr_refcount;
 165         return (n <= 0 ? 0 : n);
 166 }
 167 
 168 int
 169 zrl_is_zero(zrlock_t *zrl)
 170 {
 171         ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
 172 
 173         return (zrl->zr_refcount <= 0);
 174 }
 175 
 176 int
 177 zrl_is_locked(zrlock_t *zrl)
 178 {
 179         ASSERT3S(zrl->zr_refcount, >, ZRL_DESTROYED);
 180 
 181         return (zrl->zr_refcount == ZRL_LOCKED);
 182 }
 183 
 184 #ifdef  ZFS_DEBUG
 185 kthread_t *
 186 zrl_owner(zrlock_t *zrl)
 187 {
 188         return (zrl->zr_owner);
 189 }
 190 #endif