Print this page
3746 ZRLs are racy
Reviewed by: Boris Protopopov <bprotopopov@hotmail.com>
Reviewed by: Pavel Zakharov <pavel.zakha@gmail.com>
Reviewed by: Yuri Pankov <yuri.pankov@gmail.com>
Reviewed by: Justin T. Gibbs <gibbs@scsiguy.com>


   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


  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 {




   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


  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 {