Print this page
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-15578 SMB2 durable handle redesign
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5665 SMB2 oplock leases
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-8841 SMB2 lock request fails
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-6041 Should pass the smbtorture lock tests
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-5555 smb locks don't need l_uid or l_session_kid
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-4391 improve smb cancel support
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)
SMB-50 User-mode SMB server
Includes work by these authors:
Thomas Keiser <thomas.keiser@nexenta.com>
Albert Lee <trisk@nexenta.com>
SMB-65 SMB server in non-global zones (use zone_kcred())
SUP-599 smb_oplock_acquire thread deadlock
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/uts/common/fs/smbsrv/smb_lock.c
+++ new/usr/src/uts/common/fs/smbsrv/smb_lock.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
|
↓ open down ↓ |
12 lines elided |
↑ open up ↑ |
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 - * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
23 + * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
24 24 */
25 25
26 26 /*
27 27 * This module provides range lock functionality for CIFS/SMB clients.
28 28 * Lock range service functions process SMB lock and and unlock
29 29 * requests for a file by applying lock rules and marks file range
30 30 * as locked if the lock is successful otherwise return proper
31 31 * error code.
32 32 */
33 33
34 34 #include <smbsrv/smb_kproto.h>
35 35 #include <smbsrv/smb_fsops.h>
36 36 #include <sys/nbmlock.h>
37 37 #include <sys/param.h>
38 38
39 39 extern caller_context_t smb_ct;
40 40
41 +#ifdef DEBUG
42 +int smb_lock_debug = 0;
43 +static void smb_lock_dump1(smb_lock_t *);
44 +static void smb_lock_dumplist(smb_llist_t *);
45 +static void smb_lock_dumpnode(smb_node_t *);
46 +#endif
47 +
41 48 static void smb_lock_posix_unlock(smb_node_t *, smb_lock_t *, cred_t *);
42 49 static boolean_t smb_is_range_unlocked(uint64_t, uint64_t, uint32_t,
43 50 smb_llist_t *, uint64_t *);
44 51 static int smb_lock_range_overlap(smb_lock_t *, uint64_t, uint64_t);
45 -static uint32_t smb_lock_range_lckrules(smb_request_t *, smb_ofile_t *,
46 - smb_node_t *, smb_lock_t *, smb_lock_t **);
47 -static clock_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *);
48 -static uint32_t smb_lock_range_ulckrules(smb_request_t *, smb_node_t *,
49 - uint64_t, uint64_t, smb_lock_t **nodelock);
52 +static uint32_t smb_lock_range_lckrules(smb_ofile_t *, smb_lock_t *,
53 + smb_lock_t **);
54 +static uint32_t smb_lock_wait(smb_request_t *, smb_lock_t *, smb_lock_t *);
55 +static uint32_t smb_lock_range_ulckrules(smb_ofile_t *,
56 + uint64_t, uint64_t, uint32_t, smb_lock_t **);
50 57 static smb_lock_t *smb_lock_create(smb_request_t *, uint64_t, uint64_t,
51 - uint32_t, uint32_t);
58 + uint32_t, uint32_t, uint32_t);
52 59 static void smb_lock_destroy(smb_lock_t *);
53 60 static void smb_lock_free(smb_lock_t *);
54 61
55 62 /*
56 63 * Return the number of range locks on the specified ofile.
57 64 */
58 65 uint32_t
59 66 smb_lock_get_lock_count(smb_node_t *node, smb_ofile_t *of)
60 67 {
61 68 smb_lock_t *lock;
62 69 smb_llist_t *llist;
63 70 uint32_t count = 0;
64 71
65 72 SMB_NODE_VALID(node);
66 73 SMB_OFILE_VALID(of);
67 74
68 75 llist = &node->n_lock_list;
69 76
70 77 smb_llist_enter(llist, RW_READER);
71 78 for (lock = smb_llist_head(llist);
72 79 lock != NULL;
73 80 lock = smb_llist_next(llist, lock)) {
74 81 if (lock->l_file == of)
75 82 ++count;
76 83 }
77 84 smb_llist_exit(llist);
78 85
79 86 return (count);
80 87 }
81 88
82 89 /*
|
↓ open down ↓ |
21 lines elided |
↑ open up ↑ |
83 90 * smb_unlock_range
84 91 *
85 92 * locates lock range performed for corresponding to unlock request.
86 93 *
87 94 * NT_STATUS_SUCCESS - Lock range performed successfully.
88 95 * !NT_STATUS_SUCCESS - Error in unlock range operation.
89 96 */
90 97 uint32_t
91 98 smb_unlock_range(
92 99 smb_request_t *sr,
93 - smb_node_t *node,
94 100 uint64_t start,
95 - uint64_t length)
101 + uint64_t length,
102 + uint32_t pid)
96 103 {
104 + smb_ofile_t *file = sr->fid_ofile;
105 + smb_node_t *node = file->f_node;
97 106 smb_lock_t *lock = NULL;
98 107 uint32_t status;
99 108
109 + if (length > 1 &&
110 + (start + length) < start)
111 + return (NT_STATUS_INVALID_LOCK_RANGE);
112 +
113 +#ifdef DEBUG
114 + if (smb_lock_debug) {
115 + cmn_err(CE_CONT, "smb_unlock_range "
116 + "off=0x%llx, len=0x%llx, f=%p, pid=%d\n",
117 + (long long)start, (long long)length,
118 + (void *)sr->fid_ofile, pid);
119 + }
120 +#endif
121 +
100 122 /* Apply unlocking rules */
101 123 smb_llist_enter(&node->n_lock_list, RW_WRITER);
102 - status = smb_lock_range_ulckrules(sr, node, start, length, &lock);
124 + status = smb_lock_range_ulckrules(file, start, length, pid, &lock);
103 125 if (status != NT_STATUS_SUCCESS) {
104 126 /*
105 127 * If lock range is not matching in the list
106 128 * return error.
107 129 */
108 130 ASSERT(lock == NULL);
109 - smb_llist_exit(&node->n_lock_list);
110 - return (status);
111 131 }
132 + if (lock != NULL) {
133 + smb_llist_remove(&node->n_lock_list, lock);
134 + smb_lock_posix_unlock(node, lock, sr->user_cr);
135 + }
112 136
113 - smb_llist_remove(&node->n_lock_list, lock);
114 - smb_lock_posix_unlock(node, lock, sr->user_cr);
137 +#ifdef DEBUG
138 + if (smb_lock_debug && lock == NULL) {
139 + cmn_err(CE_CONT, "unlock failed, 0x%x\n", status);
140 + smb_lock_dumpnode(node);
141 + }
142 +#endif
143 +
115 144 smb_llist_exit(&node->n_lock_list);
116 - smb_lock_destroy(lock);
117 145
146 + if (lock != NULL)
147 + smb_lock_destroy(lock);
148 +
118 149 return (status);
119 150 }
120 151
121 152 /*
122 153 * smb_lock_range
123 154 *
124 155 * Checks for integrity of file lock operation for the given range of file data.
125 156 * This is performed by applying lock rules with all the elements of the node
126 157 * lock list.
127 158 *
128 159 * Break shared (levelII) oplocks. If there is an exclusive oplock, it is
129 160 * owned by this ofile and therefore should not be broken.
130 161 *
131 162 * The function returns with new lock added if lock request is non-conflicting
132 163 * with existing range lock for the file. Otherwise smb request is filed
|
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
133 164 * without returning.
134 165 *
135 166 * NT_STATUS_SUCCESS - Lock range performed successfully.
136 167 * !NT_STATUS_SUCCESS - Error in lock range operation.
137 168 */
138 169 uint32_t
139 170 smb_lock_range(
140 171 smb_request_t *sr,
141 172 uint64_t start,
142 173 uint64_t length,
143 - uint32_t timeout,
144 - uint32_t locktype)
174 + uint32_t pid,
175 + uint32_t locktype,
176 + uint32_t timeout)
145 177 {
146 178 smb_ofile_t *file = sr->fid_ofile;
147 179 smb_node_t *node = file->f_node;
148 180 smb_lock_t *lock;
149 - smb_lock_t *clock = NULL;
150 - uint32_t result = NT_STATUS_SUCCESS;
181 + smb_lock_t *conflict = NULL;
182 + uint32_t result;
183 + int rc;
151 184 boolean_t lock_has_timeout =
152 185 (timeout != 0 && timeout != UINT_MAX);
153 186
154 - lock = smb_lock_create(sr, start, length, locktype, timeout);
187 + if (length > 1 &&
188 + (start + length) < start)
189 + return (NT_STATUS_INVALID_LOCK_RANGE);
155 190
191 +#ifdef DEBUG
192 + if (smb_lock_debug) {
193 + cmn_err(CE_CONT, "smb_lock_range "
194 + "off=0x%llx, len=0x%llx, "
195 + "f=%p, pid=%d, typ=%d, tmo=%d\n",
196 + (long long)start, (long long)length,
197 + (void *)sr->fid_ofile, pid, locktype, timeout);
198 + }
199 +#endif
200 +
201 + lock = smb_lock_create(sr, start, length, pid, locktype, timeout);
202 +
156 203 smb_llist_enter(&node->n_lock_list, RW_WRITER);
157 204 for (;;) {
158 - clock_t rc;
159 205
160 206 /* Apply locking rules */
161 - result = smb_lock_range_lckrules(sr, file, node, lock, &clock);
162 -
163 - if ((result == NT_STATUS_CANCELLED) ||
164 - (result == NT_STATUS_SUCCESS) ||
165 - (result == NT_STATUS_RANGE_NOT_LOCKED)) {
166 - ASSERT(clock == NULL);
207 + result = smb_lock_range_lckrules(file, lock, &conflict);
208 + switch (result) {
209 + case NT_STATUS_LOCK_NOT_GRANTED: /* conflict! */
210 + /* may need to wait */
167 211 break;
168 - } else if (timeout == 0) {
169 - break;
212 + case NT_STATUS_SUCCESS:
213 + case NT_STATUS_FILE_CLOSED:
214 + goto break_loop;
215 + default:
216 + cmn_err(CE_CONT, "smb_lock_range1, status 0x%x\n",
217 + result);
218 + goto break_loop;
170 219 }
220 + if (timeout == 0)
221 + goto break_loop;
171 222
172 - ASSERT(result == NT_STATUS_LOCK_NOT_GRANTED);
173 - ASSERT(clock);
174 223 /*
175 224 * Call smb_lock_wait holding write lock for
176 225 * node lock list. smb_lock_wait will release
177 - * this lock if it blocks.
226 + * the node list lock if it blocks, so after
227 + * the call, (*conflict) may no longer exist.
178 228 */
179 - ASSERT(node == clock->l_file->f_node);
180 -
181 - rc = smb_lock_wait(sr, lock, clock);
182 - if (rc == 0) {
183 - result = NT_STATUS_CANCELLED;
229 + result = smb_lock_wait(sr, lock, conflict);
230 + conflict = NULL;
231 + switch (result) {
232 + case NT_STATUS_SUCCESS:
233 + /* conflict gone, try again */
184 234 break;
185 - }
186 - if (rc == -1)
235 + case NT_STATUS_TIMEOUT:
236 + /* try just once more */
187 237 timeout = 0;
188 -
189 - clock = NULL;
238 + break;
239 + case NT_STATUS_CANCELLED:
240 + case NT_STATUS_FILE_CLOSED:
241 + goto break_loop;
242 + default:
243 + cmn_err(CE_CONT, "smb_lock_range2, status 0x%x\n",
244 + result);
245 + goto break_loop;
246 + }
190 247 }
191 248
249 +break_loop:
192 250 lock->l_blocked_by = NULL;
193 251
194 252 if (result != NT_STATUS_SUCCESS) {
253 + if (result == NT_STATUS_FILE_CLOSED)
254 + result = NT_STATUS_RANGE_NOT_LOCKED;
255 +
195 256 /*
196 257 * Under certain conditions NT_STATUS_FILE_LOCK_CONFLICT
197 258 * should be returned instead of NT_STATUS_LOCK_NOT_GRANTED.
198 259 * All of this appears to be specific to SMB1
199 260 */
200 261 if (sr->session->dialect <= NT_LM_0_12 &&
201 262 result == NT_STATUS_LOCK_NOT_GRANTED) {
202 263 /*
203 264 * Locks with timeouts always return
204 265 * NT_STATUS_FILE_LOCK_CONFLICT
205 266 */
206 267 if (lock_has_timeout)
207 268 result = NT_STATUS_FILE_LOCK_CONFLICT;
208 269
209 270 /*
210 271 * Locks starting higher than 0xef000000 that do not
211 272 * have the MSB set always return
212 273 * NT_STATUS_FILE_LOCK_CONFLICT
213 274 */
214 275 if ((lock->l_start >= 0xef000000) &&
215 276 !(lock->l_start & (1ULL << 63))) {
216 277 result = NT_STATUS_FILE_LOCK_CONFLICT;
217 278 }
218 279
219 280 /*
220 281 * If the last lock attempt to fail on this file handle
221 282 * started at the same offset as this one then return
222 283 * NT_STATUS_FILE_LOCK_CONFLICT
223 284 */
224 285 mutex_enter(&file->f_mutex);
225 286 if ((file->f_flags & SMB_OFLAGS_LLF_POS_VALID) &&
226 287 (lock->l_start == file->f_llf_pos)) {
227 288 result = NT_STATUS_FILE_LOCK_CONFLICT;
228 289 }
229 290 mutex_exit(&file->f_mutex);
230 291 }
231 292
232 293 /* Update last lock failed offset */
233 294 mutex_enter(&file->f_mutex);
|
↓ open down ↓ |
29 lines elided |
↑ open up ↑ |
234 295 file->f_llf_pos = lock->l_start;
235 296 file->f_flags |= SMB_OFLAGS_LLF_POS_VALID;
236 297 mutex_exit(&file->f_mutex);
237 298
238 299 smb_lock_free(lock);
239 300 } else {
240 301 /*
241 302 * don't insert into the CIFS lock list unless the
242 303 * posix lock worked
243 304 */
244 - if (smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr))
305 + rc = smb_fsop_frlock(node, lock, B_FALSE, sr->user_cr);
306 + if (rc != 0) {
307 +#ifdef DEBUG
308 + if (smb_lock_debug)
309 + cmn_err(CE_CONT, "fop_frlock, err=%d\n", rc);
310 +#endif
245 311 result = NT_STATUS_FILE_LOCK_CONFLICT;
246 - else
247 - smb_llist_insert_tail(&node->n_lock_list, lock);
312 + } else {
313 + /*
314 + * We want unlock to find exclusive locks before
315 + * shared locks, so insert those at the head.
316 + */
317 + if (lock->l_type == SMB_LOCK_TYPE_READWRITE)
318 + smb_llist_insert_head(&node->n_lock_list, lock);
319 + else
320 + smb_llist_insert_tail(&node->n_lock_list, lock);
321 + }
248 322 }
323 +
324 +#ifdef DEBUG
325 + if (smb_lock_debug && result != 0) {
326 + cmn_err(CE_CONT, "lock failed, 0x%x\n", result);
327 + smb_lock_dumpnode(node);
328 + }
329 +#endif
330 +
249 331 smb_llist_exit(&node->n_lock_list);
250 332
251 - if (result == NT_STATUS_SUCCESS)
252 - smb_oplock_break_levelII(node);
333 + if (result == NT_STATUS_SUCCESS) {
334 + /* This revokes read cache delegations. */
335 + (void) smb_oplock_break_WRITE(node, file);
336 + }
253 337
254 338 return (result);
255 339 }
256 340
257 -
258 341 /*
259 342 * smb_lock_range_access
260 343 *
261 344 * scans node lock list
262 345 * to check if there is any overlapping lock. Overlapping
263 346 * lock is allowed only under same session and client pid.
264 347 *
265 348 * Return values
266 349 * NT_STATUS_SUCCESS lock access granted.
267 350 * NT_STATUS_FILE_LOCK_CONFLICT access denied due to lock conflict.
268 351 */
269 352 int
270 353 smb_lock_range_access(
271 354 smb_request_t *sr,
272 355 smb_node_t *node,
273 356 uint64_t start,
274 - uint64_t length, /* zero means to EoF */
357 + uint64_t length,
275 358 boolean_t will_write)
276 359 {
277 360 smb_lock_t *lock;
278 361 smb_llist_t *llist;
362 + uint32_t lk_pid = 0;
279 363 int status = NT_STATUS_SUCCESS;
280 364
365 + if (length == 0)
366 + return (status);
367 +
368 + /*
369 + * What PID to use for lock conflict checks?
370 + * SMB2 locking ignores PIDs (have lk_pid=0)
371 + * SMB1 uses low 16 bits of sr->smb_pid
372 + */
373 + if (sr->session->dialect < SMB_VERS_2_BASE)
374 + lk_pid = sr->smb_pid & 0xFFFF;
375 +
281 376 llist = &node->n_lock_list;
282 377 smb_llist_enter(llist, RW_READER);
283 378 /* Search for any applicable lock */
284 379 for (lock = smb_llist_head(llist);
285 380 lock != NULL;
286 381 lock = smb_llist_next(llist, lock)) {
287 382
288 383 if (!smb_lock_range_overlap(lock, start, length))
289 384 /* Lock does not overlap */
290 385 continue;
291 386
292 387 if (lock->l_type == SMB_LOCK_TYPE_READONLY && !will_write)
293 388 continue;
294 389
295 390 if (lock->l_type == SMB_LOCK_TYPE_READWRITE &&
296 - lock->l_session_kid == sr->session->s_kid &&
297 - lock->l_pid == sr->smb_pid)
391 + lock->l_file == sr->fid_ofile &&
392 + lock->l_pid == lk_pid)
298 393 continue;
299 394
395 +#ifdef DEBUG
396 + if (smb_lock_debug) {
397 + cmn_err(CE_CONT, "smb_lock_range_access conflict: "
398 + "off=0x%llx, len=0x%llx, "
399 + "f=%p, pid=%d, typ=%d\n",
400 + (long long)lock->l_start,
401 + (long long)lock->l_length,
402 + (void *)lock->l_file,
403 + lock->l_pid, lock->l_type);
404 + }
405 +#endif
300 406 status = NT_STATUS_FILE_LOCK_CONFLICT;
301 407 break;
302 408 }
303 409 smb_llist_exit(llist);
304 410 return (status);
305 411 }
306 412
413 +/*
414 + * The ofile is being closed. Wake any waiting locks and
415 + * clear any granted locks.
416 + */
307 417 void
308 418 smb_node_destroy_lock_by_ofile(smb_node_t *node, smb_ofile_t *file)
309 419 {
420 + cred_t *kcr = zone_kcred();
310 421 smb_lock_t *lock;
311 422 smb_lock_t *nxtl;
312 423 list_t destroy_list;
313 424
314 425 SMB_NODE_VALID(node);
315 426 ASSERT(node->n_refcnt);
316 427
317 428 /*
429 + * Cancel any waiting locks for this ofile
430 + */
431 + smb_llist_enter(&node->n_wlock_list, RW_READER);
432 + for (lock = smb_llist_head(&node->n_wlock_list);
433 + lock != NULL;
434 + lock = smb_llist_next(&node->n_wlock_list, lock)) {
435 +
436 + if (lock->l_file == file) {
437 + mutex_enter(&lock->l_mutex);
438 + lock->l_blocked_by = NULL;
439 + lock->l_flags |= SMB_LOCK_FLAG_CLOSED;
440 + cv_broadcast(&lock->l_cv);
441 + mutex_exit(&lock->l_mutex);
442 + }
443 + }
444 + smb_llist_exit(&node->n_wlock_list);
445 +
446 + /*
318 447 * Move locks matching the specified file from the node->n_lock_list
319 448 * to a temporary list (holding the lock the entire time) then
320 449 * destroy all the matching locks. We can't call smb_lock_destroy
321 450 * while we are holding the lock for node->n_lock_list because we will
322 451 * deadlock and we can't drop the lock because the list contents might
323 452 * change (for example nxtl might get removed on another thread).
324 453 */
325 454 list_create(&destroy_list, sizeof (smb_lock_t),
326 455 offsetof(smb_lock_t, l_lnd));
327 456
328 457 smb_llist_enter(&node->n_lock_list, RW_WRITER);
329 458 lock = smb_llist_head(&node->n_lock_list);
330 459 while (lock) {
331 460 nxtl = smb_llist_next(&node->n_lock_list, lock);
332 461 if (lock->l_file == file) {
333 462 smb_llist_remove(&node->n_lock_list, lock);
334 - smb_lock_posix_unlock(node, lock, file->f_user->u_cred);
463 + smb_lock_posix_unlock(node, lock, kcr);
335 464 list_insert_tail(&destroy_list, lock);
336 465 }
337 466 lock = nxtl;
338 467 }
339 468 smb_llist_exit(&node->n_lock_list);
340 469
341 470 lock = list_head(&destroy_list);
342 471 while (lock) {
343 472 nxtl = list_next(&destroy_list, lock);
344 473 list_remove(&destroy_list, lock);
345 474 smb_lock_destroy(lock);
346 475 lock = nxtl;
347 476 }
348 477
349 478 list_destroy(&destroy_list);
350 479 }
351 480
481 +/*
482 + * Cause a waiting lock to stop waiting and return an error.
483 + * returns same status codes as unlock:
484 + * NT_STATUS_SUCCESS, NT_STATUS_RANGE_NOT_LOCKED
485 + */
486 +uint32_t
487 +smb_lock_range_cancel(smb_request_t *sr,
488 + uint64_t start, uint64_t length, uint32_t pid)
489 +{
490 + smb_node_t *node;
491 + smb_lock_t *lock;
492 + uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
493 + int cnt = 0;
494 +
495 + node = sr->fid_ofile->f_node;
496 +
497 + smb_llist_enter(&node->n_wlock_list, RW_READER);
498 +
499 +#ifdef DEBUG
500 + if (smb_lock_debug) {
501 + cmn_err(CE_CONT, "smb_lock_range_cancel:\n"
502 + "\tstart=0x%llx, len=0x%llx, of=%p, pid=%d\n",
503 + (long long)start, (long long)length,
504 + (void *)sr->fid_ofile, pid);
505 + }
506 +#endif
507 +
508 + for (lock = smb_llist_head(&node->n_wlock_list);
509 + lock != NULL;
510 + lock = smb_llist_next(&node->n_wlock_list, lock)) {
511 +
512 + if ((start == lock->l_start) &&
513 + (length == lock->l_length) &&
514 + lock->l_file == sr->fid_ofile &&
515 + lock->l_pid == pid) {
516 +
517 + mutex_enter(&lock->l_mutex);
518 + lock->l_blocked_by = NULL;
519 + lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
520 + cv_broadcast(&lock->l_cv);
521 + mutex_exit(&lock->l_mutex);
522 + status = NT_STATUS_SUCCESS;
523 + cnt++;
524 + }
525 + }
526 +
527 +#ifdef DEBUG
528 + if (smb_lock_debug && cnt != 1) {
529 + cmn_err(CE_CONT, "cancel found %d\n", cnt);
530 + smb_lock_dumpnode(node);
531 + }
532 +#endif
533 +
534 + smb_llist_exit(&node->n_wlock_list);
535 +
536 + return (status);
537 +}
538 +
352 539 void
353 540 smb_lock_range_error(smb_request_t *sr, uint32_t status32)
354 541 {
355 542 uint16_t errcode;
356 543
357 - if (status32 == NT_STATUS_CANCELLED)
358 - errcode = ERROR_OPERATION_ABORTED;
359 - else
544 + if (status32 == NT_STATUS_CANCELLED) {
545 + status32 = NT_STATUS_FILE_LOCK_CONFLICT;
546 + errcode = ERROR_LOCK_VIOLATION;
547 + } else {
360 548 errcode = ERRlock;
549 + }
361 550
362 551 smbsr_error(sr, status32, ERRDOS, errcode);
363 552 }
364 553
365 554 /*
366 555 * An SMB variant of nbl_conflict().
367 556 *
368 557 * SMB prevents remove or rename when conflicting locks exist
369 558 * (unlike NFS, which is why we can't just use nbl_conflict).
370 559 *
371 560 * Returns:
372 561 * NT_STATUS_SHARING_VIOLATION - nbl_share_conflict
373 562 * NT_STATUS_FILE_LOCK_CONFLICT - nbl_lock_conflict
374 563 * NT_STATUS_SUCCESS - operation can proceed
375 564 *
376 565 * NB: This function used to also check the list of ofiles,
377 566 * via: smb_lock_range_access() but we _can't_ do that here
378 567 * due to lock order constraints between node->n_lock_list
379 568 * and node->vp->vnbllock (taken via nvl_start_crit).
380 569 * They must be taken in that order, and in here, we
381 570 * already hold vp->vnbllock.
382 571 */
383 572 DWORD
384 573 smb_nbl_conflict(smb_node_t *node, uint64_t off, uint64_t len, nbl_op_t op)
385 574 {
386 575 int svmand;
387 576
388 577 SMB_NODE_VALID(node);
389 578 ASSERT(smb_node_in_crit(node));
390 579 ASSERT(op == NBL_READ || op == NBL_WRITE || op == NBL_READWRITE ||
391 580 op == NBL_REMOVE || op == NBL_RENAME);
392 581
393 582 if (smb_node_is_dir(node))
394 583 return (NT_STATUS_SUCCESS);
395 584
396 585 if (nbl_share_conflict(node->vp, op, &smb_ct))
397 586 return (NT_STATUS_SHARING_VIOLATION);
398 587
399 588 /*
400 589 * When checking for lock conflicts, rename and remove
401 590 * are not allowed, so treat those as read/write.
402 591 */
403 592 if (op == NBL_RENAME || op == NBL_REMOVE)
404 593 op = NBL_READWRITE;
405 594
406 595 if (nbl_svmand(node->vp, zone_kcred(), &svmand))
407 596 svmand = 1;
408 597
409 598 if (nbl_lock_conflict(node->vp, op, off, len, svmand, &smb_ct))
410 599 return (NT_STATUS_FILE_LOCK_CONFLICT);
411 600
412 601 return (NT_STATUS_SUCCESS);
413 602 }
414 603
415 604 /*
416 605 * smb_lock_posix_unlock
417 606 *
418 607 * checks if the current unlock request is in another lock and repeatedly calls
419 608 * smb_is_range_unlocked on a sliding basis to unlock all bits of the lock
420 609 * that are not in other locks
421 610 *
422 611 */
423 612 static void
424 613 smb_lock_posix_unlock(smb_node_t *node, smb_lock_t *lock, cred_t *cr)
425 614 {
426 615 uint64_t new_mark;
427 616 uint64_t unlock_start;
428 617 uint64_t unlock_end;
429 618 smb_lock_t new_unlock;
430 619 smb_llist_t *llist;
431 620 boolean_t can_unlock;
432 621
433 622 new_mark = 0;
434 623 unlock_start = lock->l_start;
435 624 unlock_end = unlock_start + lock->l_length;
436 625 llist = &node->n_lock_list;
437 626
438 627 for (;;) {
439 628 can_unlock = smb_is_range_unlocked(unlock_start, unlock_end,
440 629 lock->l_file->f_uniqid, llist, &new_mark);
441 630 if (can_unlock) {
442 631 if (new_mark) {
443 632 new_unlock = *lock;
444 633 new_unlock.l_start = unlock_start;
445 634 new_unlock.l_length = new_mark - unlock_start;
446 635 (void) smb_fsop_frlock(node, &new_unlock,
447 636 B_TRUE, cr);
448 637 unlock_start = new_mark;
449 638 } else {
450 639 new_unlock = *lock;
451 640 new_unlock.l_start = unlock_start;
452 641 new_unlock.l_length = unlock_end - unlock_start;
453 642 (void) smb_fsop_frlock(node, &new_unlock,
454 643 B_TRUE, cr);
455 644 break;
456 645 }
457 646 } else if (new_mark) {
458 647 unlock_start = new_mark;
459 648 } else {
460 649 break;
461 650 }
462 651 }
463 652 }
464 653
465 654 /*
466 655 * smb_lock_range_overlap
467 656 *
468 657 * Checks if lock range(start, length) overlaps range in lock structure.
469 658 *
470 659 * Zero-length byte range locks actually affect no single byte of the stream,
471 660 * meaning they can still be accessed even with such locks in place. However,
472 661 * they do conflict with other ranges in the following manner:
473 662 * conflict will only exist if the positive-length range contains the
474 663 * zero-length range's offset but doesn't start at it
475 664 *
476 665 * return values:
477 666 * 0 - Lock range doesn't overlap
478 667 * 1 - Lock range overlaps.
479 668 */
480 669
481 670 #define RANGE_NO_OVERLAP 0
482 671 #define RANGE_OVERLAP 1
483 672
484 673 static int
485 674 smb_lock_range_overlap(struct smb_lock *lock, uint64_t start, uint64_t length)
486 675 {
487 676 if (length == 0) {
488 677 if ((lock->l_start < start) &&
489 678 ((lock->l_start + lock->l_length) > start))
490 679 return (RANGE_OVERLAP);
491 680
492 681 return (RANGE_NO_OVERLAP);
493 682 }
494 683
495 684 /* The following test is intended to catch roll over locks. */
496 685 if ((start == lock->l_start) && (length == lock->l_length))
497 686 return (RANGE_OVERLAP);
498 687
499 688 if (start < lock->l_start) {
500 689 if (start + length > lock->l_start)
501 690 return (RANGE_OVERLAP);
502 691 } else if (start < lock->l_start + lock->l_length)
503 692 return (RANGE_OVERLAP);
504 693
505 694 return (RANGE_NO_OVERLAP);
506 695 }
|
↓ open down ↓ |
136 lines elided |
↑ open up ↑ |
507 696
508 697 /*
509 698 * smb_lock_range_lckrules
510 699 *
511 700 * Lock range rules:
512 701 * 1. Overlapping read locks are allowed if the
513 702 * current locks in the region are only read locks
514 703 * irrespective of pid of smb client issuing lock request.
515 704 *
516 705 * 2. Read lock in the overlapped region of write lock
517 - * are allowed if the pervious lock is performed by the
706 + * are allowed if the previous lock is performed by the
518 707 * same pid and connection.
519 708 *
520 709 * return status:
521 - * NT_STATUS_SUCCESS - Input lock range adapts to lock rules.
710 + * NT_STATUS_SUCCESS - Input lock range conforms to lock rules.
522 711 * NT_STATUS_LOCK_NOT_GRANTED - Input lock conflicts lock rules.
523 - * NT_STATUS_CANCELLED - Error in processing lock rules
712 + * NT_STATUS_FILE_CLOSED
524 713 */
525 714 static uint32_t
526 715 smb_lock_range_lckrules(
527 - smb_request_t *sr,
528 716 smb_ofile_t *file,
529 - smb_node_t *node,
530 - smb_lock_t *dlock,
531 - smb_lock_t **clockp)
717 + smb_lock_t *dlock, /* desired lock */
718 + smb_lock_t **conflictp)
532 719 {
720 + smb_node_t *node = file->f_node;
533 721 smb_lock_t *lock;
534 722 uint32_t status = NT_STATUS_SUCCESS;
535 723
536 724 /* Check if file is closed */
537 725 if (!smb_ofile_is_open(file)) {
538 - return (NT_STATUS_RANGE_NOT_LOCKED);
726 + return (NT_STATUS_FILE_CLOSED);
539 727 }
540 728
541 729 /* Caller must hold lock for node->n_lock_list */
542 730 for (lock = smb_llist_head(&node->n_lock_list);
543 731 lock != NULL;
544 732 lock = smb_llist_next(&node->n_lock_list, lock)) {
545 733
546 734 if (!smb_lock_range_overlap(lock, dlock->l_start,
547 735 dlock->l_length))
548 736 continue;
549 737
550 738 /*
551 739 * Check to see if lock in the overlapping record
552 740 * is only read lock. Current finding is read
553 741 * locks can overlapped irrespective of pids.
554 742 */
555 743 if ((lock->l_type == SMB_LOCK_TYPE_READONLY) &&
|
↓ open down ↓ |
7 lines elided |
↑ open up ↑ |
556 744 (dlock->l_type == SMB_LOCK_TYPE_READONLY)) {
557 745 continue;
558 746 }
559 747
560 748 /*
561 749 * When the read lock overlaps write lock, check if
562 750 * allowed.
563 751 */
564 752 if ((dlock->l_type == SMB_LOCK_TYPE_READONLY) &&
565 753 !(lock->l_type == SMB_LOCK_TYPE_READONLY)) {
566 - if (lock->l_file == sr->fid_ofile &&
567 - lock->l_session_kid == sr->session->s_kid &&
568 - lock->l_pid == sr->smb_pid &&
569 - lock->l_uid == sr->smb_uid) {
754 + if (lock->l_file == dlock->l_file &&
755 + lock->l_pid == dlock->l_pid) {
570 756 continue;
571 757 }
572 758 }
573 759
574 760 /* Conflict in overlapping lock element */
575 - *clockp = lock;
761 + *conflictp = lock;
576 762 status = NT_STATUS_LOCK_NOT_GRANTED;
577 763 break;
578 764 }
579 765
580 766 return (status);
581 767 }
582 768
583 769 /*
770 + * Cancel method for smb_lock_wait()
771 + *
772 + * This request is waiting on a lock. Wakeup everything
773 + * waiting on the lock so that the relevant thread regains
774 + * control and notices that is has been cancelled. The
775 + * other lock request threads waiting on this lock will go
776 + * back to sleep when they discover they are still blocked.
777 + */
778 +static void
779 +smb_lock_cancel_sr(smb_request_t *sr)
780 +{
781 + smb_lock_t *lock = sr->cancel_arg2;
782 +
783 + ASSERT(lock->l_magic == SMB_LOCK_MAGIC);
784 + mutex_enter(&lock->l_mutex);
785 + lock->l_blocked_by = NULL;
786 + lock->l_flags |= SMB_LOCK_FLAG_CANCELLED;
787 + cv_broadcast(&lock->l_cv);
788 + mutex_exit(&lock->l_mutex);
789 +}
790 +
791 +/*
584 792 * smb_lock_wait
585 793 *
586 794 * Wait operation for smb overlapping lock to be released. Caller must hold
587 795 * write lock for node->n_lock_list so that the set of active locks can't
588 796 * change unexpectedly. The lock for node->n_lock_list will be released
589 797 * within this function during the sleep after the lock dependency has
590 798 * been recorded.
591 799 *
592 - * return value
593 - *
594 - * 0 The request was canceled.
595 - * -1 The timeout was reached.
596 - * >0 Condition met.
800 + * Returns NT_STATUS_SUCCESS when the lock can be granted,
801 + * otherwise NT_STATUS_CANCELLED, etc.
597 802 */
598 -static clock_t
599 -smb_lock_wait(smb_request_t *sr, smb_lock_t *b_lock, smb_lock_t *c_lock)
803 +static uint32_t
804 +smb_lock_wait(smb_request_t *sr, smb_lock_t *lock, smb_lock_t *conflict)
600 805 {
601 - clock_t rc = 0;
806 + smb_node_t *node;
807 + clock_t rc;
808 + uint32_t status = NT_STATUS_SUCCESS;
602 809
603 - ASSERT(sr->sr_awaiting == NULL);
810 + node = lock->l_file->f_node;
811 + ASSERT(node == conflict->l_file->f_node);
604 812
605 - mutex_enter(&sr->sr_mutex);
813 + /*
814 + * Let the blocked lock (lock) l_blocked_by point to the
815 + * conflicting lock (conflict), and increment a count of
816 + * conflicts with the latter. When the conflicting lock
817 + * is destroyed, we'll search the list of waiting locks
818 + * (on the node) and wake any with l_blocked_by ==
819 + * the formerly conflicting lock.
820 + */
821 + mutex_enter(&lock->l_mutex);
822 + lock->l_blocked_by = conflict;
823 + mutex_exit(&lock->l_mutex);
606 824
607 - switch (sr->sr_state) {
608 - case SMB_REQ_STATE_ACTIVE:
609 - /*
610 - * Wait up till the timeout time keeping track of actual
611 - * time waited for possible retry failure.
612 - */
613 - sr->sr_state = SMB_REQ_STATE_WAITING_LOCK;
614 - sr->sr_awaiting = c_lock;
615 - mutex_exit(&sr->sr_mutex);
825 + mutex_enter(&conflict->l_mutex);
826 + conflict->l_conflicts++;
827 + mutex_exit(&conflict->l_mutex);
616 828
617 - mutex_enter(&c_lock->l_mutex);
618 - /*
619 - * The conflict list (l_conflict_list) for a lock contains
620 - * all the locks that are blocked by and in conflict with
621 - * that lock. Add the new lock to the conflict list for the
622 - * active lock.
623 - *
624 - * l_conflict_list is currently a fancy way of representing
625 - * the references/dependencies on a lock. It could be
626 - * replaced with a reference count but this approach
627 - * has the advantage that MDB can display the lock
628 - * dependencies at any point in time. In the future
629 - * we should be able to leverage the list to implement
630 - * an asynchronous locking model.
631 - *
632 - * l_blocked_by is the reverse of the conflict list. It
633 - * points to the lock that the new lock conflicts with.
634 - * As currently implemented this value is purely for
635 - * debug purposes -- there are windows of time when
636 - * l_blocked_by may be non-NULL even though there is no
637 - * conflict list
638 - */
639 - b_lock->l_blocked_by = c_lock;
640 - smb_slist_insert_tail(&c_lock->l_conflict_list, b_lock);
641 - smb_llist_exit(&c_lock->l_file->f_node->n_lock_list);
829 + /*
830 + * Put the blocked lock on the waiting list.
831 + */
832 + smb_llist_enter(&node->n_wlock_list, RW_WRITER);
833 + smb_llist_insert_tail(&node->n_wlock_list, lock);
834 + smb_llist_exit(&node->n_wlock_list);
642 835
643 - if (SMB_LOCK_INDEFINITE_WAIT(b_lock)) {
644 - cv_wait(&c_lock->l_cv, &c_lock->l_mutex);
645 - } else {
646 - rc = cv_timedwait(&c_lock->l_cv,
647 - &c_lock->l_mutex, b_lock->l_end_time);
648 - }
836 +#ifdef DEBUG
837 + if (smb_lock_debug) {
838 + cmn_err(CE_CONT, "smb_lock_wait: lock=%p conflict=%p\n",
839 + (void *)lock, (void *)conflict);
840 + smb_lock_dumpnode(node);
841 + }
842 +#endif
649 843
650 - mutex_exit(&c_lock->l_mutex);
844 + /*
845 + * We come in with n_lock_list already held, and keep
846 + * that hold until we're done with conflict (are now).
847 + * Drop that now, and retake later. Note that the lock
848 + * (*conflict) may go away once we exit this list.
849 + */
850 + smb_llist_exit(&node->n_lock_list);
851 + conflict = NULL;
651 852
652 - smb_llist_enter(&c_lock->l_file->f_node->n_lock_list,
653 - RW_WRITER);
654 - smb_slist_remove(&c_lock->l_conflict_list, b_lock);
853 + /*
854 + * Before we actually start waiting, setup the hooks
855 + * smb_request_cancel uses to unblock this wait.
856 + */
857 + mutex_enter(&sr->sr_mutex);
858 + if (sr->sr_state == SMB_REQ_STATE_ACTIVE) {
859 + sr->sr_state = SMB_REQ_STATE_WAITING_LOCK;
860 + sr->cancel_method = smb_lock_cancel_sr;
861 + sr->cancel_arg2 = lock;
862 + } else {
863 + status = NT_STATUS_CANCELLED;
864 + }
865 + mutex_exit(&sr->sr_mutex);
655 866
656 - mutex_enter(&sr->sr_mutex);
657 - sr->sr_awaiting = NULL;
658 - if (sr->sr_state == SMB_REQ_STATE_CANCELED) {
659 - rc = 0;
867 + /*
868 + * Now we're ready to actually wait for the conflicting
869 + * lock to be removed, or for the wait to be ended by
870 + * an external cancel, or a timeout.
871 + */
872 + mutex_enter(&lock->l_mutex);
873 + while (status == NT_STATUS_SUCCESS &&
874 + lock->l_blocked_by != NULL) {
875 + if (lock->l_flags & SMB_LOCK_FLAG_INDEFINITE) {
876 + cv_wait(&lock->l_cv, &lock->l_mutex);
660 877 } else {
661 - sr->sr_state = SMB_REQ_STATE_ACTIVE;
878 + rc = cv_timedwait(&lock->l_cv,
879 + &lock->l_mutex, lock->l_end_time);
880 + if (rc < 0)
881 + status = NT_STATUS_TIMEOUT;
662 882 }
883 + }
884 + if (status == NT_STATUS_SUCCESS) {
885 + if (lock->l_flags & SMB_LOCK_FLAG_CANCELLED)
886 + status = NT_STATUS_CANCELLED;
887 + if (lock->l_flags & SMB_LOCK_FLAG_CLOSED)
888 + status = NT_STATUS_FILE_CLOSED;
889 + }
890 + mutex_exit(&lock->l_mutex);
891 +
892 + /*
893 + * Done waiting. Cleanup cancel hooks and
894 + * finish SR state transitions.
895 + */
896 + mutex_enter(&sr->sr_mutex);
897 + sr->cancel_method = NULL;
898 + sr->cancel_arg2 = NULL;
899 +
900 + switch (sr->sr_state) {
901 + case SMB_REQ_STATE_WAITING_LOCK:
902 + /* Normal wakeup. Keep status from above. */
903 + sr->sr_state = SMB_REQ_STATE_ACTIVE;
663 904 break;
664 905
906 + case SMB_REQ_STATE_CANCEL_PENDING:
907 + /* Cancelled via smb_lock_cancel_sr */
908 + sr->sr_state = SMB_REQ_STATE_CANCELLED;
909 + /* FALLTHROUGH */
910 + case SMB_REQ_STATE_CANCELLED:
911 + if (status == NT_STATUS_SUCCESS)
912 + status = NT_STATUS_CANCELLED;
913 + break;
914 +
665 915 default:
666 - ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED);
667 - rc = 0;
668 916 break;
669 917 }
670 918 mutex_exit(&sr->sr_mutex);
671 919
672 - return (rc);
920 + /* Return to the caller with n_lock_list held. */
921 + smb_llist_enter(&node->n_lock_list, RW_WRITER);
922 +
923 + smb_llist_enter(&node->n_wlock_list, RW_WRITER);
924 + smb_llist_remove(&node->n_wlock_list, lock);
925 + smb_llist_exit(&node->n_wlock_list);
926 +
927 + return (status);
673 928 }
674 929
675 930 /*
676 931 * smb_lock_range_ulckrules
677 932 *
678 933 * 1. Unlock should be performed at exactly matching ends.
679 934 * This has been changed because overlapping ends is
680 935 * allowed and there is no other precise way of locating
681 936 * lock entity in node lock list.
682 937 *
683 938 * 2. Unlock is failed if there is no corresponding lock exists.
684 939 *
685 940 * Return values
686 941 *
687 942 * NT_STATUS_SUCCESS Unlock request matches lock record
688 - * pointed by 'nodelock' lock structure.
943 + * pointed by 'foundlock' lock structure.
689 944 *
690 945 * NT_STATUS_RANGE_NOT_LOCKED Unlock request doen't match any
691 946 * of lock record in node lock request or
692 947 * error in unlock range processing.
693 948 */
694 949 static uint32_t
695 950 smb_lock_range_ulckrules(
696 - smb_request_t *sr,
697 - smb_node_t *node,
951 + smb_ofile_t *file,
698 952 uint64_t start,
699 953 uint64_t length,
700 - smb_lock_t **nodelock)
954 + uint32_t pid,
955 + smb_lock_t **foundlock)
701 956 {
957 + smb_node_t *node = file->f_node;
702 958 smb_lock_t *lock;
703 959 uint32_t status = NT_STATUS_RANGE_NOT_LOCKED;
704 960
705 961 /* Caller must hold lock for node->n_lock_list */
706 962 for (lock = smb_llist_head(&node->n_lock_list);
707 963 lock != NULL;
708 964 lock = smb_llist_next(&node->n_lock_list, lock)) {
709 965
710 966 if ((start == lock->l_start) &&
711 967 (length == lock->l_length) &&
712 - lock->l_file == sr->fid_ofile &&
713 - lock->l_session_kid == sr->session->s_kid &&
714 - lock->l_pid == sr->smb_pid &&
715 - lock->l_uid == sr->smb_uid) {
716 - *nodelock = lock;
968 + lock->l_file == file &&
969 + lock->l_pid == pid) {
970 + *foundlock = lock;
717 971 status = NT_STATUS_SUCCESS;
718 972 break;
719 973 }
720 974 }
721 975
722 976 return (status);
723 977 }
724 978
725 979 static smb_lock_t *
726 980 smb_lock_create(
727 981 smb_request_t *sr,
728 982 uint64_t start,
729 983 uint64_t length,
984 + uint32_t pid,
730 985 uint32_t locktype,
731 986 uint32_t timeout)
732 987 {
733 988 smb_lock_t *lock;
734 989
735 990 ASSERT(locktype == SMB_LOCK_TYPE_READWRITE ||
736 991 locktype == SMB_LOCK_TYPE_READONLY);
737 992
738 - lock = kmem_zalloc(sizeof (smb_lock_t), KM_SLEEP);
993 + lock = kmem_cache_alloc(smb_cache_lock, KM_SLEEP);
994 + bzero(lock, sizeof (*lock));
739 995 lock->l_magic = SMB_LOCK_MAGIC;
740 - lock->l_sr = sr; /* Invalid after lock is active */
741 - lock->l_session_kid = sr->session->s_kid;
742 - lock->l_session = sr->session;
743 996 lock->l_file = sr->fid_ofile;
744 - lock->l_uid = sr->smb_uid;
745 - lock->l_pid = sr->smb_pid;
997 + /* l_file == fid_ofile implies same connection (see ofile lookup) */
998 + lock->l_pid = pid;
746 999 lock->l_type = locktype;
747 1000 lock->l_start = start;
748 1001 lock->l_length = length;
749 1002 /*
750 1003 * Calculate the absolute end time so that we can use it
751 1004 * in cv_timedwait.
752 1005 */
753 1006 lock->l_end_time = ddi_get_lbolt() + MSEC_TO_TICK(timeout);
754 1007 if (timeout == UINT_MAX)
755 1008 lock->l_flags |= SMB_LOCK_FLAG_INDEFINITE;
756 1009
757 1010 mutex_init(&lock->l_mutex, NULL, MUTEX_DEFAULT, NULL);
758 1011 cv_init(&lock->l_cv, NULL, CV_DEFAULT, NULL);
759 - smb_slist_constructor(&lock->l_conflict_list, sizeof (smb_lock_t),
760 - offsetof(smb_lock_t, l_conflict_lnd));
761 1012
762 1013 return (lock);
763 1014 }
764 1015
765 1016 static void
766 1017 smb_lock_free(smb_lock_t *lock)
767 1018 {
768 - smb_slist_destructor(&lock->l_conflict_list);
1019 +
1020 + lock->l_magic = 0;
769 1021 cv_destroy(&lock->l_cv);
770 1022 mutex_destroy(&lock->l_mutex);
771 1023
772 - kmem_free(lock, sizeof (smb_lock_t));
1024 + kmem_cache_free(smb_cache_lock, lock);
773 1025 }
774 1026
775 1027 /*
776 1028 * smb_lock_destroy
777 1029 *
778 1030 * Caller must hold node->n_lock_list
779 1031 */
780 1032 static void
781 1033 smb_lock_destroy(smb_lock_t *lock)
782 1034 {
1035 + smb_lock_t *tl;
1036 + smb_node_t *node;
1037 + uint32_t ccnt;
1038 +
783 1039 /*
784 - * Caller must hold node->n_lock_list lock.
1040 + * Wake any waiting locks that were blocked by this.
1041 + * We want them to wake and continue in FIFO order,
1042 + * so enter/exit the llist every time...
785 1043 */
786 1044 mutex_enter(&lock->l_mutex);
787 - cv_broadcast(&lock->l_cv);
1045 + ccnt = lock->l_conflicts;
1046 + lock->l_conflicts = 0;
788 1047 mutex_exit(&lock->l_mutex);
789 1048
790 - /*
791 - * The cv_broadcast above should wake up any locks that previous
792 - * had conflicts with this lock. Wait for the locking threads
793 - * to remove their references to this lock.
794 - */
795 - smb_slist_wait_for_empty(&lock->l_conflict_list);
1049 + node = lock->l_file->f_node;
1050 + while (ccnt) {
796 1051
1052 + smb_llist_enter(&node->n_wlock_list, RW_READER);
1053 +
1054 + for (tl = smb_llist_head(&node->n_wlock_list);
1055 + tl != NULL;
1056 + tl = smb_llist_next(&node->n_wlock_list, tl)) {
1057 + mutex_enter(&tl->l_mutex);
1058 + if (tl->l_blocked_by == lock) {
1059 + tl->l_blocked_by = NULL;
1060 + cv_broadcast(&tl->l_cv);
1061 + mutex_exit(&tl->l_mutex);
1062 + goto woke_one;
1063 + }
1064 + mutex_exit(&tl->l_mutex);
1065 + }
1066 + /* No more in the list blocked by this lock. */
1067 + ccnt = 0;
1068 + woke_one:
1069 + smb_llist_exit(&node->n_wlock_list);
1070 + if (ccnt) {
1071 + /*
1072 + * Let the thread we woke have a chance to run
1073 + * before we wake competitors for their lock.
1074 + */
1075 + delay(MSEC_TO_TICK(1));
1076 + }
1077 + }
1078 +
797 1079 smb_lock_free(lock);
798 1080 }
799 1081
800 1082 /*
801 1083 * smb_is_range_unlocked
802 1084 *
803 1085 * Checks if the current unlock byte range request overlaps another lock
804 1086 * This function is used to determine where POSIX unlocks should be
805 1087 * applied.
806 1088 *
807 1089 * The return code and the value of new_mark must be interpreted as
808 1090 * follows:
809 1091 *
810 1092 * B_TRUE and (new_mark == 0):
811 1093 * This is the last or only lock left to be unlocked
812 1094 *
813 1095 * B_TRUE and (new_mark > 0):
814 1096 * The range from start to new_mark can be unlocked
815 1097 *
816 1098 * B_FALSE and (new_mark == 0):
817 1099 * The unlock can't be performed and we are done
818 1100 *
819 1101 * B_FALSE and (new_mark > 0),
820 1102 * The range from start to new_mark can't be unlocked
821 1103 * Start should be reset to new_mark for the next pass
822 1104 */
823 1105
824 1106 static boolean_t
825 1107 smb_is_range_unlocked(uint64_t start, uint64_t end, uint32_t uniqid,
826 1108 smb_llist_t *llist_head, uint64_t *new_mark)
827 1109 {
828 1110 struct smb_lock *lk = NULL;
829 1111 uint64_t low_water_mark = MAXOFFSET_T;
830 1112 uint64_t lk_start;
831 1113 uint64_t lk_end;
832 1114
833 1115 *new_mark = 0;
834 1116 lk = smb_llist_head(llist_head);
835 1117 while (lk) {
836 1118 if (lk->l_length == 0) {
837 1119 lk = smb_llist_next(llist_head, lk);
838 1120 continue;
839 1121 }
840 1122
841 1123 if (lk->l_file->f_uniqid != uniqid) {
842 1124 lk = smb_llist_next(llist_head, lk);
843 1125 continue;
844 1126 }
845 1127
846 1128 lk_end = lk->l_start + lk->l_length - 1;
847 1129 lk_start = lk->l_start;
848 1130
849 1131 /*
850 1132 * there is no overlap for the first 2 cases
851 1133 * check next node
852 1134 */
853 1135 if (lk_end < start) {
854 1136 lk = smb_llist_next(llist_head, lk);
855 1137 continue;
856 1138 }
857 1139 if (lk_start > end) {
858 1140 lk = smb_llist_next(llist_head, lk);
859 1141 continue;
860 1142 }
861 1143
862 1144 /* this range is completely locked */
863 1145 if ((lk_start <= start) && (lk_end >= end)) {
864 1146 return (B_FALSE);
865 1147 }
866 1148
867 1149 /* the first part of this range is locked */
868 1150 if ((start >= lk_start) && (start <= lk_end)) {
869 1151 if (end > lk_end)
870 1152 *new_mark = lk_end + 1;
871 1153 return (B_FALSE);
872 1154 }
873 1155
874 1156 /* this piece is unlocked */
875 1157 if ((lk_start >= start) && (lk_start <= end)) {
876 1158 if (low_water_mark > lk_start)
877 1159 low_water_mark = lk_start;
878 1160 }
879 1161
|
↓ open down ↓ |
73 lines elided |
↑ open up ↑ |
880 1162 lk = smb_llist_next(llist_head, lk);
881 1163 }
882 1164
883 1165 if (low_water_mark != MAXOFFSET_T) {
884 1166 *new_mark = low_water_mark;
885 1167 return (B_TRUE);
886 1168 }
887 1169 /* the range is completely unlocked */
888 1170 return (B_TRUE);
889 1171 }
1172 +
1173 +#ifdef DEBUG
1174 +static void
1175 +smb_lock_dump1(smb_lock_t *lock)
1176 +{
1177 + cmn_err(CE_CONT, "\t0x%p: 0x%llx, 0x%llx, %p, %d\n",
1178 + (void *)lock,
1179 + (long long)lock->l_start,
1180 + (long long)lock->l_length,
1181 + (void *)lock->l_file,
1182 + lock->l_pid);
1183 +
1184 +}
1185 +
1186 +static void
1187 +smb_lock_dumplist(smb_llist_t *llist)
1188 +{
1189 + smb_lock_t *lock;
1190 +
1191 + for (lock = smb_llist_head(llist);
1192 + lock != NULL;
1193 + lock = smb_llist_next(llist, lock)) {
1194 + smb_lock_dump1(lock);
1195 + }
1196 +}
1197 +
1198 +static void
1199 +smb_lock_dumpnode(smb_node_t *node)
1200 +{
1201 + cmn_err(CE_CONT, "Granted Locks on %p (%d)\n",
1202 + (void *)node, node->n_lock_list.ll_count);
1203 + smb_lock_dumplist(&node->n_lock_list);
1204 +
1205 + cmn_err(CE_CONT, "Waiting Locks on %p (%d)\n",
1206 + (void *)node, node->n_wlock_list.ll_count);
1207 + smb_lock_dumplist(&node->n_wlock_list);
1208 +}
1209 +
1210 +#endif
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX