1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 /*
26 * smb_oplock_wait / smb_oplock_broadcast
27 * When an oplock is being acquired, we must ensure that the acquisition
28 * response is submitted to the network stack before any other operation
29 * is permitted on the oplock.
30 * In smb_oplock_acquire, oplock.ol_xthread is set to point to the worker
31 * thread processing the command that is granting the oplock.
32 * Other threads accessing the oplock will be suspended in smb_oplock_wait().
33 * They will be awakened when the worker thread referenced in 'ol_xthread'
34 * calls smb_oplock_broadcast().
35 *
36 * The purpose of this mechanism is to prevent another thread from
37 * triggering an oplock break before the response conveying the grant
38 * has been sent.
39 */
40
41 #include <smbsrv/smb_kproto.h>
42 #include <sys/nbmlock.h>
43 #include <inet/tcp.h>
44
45 #define SMB_OPLOCK_IS_EXCLUSIVE(level) \
46 (((level) == SMB_OPLOCK_EXCLUSIVE) || \
47 ((level) == SMB_OPLOCK_BATCH))
48
49 extern int smb_fem_oplock_install(smb_node_t *);
50 extern int smb_fem_oplock_uninstall(smb_node_t *);
51
52 static int smb_oplock_install_fem(smb_node_t *);
53 static void smb_oplock_uninstall_fem(smb_node_t *);
54
55 static void smb_oplock_wait(smb_node_t *);
56 static void smb_oplock_wait_ack(smb_node_t *, uint32_t);
57 static void smb_oplock_timedout(smb_node_t *);
58
59 static smb_oplock_grant_t *smb_oplock_set_grant(smb_ofile_t *, uint8_t);
60 void smb_oplock_clear_grant(smb_oplock_grant_t *);
61 static int smb_oplock_insert_grant(smb_node_t *, smb_oplock_grant_t *);
62 static void smb_oplock_remove_grant(smb_node_t *, smb_oplock_grant_t *);
63 static smb_oplock_grant_t *smb_oplock_exclusive_grant(list_t *);
64 static smb_oplock_grant_t *smb_oplock_get_grant(smb_oplock_t *, smb_ofile_t *);
65
66 static smb_oplock_break_t *smb_oplock_create_break(smb_node_t *);
67 static smb_oplock_break_t *smb_oplock_get_break(void);
68 static void smb_oplock_delete_break(smb_oplock_break_t *);
69 static void smb_oplock_process_levelII_break(smb_node_t *);
70
71 static void smb_oplock_break_thread();
72
73 /* levelII oplock break requests (smb_oplock_break_t) */
74 static boolean_t smb_oplock_initialized = B_FALSE;
75 static kmem_cache_t *smb_oplock_break_cache = NULL;
76 static smb_llist_t smb_oplock_breaks;
77 static smb_thread_t smb_oplock_thread;
78
79
80 /*
81 * smb_oplock_init
82 *
83 * This function is not multi-thread safe. The caller must make sure only one
84 * thread makes the call.
85 */
86 int
smb_oplock_init(void)87 smb_oplock_init(void)
88 {
89 int rc;
90
91 if (smb_oplock_initialized)
92 return (0);
93
94 smb_oplock_break_cache = kmem_cache_create("smb_oplock_break_cache",
95 sizeof (smb_oplock_break_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
96
97 smb_llist_constructor(&smb_oplock_breaks, sizeof (smb_oplock_break_t),
98 offsetof(smb_oplock_break_t, ob_lnd));
99
100 smb_thread_init(&smb_oplock_thread, "smb_thread_oplock_break",
101 smb_oplock_break_thread, NULL);
102
103 rc = smb_thread_start(&smb_oplock_thread);
104 if (rc != 0) {
105 smb_thread_destroy(&smb_oplock_thread);
106 smb_llist_destructor(&smb_oplock_breaks);
107 kmem_cache_destroy(smb_oplock_break_cache);
108 return (rc);
109 }
110
111 smb_oplock_initialized = B_TRUE;
112 return (0);
113 }
114
115 /*
116 * smb_oplock_fini
117 * This function is not multi-thread safe. The caller must make sure only one
118 * thread makes the call.
119 */
120 void
smb_oplock_fini(void)121 smb_oplock_fini(void)
122 {
123 smb_oplock_break_t *ob;
124
125 if (!smb_oplock_initialized)
126 return;
127
128 smb_thread_stop(&smb_oplock_thread);
129 smb_thread_destroy(&smb_oplock_thread);
130
131 while ((ob = smb_llist_head(&smb_oplock_breaks)) != NULL) {
132 SMB_OPLOCK_BREAK_VALID(ob);
133 smb_llist_remove(&smb_oplock_breaks, ob);
134 smb_oplock_delete_break(ob);
135 }
136 smb_llist_destructor(&smb_oplock_breaks);
137
138 kmem_cache_destroy(smb_oplock_break_cache);
139 }
140
141 /*
142 * smb_oplock_install_fem
143 * Install fem monitor for cross protocol oplock breaking.
144 */
145 static int
smb_oplock_install_fem(smb_node_t * node)146 smb_oplock_install_fem(smb_node_t *node)
147 {
148 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
149
150 if (node->n_oplock.ol_fem == B_FALSE) {
151 if (smb_fem_oplock_install(node) != 0) {
152 cmn_err(CE_NOTE, "No oplock granted: "
153 "failed to install fem monitor %s",
154 node->vp->v_path);
155 return (-1);
156 }
157 node->n_oplock.ol_fem = B_TRUE;
158 }
159 return (0);
160 }
161
162 /*
163 * smb_oplock_uninstall_fem
164 * Uninstall fem monitor for cross protocol oplock breaking.
165 */
166 static void
smb_oplock_uninstall_fem(smb_node_t * node)167 smb_oplock_uninstall_fem(smb_node_t *node)
168 {
169 ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
170
171 if (node->n_oplock.ol_fem) {
172 if (smb_fem_oplock_uninstall(node) == 0) {
173 node->n_oplock.ol_fem = B_FALSE;
174 } else {
175 cmn_err(CE_NOTE,
176 "failed to uninstall fem monitor %s",
177 node->vp->v_path);
178 }
179 }
180 }
181
182 /*
183 * smb_oplock_acquire
184 *
185 * Attempt to acquire an oplock. Clients will request EXCLUSIVE or BATCH,
186 * but might only be granted LEVEL_II or NONE.
187 *
188 * If oplocks are not supported on the tree, or node, grant NONE.
189 * If nobody else has the file open, grant the requested level.
190 * If any of the following are true, grant NONE:
191 * - there is an exclusive oplock on the node
192 * - op->op_oplock_levelII is B_FALSE (LEVEL_II not supported by open cmd.
193 * - LEVEL_II oplocks are not supported for the session
194 * - a BATCH oplock is requested on a named stream
195 * - there are any range locks on the node
196 * Otherwise, grant LEVEL_II.
197 *
198 * ol->ol_xthread is set to the current thread to lock the oplock against
199 * other operations until the acquire response is on the wire. When the
200 * acquire response is on the wire, smb_oplock_broadcast() is called to
201 * reset ol->ol_xthread and wake any waiting threads.
202 */
203 void
smb_oplock_acquire(smb_request_t * sr,smb_node_t * node,smb_ofile_t * ofile)204 smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
205 {
206 smb_oplock_t *ol;
207 smb_oplock_grant_t *og;
208 list_t *grants;
209 smb_arg_open_t *op;
210 smb_tree_t *tree;
211 smb_session_t *session;
212
213 SMB_NODE_VALID(node);
214 SMB_OFILE_VALID(ofile);
215
216 ASSERT(node == SMB_OFILE_GET_NODE(ofile));
217
218 op = &sr->sr_open;
219 tree = SMB_OFILE_GET_TREE(ofile);
220 session = SMB_OFILE_GET_SESSION(ofile);
221
222 if (!smb_tree_has_feature(tree, SMB_TREE_OPLOCKS) ||
223 (op->op_oplock_level == SMB_OPLOCK_NONE) ||
224 ((op->op_oplock_level == SMB_OPLOCK_BATCH) &&
225 SMB_IS_STREAM(node))) {
226 op->op_oplock_level = SMB_OPLOCK_NONE;
227 return;
228 }
229
230 ol = &node->n_oplock;
231 grants = &ol->ol_grants;
232
233 mutex_enter(&ol->ol_mutex);
234 smb_oplock_wait(node);
235
236 nbl_start_crit(node->vp, RW_READER);
237
238 if ((node->n_open_count > 1) ||
239 (node->n_opening_count > 1) ||
240 smb_vop_other_opens(node->vp, ofile->f_mode)) {
241 if ((!op->op_oplock_levelII) ||
242 (!smb_session_levelII_oplocks(session)) ||
243 (smb_oplock_exclusive_grant(grants) != NULL) ||
244 (smb_range_check(sr, node, 0, UINT64_MAX, B_TRUE) != 0)) {
245 op->op_oplock_level = SMB_OPLOCK_NONE;
246 nbl_end_crit(node->vp);
247 mutex_exit(&ol->ol_mutex);
248 return;
249 }
250
251 op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
252 }
253
254 nbl_end_crit(node->vp);
255
256 og = smb_oplock_set_grant(ofile, op->op_oplock_level);
257 if (smb_oplock_insert_grant(node, og) != 0) {
258 smb_oplock_clear_grant(og);
259 op->op_oplock_level = SMB_OPLOCK_NONE;
260 mutex_exit(&ol->ol_mutex);
261 return;
262 }
263
264 ol->ol_xthread = curthread;
265 mutex_exit(&ol->ol_mutex);
266 }
267
268 /*
269 * smb_oplock_break
270 *
271 * Break granted oplocks according to the following rules:
272 *
273 * If there's an exclusive oplock granted on the node
274 * - if the BREAK_BATCH flags is specified and the oplock is not
275 * a batch oplock, no break is required.
276 * - if the session doesn't support LEVEL II oplocks, and 'brk' is
277 * BREAK_TO_LEVEL_II, do a BREAK_TO_NONE.
278 * - if the oplock is already breaking update the break level (if
279 * the requested break is to a lesser level), otherwise send an
280 * oplock break.
281 * Wait for acknowledgement of the break (unless NOWAIT flag is set)
282 *
283 * Otherwise:
284 * If there are level II oplocks granted on the node, and the flags
285 * indicate that they should be broken (BREAK_TO_NONE specified,
286 * BREAK_EXCLUSIVE, BREAK_BATCH not specified) queue the levelII
287 * break request for asynchronous processing.
288 *
289 * Returns:
290 * 0 - oplock broken (or no break required)
291 * EAGAIN - oplock break request sent and would block
292 * awaiting the reponse but NOWAIT was specified
293 */
294 int
smb_oplock_break(smb_request_t * sr,smb_node_t * node,uint32_t flags)295 smb_oplock_break(smb_request_t *sr, smb_node_t *node, uint32_t flags)
296 {
297 smb_oplock_t *ol;
298 smb_oplock_grant_t *og;
299 list_t *grants;
300 uint32_t timeout;
301 uint8_t brk;
302
303 SMB_NODE_VALID(node);
304 ol = &node->n_oplock;
305 grants = &ol->ol_grants;
306
307 mutex_enter(&ol->ol_mutex);
308 smb_oplock_wait(node);
309
310 og = list_head(grants);
311 if (og == NULL) {
312 mutex_exit(&ol->ol_mutex);
313 return (0);
314 }
315
316 SMB_OPLOCK_GRANT_VALID(og);
317
318 /* break levelII oplocks */
319 if (og->og_level == SMB_OPLOCK_LEVEL_II) {
320 mutex_exit(&ol->ol_mutex);
321
322 if ((flags & SMB_OPLOCK_BREAK_TO_NONE) &&
323 !(flags & SMB_OPLOCK_BREAK_EXCLUSIVE) &&
324 !(flags & SMB_OPLOCK_BREAK_BATCH)) {
325 smb_oplock_break_levelII(node);
326 }
327 return (0);
328 }
329
330 /* break exclusive oplock */
331 if ((flags & SMB_OPLOCK_BREAK_BATCH) &&
332 (og->og_level != SMB_OPLOCK_BATCH)) {
333 mutex_exit(&ol->ol_mutex);
334 return (0);
335 }
336
337 if ((flags & SMB_OPLOCK_BREAK_TO_LEVEL_II) &&
338 smb_session_levelII_oplocks(og->og_session)) {
339 brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
340 } else {
341 brk = SMB_OPLOCK_BREAK_TO_NONE;
342 }
343
344 switch (ol->ol_break) {
345 case SMB_OPLOCK_NO_BREAK:
346 ol->ol_break = brk;
347 smb_session_oplock_break(og->og_session,
348 og->og_tid, og->og_fid, brk);
349 break;
350 case SMB_OPLOCK_BREAK_TO_LEVEL_II:
351 if (brk == SMB_OPLOCK_BREAK_TO_NONE)
352 ol->ol_break = SMB_OPLOCK_BREAK_TO_NONE;
353 break;
354 case SMB_OPLOCK_BREAK_TO_NONE:
355 default:
356 break;
357 }
358
359 if (flags & SMB_OPLOCK_BREAK_NOWAIT) {
360 mutex_exit(&ol->ol_mutex);
361 return (EAGAIN);
362 }
363
364 if (sr && (sr->session == og->og_session) &&
365 (sr->smb_uid == og->og_uid)) {
366 timeout = smb_oplock_min_timeout;
367 } else {
368 timeout = smb_oplock_timeout;
369 }
370
371 mutex_exit(&ol->ol_mutex);
372 smb_oplock_wait_ack(node, timeout);
373 return (0);
374 }
375
376 /*
377 * smb_oplock_break_levelII
378 *
379 * LevelII (shared) oplock breaks are processed asynchronously.
380 * Unlike exclusive oplock breaks, the thread initiating the break
381 * is NOT blocked while the request is processed.
382 *
383 * Create an oplock_break_request and add it to the list for async
384 * processing.
385 */
386 void
smb_oplock_break_levelII(smb_node_t * node)387 smb_oplock_break_levelII(smb_node_t *node)
388 {
389 smb_oplock_break_t *ob;
390
391 ob = smb_oplock_create_break(node);
392
393 smb_llist_enter(&smb_oplock_breaks, RW_WRITER);
394 smb_llist_insert_tail(&smb_oplock_breaks, ob);
395 smb_llist_exit(&smb_oplock_breaks);
396
397 smb_thread_signal(&smb_oplock_thread);
398 }
399
400 /*
401 * smb_oplock_break_thread
402 *
403 * The smb_oplock_thread is woken when an oplock break request is
404 * added to the list of pending levelII oplock break requests.
405 * Gets the oplock break request from the list, processes it and
406 * deletes it.
407 */
408 /*ARGSUSED*/
409 static void
smb_oplock_break_thread(smb_thread_t * thread,void * arg)410 smb_oplock_break_thread(smb_thread_t *thread, void *arg)
411 {
412 smb_oplock_break_t *ob;
413
414 while (smb_thread_continue(thread)) {
415 while ((ob = smb_oplock_get_break()) != NULL) {
416 smb_oplock_process_levelII_break(ob->ob_node);
417 smb_oplock_delete_break(ob);
418 }
419 }
420 }
421
422 /*
423 * smb_oplock_get_break
424 *
425 * Remove and return the next oplock break request from the list
426 */
427 static smb_oplock_break_t *
smb_oplock_get_break(void)428 smb_oplock_get_break(void)
429 {
430 smb_oplock_break_t *ob;
431
432 smb_llist_enter(&smb_oplock_breaks, RW_WRITER);
433 if ((ob = smb_llist_head(&smb_oplock_breaks)) != NULL) {
434 SMB_OPLOCK_BREAK_VALID(ob);
435 smb_llist_remove(&smb_oplock_breaks, ob);
436 }
437 smb_llist_exit(&smb_oplock_breaks);
438 return (ob);
439 }
440
441 /*
442 * smb_oplock_process_levelII_break
443 */
444 void
smb_oplock_process_levelII_break(smb_node_t * node)445 smb_oplock_process_levelII_break(smb_node_t *node)
446 {
447 smb_oplock_t *ol;
448 smb_oplock_grant_t *og;
449 list_t *grants;
450
451 if (!smb_oplock_levelII)
452 return;
453
454 ol = &node->n_oplock;
455 mutex_enter(&ol->ol_mutex);
456 smb_oplock_wait(node);
457 grants = &node->n_oplock.ol_grants;
458
459 while ((og = list_head(grants)) != NULL) {
460 SMB_OPLOCK_GRANT_VALID(og);
461
462 if (SMB_OPLOCK_IS_EXCLUSIVE(og->og_level))
463 break;
464
465 smb_session_oplock_break(og->og_session,
466 og->og_tid, og->og_fid, SMB_OPLOCK_BREAK_TO_NONE);
467 smb_oplock_remove_grant(node, og);
468 smb_oplock_clear_grant(og);
469 }
470
471 mutex_exit(&ol->ol_mutex);
472 }
473
474 /*
475 * smb_oplock_wait_ack
476 *
477 * Timed wait for an oplock break acknowledgement (or oplock release).
478 */
479 static void
smb_oplock_wait_ack(smb_node_t * node,uint32_t timeout)480 smb_oplock_wait_ack(smb_node_t *node, uint32_t timeout)
481 {
482 smb_oplock_t *ol;
483 clock_t time;
484
485 ol = &node->n_oplock;
486 mutex_enter(&ol->ol_mutex);
487 time = MSEC_TO_TICK(timeout) + ddi_get_lbolt();
488
489 while (ol->ol_break != SMB_OPLOCK_NO_BREAK) {
490 if (cv_timedwait(&ol->ol_cv, &ol->ol_mutex, time) < 0) {
491 smb_oplock_timedout(node);
492 cv_broadcast(&ol->ol_cv);
493 break;
494 }
495 }
496 mutex_exit(&ol->ol_mutex);
497 }
498
499 /*
500 * smb_oplock_timedout
501 *
502 * An oplock break has not been acknowledged within timeout
503 * 'smb_oplock_timeout'.
504 * Set oplock grant to the desired break level.
505 */
506 static void
smb_oplock_timedout(smb_node_t * node)507 smb_oplock_timedout(smb_node_t *node)
508 {
509 smb_oplock_t *ol;
510 smb_oplock_grant_t *og;
511 list_t *grants;
512
513 ol = &node->n_oplock;
514 grants = &ol->ol_grants;
515
516 ASSERT(MUTEX_HELD(&ol->ol_mutex));
517
518 og = smb_oplock_exclusive_grant(grants);
519 if (og) {
520 switch (ol->ol_break) {
521 case SMB_OPLOCK_BREAK_TO_NONE:
522 og->og_level = SMB_OPLOCK_NONE;
523 smb_oplock_remove_grant(node, og);
524 smb_oplock_clear_grant(og);
525 break;
526 case SMB_OPLOCK_BREAK_TO_LEVEL_II:
527 og->og_level = SMB_OPLOCK_LEVEL_II;
528 break;
529 default:
530 SMB_PANIC();
531 }
532 }
533 ol->ol_break = SMB_OPLOCK_NO_BREAK;
534 }
535
536 /*
537 * smb_oplock_release
538 *
539 * Release the oplock granted on ofile 'of'.
540 * Wake any threads waiting for an oplock break acknowledgement for
541 * this oplock.
542 * This is called when the ofile is being closed.
543 */
544 void
smb_oplock_release(smb_node_t * node,smb_ofile_t * of)545 smb_oplock_release(smb_node_t *node, smb_ofile_t *of)
546 {
547 smb_oplock_t *ol;
548 smb_oplock_grant_t *og;
549
550 ol = &node->n_oplock;
551 mutex_enter(&ol->ol_mutex);
552 smb_oplock_wait(node);
553
554 og = smb_oplock_get_grant(ol, of);
555 if (og) {
556 smb_oplock_remove_grant(node, og);
557 smb_oplock_clear_grant(og);
558
559 if (ol->ol_break != SMB_OPLOCK_NO_BREAK) {
560 ol->ol_break = SMB_OPLOCK_NO_BREAK;
561 cv_broadcast(&ol->ol_cv);
562 }
563 }
564
565 mutex_exit(&ol->ol_mutex);
566 }
567
568 /*
569 * smb_oplock_ack
570 *
571 * Process oplock acknowledgement received for ofile 'of'.
572 * - oplock.ol_break is the break level that was requested.
573 * - brk is the break level being acknowledged by the client.
574 *
575 * Update the oplock grant level to the lesser of ol_break and brk.
576 * If the grant is now SMB_OPLOCK_NONE, remove the grant from the
577 * oplock's grant list and delete it.
578 * If the requested break level (ol_break) was NONE and the brk is
579 * LEVEL_II, send another oplock break (NONE). Do not wait for an
580 * acknowledgement.
581 * Wake any threads waiting for the oplock break acknowledgement.
582 */
583 void
smb_oplock_ack(smb_node_t * node,smb_ofile_t * of,uint8_t brk)584 smb_oplock_ack(smb_node_t *node, smb_ofile_t *of, uint8_t brk)
585 {
586 smb_oplock_t *ol;
587 smb_oplock_grant_t *og;
588 boolean_t brk_to_none = B_FALSE;
589
590 ol = &node->n_oplock;
591 mutex_enter(&ol->ol_mutex);
592 smb_oplock_wait(node);
593
594 if ((ol->ol_break == SMB_OPLOCK_NO_BREAK) ||
595 ((og = smb_oplock_get_grant(ol, of)) == NULL)) {
596 mutex_exit(&ol->ol_mutex);
597 return;
598 }
599
600 switch (brk) {
601 case SMB_OPLOCK_BREAK_TO_NONE:
602 og->og_level = SMB_OPLOCK_NONE;
603 break;
604 case SMB_OPLOCK_BREAK_TO_LEVEL_II:
605 if (ol->ol_break == SMB_OPLOCK_BREAK_TO_LEVEL_II) {
606 og->og_level = SMB_OPLOCK_LEVEL_II;
607 } else {
608 /* SMB_OPLOCK_BREAK_TO_NONE */
609 og->og_level = SMB_OPLOCK_NONE;
610 brk_to_none = B_TRUE;
611 }
612 break;
613 default:
614 SMB_PANIC();
615 }
616
617 if (og->og_level == SMB_OPLOCK_NONE) {
618 smb_oplock_remove_grant(node, og);
619 smb_oplock_clear_grant(og);
620 }
621
622 ol->ol_break = SMB_OPLOCK_NO_BREAK;
623 cv_broadcast(&ol->ol_cv);
624
625 if (brk_to_none) {
626 smb_session_oplock_break(of->f_session,
627 of->f_tree->t_tid, of->f_fid,
628 SMB_OPLOCK_BREAK_TO_NONE);
629 }
630
631 mutex_exit(&ol->ol_mutex);
632 }
633
634 /*
635 * smb_oplock_broadcast
636 *
637 * ol->ol_xthread identifies the thread that was performing an oplock
638 * acquire. Other threads may be blocked awaiting completion of the
639 * acquire.
640 * If the calling thread is ol_ol_xthread, wake any waiting threads.
641 */
642 void
smb_oplock_broadcast(smb_node_t * node)643 smb_oplock_broadcast(smb_node_t *node)
644 {
645 smb_oplock_t *ol;
646
647 SMB_NODE_VALID(node);
648 ol = &node->n_oplock;
649
650 mutex_enter(&ol->ol_mutex);
651 if ((ol->ol_xthread != NULL) && (ol->ol_xthread == curthread)) {
652 ol->ol_xthread = NULL;
653 cv_broadcast(&ol->ol_cv);
654 }
655 mutex_exit(&ol->ol_mutex);
656 }
657
658 /*
659 * smb_oplock_wait
660 *
661 * Wait for the completion of an oplock acquire.
662 * If ol_xthread is not NULL and doesn't contain the pointer to the
663 * context of the calling thread, the caller will sleep until the
664 * ol_xthread is reset to NULL (via smb_oplock_broadcast()).
665 */
666 static void
smb_oplock_wait(smb_node_t * node)667 smb_oplock_wait(smb_node_t *node)
668 {
669 smb_oplock_t *ol;
670
671 ol = &node->n_oplock;
672 ASSERT(MUTEX_HELD(&ol->ol_mutex));
673
674 if ((ol->ol_xthread != NULL) && (ol->ol_xthread != curthread)) {
675 while (ol->ol_xthread != NULL)
676 cv_wait(&ol->ol_cv, &ol->ol_mutex);
677 }
678 }
679
680 /*
681 * smb_oplock_set_grant
682 */
683 static smb_oplock_grant_t *
smb_oplock_set_grant(smb_ofile_t * of,uint8_t level)684 smb_oplock_set_grant(smb_ofile_t *of, uint8_t level)
685 {
686 smb_oplock_grant_t *og;
687
688 og = &of->f_oplock_grant;
689
690 og->og_magic = SMB_OPLOCK_GRANT_MAGIC;
691 og->og_level = level;
692 og->og_ofile = of;
693 og->og_fid = of->f_fid;
694 og->og_tid = of->f_tree->t_tid;
695 og->og_uid = of->f_user->u_uid;
696 og->og_session = of->f_session;
697 return (og);
698 }
699
700 /*
701 * smb_oplock_clear_grant
702 */
703 void
smb_oplock_clear_grant(smb_oplock_grant_t * og)704 smb_oplock_clear_grant(smb_oplock_grant_t *og)
705 {
706 bzero(og, sizeof (smb_oplock_grant_t));
707 }
708
709 /*
710 * smb_oplock_insert_grant
711 *
712 * If there are no grants in the oplock's list install the fem
713 * monitor.
714 * Insert the grant into the list and increment the grant count.
715 */
716 static int
smb_oplock_insert_grant(smb_node_t * node,smb_oplock_grant_t * og)717 smb_oplock_insert_grant(smb_node_t *node, smb_oplock_grant_t *og)
718 {
719 smb_oplock_t *ol = &node->n_oplock;
720
721 ASSERT(MUTEX_HELD(&ol->ol_mutex));
722
723 if (ol->ol_count == 0) {
724 if (smb_oplock_install_fem(node) != 0)
725 return (-1);
726 }
727
728 list_insert_tail(&ol->ol_grants, og);
729 ++ol->ol_count;
730 return (0);
731 }
732
733 /*
734 * smb_oplock_remove_grant
735 *
736 * Remove the oplock grant from the list, decrement the grant count
737 * and, if there are no other grants in the list, uninstall the fem
738 * monitor.
739 */
740 static void
smb_oplock_remove_grant(smb_node_t * node,smb_oplock_grant_t * og)741 smb_oplock_remove_grant(smb_node_t *node, smb_oplock_grant_t *og)
742 {
743 smb_oplock_t *ol = &node->n_oplock;
744
745 ASSERT(MUTEX_HELD(&ol->ol_mutex));
746 ASSERT(ol->ol_count > 0);
747
748 list_remove(&ol->ol_grants, og);
749 if (--ol->ol_count == 0)
750 smb_oplock_uninstall_fem(node);
751 }
752
753 /*
754 * smb_oplock_exclusive_grant
755 *
756 * If an exclusive (EXCLUSIVE or BATCH) oplock grant exists,
757 * return it. Otherwise return NULL.
758 */
759 static smb_oplock_grant_t *
smb_oplock_exclusive_grant(list_t * grants)760 smb_oplock_exclusive_grant(list_t *grants)
761 {
762 smb_oplock_grant_t *og;
763
764 og = list_head(grants);
765 if (og) {
766 SMB_OPLOCK_GRANT_VALID(og);
767 if (SMB_OPLOCK_IS_EXCLUSIVE(og->og_level))
768 return (og);
769 }
770 return (NULL);
771 }
772
773 /*
774 * smb_oplock_get_grant
775 *
776 * Find oplock grant corresponding to the specified ofile.
777 */
778 static smb_oplock_grant_t *
smb_oplock_get_grant(smb_oplock_t * ol,smb_ofile_t * ofile)779 smb_oplock_get_grant(smb_oplock_t *ol, smb_ofile_t *ofile)
780 {
781 ASSERT(MUTEX_HELD(&ol->ol_mutex));
782
783 if (SMB_OFILE_OPLOCK_GRANTED(ofile))
784 return (&ofile->f_oplock_grant);
785 else
786 return (NULL);
787 }
788
789 /*
790 * smb_oplock_create_break
791 */
792 static smb_oplock_break_t *
smb_oplock_create_break(smb_node_t * node)793 smb_oplock_create_break(smb_node_t *node)
794 {
795 smb_oplock_break_t *ob;
796
797 ob = kmem_cache_alloc(smb_oplock_break_cache, KM_SLEEP);
798
799 smb_node_ref(node);
800 ob->ob_magic = SMB_OPLOCK_BREAK_MAGIC;
801 ob->ob_node = node;
802
803 return (ob);
804 }
805
806 /*
807 * smb_oplock_delete_break
808 */
809 static void
smb_oplock_delete_break(smb_oplock_break_t * ob)810 smb_oplock_delete_break(smb_oplock_break_t *ob)
811 {
812 smb_node_release(ob->ob_node);
813 kmem_cache_free(smb_oplock_break_cache, ob);
814 }
815