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
49extern int smb_fem_oplock_install(smb_node_t *);
50extern int smb_fem_oplock_uninstall(smb_node_t *);
51
52static int smb_oplock_install_fem(smb_node_t *);
53static void smb_oplock_uninstall_fem(smb_node_t *);
54
55static void smb_oplock_wait(smb_node_t *);
56static void smb_oplock_wait_ack(smb_node_t *, uint32_t);
57static void smb_oplock_timedout(smb_node_t *);
58
59static smb_oplock_grant_t *smb_oplock_set_grant(smb_ofile_t *, uint8_t);
60void smb_oplock_clear_grant(smb_oplock_grant_t *);
61static int smb_oplock_insert_grant(smb_node_t *, smb_oplock_grant_t *);
62static void smb_oplock_remove_grant(smb_node_t *, smb_oplock_grant_t *);
63static smb_oplock_grant_t *smb_oplock_exclusive_grant(list_t *);
64static smb_oplock_grant_t *smb_oplock_get_grant(smb_oplock_t *, smb_ofile_t *);
65
66static smb_oplock_break_t *smb_oplock_create_break(smb_node_t *);
67static smb_oplock_break_t *smb_oplock_get_break(void);
68static void smb_oplock_delete_break(smb_oplock_break_t *);
69static void smb_oplock_process_levelII_break(smb_node_t *);
70
71static void smb_oplock_break_thread();
72
73/* levelII oplock break requests (smb_oplock_break_t) */
74static boolean_t	smb_oplock_initialized = B_FALSE;
75static kmem_cache_t	*smb_oplock_break_cache = NULL;
76static smb_llist_t	smb_oplock_breaks;
77static 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 */
86int
87smb_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 */
120void
121smb_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 */
145static int
146smb_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 */
166static void
167smb_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 */
203void
204smb_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 */
294int
295smb_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 */
386void
387smb_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*/
409static void
410smb_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 */
427static smb_oplock_break_t *
428smb_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 */
444void
445smb_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 */
479static void
480smb_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 */
506static void
507smb_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 */
544void
545smb_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 */
583void
584smb_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 */
642void
643smb_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 */
666static void
667smb_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 */
683static smb_oplock_grant_t *
684smb_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 */
703void
704smb_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 */
716static int
717smb_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 */
740static void
741smb_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 */
759static smb_oplock_grant_t *
760smb_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 */
778static smb_oplock_grant_t *
779smb_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 */
792static smb_oplock_break_t *
793smb_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 */
809static void
810smb_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