xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_lock.c (revision 66b505f139af4f5c796430bb95355707fc3f9212)
1c8ec8eeaSjose borrego /*
2c8ec8eeaSjose borrego  * CDDL HEADER START
3c8ec8eeaSjose borrego  *
4c8ec8eeaSjose borrego  * The contents of this file are subject to the terms of the
5c8ec8eeaSjose borrego  * Common Development and Distribution License (the "License").
6c8ec8eeaSjose borrego  * You may not use this file except in compliance with the License.
7c8ec8eeaSjose borrego  *
8c8ec8eeaSjose borrego  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c8ec8eeaSjose borrego  * or http://www.opensolaris.org/os/licensing.
10c8ec8eeaSjose borrego  * See the License for the specific language governing permissions
11c8ec8eeaSjose borrego  * and limitations under the License.
12c8ec8eeaSjose borrego  *
13c8ec8eeaSjose borrego  * When distributing Covered Code, include this CDDL HEADER in each
14c8ec8eeaSjose borrego  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c8ec8eeaSjose borrego  * If applicable, add the following below this CDDL HEADER, with the
16c8ec8eeaSjose borrego  * fields enclosed by brackets "[]" replaced with your own identifying
17c8ec8eeaSjose borrego  * information: Portions Copyright [yyyy] [name of copyright owner]
18c8ec8eeaSjose borrego  *
19c8ec8eeaSjose borrego  * CDDL HEADER END
20c8ec8eeaSjose borrego  */
21c8ec8eeaSjose borrego /*
22148c5f43SAlan Wright  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2394047d49SGordon Ross  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
24c8ec8eeaSjose borrego  */
25c8ec8eeaSjose borrego 
26c8ec8eeaSjose borrego /*
27c8ec8eeaSjose borrego  * This module provides range lock functionality for CIFS/SMB clients.
28c8ec8eeaSjose borrego  * Lock range service functions process SMB lock and and unlock
29c8ec8eeaSjose borrego  * requests for a file by applying lock rules and marks file range
30c8ec8eeaSjose borrego  * as locked if the lock is successful otherwise return proper
31c8ec8eeaSjose borrego  * error code.
32c8ec8eeaSjose borrego  */
33c8ec8eeaSjose borrego 
34bbf6f00cSJordan Brown #include <smbsrv/smb_kproto.h>
35c8ec8eeaSjose borrego #include <smbsrv/smb_fsops.h>
36c8ec8eeaSjose borrego #include <sys/nbmlock.h>
37c8ec8eeaSjose borrego #include <sys/param.h>
38c8ec8eeaSjose borrego 
39c8ec8eeaSjose borrego extern caller_context_t smb_ct;
40c8ec8eeaSjose borrego 
410897f7fbSGordon Ross #ifdef	DEBUG
420897f7fbSGordon Ross int smb_lock_debug = 0;
430897f7fbSGordon Ross static void smb_lock_dump1(smb_lock_t *);
440897f7fbSGordon Ross static void smb_lock_dumplist(smb_llist_t *);
450897f7fbSGordon Ross static void smb_lock_dumpnode(smb_node_t *);
460897f7fbSGordon Ross #endif
470897f7fbSGordon Ross 
48c8ec8eeaSjose borrego static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *);
49c8ec8eeaSjose borrego static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t,
50c8ec8eeaSjose borrego     smb_llist_t *, uint64_t *);
51c8ec8eeaSjose borrego static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t);
520897f7fbSGordon Ross static uint32_t smb_lock_range_lckrules(smb_ofile_t *, smb_lock_t *,
530897f7fbSGordon Ross     smb_lock_t **);
540897f7fbSGordon Ross static uint32_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *);
550897f7fbSGordon Ross static uint32_t smb_lock_range_ulckrules(smb_ofile_t *,
560897f7fbSGordon Ross     uint64_t, uint64_t, uint32_t, smb_lock_t **);
57c8ec8eeaSjose borrego static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t,
580897f7fbSGordon Ross     uint32_t, uint32_t, uint32_t);
59c8ec8eeaSjose borrego static void smb_lock_destroy(smb_lock_t *);
60c8ec8eeaSjose borrego static void smb_lock_free(smb_lock_t *);
61c8ec8eeaSjose borrego 
621fcced4cSJordan Brown /*
63148c5f43SAlan Wright  * Return the number of range locks on the specified ofile.
641fcced4cSJordan Brown  */
651fcced4cSJordan Brown uint32_t
smb_lock_get_lock_count(smb_node_t * node,smb_ofile_t * of)66148c5f43SAlan Wright smb_lock_get_lock_count(smb_node_t *node, smb_ofile_t *of)
671fcced4cSJordan Brown {
68148c5f43SAlan Wright 	smb_lock_t	*lock;
69148c5f43SAlan Wright 	smb_llist_t	*llist;
70148c5f43SAlan Wright 	uint32_t	count = 0;
71c8ec8eeaSjose borrego 
721fcced4cSJordan Brown 	SMB_NODE_VALID(node);
73148c5f43SAlan Wright 	SMB_OFILE_VALID(of);
741fcced4cSJordan Brown 
75148c5f43SAlan Wright 	llist = &node->n_lock_list;
76148c5f43SAlan Wright 
77148c5f43SAlan Wright 	smb_llist_enter(llist, RW_READER);
78148c5f43SAlan Wright 	for (lock = smb_llist_head(llist);
79148c5f43SAlan Wright 	    lock != NULL;
80148c5f43SAlan Wright 	    lock = smb_llist_next(llist, lock)) {
81148c5f43SAlan Wright 		if (lock->l_file == of)
82148c5f43SAlan Wright 			++count;
83148c5f43SAlan Wright 	}
84148c5f43SAlan Wright 	smb_llist_exit(llist);
851fcced4cSJordan Brown 
861fcced4cSJordan Brown 	return (count);
871fcced4cSJordan Brown }
88c8ec8eeaSjose borrego 
89c8ec8eeaSjose borrego /*
90c8ec8eeaSjose borrego  * smb_unlock_range
91c8ec8eeaSjose borrego  *
92c8ec8eeaSjose borrego  * locates lock range performed for corresponding to unlock request.
93c8ec8eeaSjose borrego  *
94c8ec8eeaSjose borrego  * NT_STATUS_SUCCESS - Lock range performed successfully.
95c8ec8eeaSjose borrego  * !NT_STATUS_SUCCESS - Error in unlock range operation.
96c8ec8eeaSjose borrego  */
97c8ec8eeaSjose borrego uint32_t
smb_unlock_range(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid)98c8ec8eeaSjose borrego smb_unlock_range(
99c8ec8eeaSjose borrego     smb_request_t	*sr,
100c8ec8eeaSjose borrego     uint64_t		start,
1010897f7fbSGordon Ross     uint64_t		length,
1020897f7fbSGordon Ross     uint32_t		pid)
103c8ec8eeaSjose borrego {
1040897f7fbSGordon Ross 	smb_ofile_t	*file = sr->fid_ofile;
1050897f7fbSGordon Ross 	smb_node_t	*node = file->f_node;
106c8ec8eeaSjose borrego 	smb_lock_t	*lock = NULL;
107c8ec8eeaSjose borrego 	uint32_t	status;
108c8ec8eeaSjose borrego 
1090897f7fbSGordon Ross 	if (length > 1 &&
1100897f7fbSGordon Ross 	    (start + length) < start)
1110897f7fbSGordon Ross 		return (NT_STATUS_INVALID_LOCK_RANGE);
1120897f7fbSGordon Ross 
1130897f7fbSGordon Ross #ifdef	DEBUG
1140897f7fbSGordon Ross 	if (smb_lock_debug) {
1150897f7fbSGordon Ross 		cmn_err(CE_CONT, "smb_unlock_range "
1160897f7fbSGordon Ross 		    "off=0x%llx, len=0x%llx, f=%p, pid=%d\n",
1170897f7fbSGordon Ross 		    (long long)start, (long long)length,
1180897f7fbSGordon Ross 		    (void *)sr->fid_ofile, pid);
1190897f7fbSGordon Ross 	}
1200897f7fbSGordon Ross #endif
1210897f7fbSGordon Ross 
122c8ec8eeaSjose borrego 	/* Apply unlocking rules */
123c8ec8eeaSjose borrego 	smb_llist_enter(&node->n_lock_list, RW_WRITER);
1240897f7fbSGordon Ross 	status = smb_lock_range_ulckrules(file, start, length, pid, &lock);
125c8ec8eeaSjose borrego 	if (status != NT_STATUS_SUCCESS) {
126c8ec8eeaSjose borrego 		/*
127c8ec8eeaSjose borrego 		 * If lock range is not matching in the list
128c8ec8eeaSjose borrego 		 * return error.
129c8ec8eeaSjose borrego 		 */
130c8ec8eeaSjose borrego 		ASSERT(lock == NULL);
131c8ec8eeaSjose borrego 	}
1320897f7fbSGordon Ross 	if (lock != NULL) {
133c8ec8eeaSjose borrego 		smb_llist_remove(&node->n_lock_list, lock);
134c8ec8eeaSjose borrego 		smb_lock_posix_unlock(node, lock, sr->user_cr);
1350897f7fbSGordon Ross 	}
1360897f7fbSGordon Ross 
1370897f7fbSGordon Ross #ifdef	DEBUG
1380897f7fbSGordon Ross 	if (smb_lock_debug && lock == NULL) {
1390897f7fbSGordon Ross 		cmn_err(CE_CONT, "unlock failed, 0x%x\n", status);
1400897f7fbSGordon Ross 		smb_lock_dumpnode(node);
1410897f7fbSGordon Ross 	}
1420897f7fbSGordon Ross #endif
1430897f7fbSGordon Ross 
144c8ec8eeaSjose borrego 	smb_llist_exit(&node->n_lock_list);
1450897f7fbSGordon Ross 
1460897f7fbSGordon Ross 	if (lock != NULL)
147c8ec8eeaSjose borrego 		smb_lock_destroy(lock);
148c8ec8eeaSjose borrego 
149c8ec8eeaSjose borrego 	return (status);
150c8ec8eeaSjose borrego }
151c8ec8eeaSjose borrego 
152c8ec8eeaSjose borrego /*
153c8ec8eeaSjose borrego  * smb_lock_range
154c8ec8eeaSjose borrego  *
155cb174861Sjoyce mcintosh  * Checks for integrity of file lock operation for the given range of file data.
156c8ec8eeaSjose borrego  * This is performed by applying lock rules with all the elements of the node
157c8ec8eeaSjose borrego  * lock list.
158c8ec8eeaSjose borrego  *
159cb174861Sjoyce mcintosh  * Break shared (levelII) oplocks. If there is an exclusive oplock, it is
160cb174861Sjoyce mcintosh  * owned by this ofile and therefore should not be broken.
161cb174861Sjoyce mcintosh  *
162c8ec8eeaSjose borrego  * The function returns with new lock added if lock request is non-conflicting
163c8ec8eeaSjose borrego  * with existing range lock for the file. Otherwise smb request is filed
164c8ec8eeaSjose borrego  * without returning.
165c8ec8eeaSjose borrego  *
166c8ec8eeaSjose borrego  * NT_STATUS_SUCCESS - Lock range performed successfully.
167c8ec8eeaSjose borrego  * !NT_STATUS_SUCCESS - Error in lock range operation.
168c8ec8eeaSjose borrego  */
169c8ec8eeaSjose borrego uint32_t
smb_lock_range(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid,uint32_t locktype,uint32_t timeout)170c8ec8eeaSjose borrego smb_lock_range(
171c8ec8eeaSjose borrego     smb_request_t	*sr,
172c8ec8eeaSjose borrego     uint64_t		start,
173c8ec8eeaSjose borrego     uint64_t		length,
1740897f7fbSGordon Ross     uint32_t		pid,
1750897f7fbSGordon Ross     uint32_t		locktype,
1760897f7fbSGordon Ross     uint32_t		timeout)
177c8ec8eeaSjose borrego {
178c8ec8eeaSjose borrego 	smb_ofile_t	*file = sr->fid_ofile;
179c8ec8eeaSjose borrego 	smb_node_t	*node = file->f_node;
180c8ec8eeaSjose borrego 	smb_lock_t	*lock;
1810897f7fbSGordon Ross 	smb_lock_t	*conflict = NULL;
1820897f7fbSGordon Ross 	uint32_t	result;
1830897f7fbSGordon Ross 	int		rc;
184a90cf9f2SGordon Ross 	boolean_t	lock_has_timeout =
185a90cf9f2SGordon Ross 	    (timeout != 0 && timeout != UINT_MAX);
186c8ec8eeaSjose borrego 
1870897f7fbSGordon Ross 	if (length > 1 &&
1880897f7fbSGordon Ross 	    (start + length) < start)
1890897f7fbSGordon Ross 		return (NT_STATUS_INVALID_LOCK_RANGE);
1900897f7fbSGordon Ross 
1910897f7fbSGordon Ross #ifdef	DEBUG
1920897f7fbSGordon Ross 	if (smb_lock_debug) {
1930897f7fbSGordon Ross 		cmn_err(CE_CONT, "smb_lock_range "
1940897f7fbSGordon Ross 		    "off=0x%llx, len=0x%llx, "
1950897f7fbSGordon Ross 		    "f=%p, pid=%d, typ=%d, tmo=%d\n",
1960897f7fbSGordon Ross 		    (long long)start, (long long)length,
1970897f7fbSGordon Ross 		    (void *)sr->fid_ofile, pid, locktype, timeout);
1980897f7fbSGordon Ross 	}
1990897f7fbSGordon Ross #endif
2000897f7fbSGordon Ross 
2010897f7fbSGordon Ross 	lock = smb_lock_create(sr, start, length, pid, locktype, timeout);
202c8ec8eeaSjose borrego 
203c8ec8eeaSjose borrego 	smb_llist_enter(&node->n_lock_list, RW_WRITER);
204c8ec8eeaSjose borrego 	for (;;) {
205c8ec8eeaSjose borrego 
206c8ec8eeaSjose borrego 		/* Apply locking rules */
2070897f7fbSGordon Ross 		result = smb_lock_range_lckrules(file, lock, &conflict);
2080897f7fbSGordon Ross 		switch (result) {
2090897f7fbSGordon Ross 		case NT_STATUS_LOCK_NOT_GRANTED: /* conflict! */
2100897f7fbSGordon Ross 			/* may need to wait */
211c8ec8eeaSjose borrego 			break;
2120897f7fbSGordon Ross 		case NT_STATUS_SUCCESS:
2130897f7fbSGordon Ross 		case NT_STATUS_FILE_CLOSED:
2140897f7fbSGordon Ross 			goto break_loop;
2150897f7fbSGordon Ross 		default:
2160897f7fbSGordon Ross 			cmn_err(CE_CONT, "smb_lock_range1, status 0x%x\n",
2170897f7fbSGordon Ross 			    result);
2180897f7fbSGordon Ross 			goto break_loop;
219c8ec8eeaSjose borrego 		}
2200897f7fbSGordon Ross 		if (timeout == 0)
2210897f7fbSGordon Ross 			goto break_loop;
222c8ec8eeaSjose borrego 
223c8ec8eeaSjose borrego 		/*
224c8ec8eeaSjose borrego 		 * Call smb_lock_wait holding write lock for
225c8ec8eeaSjose borrego 		 * node lock list.  smb_lock_wait will release
2260897f7fbSGordon Ross 		 * the node list lock if it blocks, so after
2270897f7fbSGordon Ross 		 * the call, (*conflict) may no longer exist.
228c8ec8eeaSjose borrego 		 */
2290897f7fbSGordon Ross 		result = smb_lock_wait(sr, lock, conflict);
2300897f7fbSGordon Ross 		conflict = NULL;
2310897f7fbSGordon Ross 		switch (result) {
2320897f7fbSGordon Ross 		case NT_STATUS_SUCCESS:
2330897f7fbSGordon Ross 			/* conflict gone, try again */
234c8ec8eeaSjose borrego 			break;
2350897f7fbSGordon Ross 		case NT_STATUS_TIMEOUT:
2360897f7fbSGordon Ross 			/* try just once more */
237c8ec8eeaSjose borrego 			timeout = 0;
2380897f7fbSGordon Ross 			break;
2390897f7fbSGordon Ross 		case NT_STATUS_CANCELLED:
2400897f7fbSGordon Ross 		case NT_STATUS_FILE_CLOSED:
2410897f7fbSGordon Ross 			goto break_loop;
2420897f7fbSGordon Ross 		default:
2430897f7fbSGordon Ross 			cmn_err(CE_CONT, "smb_lock_range2, status 0x%x\n",
2440897f7fbSGordon Ross 			    result);
2450897f7fbSGordon Ross 			goto break_loop;
2460897f7fbSGordon Ross 		}
247c8ec8eeaSjose borrego 	}
248c8ec8eeaSjose borrego 
2490897f7fbSGordon Ross break_loop:
250c8ec8eeaSjose borrego 	lock->l_blocked_by = NULL;
251c8ec8eeaSjose borrego 
252c8ec8eeaSjose borrego 	if (result != NT_STATUS_SUCCESS) {
2530897f7fbSGordon Ross 		if (result == NT_STATUS_FILE_CLOSED)
2540897f7fbSGordon Ross 			result = NT_STATUS_RANGE_NOT_LOCKED;
2550897f7fbSGordon Ross 
256c8ec8eeaSjose borrego 		/*
257c8ec8eeaSjose borrego 		 * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT
258c8ec8eeaSjose borrego 		 * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED.
259a90cf9f2SGordon Ross 		 * All of this appears to be specific to SMB1
260c8ec8eeaSjose borrego 		 */
261a90cf9f2SGordon Ross 		if (sr->session->dialect <= NT_LM_0_12 &&
262a90cf9f2SGordon Ross 		    result == NT_STATUS_LOCK_NOT_GRANTED) {
263c8ec8eeaSjose borrego 			/*
264c8ec8eeaSjose borrego 			 * Locks with timeouts always return
265c8ec8eeaSjose borrego 			 * NT_STATUS_FILE_LOCK_CONFLICT
266c8ec8eeaSjose borrego 			 */
267c8ec8eeaSjose borrego 			if (lock_has_timeout)
268c8ec8eeaSjose borrego 				result = NT_STATUS_FILE_LOCK_CONFLICT;
269c8ec8eeaSjose borrego 
270c8ec8eeaSjose borrego 			/*
271c8ec8eeaSjose borrego 			 * Locks starting higher than 0xef000000 that do not
272c8ec8eeaSjose borrego 			 * have the MSB set always return
273c8ec8eeaSjose borrego 			 * NT_STATUS_FILE_LOCK_CONFLICT
274c8ec8eeaSjose borrego 			 */
275c8ec8eeaSjose borrego 			if ((lock->l_start >= 0xef000000) &&
276c8ec8eeaSjose borrego 			    !(lock->l_start & (1ULL << 63))) {
277c8ec8eeaSjose borrego 				result = NT_STATUS_FILE_LOCK_CONFLICT;
278c8ec8eeaSjose borrego 			}
279c8ec8eeaSjose borrego 
280c8ec8eeaSjose borrego 			/*
281c8ec8eeaSjose borrego 			 * If the last lock attempt to fail on this file handle
282c8ec8eeaSjose borrego 			 * started at the same offset as this one then return
283c8ec8eeaSjose borrego 			 * NT_STATUS_FILE_LOCK_CONFLICT
284c8ec8eeaSjose borrego 			 */
285c8ec8eeaSjose borrego 			mutex_enter(&file->f_mutex);
286c8ec8eeaSjose borrego 			if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) &&
287c8ec8eeaSjose borrego 			    (lock->l_start == file->f_llf_pos)) {
288c8ec8eeaSjose borrego 				result = NT_STATUS_FILE_LOCK_CONFLICT;
289c8ec8eeaSjose borrego 			}
290c8ec8eeaSjose borrego 			mutex_exit(&file->f_mutex);
291c8ec8eeaSjose borrego 		}
292c8ec8eeaSjose borrego 
293c8ec8eeaSjose borrego 		/* Update last lock failed offset */
294c8ec8eeaSjose borrego 		mutex_enter(&file->f_mutex);
295c8ec8eeaSjose borrego 		file->f_llf_pos = lock->l_start;
296c8ec8eeaSjose borrego 		file->f_flags |= SMB_OFLAGS_LLF_POS_VALID;
297c8ec8eeaSjose borrego 		mutex_exit(&file->f_mutex);
298c8ec8eeaSjose borrego 
299c8ec8eeaSjose borrego 		smb_lock_free(lock);
300c8ec8eeaSjose borrego 	} else {
301c8ec8eeaSjose borrego 		/*
302c8ec8eeaSjose borrego 		 * don't insert into the CIFS lock list unless the
303c8ec8eeaSjose borrego 		 * posix lock worked
304c8ec8eeaSjose borrego 		 */
3050897f7fbSGordon Ross 		rc = smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr);
3060897f7fbSGordon Ross 		if (rc != 0) {
3070897f7fbSGordon Ross #ifdef	DEBUG
3080897f7fbSGordon Ross 			if (smb_lock_debug)
3090897f7fbSGordon Ross 				cmn_err(CE_CONT, "fop_frlock, err=%d\n", rc);
3100897f7fbSGordon Ross #endif
311c8ec8eeaSjose borrego 			result = NT_STATUS_FILE_LOCK_CONFLICT;
3120897f7fbSGordon Ross 		} else {
3130897f7fbSGordon Ross 			/*
3140897f7fbSGordon Ross 			 * We want unlock to find exclusive locks before
3150897f7fbSGordon Ross 			 * shared locks, so insert those at the head.
3160897f7fbSGordon Ross 			 */
3170897f7fbSGordon Ross 			if (lock->l_type == SMB_LOCK_TYPE_READWRITE)
3180897f7fbSGordon Ross 				smb_llist_insert_head(&node->n_lock_list, lock);
319c8ec8eeaSjose borrego 			else
320c8ec8eeaSjose borrego 				smb_llist_insert_tail(&node->n_lock_list, lock);
321c8ec8eeaSjose borrego 		}
3220897f7fbSGordon Ross 	}
3230897f7fbSGordon Ross 
3240897f7fbSGordon Ross #ifdef	DEBUG
3250897f7fbSGordon Ross 	if (smb_lock_debug && result != 0) {
3260897f7fbSGordon Ross 		cmn_err(CE_CONT, "lock failed, 0x%x\n", result);
3270897f7fbSGordon Ross 		smb_lock_dumpnode(node);
3280897f7fbSGordon Ross 	}
3290897f7fbSGordon Ross #endif
3300897f7fbSGordon Ross 
331c8ec8eeaSjose borrego 	smb_llist_exit(&node->n_lock_list);
332c8ec8eeaSjose borrego 
33394047d49SGordon Ross 	if (result == NT_STATUS_SUCCESS) {
33494047d49SGordon Ross 		/* This revokes read cache delegations. */
33594047d49SGordon Ross 		(void) smb_oplock_break_WRITE(node, file);
33694047d49SGordon Ross 	}
337cb174861Sjoyce mcintosh 
338c8ec8eeaSjose borrego 	return (result);
339c8ec8eeaSjose borrego }
340c8ec8eeaSjose borrego 
341c8ec8eeaSjose borrego /*
342c8ec8eeaSjose borrego  * smb_lock_range_access
343c8ec8eeaSjose borrego  *
344c8ec8eeaSjose borrego  * scans node lock list
345c8ec8eeaSjose borrego  * to check if there is any overlapping lock. Overlapping
346c8ec8eeaSjose borrego  * lock is allowed only under same session and client pid.
347c8ec8eeaSjose borrego  *
348c8ec8eeaSjose borrego  * Return values
349c8ec8eeaSjose borrego  *	NT_STATUS_SUCCESS		lock access granted.
350c8ec8eeaSjose borrego  *	NT_STATUS_FILE_LOCK_CONFLICT	access denied due to lock conflict.
351c8ec8eeaSjose borrego  */
352c8ec8eeaSjose borrego int
smb_lock_range_access(smb_request_t * sr,smb_node_t * node,uint64_t start,uint64_t length,boolean_t will_write)353c8ec8eeaSjose borrego smb_lock_range_access(
354c8ec8eeaSjose borrego     smb_request_t	*sr,
355c8ec8eeaSjose borrego     smb_node_t		*node,
356c8ec8eeaSjose borrego     uint64_t		start,
3570897f7fbSGordon Ross     uint64_t		length,
358c8ec8eeaSjose borrego     boolean_t		will_write)
359c8ec8eeaSjose borrego {
360c8ec8eeaSjose borrego 	smb_lock_t	*lock;
361c8ec8eeaSjose borrego 	smb_llist_t	*llist;
3620897f7fbSGordon Ross 	uint32_t	lk_pid = 0;
363c8ec8eeaSjose borrego 	int		status = NT_STATUS_SUCCESS;
364c8ec8eeaSjose borrego 
3650897f7fbSGordon Ross 	if (length == 0)
3660897f7fbSGordon Ross 		return (status);
3670897f7fbSGordon Ross 
3680897f7fbSGordon Ross 	/*
3690897f7fbSGordon Ross 	 * What PID to use for lock conflict checks?
3700897f7fbSGordon Ross 	 * SMB2 locking ignores PIDs (have lk_pid=0)
3710897f7fbSGordon Ross 	 * SMB1 uses low 16 bits of sr->smb_pid
3720897f7fbSGordon Ross 	 */
3730897f7fbSGordon Ross 	if (sr->session->dialect < SMB_VERS_2_BASE)
3740897f7fbSGordon Ross 		lk_pid = sr->smb_pid & 0xFFFF;
3750897f7fbSGordon Ross 
376c8ec8eeaSjose borrego 	llist = &node->n_lock_list;
377c8ec8eeaSjose borrego 	smb_llist_enter(llist, RW_READER);
378c8ec8eeaSjose borrego 	/* Search for any applicable lock */
379c8ec8eeaSjose borrego 	for (lock = smb_llist_head(llist);
380c8ec8eeaSjose borrego 	    lock != NULL;
381c8ec8eeaSjose borrego 	    lock = smb_llist_next(llist, lock)) {
382c8ec8eeaSjose borrego 
383c8ec8eeaSjose borrego 		if (!smb_lock_range_overlap(lock, start, length))
384c8ec8eeaSjose borrego 			/* Lock does not overlap */
385c8ec8eeaSjose borrego 			continue;
386c8ec8eeaSjose borrego 
387c8ec8eeaSjose borrego 		if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write)
388c8ec8eeaSjose borrego 			continue;
389c8ec8eeaSjose borrego 
390c8ec8eeaSjose borrego 		if (lock->l_type == SMB_LOCK_TYPE_READWRITE &&
3910897f7fbSGordon Ross 		    lock->l_file == sr->fid_ofile &&
3920897f7fbSGordon Ross 		    lock->l_pid == lk_pid)
393c8ec8eeaSjose borrego 			continue;
394c8ec8eeaSjose borrego 
3950897f7fbSGordon Ross #ifdef	DEBUG
3960897f7fbSGordon Ross 		if (smb_lock_debug) {
3970897f7fbSGordon Ross 			cmn_err(CE_CONT, "smb_lock_range_access conflict: "
3980897f7fbSGordon Ross 			    "off=0x%llx, len=0x%llx, "
3990897f7fbSGordon Ross 			    "f=%p, pid=%d, typ=%d\n",
4000897f7fbSGordon Ross 			    (long long)lock->l_start,
4010897f7fbSGordon Ross 			    (long long)lock->l_length,
4020897f7fbSGordon Ross 			    (void *)lock->l_file,
4030897f7fbSGordon Ross 			    lock->l_pid, lock->l_type);
4040897f7fbSGordon Ross 		}
4050897f7fbSGordon Ross #endif
406c8ec8eeaSjose borrego 		status = NT_STATUS_FILE_LOCK_CONFLICT;
407c8ec8eeaSjose borrego 		break;
408c8ec8eeaSjose borrego 	}
409c8ec8eeaSjose borrego 	smb_llist_exit(llist);
410c8ec8eeaSjose borrego 	return (status);
411c8ec8eeaSjose borrego }
412c8ec8eeaSjose borrego 
4130897f7fbSGordon Ross /*
4140897f7fbSGordon Ross  * The ofile is being closed.  Wake any waiting locks and
4150897f7fbSGordon Ross  * clear any granted locks.
4160897f7fbSGordon Ross  */
417c8ec8eeaSjose borrego void
smb_node_destroy_lock_by_ofile(smb_node_t * node,smb_ofile_t * file)418c8ec8eeaSjose borrego smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
419c8ec8eeaSjose borrego {
420811599a4SMatt Barden 	cred_t		*kcr = zone_kcred();
421c8ec8eeaSjose borrego 	smb_lock_t	*lock;
422c8ec8eeaSjose borrego 	smb_lock_t	*nxtl;
423c8ec8eeaSjose borrego 	list_t		destroy_list;
424c8ec8eeaSjose borrego 
4252c2961f8Sjose borrego 	SMB_NODE_VALID(node);
426c8ec8eeaSjose borrego 	ASSERT(node->n_refcnt);
427c8ec8eeaSjose borrego 
428c8ec8eeaSjose borrego 	/*
4290897f7fbSGordon Ross 	 * Cancel any waiting locks for this ofile
4300897f7fbSGordon Ross 	 */
4310897f7fbSGordon Ross 	smb_llist_enter(&node->n_wlock_list, RW_READER);
4320897f7fbSGordon Ross 	for (lock = smb_llist_head(&node->n_wlock_list);
4330897f7fbSGordon Ross 	    lock != NULL;
4340897f7fbSGordon Ross 	    lock = smb_llist_next(&node->n_wlock_list, lock)) {
4350897f7fbSGordon Ross 
4360897f7fbSGordon Ross 		if (lock->l_file == file) {
4370897f7fbSGordon Ross 			mutex_enter(&lock->l_mutex);
4380897f7fbSGordon Ross 			lock->l_blocked_by = NULL;
4390897f7fbSGordon Ross 			lock->l_flags |= SMB_LOCK_FLAG_CLOSED;
4400897f7fbSGordon Ross 			cv_broadcast(&lock->l_cv);
4410897f7fbSGordon Ross 			mutex_exit(&lock->l_mutex);
4420897f7fbSGordon Ross 		}
4430897f7fbSGordon Ross 	}
4440897f7fbSGordon Ross 	smb_llist_exit(&node->n_wlock_list);
4450897f7fbSGordon Ross 
4460897f7fbSGordon Ross 	/*
447c8ec8eeaSjose borrego 	 * Move locks matching the specified file from the node->n_lock_list
448c8ec8eeaSjose borrego 	 * to a temporary list (holding the lock the entire time) then
449c8ec8eeaSjose borrego 	 * destroy all the matching locks.  We can't call smb_lock_destroy
450c8ec8eeaSjose borrego 	 * while we are holding the lock for node->n_lock_list because we will
451c8ec8eeaSjose borrego 	 * deadlock and we can't drop the lock because the list contents might
452c8ec8eeaSjose borrego 	 * change (for example nxtl might get removed on another thread).
453c8ec8eeaSjose borrego 	 */
454c8ec8eeaSjose borrego 	list_create(&destroy_list, sizeof (smb_lock_t),
455c8ec8eeaSjose borrego 	    offsetof(smb_lock_t, l_lnd));
456c8ec8eeaSjose borrego 
457c8ec8eeaSjose borrego 	smb_llist_enter(&node->n_lock_list, RW_WRITER);
458c8ec8eeaSjose borrego 	lock = smb_llist_head(&node->n_lock_list);
459c8ec8eeaSjose borrego 	while (lock) {
460c8ec8eeaSjose borrego 		nxtl = smb_llist_next(&node->n_lock_list, lock);
461c8ec8eeaSjose borrego 		if (lock->l_file == file) {
462c8ec8eeaSjose borrego 			smb_llist_remove(&node->n_lock_list, lock);
463811599a4SMatt Barden 			smb_lock_posix_unlock(node, lock, kcr);
464c8ec8eeaSjose borrego 			list_insert_tail(&destroy_list, lock);
465c8ec8eeaSjose borrego 		}
466c8ec8eeaSjose borrego 		lock = nxtl;
467c8ec8eeaSjose borrego 	}
468c8ec8eeaSjose borrego 	smb_llist_exit(&node->n_lock_list);
469c8ec8eeaSjose borrego 
470c8ec8eeaSjose borrego 	lock = list_head(&destroy_list);
471c8ec8eeaSjose borrego 	while (lock) {
472c8ec8eeaSjose borrego 		nxtl = list_next(&destroy_list, lock);
473c8ec8eeaSjose borrego 		list_remove(&destroy_list, lock);
474c8ec8eeaSjose borrego 		smb_lock_destroy(lock);
475c8ec8eeaSjose borrego 		lock = nxtl;
476c8ec8eeaSjose borrego 	}
477c8ec8eeaSjose borrego 
478c8ec8eeaSjose borrego 	list_destroy(&destroy_list);
479c8ec8eeaSjose borrego }
480c8ec8eeaSjose borrego 
4810897f7fbSGordon Ross /*
4820897f7fbSGordon Ross  * Cause a waiting lock to stop waiting and return an error.
4830897f7fbSGordon Ross  * returns same status codes as unlock:
4840897f7fbSGordon Ross  * NT_STATUS_SUCCESS, NT_STATUS_RANGE_NOT_LOCKED
4850897f7fbSGordon Ross  */
4860897f7fbSGordon Ross uint32_t
smb_lock_range_cancel(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid)4870897f7fbSGordon Ross smb_lock_range_cancel(smb_request_t *sr,
4880897f7fbSGordon Ross     uint64_t start, uint64_t length, uint32_t pid)
4890897f7fbSGordon Ross {
4900897f7fbSGordon Ross 	smb_node_t *node;
4910897f7fbSGordon Ross 	smb_lock_t *lock;
4920897f7fbSGordon Ross 	uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
4930897f7fbSGordon Ross 	int cnt = 0;
4940897f7fbSGordon Ross 
4950897f7fbSGordon Ross 	node = sr->fid_ofile->f_node;
4960897f7fbSGordon Ross 
4970897f7fbSGordon Ross 	smb_llist_enter(&node->n_wlock_list, RW_READER);
4980897f7fbSGordon Ross 
4990897f7fbSGordon Ross #ifdef	DEBUG
5000897f7fbSGordon Ross 	if (smb_lock_debug) {
5010897f7fbSGordon Ross 		cmn_err(CE_CONT, "smb_lock_range_cancel:\n"
5020897f7fbSGordon Ross 		    "\tstart=0x%llx, len=0x%llx, of=%p, pid=%d\n",
5030897f7fbSGordon Ross 		    (long long)start, (long long)length,
5040897f7fbSGordon Ross 		    (void *)sr->fid_ofile, pid);
5050897f7fbSGordon Ross 	}
5060897f7fbSGordon Ross #endif
5070897f7fbSGordon Ross 
5080897f7fbSGordon Ross 	for (lock = smb_llist_head(&node->n_wlock_list);
5090897f7fbSGordon Ross 	    lock != NULL;
5100897f7fbSGordon Ross 	    lock = smb_llist_next(&node->n_wlock_list, lock)) {
5110897f7fbSGordon Ross 
5120897f7fbSGordon Ross 		if ((start == lock->l_start) &&
5130897f7fbSGordon Ross 		    (length == lock->l_length) &&
5140897f7fbSGordon Ross 		    lock->l_file == sr->fid_ofile &&
5150897f7fbSGordon Ross 		    lock->l_pid == pid) {
5160897f7fbSGordon Ross 
5170897f7fbSGordon Ross 			mutex_enter(&lock->l_mutex);
5180897f7fbSGordon Ross 			lock->l_blocked_by = NULL;
5190897f7fbSGordon Ross 			lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
5200897f7fbSGordon Ross 			cv_broadcast(&lock->l_cv);
5210897f7fbSGordon Ross 			mutex_exit(&lock->l_mutex);
5220897f7fbSGordon Ross 			status = NT_STATUS_SUCCESS;
5230897f7fbSGordon Ross 			cnt++;
5240897f7fbSGordon Ross 		}
5250897f7fbSGordon Ross 	}
5260897f7fbSGordon Ross 
5270897f7fbSGordon Ross #ifdef	DEBUG
5280897f7fbSGordon Ross 	if (smb_lock_debug && cnt != 1) {
5290897f7fbSGordon Ross 		cmn_err(CE_CONT, "cancel found %d\n", cnt);
5300897f7fbSGordon Ross 		smb_lock_dumpnode(node);
5310897f7fbSGordon Ross 	}
5320897f7fbSGordon Ross #endif
5330897f7fbSGordon Ross 
5340897f7fbSGordon Ross 	smb_llist_exit(&node->n_wlock_list);
5350897f7fbSGordon Ross 
5360897f7fbSGordon Ross 	return (status);
5370897f7fbSGordon Ross }
5380897f7fbSGordon Ross 
539c8ec8eeaSjose borrego void
smb_lock_range_error(smb_request_t * sr,uint32_t status32)540c8ec8eeaSjose borrego smb_lock_range_error(smb_request_t *sr, uint32_t status32)
541c8ec8eeaSjose borrego {
542c8ec8eeaSjose borrego 	uint16_t errcode;
543c8ec8eeaSjose borrego 
5440897f7fbSGordon Ross 	if (status32 == NT_STATUS_CANCELLED) {
5450897f7fbSGordon Ross 		status32 = NT_STATUS_FILE_LOCK_CONFLICT;
5460897f7fbSGordon Ross 		errcode = ERROR_LOCK_VIOLATION;
5470897f7fbSGordon Ross 	} else {
548c8ec8eeaSjose borrego 		errcode = ERRlock;
5490897f7fbSGordon Ross 	}
550c8ec8eeaSjose borrego 
551c8ec8eeaSjose borrego 	smbsr_error(sr, status32, ERRDOS, errcode);
552c8ec8eeaSjose borrego }
553c8ec8eeaSjose borrego 
554c8ec8eeaSjose borrego /*
555bc7c423fSGordon Ross  * An SMB variant of nbl_conflict().
556c8ec8eeaSjose borrego  *
557bc7c423fSGordon Ross  * SMB prevents remove or rename when conflicting locks exist
558bc7c423fSGordon Ross  * (unlike NFS, which is why we can't just use nbl_conflict).
559c8ec8eeaSjose borrego  *
560bc7c423fSGordon Ross  * Returns:
561bc7c423fSGordon Ross  *	NT_STATUS_SHARING_VIOLATION - nbl_share_conflict
562bc7c423fSGordon Ross  *	NT_STATUS_FILE_LOCK_CONFLICT - nbl_lock_conflict
563bc7c423fSGordon Ross  *	NT_STATUS_SUCCESS - operation can proceed
564bc7c423fSGordon Ross  *
565bc7c423fSGordon Ross  * NB: This function used to also check the list of ofiles,
566bc7c423fSGordon Ross  * via: smb_lock_range_access() but we _can't_ do that here
567bc7c423fSGordon Ross  * due to lock order constraints between node->n_lock_list
568bc7c423fSGordon Ross  * and node->vp->vnbllock (taken via nvl_start_crit).
569bc7c423fSGordon Ross  * They must be taken in that order, and in here, we
570bc7c423fSGordon Ross  * already hold vp->vnbllock.
571c8ec8eeaSjose borrego  */
572c8ec8eeaSjose borrego DWORD
smb_nbl_conflict(smb_node_t * node,uint64_t off,uint64_t len,nbl_op_t op)573bc7c423fSGordon Ross smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
574c8ec8eeaSjose borrego {
575c8ec8eeaSjose borrego 	int svmand;
576c8ec8eeaSjose borrego 
5772c2961f8Sjose borrego 	SMB_NODE_VALID(node);
578c8ec8eeaSjose borrego 	ASSERT(smb_node_in_crit(node));
579bc7c423fSGordon Ross 	ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
580bc7c423fSGordon Ross 	    op == NBL_REMOVE || op == NBL_RENAME);
581c8ec8eeaSjose borrego 
582037cac00Sjoyce mcintosh 	if (smb_node_is_dir(node))
583c8ec8eeaSjose borrego 		return (NT_STATUS_SUCCESS);
584c8ec8eeaSjose borrego 
585bc7c423fSGordon Ross 	if (nbl_share_conflict(node->vp, op, &smb_ct))
586bc7c423fSGordon Ross 		return (NT_STATUS_SHARING_VIOLATION);
587c8ec8eeaSjose borrego 
588bc7c423fSGordon Ross 	/*
589bc7c423fSGordon Ross 	 * When checking for lock conflicts, rename and remove
590bc7c423fSGordon Ross 	 * are not allowed, so treat those as read/write.
591bc7c423fSGordon Ross 	 */
592bc7c423fSGordon Ross 	if (op == NBL_RENAME || op == NBL_REMOVE)
593bc7c423fSGordon Ross 		op = NBL_READWRITE;
594c8ec8eeaSjose borrego 
5958622ec45SGordon Ross 	if (nbl_svmand(node->vp, zone_kcred(), &svmand))
596bc7c423fSGordon Ross 		svmand = 1;
597c8ec8eeaSjose borrego 
598bc7c423fSGordon Ross 	if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
599c8ec8eeaSjose borrego 		return (NT_STATUS_FILE_LOCK_CONFLICT);
600c8ec8eeaSjose borrego 
601c8ec8eeaSjose borrego 	return (NT_STATUS_SUCCESS);
602c8ec8eeaSjose borrego }
603c8ec8eeaSjose borrego 
604c8ec8eeaSjose borrego /*
605c8ec8eeaSjose borrego  * smb_lock_posix_unlock
606c8ec8eeaSjose borrego  *
607c8ec8eeaSjose borrego  * checks if the current unlock request is in another lock and repeatedly calls
608c8ec8eeaSjose borrego  * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock
609c8ec8eeaSjose borrego  * that are not in other locks
610c8ec8eeaSjose borrego  *
611c8ec8eeaSjose borrego  */
612c8ec8eeaSjose borrego static void
smb_lock_posix_unlock(smb_node_t * node,smb_lock_t * lock,cred_t * cr)613c8ec8eeaSjose borrego smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
614c8ec8eeaSjose borrego {
615c8ec8eeaSjose borrego 	uint64_t	new_mark;
616c8ec8eeaSjose borrego 	uint64_t	unlock_start;
617c8ec8eeaSjose borrego 	uint64_t	unlock_end;
618c8ec8eeaSjose borrego 	smb_lock_t	new_unlock;
619c8ec8eeaSjose borrego 	smb_llist_t	*llist;
620c8ec8eeaSjose borrego 	boolean_t	can_unlock;
621c8ec8eeaSjose borrego 
622c8ec8eeaSjose borrego 	new_mark = 0;
623c8ec8eeaSjose borrego 	unlock_start = lock->l_start;
624c8ec8eeaSjose borrego 	unlock_end = unlock_start + lock->l_length;
625c8ec8eeaSjose borrego 	llist = &node->n_lock_list;
626c8ec8eeaSjose borrego 
627c8ec8eeaSjose borrego 	for (;;) {
628c8ec8eeaSjose borrego 		can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
629c8ec8eeaSjose borrego 		    lock->l_file->f_uniqid, llist, &new_mark);
630c8ec8eeaSjose borrego 		if (can_unlock) {
631c8ec8eeaSjose borrego 			if (new_mark) {
632c8ec8eeaSjose borrego 				new_unlock = *lock;
633c8ec8eeaSjose borrego 				new_unlock.l_start = unlock_start;
634c8ec8eeaSjose borrego 				new_unlock.l_length = new_mark - unlock_start;
635c8ec8eeaSjose borrego 				(void) smb_fsop_frlock(node, &new_unlock,
636c8ec8eeaSjose borrego 				    B_TRUE, cr);
637c8ec8eeaSjose borrego 				unlock_start = new_mark;
638c8ec8eeaSjose borrego 			} else {
639c8ec8eeaSjose borrego 				new_unlock = *lock;
640c8ec8eeaSjose borrego 				new_unlock.l_start = unlock_start;
641c8ec8eeaSjose borrego 				new_unlock.l_length = unlock_end - unlock_start;
642c8ec8eeaSjose borrego 				(void) smb_fsop_frlock(node, &new_unlock,
643c8ec8eeaSjose borrego 				    B_TRUE, cr);
644c8ec8eeaSjose borrego 				break;
645c8ec8eeaSjose borrego 			}
646c8ec8eeaSjose borrego 		} else if (new_mark) {
647c8ec8eeaSjose borrego 			unlock_start = new_mark;
648c8ec8eeaSjose borrego 		} else {
649c8ec8eeaSjose borrego 			break;
650c8ec8eeaSjose borrego 		}
651c8ec8eeaSjose borrego 	}
652c8ec8eeaSjose borrego }
653c8ec8eeaSjose borrego 
654c8ec8eeaSjose borrego /*
655c8ec8eeaSjose borrego  * smb_lock_range_overlap
656c8ec8eeaSjose borrego  *
657c8ec8eeaSjose borrego  * Checks if lock range(start, length) overlaps range in lock structure.
658c8ec8eeaSjose borrego  *
659c8ec8eeaSjose borrego  * Zero-length byte range locks actually affect no single byte of the stream,
660c8ec8eeaSjose borrego  * meaning they can still be accessed even with such locks in place. However,
661c8ec8eeaSjose borrego  * they do conflict with other ranges in the following manner:
662c8ec8eeaSjose borrego  *  conflict will only exist if the positive-length range contains the
663c8ec8eeaSjose borrego  *  zero-length range's offset but doesn't start at it
664c8ec8eeaSjose borrego  *
665c8ec8eeaSjose borrego  * return values:
666c8ec8eeaSjose borrego  *	0 - Lock range doesn't overlap
667c8ec8eeaSjose borrego  *	1 - Lock range overlaps.
668c8ec8eeaSjose borrego  */
669c8ec8eeaSjose borrego 
670c8ec8eeaSjose borrego #define	RANGE_NO_OVERLAP	0
671c8ec8eeaSjose borrego #define	RANGE_OVERLAP		1
672c8ec8eeaSjose borrego 
673c8ec8eeaSjose borrego static int
smb_lock_range_overlap(struct smb_lock * lock,uint64_t start,uint64_t length)674c8ec8eeaSjose borrego smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
675c8ec8eeaSjose borrego {
676c8ec8eeaSjose borrego 	if (length == 0) {
677c8ec8eeaSjose borrego 		if ((lock->l_start < start) &&
678c8ec8eeaSjose borrego 		    ((lock->l_start + lock->l_length) > start))
679c8ec8eeaSjose borrego 			return (RANGE_OVERLAP);
680c8ec8eeaSjose borrego 
681c8ec8eeaSjose borrego 		return (RANGE_NO_OVERLAP);
682c8ec8eeaSjose borrego 	}
683c8ec8eeaSjose borrego 
684c8ec8eeaSjose borrego 	/* The following test is intended to catch roll over locks. */
685c8ec8eeaSjose borrego 	if ((start == lock->l_start) && (length == lock->l_length))
686c8ec8eeaSjose borrego 		return (RANGE_OVERLAP);
687c8ec8eeaSjose borrego 
688c8ec8eeaSjose borrego 	if (start < lock->l_start) {
689c8ec8eeaSjose borrego 		if (start + length > lock->l_start)
690c8ec8eeaSjose borrego 			return (RANGE_OVERLAP);
691c8ec8eeaSjose borrego 	} else if (start < lock->l_start + lock->l_length)
692c8ec8eeaSjose borrego 		return (RANGE_OVERLAP);
693c8ec8eeaSjose borrego 
694c8ec8eeaSjose borrego 	return (RANGE_NO_OVERLAP);
695c8ec8eeaSjose borrego }
696c8ec8eeaSjose borrego 
697c8ec8eeaSjose borrego /*
698c8ec8eeaSjose borrego  * smb_lock_range_lckrules
699c8ec8eeaSjose borrego  *
700c8ec8eeaSjose borrego  * Lock range rules:
701c8ec8eeaSjose borrego  *	1. Overlapping read locks are allowed if the
702c8ec8eeaSjose borrego  *	   current locks in the region are only read locks
703c8ec8eeaSjose borrego  *	   irrespective of pid of smb client issuing lock request.
704c8ec8eeaSjose borrego  *
705c8ec8eeaSjose borrego  *	2. Read lock in the overlapped region of write lock
7060897f7fbSGordon Ross  *	   are allowed if the previous lock is performed by the
707c8ec8eeaSjose borrego  *	   same pid and connection.
708c8ec8eeaSjose borrego  *
709c8ec8eeaSjose borrego  * return status:
7100897f7fbSGordon Ross  *	NT_STATUS_SUCCESS - Input lock range conforms to lock rules.
711c8ec8eeaSjose borrego  *	NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules.
7120897f7fbSGordon Ross  *	NT_STATUS_FILE_CLOSED
713c8ec8eeaSjose borrego  */
714c8ec8eeaSjose borrego static uint32_t
smb_lock_range_lckrules(smb_ofile_t * file,smb_lock_t * dlock,smb_lock_t ** conflictp)715c8ec8eeaSjose borrego smb_lock_range_lckrules(
716c8ec8eeaSjose borrego     smb_ofile_t		*file,
7170897f7fbSGordon Ross     smb_lock_t		*dlock,		/* desired lock */
7180897f7fbSGordon Ross     smb_lock_t		**conflictp)
719c8ec8eeaSjose borrego {
7200897f7fbSGordon Ross 	smb_node_t	*node = file->f_node;
721c8ec8eeaSjose borrego 	smb_lock_t	*lock;
722c8ec8eeaSjose borrego 	uint32_t	status = NT_STATUS_SUCCESS;
723c8ec8eeaSjose borrego 
724c8ec8eeaSjose borrego 	/* Check if file is closed */
725c8ec8eeaSjose borrego 	if (!smb_ofile_is_open(file)) {
7260897f7fbSGordon Ross 		return (NT_STATUS_FILE_CLOSED);
727c8ec8eeaSjose borrego 	}
728c8ec8eeaSjose borrego 
729c8ec8eeaSjose borrego 	/* Caller must hold lock for node->n_lock_list */
730c8ec8eeaSjose borrego 	for (lock = smb_llist_head(&node->n_lock_list);
731c8ec8eeaSjose borrego 	    lock != NULL;
732c8ec8eeaSjose borrego 	    lock = smb_llist_next(&node->n_lock_list, lock)) {
733c8ec8eeaSjose borrego 
734c8ec8eeaSjose borrego 		if (!smb_lock_range_overlap(lock, dlock->l_start,
735c8ec8eeaSjose borrego 		    dlock->l_length))
736c8ec8eeaSjose borrego 			continue;
737c8ec8eeaSjose borrego 
738c8ec8eeaSjose borrego 		/*
739c8ec8eeaSjose borrego 		 * Check to see if lock in the overlapping record
740c8ec8eeaSjose borrego 		 * is only read lock. Current finding is read
741c8ec8eeaSjose borrego 		 * locks can overlapped irrespective of pids.
742c8ec8eeaSjose borrego 		 */
743c8ec8eeaSjose borrego 		if ((lock->l_type == SMB_LOCK_TYPE_READONLY) &&
744c8ec8eeaSjose borrego 		    (dlock->l_type == SMB_LOCK_TYPE_READONLY)) {
745c8ec8eeaSjose borrego 			continue;
746c8ec8eeaSjose borrego 		}
747c8ec8eeaSjose borrego 
748c8ec8eeaSjose borrego 		/*
749c8ec8eeaSjose borrego 		 * When the read lock overlaps write lock, check if
750c8ec8eeaSjose borrego 		 * allowed.
751c8ec8eeaSjose borrego 		 */
752c8ec8eeaSjose borrego 		if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) &&
753c8ec8eeaSjose borrego 		    !(lock->l_type == SMB_LOCK_TYPE_READONLY)) {
7540897f7fbSGordon Ross 			if (lock->l_file == dlock->l_file &&
7550897f7fbSGordon Ross 			    lock->l_pid == dlock->l_pid) {
756c8ec8eeaSjose borrego 				continue;
757c8ec8eeaSjose borrego 			}
758c8ec8eeaSjose borrego 		}
759c8ec8eeaSjose borrego 
760c8ec8eeaSjose borrego 		/* Conflict in overlapping lock element */
7610897f7fbSGordon Ross 		*conflictp = lock;
762c8ec8eeaSjose borrego 		status = NT_STATUS_LOCK_NOT_GRANTED;
763c8ec8eeaSjose borrego 		break;
764c8ec8eeaSjose borrego 	}
765c8ec8eeaSjose borrego 
766c8ec8eeaSjose borrego 	return (status);
767c8ec8eeaSjose borrego }
768c8ec8eeaSjose borrego 
769c8ec8eeaSjose borrego /*
7705677e049SGordon Ross  * Cancel method for smb_lock_wait()
7715677e049SGordon Ross  *
7725677e049SGordon Ross  * This request is waiting on a lock.  Wakeup everything
7735677e049SGordon Ross  * waiting on the lock so that the relevant thread regains
7745677e049SGordon Ross  * control and notices that is has been cancelled.  The
7755677e049SGordon Ross  * other lock request threads waiting on this lock will go
7765677e049SGordon Ross  * back to sleep when they discover they are still blocked.
7775677e049SGordon Ross  */
7785677e049SGordon Ross static void
smb_lock_cancel_sr(smb_request_t * sr)7790897f7fbSGordon Ross smb_lock_cancel_sr(smb_request_t *sr)
7805677e049SGordon Ross {
7815677e049SGordon Ross 	smb_lock_t *lock = sr->cancel_arg2;
7825677e049SGordon Ross 
7830897f7fbSGordon Ross 	ASSERT(lock->l_magic == SMB_LOCK_MAGIC);
7840897f7fbSGordon Ross 	mutex_enter(&lock->l_mutex);
7850897f7fbSGordon Ross 	lock->l_blocked_by = NULL;
7860897f7fbSGordon Ross 	lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
7875677e049SGordon Ross 	cv_broadcast(&lock->l_cv);
7880897f7fbSGordon Ross 	mutex_exit(&lock->l_mutex);
7895677e049SGordon Ross }
7905677e049SGordon Ross 
7915677e049SGordon Ross /*
792c8ec8eeaSjose borrego  * smb_lock_wait
793c8ec8eeaSjose borrego  *
794c8ec8eeaSjose borrego  * Wait operation for smb overlapping lock to be released.  Caller must hold
795c8ec8eeaSjose borrego  * write lock for node->n_lock_list so that the set of active locks can't
796c8ec8eeaSjose borrego  * change unexpectedly.  The lock for node->n_lock_list  will be released
797c8ec8eeaSjose borrego  * within this function during the sleep after the lock dependency has
798c8ec8eeaSjose borrego  * been recorded.
799c8ec8eeaSjose borrego  *
8000897f7fbSGordon Ross  * Returns NT_STATUS_SUCCESS when the lock can be granted,
8010897f7fbSGordon Ross  * otherwise NT_STATUS_CANCELLED, etc.
802c8ec8eeaSjose borrego  */
8030897f7fbSGordon Ross static uint32_t
smb_lock_wait(smb_request_t * sr,smb_lock_t * lock,smb_lock_t * conflict)8040897f7fbSGordon Ross smb_lock_wait(smb_request_t *sr, smb_lock_t *lock, smb_lock_t *conflict)
805c8ec8eeaSjose borrego {
8060897f7fbSGordon Ross 	smb_node_t	*node;
8070897f7fbSGordon Ross 	clock_t		rc;
8080897f7fbSGordon Ross 	uint32_t	status = NT_STATUS_SUCCESS;
809c8ec8eeaSjose borrego 
8100897f7fbSGordon Ross 	node = lock->l_file->f_node;
8110897f7fbSGordon Ross 	ASSERT(node == conflict->l_file->f_node);
8120897f7fbSGordon Ross 
8130897f7fbSGordon Ross 	/*
8140897f7fbSGordon Ross 	 * Let the blocked lock (lock) l_blocked_by point to the
8150897f7fbSGordon Ross 	 * conflicting lock (conflict), and increment a count of
8160897f7fbSGordon Ross 	 * conflicts with the latter.  When the conflicting lock
8170897f7fbSGordon Ross 	 * is destroyed, we'll search the list of waiting locks
8180897f7fbSGordon Ross 	 * (on the node) and wake any with l_blocked_by ==
8190897f7fbSGordon Ross 	 * the formerly conflicting lock.
8200897f7fbSGordon Ross 	 */
8210897f7fbSGordon Ross 	mutex_enter(&lock->l_mutex);
8220897f7fbSGordon Ross 	lock->l_blocked_by = conflict;
8230897f7fbSGordon Ross 	mutex_exit(&lock->l_mutex);
8240897f7fbSGordon Ross 
8250897f7fbSGordon Ross 	mutex_enter(&conflict->l_mutex);
8260897f7fbSGordon Ross 	conflict->l_conflicts++;
8270897f7fbSGordon Ross 	mutex_exit(&conflict->l_mutex);
8280897f7fbSGordon Ross 
8290897f7fbSGordon Ross 	/*
8300897f7fbSGordon Ross 	 * Put the blocked lock on the waiting list.
8310897f7fbSGordon Ross 	 */
8320897f7fbSGordon Ross 	smb_llist_enter(&node->n_wlock_list, RW_WRITER);
8330897f7fbSGordon Ross 	smb_llist_insert_tail(&node->n_wlock_list, lock);
8340897f7fbSGordon Ross 	smb_llist_exit(&node->n_wlock_list);
8350897f7fbSGordon Ross 
8360897f7fbSGordon Ross #ifdef	DEBUG
8370897f7fbSGordon Ross 	if (smb_lock_debug) {
8380897f7fbSGordon Ross 		cmn_err(CE_CONT, "smb_lock_wait: lock=%p conflict=%p\n",
8390897f7fbSGordon Ross 		    (void *)lock, (void *)conflict);
8400897f7fbSGordon Ross 		smb_lock_dumpnode(node);
8410897f7fbSGordon Ross 	}
8420897f7fbSGordon Ross #endif
8430897f7fbSGordon Ross 
8440897f7fbSGordon Ross 	/*
8450897f7fbSGordon Ross 	 * We come in with n_lock_list already held, and keep
8460897f7fbSGordon Ross 	 * that hold until we're done with conflict (are now).
8470897f7fbSGordon Ross 	 * Drop that now, and retake later.  Note that the lock
8480897f7fbSGordon Ross 	 * (*conflict) may go away once we exit this list.
8490897f7fbSGordon Ross 	 */
8500897f7fbSGordon Ross 	smb_llist_exit(&node->n_lock_list);
8510897f7fbSGordon Ross 	conflict = NULL;
8520897f7fbSGordon Ross 
8530897f7fbSGordon Ross 	/*
854*66b505f1SGordon Ross 	 * Prepare for cancellable lock wait.
855*66b505f1SGordon Ross 	 *
856*66b505f1SGordon Ross 	 * If cancelled, smb_lock_cancel_sr sets
857*66b505f1SGordon Ross 	 * l_flags |= SMB_LOCK_FLAG_CANCELLED
8580897f7fbSGordon Ross 	 */
859c8ec8eeaSjose borrego 	mutex_enter(&sr->sr_mutex);
8600897f7fbSGordon Ross 	if (sr->sr_state == SMB_REQ_STATE_ACTIVE) {
861c8ec8eeaSjose borrego 		sr->sr_state = SMB_REQ_STATE_WAITING_LOCK;
8620897f7fbSGordon Ross 		sr->cancel_method = smb_lock_cancel_sr;
8630897f7fbSGordon Ross 		sr->cancel_arg2 = lock;
8640897f7fbSGordon Ross 	} else {
8650897f7fbSGordon Ross 		status = NT_STATUS_CANCELLED;
8660897f7fbSGordon Ross 	}
867c8ec8eeaSjose borrego 	mutex_exit(&sr->sr_mutex);
868c8ec8eeaSjose borrego 
869c8ec8eeaSjose borrego 	/*
8700897f7fbSGordon Ross 	 * Now we're ready to actually wait for the conflicting
8710897f7fbSGordon Ross 	 * lock to be removed, or for the wait to be ended by
8720897f7fbSGordon Ross 	 * an external cancel, or a timeout.
873c8ec8eeaSjose borrego 	 */
8740897f7fbSGordon Ross 	mutex_enter(&lock->l_mutex);
8750897f7fbSGordon Ross 	while (status == NT_STATUS_SUCCESS &&
8760897f7fbSGordon Ross 	    lock->l_blocked_by != NULL) {
8770897f7fbSGordon Ross 		if (lock->l_flags & SMB_LOCK_FLAG_INDEFINITE) {
8780897f7fbSGordon Ross 			cv_wait(&lock->l_cv, &lock->l_mutex);
879c8ec8eeaSjose borrego 		} else {
8800897f7fbSGordon Ross 			rc = cv_timedwait(&lock->l_cv,
8810897f7fbSGordon Ross 			    &lock->l_mutex, lock->l_end_time);
8820897f7fbSGordon Ross 			if (rc < 0)
8830897f7fbSGordon Ross 				status = NT_STATUS_TIMEOUT;
884c8ec8eeaSjose borrego 		}
8850897f7fbSGordon Ross 	}
8860897f7fbSGordon Ross 	if (status == NT_STATUS_SUCCESS) {
8870897f7fbSGordon Ross 		if (lock->l_flags & SMB_LOCK_FLAG_CANCELLED)
8880897f7fbSGordon Ross 			status = NT_STATUS_CANCELLED;
8890897f7fbSGordon Ross 		if (lock->l_flags & SMB_LOCK_FLAG_CLOSED)
8900897f7fbSGordon Ross 			status = NT_STATUS_FILE_CLOSED;
8910897f7fbSGordon Ross 	}
8920897f7fbSGordon Ross 	mutex_exit(&lock->l_mutex);
893c8ec8eeaSjose borrego 
8940897f7fbSGordon Ross 	/*
895*66b505f1SGordon Ross 	 * Did we get the lock or were we cancelled?
8960897f7fbSGordon Ross 	 */
897c8ec8eeaSjose borrego 	mutex_enter(&sr->sr_mutex);
898*66b505f1SGordon Ross switch_state:
8995677e049SGordon Ross 	switch (sr->sr_state) {
9005677e049SGordon Ross 	case SMB_REQ_STATE_WAITING_LOCK:
9010897f7fbSGordon Ross 		/* Normal wakeup.  Keep status from above. */
902c8ec8eeaSjose borrego 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
9035677e049SGordon Ross 		break;
9045677e049SGordon Ross 	case SMB_REQ_STATE_CANCEL_PENDING:
905*66b505f1SGordon Ross 		/* cancel_method running. wait. */
906*66b505f1SGordon Ross 		cv_wait(&sr->sr_st_cv, &sr->sr_mutex);
907*66b505f1SGordon Ross 		goto switch_state;
9085677e049SGordon Ross 	case SMB_REQ_STATE_CANCELLED:
909*66b505f1SGordon Ross 		/* Call should return an error. */
9100897f7fbSGordon Ross 		if (status == NT_STATUS_SUCCESS)
9110897f7fbSGordon Ross 			status = NT_STATUS_CANCELLED;
912c8ec8eeaSjose borrego 		break;
913c8ec8eeaSjose borrego 	default:
914c8ec8eeaSjose borrego 		break;
915c8ec8eeaSjose borrego 	}
916*66b505f1SGordon Ross 	sr->cancel_method = NULL;
917*66b505f1SGordon Ross 	sr->cancel_arg2 = NULL;
918c8ec8eeaSjose borrego 	mutex_exit(&sr->sr_mutex);
919c8ec8eeaSjose borrego 
9200897f7fbSGordon Ross 	/* Return to the caller with n_lock_list held. */
9210897f7fbSGordon Ross 	smb_llist_enter(&node->n_lock_list, RW_WRITER);
9220897f7fbSGordon Ross 
9230897f7fbSGordon Ross 	smb_llist_enter(&node->n_wlock_list, RW_WRITER);
9240897f7fbSGordon Ross 	smb_llist_remove(&node->n_wlock_list, lock);
9250897f7fbSGordon Ross 	smb_llist_exit(&node->n_wlock_list);
9260897f7fbSGordon Ross 
9270897f7fbSGordon Ross 	return (status);
928c8ec8eeaSjose borrego }
929c8ec8eeaSjose borrego 
930c8ec8eeaSjose borrego /*
931c8ec8eeaSjose borrego  * smb_lock_range_ulckrules
932c8ec8eeaSjose borrego  *
933c8ec8eeaSjose borrego  *	1. Unlock should be performed at exactly matching ends.
934c8ec8eeaSjose borrego  *	   This has been changed because overlapping ends is
935c8ec8eeaSjose borrego  *	   allowed and there is no other precise way of locating
936c8ec8eeaSjose borrego  *	   lock entity in node lock list.
937c8ec8eeaSjose borrego  *
938c8ec8eeaSjose borrego  *	2. Unlock is failed if there is no corresponding lock exists.
939c8ec8eeaSjose borrego  *
940c8ec8eeaSjose borrego  * Return values
941c8ec8eeaSjose borrego  *
942c8ec8eeaSjose borrego  *	NT_STATUS_SUCCESS		Unlock request matches lock record
9430897f7fbSGordon Ross  *					pointed by 'foundlock' lock structure.
944c8ec8eeaSjose borrego  *
945c8ec8eeaSjose borrego  *	NT_STATUS_RANGE_NOT_LOCKED	Unlock request doen't match any
946c8ec8eeaSjose borrego  *					of lock record in node lock request or
947c8ec8eeaSjose borrego  *					error in unlock range processing.
948c8ec8eeaSjose borrego  */
949c8ec8eeaSjose borrego static uint32_t
smb_lock_range_ulckrules(smb_ofile_t * file,uint64_t start,uint64_t length,uint32_t pid,smb_lock_t ** foundlock)950c8ec8eeaSjose borrego smb_lock_range_ulckrules(
9510897f7fbSGordon Ross     smb_ofile_t		*file,
952c8ec8eeaSjose borrego     uint64_t		start,
953c8ec8eeaSjose borrego     uint64_t		length,
9540897f7fbSGordon Ross     uint32_t		pid,
9550897f7fbSGordon Ross     smb_lock_t		**foundlock)
956c8ec8eeaSjose borrego {
9570897f7fbSGordon Ross 	smb_node_t	*node = file->f_node;
958c8ec8eeaSjose borrego 	smb_lock_t	*lock;
959c8ec8eeaSjose borrego 	uint32_t	status = NT_STATUS_RANGE_NOT_LOCKED;
960c8ec8eeaSjose borrego 
961c8ec8eeaSjose borrego 	/* Caller must hold lock for node->n_lock_list */
962c8ec8eeaSjose borrego 	for (lock = smb_llist_head(&node->n_lock_list);
963c8ec8eeaSjose borrego 	    lock != NULL;
964c8ec8eeaSjose borrego 	    lock = smb_llist_next(&node->n_lock_list, lock)) {
965c8ec8eeaSjose borrego 
966c8ec8eeaSjose borrego 		if ((start == lock->l_start) &&
967c8ec8eeaSjose borrego 		    (length == lock->l_length) &&
9680897f7fbSGordon Ross 		    lock->l_file == file &&
9690897f7fbSGordon Ross 		    lock->l_pid == pid) {
9700897f7fbSGordon Ross 			*foundlock = lock;
971c8ec8eeaSjose borrego 			status = NT_STATUS_SUCCESS;
972c8ec8eeaSjose borrego 			break;
973c8ec8eeaSjose borrego 		}
974c8ec8eeaSjose borrego 	}
975c8ec8eeaSjose borrego 
976c8ec8eeaSjose borrego 	return (status);
977c8ec8eeaSjose borrego }
978c8ec8eeaSjose borrego 
979c8ec8eeaSjose borrego static smb_lock_t *
smb_lock_create(smb_request_t * sr,uint64_t start,uint64_t length,uint32_t pid,uint32_t locktype,uint32_t timeout)980c8ec8eeaSjose borrego smb_lock_create(
981c8ec8eeaSjose borrego     smb_request_t *sr,
982c8ec8eeaSjose borrego     uint64_t start,
983c8ec8eeaSjose borrego     uint64_t length,
9840897f7fbSGordon Ross     uint32_t pid,
985c8ec8eeaSjose borrego     uint32_t locktype,
986c8ec8eeaSjose borrego     uint32_t timeout)
987c8ec8eeaSjose borrego {
988c8ec8eeaSjose borrego 	smb_lock_t *lock;
989c8ec8eeaSjose borrego 
990c8ec8eeaSjose borrego 	ASSERT(locktype == SMB_LOCK_TYPE_READWRITE ||
991c8ec8eeaSjose borrego 	    locktype == SMB_LOCK_TYPE_READONLY);
992c8ec8eeaSjose borrego 
9930897f7fbSGordon Ross 	lock = kmem_cache_alloc(smb_cache_lock, KM_SLEEP);
9940897f7fbSGordon Ross 	bzero(lock, sizeof (*lock));
995c8ec8eeaSjose borrego 	lock->l_magic = SMB_LOCK_MAGIC;
996c8ec8eeaSjose borrego 	lock->l_file = sr->fid_ofile;
9970897f7fbSGordon Ross 	/* l_file == fid_ofile implies same connection (see ofile lookup) */
9980897f7fbSGordon Ross 	lock->l_pid = pid;
999c8ec8eeaSjose borrego 	lock->l_type = locktype;
1000c8ec8eeaSjose borrego 	lock->l_start = start;
1001c8ec8eeaSjose borrego 	lock->l_length = length;
1002c8ec8eeaSjose borrego 	/*
1003c8ec8eeaSjose borrego 	 * Calculate the absolute end time so that we can use it
1004c8ec8eeaSjose borrego 	 * in cv_timedwait.
1005c8ec8eeaSjose borrego 	 */
1006d3d50737SRafael Vanoni 	lock->l_end_time = ddi_get_lbolt() + MSEC_TO_TICK(timeout);
1007c8ec8eeaSjose borrego 	if (timeout == UINT_MAX)
1008c8ec8eeaSjose borrego 		lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE;
1009c8ec8eeaSjose borrego 
1010c8ec8eeaSjose borrego 	mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL);
1011c8ec8eeaSjose borrego 	cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL);
1012c8ec8eeaSjose borrego 
1013c8ec8eeaSjose borrego 	return (lock);
1014c8ec8eeaSjose borrego }
1015c8ec8eeaSjose borrego 
1016c8ec8eeaSjose borrego static void
smb_lock_free(smb_lock_t * lock)1017c8ec8eeaSjose borrego smb_lock_free(smb_lock_t *lock)
1018c8ec8eeaSjose borrego {
10190897f7fbSGordon Ross 
10200897f7fbSGordon Ross 	lock->l_magic = 0;
1021c8ec8eeaSjose borrego 	cv_destroy(&lock->l_cv);
1022c8ec8eeaSjose borrego 	mutex_destroy(&lock->l_mutex);
1023c8ec8eeaSjose borrego 
10240897f7fbSGordon Ross 	kmem_cache_free(smb_cache_lock, lock);
1025c8ec8eeaSjose borrego }
1026c8ec8eeaSjose borrego 
1027c8ec8eeaSjose borrego /*
1028c8ec8eeaSjose borrego  * smb_lock_destroy
1029c8ec8eeaSjose borrego  *
1030c8ec8eeaSjose borrego  * Caller must hold node->n_lock_list
1031c8ec8eeaSjose borrego  */
1032c8ec8eeaSjose borrego static void
smb_lock_destroy(smb_lock_t * lock)1033c8ec8eeaSjose borrego smb_lock_destroy(smb_lock_t *lock)
1034c8ec8eeaSjose borrego {
10350897f7fbSGordon Ross 	smb_lock_t *tl;
10360897f7fbSGordon Ross 	smb_node_t *node;
10370897f7fbSGordon Ross 	uint32_t ccnt;
1038c8ec8eeaSjose borrego 
1039c8ec8eeaSjose borrego 	/*
10400897f7fbSGordon Ross 	 * Wake any waiting locks that were blocked by this.
10410897f7fbSGordon Ross 	 * We want them to wake and continue in FIFO order,
10420897f7fbSGordon Ross 	 * so enter/exit the llist every time...
1043c8ec8eeaSjose borrego 	 */
10440897f7fbSGordon Ross 	mutex_enter(&lock->l_mutex);
10450897f7fbSGordon Ross 	ccnt = lock->l_conflicts;
10460897f7fbSGordon Ross 	lock->l_conflicts = 0;
10470897f7fbSGordon Ross 	mutex_exit(&lock->l_mutex);
10480897f7fbSGordon Ross 
10490897f7fbSGordon Ross 	node = lock->l_file->f_node;
10500897f7fbSGordon Ross 	while (ccnt) {
10510897f7fbSGordon Ross 
10520897f7fbSGordon Ross 		smb_llist_enter(&node->n_wlock_list, RW_READER);
10530897f7fbSGordon Ross 
10540897f7fbSGordon Ross 		for (tl = smb_llist_head(&node->n_wlock_list);
10550897f7fbSGordon Ross 		    tl != NULL;
10560897f7fbSGordon Ross 		    tl = smb_llist_next(&node->n_wlock_list, tl)) {
10570897f7fbSGordon Ross 			mutex_enter(&tl->l_mutex);
10580897f7fbSGordon Ross 			if (tl->l_blocked_by == lock) {
10590897f7fbSGordon Ross 				tl->l_blocked_by = NULL;
10600897f7fbSGordon Ross 				cv_broadcast(&tl->l_cv);
10610897f7fbSGordon Ross 				mutex_exit(&tl->l_mutex);
10620897f7fbSGordon Ross 				goto woke_one;
10630897f7fbSGordon Ross 			}
10640897f7fbSGordon Ross 			mutex_exit(&tl->l_mutex);
10650897f7fbSGordon Ross 		}
10660897f7fbSGordon Ross 		/* No more in the list blocked by this lock. */
10670897f7fbSGordon Ross 		ccnt = 0;
10680897f7fbSGordon Ross 	woke_one:
10690897f7fbSGordon Ross 		smb_llist_exit(&node->n_wlock_list);
10700897f7fbSGordon Ross 		if (ccnt) {
10710897f7fbSGordon Ross 			/*
10720897f7fbSGordon Ross 			 * Let the thread we woke have a chance to run
10730897f7fbSGordon Ross 			 * before we wake competitors for their lock.
10740897f7fbSGordon Ross 			 */
10750897f7fbSGordon Ross 			delay(MSEC_TO_TICK(1));
10760897f7fbSGordon Ross 		}
10770897f7fbSGordon Ross 	}
1078c8ec8eeaSjose borrego 
1079c8ec8eeaSjose borrego 	smb_lock_free(lock);
1080c8ec8eeaSjose borrego }
1081c8ec8eeaSjose borrego 
1082c8ec8eeaSjose borrego /*
1083c8ec8eeaSjose borrego  * smb_is_range_unlocked
1084c8ec8eeaSjose borrego  *
1085c8ec8eeaSjose borrego  * Checks if the current unlock byte range request overlaps another lock
1086c8ec8eeaSjose borrego  * This function is used to determine where POSIX unlocks should be
1087c8ec8eeaSjose borrego  * applied.
1088c8ec8eeaSjose borrego  *
1089c8ec8eeaSjose borrego  * The return code and the value of new_mark must be interpreted as
1090c8ec8eeaSjose borrego  * follows:
1091c8ec8eeaSjose borrego  *
1092c8ec8eeaSjose borrego  * B_TRUE and (new_mark == 0):
1093c8ec8eeaSjose borrego  *   This is the last or only lock left to be unlocked
1094c8ec8eeaSjose borrego  *
1095c8ec8eeaSjose borrego  * B_TRUE and (new_mark > 0):
1096c8ec8eeaSjose borrego  *   The range from start to new_mark can be unlocked
1097c8ec8eeaSjose borrego  *
1098c8ec8eeaSjose borrego  * B_FALSE and (new_mark == 0):
1099c8ec8eeaSjose borrego  *   The unlock can't be performed and we are done
1100c8ec8eeaSjose borrego  *
1101c8ec8eeaSjose borrego  * B_FALSE and (new_mark > 0),
1102c8ec8eeaSjose borrego  *   The range from start to new_mark can't be unlocked
1103c8ec8eeaSjose borrego  *   Start should be reset to new_mark for the next pass
1104c8ec8eeaSjose borrego  */
1105c8ec8eeaSjose borrego 
1106c8ec8eeaSjose borrego static boolean_t
smb_is_range_unlocked(uint64_t start,uint64_t end,uint32_t uniqid,smb_llist_t * llist_head,uint64_t * new_mark)1107c8ec8eeaSjose borrego smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
1108c8ec8eeaSjose borrego     smb_llist_t *llist_head, uint64_t *new_mark)
1109c8ec8eeaSjose borrego {
1110c8ec8eeaSjose borrego 	struct smb_lock *lk = NULL;
1111c8ec8eeaSjose borrego 	uint64_t low_water_mark = MAXOFFSET_T;
1112c8ec8eeaSjose borrego 	uint64_t lk_start;
1113c8ec8eeaSjose borrego 	uint64_t lk_end;
1114c8ec8eeaSjose borrego 
1115c8ec8eeaSjose borrego 	*new_mark = 0;
1116c8ec8eeaSjose borrego 	lk = smb_llist_head(llist_head);
1117c8ec8eeaSjose borrego 	while (lk) {
1118c8ec8eeaSjose borrego 		if (lk->l_length == 0) {
1119c8ec8eeaSjose borrego 			lk = smb_llist_next(llist_head, lk);
1120c8ec8eeaSjose borrego 			continue;
1121c8ec8eeaSjose borrego 		}
1122c8ec8eeaSjose borrego 
1123c8ec8eeaSjose borrego 		if (lk->l_file->f_uniqid != uniqid) {
1124c8ec8eeaSjose borrego 			lk = smb_llist_next(llist_head, lk);
1125c8ec8eeaSjose borrego 			continue;
1126c8ec8eeaSjose borrego 		}
1127c8ec8eeaSjose borrego 
1128c8ec8eeaSjose borrego 		lk_end = lk->l_start + lk->l_length - 1;
1129c8ec8eeaSjose borrego 		lk_start = lk->l_start;
1130c8ec8eeaSjose borrego 
1131c8ec8eeaSjose borrego 		/*
1132c8ec8eeaSjose borrego 		 * there is no overlap for the first 2 cases
1133c8ec8eeaSjose borrego 		 * check next node
1134c8ec8eeaSjose borrego 		 */
1135c8ec8eeaSjose borrego 		if (lk_end < start) {
1136c8ec8eeaSjose borrego 			lk = smb_llist_next(llist_head, lk);
1137c8ec8eeaSjose borrego 			continue;
1138c8ec8eeaSjose borrego 		}
1139c8ec8eeaSjose borrego 		if (lk_start > end) {
1140c8ec8eeaSjose borrego 			lk = smb_llist_next(llist_head, lk);
1141c8ec8eeaSjose borrego 			continue;
1142c8ec8eeaSjose borrego 		}
1143c8ec8eeaSjose borrego 
1144c8ec8eeaSjose borrego 		/* this range is completely locked */
1145c8ec8eeaSjose borrego 		if ((lk_start <= start) && (lk_end >= end)) {
1146c8ec8eeaSjose borrego 			return (B_FALSE);
1147c8ec8eeaSjose borrego 		}
1148c8ec8eeaSjose borrego 
1149c8ec8eeaSjose borrego 		/* the first part of this range is locked */
1150c8ec8eeaSjose borrego 		if ((start >= lk_start) && (start <= lk_end)) {
1151c8ec8eeaSjose borrego 			if (end > lk_end)
1152c8ec8eeaSjose borrego 				*new_mark = lk_end + 1;
1153c8ec8eeaSjose borrego 			return (B_FALSE);
1154c8ec8eeaSjose borrego 		}
1155c8ec8eeaSjose borrego 
1156c8ec8eeaSjose borrego 		/* this piece is unlocked */
1157c8ec8eeaSjose borrego 		if ((lk_start >= start) && (lk_start <= end)) {
1158c8ec8eeaSjose borrego 			if (low_water_mark > lk_start)
1159c8ec8eeaSjose borrego 				low_water_mark  = lk_start;
1160c8ec8eeaSjose borrego 		}
1161c8ec8eeaSjose borrego 
1162c8ec8eeaSjose borrego 		lk = smb_llist_next(llist_head, lk);
1163c8ec8eeaSjose borrego 	}
1164c8ec8eeaSjose borrego 
1165c8ec8eeaSjose borrego 	if (low_water_mark != MAXOFFSET_T) {
1166c8ec8eeaSjose borrego 		*new_mark = low_water_mark;
1167c8ec8eeaSjose borrego 		return (B_TRUE);
1168c8ec8eeaSjose borrego 	}
1169c8ec8eeaSjose borrego 	/* the range is completely unlocked */
1170c8ec8eeaSjose borrego 	return (B_TRUE);
1171c8ec8eeaSjose borrego }
11720897f7fbSGordon Ross 
11730897f7fbSGordon Ross #ifdef	DEBUG
11740897f7fbSGordon Ross static void
smb_lock_dump1(smb_lock_t * lock)11750897f7fbSGordon Ross smb_lock_dump1(smb_lock_t *lock)
11760897f7fbSGordon Ross {
11770897f7fbSGordon Ross 	cmn_err(CE_CONT, "\t0x%p: 0x%llx, 0x%llx, %p, %d\n",
11780897f7fbSGordon Ross 	    (void *)lock,
11790897f7fbSGordon Ross 	    (long long)lock->l_start,
11800897f7fbSGordon Ross 	    (long long)lock->l_length,
11810897f7fbSGordon Ross 	    (void *)lock->l_file,
11820897f7fbSGordon Ross 	    lock->l_pid);
11830897f7fbSGordon Ross 
11840897f7fbSGordon Ross }
11850897f7fbSGordon Ross 
11860897f7fbSGordon Ross static void
smb_lock_dumplist(smb_llist_t * llist)11870897f7fbSGordon Ross smb_lock_dumplist(smb_llist_t *llist)
11880897f7fbSGordon Ross {
11890897f7fbSGordon Ross 	smb_lock_t *lock;
11900897f7fbSGordon Ross 
11910897f7fbSGordon Ross 	for (lock = smb_llist_head(llist);
11920897f7fbSGordon Ross 	    lock != NULL;
11930897f7fbSGordon Ross 	    lock = smb_llist_next(llist, lock)) {
11940897f7fbSGordon Ross 		smb_lock_dump1(lock);
11950897f7fbSGordon Ross 	}
11960897f7fbSGordon Ross }
11970897f7fbSGordon Ross 
11980897f7fbSGordon Ross static void
smb_lock_dumpnode(smb_node_t * node)11990897f7fbSGordon Ross smb_lock_dumpnode(smb_node_t *node)
12000897f7fbSGordon Ross {
12010897f7fbSGordon Ross 	cmn_err(CE_CONT, "Granted Locks on %p (%d)\n",
12020897f7fbSGordon Ross 	    (void *)node, node->n_lock_list.ll_count);
12030897f7fbSGordon Ross 	smb_lock_dumplist(&node->n_lock_list);
12040897f7fbSGordon Ross 
12050897f7fbSGordon Ross 	cmn_err(CE_CONT, "Waiting Locks on %p (%d)\n",
12060897f7fbSGordon Ross 	    (void *)node, node->n_wlock_list.ll_count);
12070897f7fbSGordon Ross 	smb_lock_dumplist(&node->n_wlock_list);
12080897f7fbSGordon Ross }
12090897f7fbSGordon Ross 
12100897f7fbSGordon Ross #endif
1211