usr/src/uts/common/fs/zfs/zfs_rlock.c
author gw25295
Wed, 08 Aug 2007 15:35:58 -0700
changeset 4831 41ec732c6d9f
parent 3755 8708c35cb823
child 8636 7e4ce9158df3
permissions -rw-r--r--
6584470 zdb needs to initialize the bpl_lock mutex 6583739 libzpool should check for properly initialized mutexes 6548010 unbalanced mutex_init/mutex_destroy issues in zfs 6502263 ZFS needs some more FreeBSD porting love Contributed by Pawel Dawidek 6576827 multiple calls to spa_activate() can end up reinitializing all its mutexes 6576830 certain spa mutexes and condition variables need some love
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     1
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     2
 * CDDL HEADER START
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     3
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     4
 * The contents of this file are subject to the terms of the
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     5
 * Common Development and Distribution License (the "License").
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     6
 * You may not use this file except in compliance with the License.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     7
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     8
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
     9
 * or http://www.opensolaris.org/os/licensing.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    10
 * See the License for the specific language governing permissions
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    11
 * and limitations under the License.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    12
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    13
 * When distributing Covered Code, include this CDDL HEADER in each
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    14
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    15
 * If applicable, add the following below this CDDL HEADER, with the
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    16
 * fields enclosed by brackets "[]" replaced with your own identifying
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    17
 * information: Portions Copyright [yyyy] [name of copyright owner]
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    18
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    19
 * CDDL HEADER END
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    20
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    21
/*
3755
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
    22
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    23
 * Use is subject to license terms.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    24
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    25
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    26
#pragma ident	"%Z%%M%	%I%	%E% SMI"
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    27
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    28
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    29
 * This file contains the code to implement file range locking in
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    30
 * ZFS, although there isn't much specific to ZFS (all that comes to mind
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    31
 * support for growing the blocksize).
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    32
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    33
 * Interface
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    34
 * ---------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    35
 * Defined in zfs_rlock.h but essentially:
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    36
 *	rl = zfs_range_lock(zp, off, len, lock_type);
2237
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
    37
 *	zfs_range_unlock(rl);
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
    38
 *	zfs_range_reduce(rl, off, len);
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    39
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    40
 * AVL tree
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    41
 * --------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    42
 * An AVL tree is used to maintain the state of the existing ranges
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    43
 * that are locked for exclusive (writer) or shared (reader) use.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    44
 * The starting range offset is used for searching and sorting the tree.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    45
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    46
 * Common case
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    47
 * -----------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    48
 * The (hopefully) usual case is of no overlaps or contention for
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    49
 * locks. On entry to zfs_lock_range() a rl_t is allocated; the tree
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    50
 * searched that finds no overlap, and *this* rl_t is placed in the tree.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    51
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    52
 * Overlaps/Reference counting/Proxy locks
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    53
 * ---------------------------------------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    54
 * The avl code only allows one node at a particular offset. Also it's very
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    55
 * inefficient to search through all previous entries looking for overlaps
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    56
 * (because the very 1st in the ordered list might be at offset 0 but
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    57
 * cover the whole file).
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    58
 * So this implementation uses reference counts and proxy range locks.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    59
 * Firstly, only reader locks use reference counts and proxy locks,
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    60
 * because writer locks are exclusive.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    61
 * When a reader lock overlaps with another then a proxy lock is created
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    62
 * for that range and replaces the original lock. If the overlap
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    63
 * is exact then the reference count of the proxy is simply incremented.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    64
 * Otherwise, the proxy lock is split into smaller lock ranges and
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    65
 * new proxy locks created for non overlapping ranges.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    66
 * The reference counts are adjusted accordingly.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    67
 * Meanwhile, the orginal lock is kept around (this is the callers handle)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    68
 * and its offset and length are used when releasing the lock.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    69
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    70
 * Thread coordination
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    71
 * -------------------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    72
 * In order to make wakeups efficient and to ensure multiple continuous
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    73
 * readers on a range don't starve a writer for the same range lock,
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    74
 * two condition variables are allocated in each rl_t.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    75
 * If a writer (or reader) can't get a range it initialises the writer
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    76
 * (or reader) cv; sets a flag saying there's a writer (or reader) waiting;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    77
 * and waits on that cv. When a thread unlocks that range it wakes up all
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    78
 * writers then all readers before destroying the lock.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    79
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    80
 * Append mode writes
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    81
 * ------------------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    82
 * Append mode writes need to lock a range at the end of a file.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    83
 * The offset of the end of the file is determined under the
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    84
 * range locking mutex, and the lock type converted from RL_APPEND to
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    85
 * RL_WRITER and the range locked.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    86
 *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    87
 * Grow block handling
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    88
 * -------------------
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    89
 * ZFS supports multiple block sizes currently upto 128K. The smallest
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    90
 * block size is used for the file which is grown as needed. During this
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    91
 * growth all other writers and readers must be excluded.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    92
 * So if the block size needs to be grown then the whole file is
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    93
 * exclusively locked, then later the caller will reduce the lock
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    94
 * range to just the range to be written using zfs_reduce_range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    95
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    96
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    97
#include <sys/zfs_rlock.h>
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    98
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
    99
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   100
 * Check if a write lock can be grabbed, or wait and recheck until available.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   101
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   102
static void
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   103
zfs_range_lock_writer(znode_t *zp, rl_t *new)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   104
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   105
	avl_tree_t *tree = &zp->z_range_avl;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   106
	rl_t *rl;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   107
	avl_index_t where;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   108
	uint64_t end_size;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   109
	uint64_t off = new->r_off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   110
	uint64_t len = new->r_len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   111
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   112
	for (;;) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   113
		/*
3755
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   114
		 * Range locking is also used by zvol and uses a
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   115
		 * dummied up znode. However, for zvol, we don't need to
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   116
		 * append or grow blocksize, and besides we don't have
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   117
		 * a z_phys or z_zfsvfs - so skip that processing.
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   118
		 *
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   119
		 * Yes, this is ugly, and would be solved by not handling
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   120
		 * grow or append in range lock code. If that was done then
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   121
		 * we could make the range locking code generically available
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   122
		 * to other non-zfs consumers.
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   123
		 */
3755
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   124
		if (zp->z_vnode) { /* caller is ZPL */
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   125
			/*
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   126
			 * If in append mode pick up the current end of file.
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   127
			 * This is done under z_range_lock to avoid races.
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   128
			 */
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   129
			if (new->r_type == RL_APPEND)
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   130
				new->r_off = zp->z_phys->zp_size;
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   131
3755
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   132
			/*
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   133
			 * If we need to grow the block size then grab the whole
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   134
			 * file range. This is also done under z_range_lock to
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   135
			 * avoid races.
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   136
			 */
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   137
			end_size = MAX(zp->z_phys->zp_size, new->r_off + len);
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   138
			if (end_size > zp->z_blksz && (!ISP2(zp->z_blksz) ||
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   139
			    zp->z_blksz < zp->z_zfsvfs->z_max_blksz)) {
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   140
				new->r_off = 0;
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   141
				new->r_len = UINT64_MAX;
8708c35cb823 6525008 panic: dr->dt.dl.dr_override_state != DR_IN_DMU_SYNC, file: ../../common/fs/zfs/dbuf.c, line: 676
perrin
parents: 2237
diff changeset
   142
			}
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   143
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   144
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   145
		/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   146
		 * First check for the usual case of no locks
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   147
		 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   148
		if (avl_numnodes(tree) == 0) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   149
			new->r_type = RL_WRITER; /* convert to writer */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   150
			avl_add(tree, new);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   151
			return;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   152
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   153
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   154
		/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   155
		 * Look for any locks in the range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   156
		 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   157
		rl = avl_find(tree, new, &where);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   158
		if (rl)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   159
			goto wait; /* already locked at same offset */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   160
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   161
		rl = (rl_t *)avl_nearest(tree, where, AVL_AFTER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   162
		if (rl && (rl->r_off < new->r_off + new->r_len))
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   163
			goto wait;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   164
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   165
		rl = (rl_t *)avl_nearest(tree, where, AVL_BEFORE);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   166
		if (rl && rl->r_off + rl->r_len > new->r_off)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   167
			goto wait;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   168
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   169
		new->r_type = RL_WRITER; /* convert possible RL_APPEND */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   170
		avl_insert(tree, new, where);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   171
		return;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   172
wait:
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   173
		if (!rl->r_write_wanted) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   174
			cv_init(&rl->r_wr_cv, NULL, CV_DEFAULT, NULL);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   175
			rl->r_write_wanted = B_TRUE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   176
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   177
		cv_wait(&rl->r_wr_cv, &zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   178
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   179
		/* reset to original */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   180
		new->r_off = off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   181
		new->r_len = len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   182
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   183
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   184
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   185
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   186
 * If this is an original (non-proxy) lock then replace it by
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   187
 * a proxy and return the proxy.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   188
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   189
static rl_t *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   190
zfs_range_proxify(avl_tree_t *tree, rl_t *rl)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   191
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   192
	rl_t *proxy;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   193
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   194
	if (rl->r_proxy)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   195
		return (rl); /* already a proxy */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   196
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   197
	ASSERT3U(rl->r_cnt, ==, 1);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   198
	ASSERT(rl->r_write_wanted == B_FALSE);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   199
	ASSERT(rl->r_read_wanted == B_FALSE);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   200
	avl_remove(tree, rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   201
	rl->r_cnt = 0;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   202
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   203
	/* create a proxy range lock */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   204
	proxy = kmem_alloc(sizeof (rl_t), KM_SLEEP);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   205
	proxy->r_off = rl->r_off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   206
	proxy->r_len = rl->r_len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   207
	proxy->r_cnt = 1;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   208
	proxy->r_type = RL_READER;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   209
	proxy->r_proxy = B_TRUE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   210
	proxy->r_write_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   211
	proxy->r_read_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   212
	avl_add(tree, proxy);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   213
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   214
	return (proxy);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   215
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   216
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   217
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   218
 * Split the range lock at the supplied offset
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   219
 * returning the *front* proxy.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   220
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   221
static rl_t *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   222
zfs_range_split(avl_tree_t *tree, rl_t *rl, uint64_t off)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   223
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   224
	rl_t *front, *rear;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   225
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   226
	ASSERT3U(rl->r_len, >, 1);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   227
	ASSERT3U(off, >, rl->r_off);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   228
	ASSERT3U(off, <, rl->r_off + rl->r_len);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   229
	ASSERT(rl->r_write_wanted == B_FALSE);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   230
	ASSERT(rl->r_read_wanted == B_FALSE);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   231
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   232
	/* create the rear proxy range lock */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   233
	rear = kmem_alloc(sizeof (rl_t), KM_SLEEP);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   234
	rear->r_off = off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   235
	rear->r_len = rl->r_off + rl->r_len - off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   236
	rear->r_cnt = rl->r_cnt;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   237
	rear->r_type = RL_READER;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   238
	rear->r_proxy = B_TRUE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   239
	rear->r_write_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   240
	rear->r_read_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   241
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   242
	front = zfs_range_proxify(tree, rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   243
	front->r_len = off - rl->r_off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   244
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   245
	avl_insert_here(tree, rear, front, AVL_AFTER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   246
	return (front);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   247
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   248
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   249
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   250
 * Create and add a new proxy range lock for the supplied range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   251
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   252
static void
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   253
zfs_range_new_proxy(avl_tree_t *tree, uint64_t off, uint64_t len)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   254
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   255
	rl_t *rl;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   256
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   257
	ASSERT(len);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   258
	rl = kmem_alloc(sizeof (rl_t), KM_SLEEP);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   259
	rl->r_off = off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   260
	rl->r_len = len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   261
	rl->r_cnt = 1;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   262
	rl->r_type = RL_READER;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   263
	rl->r_proxy = B_TRUE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   264
	rl->r_write_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   265
	rl->r_read_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   266
	avl_add(tree, rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   267
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   268
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   269
static void
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   270
zfs_range_add_reader(avl_tree_t *tree, rl_t *new, rl_t *prev, avl_index_t where)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   271
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   272
	rl_t *next;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   273
	uint64_t off = new->r_off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   274
	uint64_t len = new->r_len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   275
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   276
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   277
	 * prev arrives either:
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   278
	 * - pointing to an entry at the same offset
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   279
	 * - pointing to the entry with the closest previous offset whose
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   280
	 *   range may overlap with the new range
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   281
	 * - null, if there were no ranges starting before the new one
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   282
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   283
	if (prev) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   284
		if (prev->r_off + prev->r_len <= off) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   285
			prev = NULL;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   286
		} else if (prev->r_off != off) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   287
			/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   288
			 * convert to proxy if needed then
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   289
			 * split this entry and bump ref count
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   290
			 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   291
			prev = zfs_range_split(tree, prev, off);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   292
			prev = AVL_NEXT(tree, prev); /* move to rear range */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   293
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   294
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   295
	ASSERT((prev == NULL) || (prev->r_off == off));
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   296
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   297
	if (prev)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   298
		next = prev;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   299
	else
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   300
		next = (rl_t *)avl_nearest(tree, where, AVL_AFTER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   301
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   302
	if (next == NULL || off + len <= next->r_off) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   303
		/* no overlaps, use the original new rl_t in the tree */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   304
		avl_insert(tree, new, where);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   305
		return;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   306
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   307
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   308
	if (off < next->r_off) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   309
		/* Add a proxy for initial range before the overlap */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   310
		zfs_range_new_proxy(tree, off, next->r_off - off);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   311
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   312
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   313
	new->r_cnt = 0; /* will use proxies in tree */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   314
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   315
	 * We now search forward through the ranges, until we go past the end
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   316
	 * of the new range. For each entry we make it a proxy if it
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   317
	 * isn't already, then bump its reference count. If there's any
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   318
	 * gaps between the ranges then we create a new proxy range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   319
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   320
	for (prev = NULL; next; prev = next, next = AVL_NEXT(tree, next)) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   321
		if (off + len <= next->r_off)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   322
			break;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   323
		if (prev && prev->r_off + prev->r_len < next->r_off) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   324
			/* there's a gap */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   325
			ASSERT3U(next->r_off, >, prev->r_off + prev->r_len);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   326
			zfs_range_new_proxy(tree, prev->r_off + prev->r_len,
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   327
			    next->r_off - (prev->r_off + prev->r_len));
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   328
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   329
		if (off + len == next->r_off + next->r_len) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   330
			/* exact overlap with end */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   331
			next = zfs_range_proxify(tree, next);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   332
			next->r_cnt++;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   333
			return;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   334
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   335
		if (off + len < next->r_off + next->r_len) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   336
			/* new range ends in the middle of this block */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   337
			next = zfs_range_split(tree, next, off + len);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   338
			next->r_cnt++;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   339
			return;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   340
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   341
		ASSERT3U(off + len, >, next->r_off + next->r_len);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   342
		next = zfs_range_proxify(tree, next);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   343
		next->r_cnt++;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   344
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   345
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   346
	/* Add the remaining end range. */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   347
	zfs_range_new_proxy(tree, prev->r_off + prev->r_len,
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   348
	    (off + len) - (prev->r_off + prev->r_len));
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   349
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   350
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   351
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   352
 * Check if a reader lock can be grabbed, or wait and recheck until available.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   353
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   354
static void
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   355
zfs_range_lock_reader(znode_t *zp, rl_t *new)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   356
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   357
	avl_tree_t *tree = &zp->z_range_avl;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   358
	rl_t *prev, *next;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   359
	avl_index_t where;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   360
	uint64_t off = new->r_off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   361
	uint64_t len = new->r_len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   362
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   363
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   364
	 * Look for any writer locks in the range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   365
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   366
retry:
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   367
	prev = avl_find(tree, new, &where);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   368
	if (prev == NULL)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   369
		prev = (rl_t *)avl_nearest(tree, where, AVL_BEFORE);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   370
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   371
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   372
	 * Check the previous range for a writer lock overlap.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   373
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   374
	if (prev && (off < prev->r_off + prev->r_len)) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   375
		if ((prev->r_type == RL_WRITER) || (prev->r_write_wanted)) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   376
			if (!prev->r_read_wanted) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   377
				cv_init(&prev->r_rd_cv, NULL, CV_DEFAULT, NULL);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   378
				prev->r_read_wanted = B_TRUE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   379
			}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   380
			cv_wait(&prev->r_rd_cv, &zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   381
			goto retry;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   382
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   383
		if (off + len < prev->r_off + prev->r_len)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   384
			goto got_lock;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   385
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   386
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   387
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   388
	 * Search through the following ranges to see if there's
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   389
	 * write lock any overlap.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   390
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   391
	if (prev)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   392
		next = AVL_NEXT(tree, prev);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   393
	else
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   394
		next = (rl_t *)avl_nearest(tree, where, AVL_AFTER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   395
	for (; next; next = AVL_NEXT(tree, next)) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   396
		if (off + len <= next->r_off)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   397
			goto got_lock;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   398
		if ((next->r_type == RL_WRITER) || (next->r_write_wanted)) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   399
			if (!next->r_read_wanted) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   400
				cv_init(&next->r_rd_cv, NULL, CV_DEFAULT, NULL);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   401
				next->r_read_wanted = B_TRUE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   402
			}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   403
			cv_wait(&next->r_rd_cv, &zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   404
			goto retry;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   405
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   406
		if (off + len <= next->r_off + next->r_len)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   407
			goto got_lock;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   408
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   409
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   410
got_lock:
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   411
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   412
	 * Add the read lock, which may involve splitting existing
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   413
	 * locks and bumping ref counts (r_cnt).
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   414
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   415
	zfs_range_add_reader(tree, new, prev, where);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   416
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   417
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   418
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   419
 * Lock a range (offset, length) as either shared (RL_READER)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   420
 * or exclusive (RL_WRITER). Returns the range lock structure
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   421
 * for later unlocking or reduce range (if entire file
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   422
 * previously locked as RL_WRITER).
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   423
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   424
rl_t *
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   425
zfs_range_lock(znode_t *zp, uint64_t off, uint64_t len, rl_type_t type)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   426
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   427
	rl_t *new;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   428
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   429
	ASSERT(type == RL_READER || type == RL_WRITER || type == RL_APPEND);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   430
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   431
	new = kmem_alloc(sizeof (rl_t), KM_SLEEP);
2237
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   432
	new->r_zp = zp;
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   433
	new->r_off = off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   434
	new->r_len = len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   435
	new->r_cnt = 1; /* assume it's going to be in the tree */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   436
	new->r_type = type;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   437
	new->r_proxy = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   438
	new->r_write_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   439
	new->r_read_wanted = B_FALSE;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   440
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   441
	mutex_enter(&zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   442
	if (type == RL_READER) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   443
		/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   444
		 * First check for the usual case of no locks
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   445
		 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   446
		if (avl_numnodes(&zp->z_range_avl) == 0)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   447
			avl_add(&zp->z_range_avl, new);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   448
		else
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   449
			zfs_range_lock_reader(zp, new);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   450
	} else
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   451
		zfs_range_lock_writer(zp, new); /* RL_WRITER or RL_APPEND */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   452
	mutex_exit(&zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   453
	return (new);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   454
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   455
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   456
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   457
 * Unlock a reader lock
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   458
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   459
static void
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   460
zfs_range_unlock_reader(znode_t *zp, rl_t *remove)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   461
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   462
	avl_tree_t *tree = &zp->z_range_avl;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   463
	rl_t *rl, *next;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   464
	uint64_t len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   465
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   466
	/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   467
	 * The common case is when the remove entry is in the tree
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   468
	 * (cnt == 1) meaning there's been no other reader locks overlapping
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   469
	 * with this one. Otherwise the remove entry will have been
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   470
	 * removed from the tree and replaced by proxies (one or
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   471
	 * more ranges mapping to the entire range).
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   472
	 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   473
	if (remove->r_cnt == 1) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   474
		avl_remove(tree, remove);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   475
		if (remove->r_write_wanted) {
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   476
			cv_broadcast(&remove->r_wr_cv);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   477
			cv_destroy(&remove->r_wr_cv);
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   478
		}
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   479
		if (remove->r_read_wanted) {
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   480
			cv_broadcast(&remove->r_rd_cv);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   481
			cv_destroy(&remove->r_rd_cv);
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   482
		}
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   483
	} else {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   484
		ASSERT3U(remove->r_cnt, ==, 0);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   485
		ASSERT3U(remove->r_write_wanted, ==, 0);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   486
		ASSERT3U(remove->r_read_wanted, ==, 0);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   487
		/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   488
		 * Find start proxy representing this reader lock,
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   489
		 * then decrement ref count on all proxies
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   490
		 * that make up this range, freeing them as needed.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   491
		 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   492
		rl = avl_find(tree, remove, NULL);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   493
		ASSERT(rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   494
		ASSERT(rl->r_cnt);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   495
		ASSERT(rl->r_type == RL_READER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   496
		for (len = remove->r_len; len != 0; rl = next) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   497
			len -= rl->r_len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   498
			if (len) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   499
				next = AVL_NEXT(tree, rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   500
				ASSERT(next);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   501
				ASSERT(rl->r_off + rl->r_len == next->r_off);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   502
				ASSERT(next->r_cnt);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   503
				ASSERT(next->r_type == RL_READER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   504
			}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   505
			rl->r_cnt--;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   506
			if (rl->r_cnt == 0) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   507
				avl_remove(tree, rl);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   508
				if (rl->r_write_wanted) {
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   509
					cv_broadcast(&rl->r_wr_cv);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   510
					cv_destroy(&rl->r_wr_cv);
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   511
				}
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   512
				if (rl->r_read_wanted) {
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   513
					cv_broadcast(&rl->r_rd_cv);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   514
					cv_destroy(&rl->r_rd_cv);
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   515
				}
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   516
				kmem_free(rl, sizeof (rl_t));
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   517
			}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   518
		}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   519
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   520
	kmem_free(remove, sizeof (rl_t));
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   521
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   522
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   523
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   524
 * Unlock range and destroy range lock structure.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   525
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   526
void
2237
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   527
zfs_range_unlock(rl_t *rl)
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   528
{
2237
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   529
	znode_t *zp = rl->r_zp;
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   530
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   531
	ASSERT(rl->r_type == RL_WRITER || rl->r_type == RL_READER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   532
	ASSERT(rl->r_cnt == 1 || rl->r_cnt == 0);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   533
	ASSERT(!rl->r_proxy);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   534
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   535
	mutex_enter(&zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   536
	if (rl->r_type == RL_WRITER) {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   537
		/* writer locks can't be shared or split */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   538
		avl_remove(&zp->z_range_avl, rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   539
		mutex_exit(&zp->z_range_lock);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   540
		if (rl->r_write_wanted) {
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   541
			cv_broadcast(&rl->r_wr_cv);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   542
			cv_destroy(&rl->r_wr_cv);
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   543
		}
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   544
		if (rl->r_read_wanted) {
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   545
			cv_broadcast(&rl->r_rd_cv);
4831
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   546
			cv_destroy(&rl->r_rd_cv);
41ec732c6d9f 6584470 zdb needs to initialize the bpl_lock mutex
gw25295
parents: 3755
diff changeset
   547
		}
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   548
		kmem_free(rl, sizeof (rl_t));
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   549
	} else {
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   550
		/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   551
		 * lock may be shared, let zfs_range_unlock_reader()
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   552
		 * release the lock and free the rl_t
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   553
		 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   554
		zfs_range_unlock_reader(zp, rl);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   555
		mutex_exit(&zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   556
	}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   557
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   558
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   559
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   560
 * Reduce range locked as RL_WRITER from whole file to specified range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   561
 * Asserts the whole file is exclusivly locked and so there's only one
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   562
 * entry in the tree.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   563
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   564
void
2237
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   565
zfs_range_reduce(rl_t *rl, uint64_t off, uint64_t len)
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   566
{
2237
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   567
	znode_t *zp = rl->r_zp;
45affe88ed99 6416482 filebench oltp workload hangs in zfs
maybee
parents: 1669
diff changeset
   568
1669
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   569
	/* Ensure there are no other locks */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   570
	ASSERT(avl_numnodes(&zp->z_range_avl) == 1);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   571
	ASSERT(rl->r_off == 0);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   572
	ASSERT(rl->r_type == RL_WRITER);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   573
	ASSERT(!rl->r_proxy);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   574
	ASSERT3U(rl->r_len, ==, UINT64_MAX);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   575
	ASSERT3U(rl->r_cnt, ==, 1);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   576
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   577
	mutex_enter(&zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   578
	rl->r_off = off;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   579
	rl->r_len = len;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   580
	mutex_exit(&zp->z_range_lock);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   581
	if (rl->r_write_wanted)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   582
		cv_broadcast(&rl->r_wr_cv);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   583
	if (rl->r_read_wanted)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   584
		cv_broadcast(&rl->r_rd_cv);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   585
}
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   586
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   587
/*
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   588
 * AVL comparison function used to order range locks
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   589
 * Locks are ordered on the start offset of the range.
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   590
 */
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   591
int
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   592
zfs_range_compare(const void *arg1, const void *arg2)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   593
{
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   594
	const rl_t *rl1 = arg1;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   595
	const rl_t *rl2 = arg2;
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   596
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   597
	if (rl1->r_off > rl2->r_off)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   598
		return (1);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   599
	if (rl1->r_off < rl2->r_off)
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   600
		return (-1);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   601
	return (0);
3521dbbcb2e8 6343608 ZFS file range locking
perrin
parents:
diff changeset
   602
}