xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_oplock.c (revision 13138:89c014c50a5f)
16600Sas200622 /*
26600Sas200622  * CDDL HEADER START
36600Sas200622  *
46600Sas200622  * The contents of this file are subject to the terms of the
56600Sas200622  * Common Development and Distribution License (the "License").
66600Sas200622  * You may not use this file except in compliance with the License.
76600Sas200622  *
86600Sas200622  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96600Sas200622  * or http://www.opensolaris.org/os/licensing.
106600Sas200622  * See the License for the specific language governing permissions
116600Sas200622  * and limitations under the License.
126600Sas200622  *
136600Sas200622  * When distributing Covered Code, include this CDDL HEADER in each
146600Sas200622  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156600Sas200622  * If applicable, add the following below this CDDL HEADER, with the
166600Sas200622  * fields enclosed by brackets "[]" replaced with your own identifying
176600Sas200622  * information: Portions Copyright [yyyy] [name of copyright owner]
186600Sas200622  *
196600Sas200622  * CDDL HEADER END
206600Sas200622  */
216600Sas200622 /*
2212508Samw@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
236600Sas200622  */
2412890SJoyce.McIntosh@Sun.COM 
256600Sas200622 /*
2612890SJoyce.McIntosh@Sun.COM  * smb_oplock_wait / smb_oplock_broadcast
2712890SJoyce.McIntosh@Sun.COM  * When an oplock is being acquired, we must ensure that the acquisition
2812890SJoyce.McIntosh@Sun.COM  * response is submitted to the network stack before any other operation
2912890SJoyce.McIntosh@Sun.COM  * is permitted on the oplock.
3012890SJoyce.McIntosh@Sun.COM  * In smb_oplock_acquire, oplock.ol_xthread is set to point to the worker
3112890SJoyce.McIntosh@Sun.COM  * thread processing the command that is granting the oplock.
3212890SJoyce.McIntosh@Sun.COM  * Other threads accessing the oplock will be suspended in smb_oplock_wait().
3312890SJoyce.McIntosh@Sun.COM  * They will be awakened when the worker thread referenced in 'ol_xthread'
3412890SJoyce.McIntosh@Sun.COM  * calls smb_oplock_broadcast().
359231SAfshin.Ardakani@Sun.COM  *
3612890SJoyce.McIntosh@Sun.COM  * The purpose of this mechanism is to prevent another thread from
3712890SJoyce.McIntosh@Sun.COM  * triggering an oplock break before the response conveying the grant
3812890SJoyce.McIntosh@Sun.COM  * has been sent.
396600Sas200622  */
406600Sas200622 
4110966SJordan.Brown@Sun.COM #include <smbsrv/smb_kproto.h>
4212890SJoyce.McIntosh@Sun.COM #include <sys/nbmlock.h>
438670SJose.Borrego@Sun.COM #include <inet/tcp.h>
446600Sas200622 
4512890SJoyce.McIntosh@Sun.COM #define	SMB_OPLOCK_IS_EXCLUSIVE(level)		\
4612890SJoyce.McIntosh@Sun.COM 	(((level) == SMB_OPLOCK_EXCLUSIVE) ||	\
4712890SJoyce.McIntosh@Sun.COM 	((level) == SMB_OPLOCK_BATCH))
4812890SJoyce.McIntosh@Sun.COM 
4912890SJoyce.McIntosh@Sun.COM extern int smb_fem_oplock_install(smb_node_t *);
5012890SJoyce.McIntosh@Sun.COM extern int smb_fem_oplock_uninstall(smb_node_t *);
5112890SJoyce.McIntosh@Sun.COM 
5212890SJoyce.McIntosh@Sun.COM static int smb_oplock_install_fem(smb_node_t *);
5312890SJoyce.McIntosh@Sun.COM static void smb_oplock_uninstall_fem(smb_node_t *);
5412890SJoyce.McIntosh@Sun.COM 
559021Samw@Sun.COM static void smb_oplock_wait(smb_node_t *);
5612890SJoyce.McIntosh@Sun.COM static void smb_oplock_wait_ack(smb_node_t *, uint32_t);
5712890SJoyce.McIntosh@Sun.COM static void smb_oplock_timedout(smb_node_t *);
5812890SJoyce.McIntosh@Sun.COM 
5912914SJoyce.McIntosh@Sun.COM static smb_oplock_grant_t *smb_oplock_set_grant(smb_ofile_t *, uint8_t);
6012914SJoyce.McIntosh@Sun.COM void smb_oplock_clear_grant(smb_oplock_grant_t *);
6112890SJoyce.McIntosh@Sun.COM static int smb_oplock_insert_grant(smb_node_t *, smb_oplock_grant_t *);
6212890SJoyce.McIntosh@Sun.COM static void smb_oplock_remove_grant(smb_node_t *, smb_oplock_grant_t *);
6312890SJoyce.McIntosh@Sun.COM static smb_oplock_grant_t *smb_oplock_exclusive_grant(list_t *);
6412914SJoyce.McIntosh@Sun.COM static smb_oplock_grant_t *smb_oplock_get_grant(smb_oplock_t *, smb_ofile_t *);
6512890SJoyce.McIntosh@Sun.COM 
6612890SJoyce.McIntosh@Sun.COM static smb_oplock_break_t *smb_oplock_create_break(smb_node_t *);
6712890SJoyce.McIntosh@Sun.COM static smb_oplock_break_t *smb_oplock_get_break(void);
6812890SJoyce.McIntosh@Sun.COM static void smb_oplock_delete_break(smb_oplock_break_t *);
6912890SJoyce.McIntosh@Sun.COM static void smb_oplock_process_levelII_break(smb_node_t *);
7012890SJoyce.McIntosh@Sun.COM 
7112890SJoyce.McIntosh@Sun.COM static void smb_oplock_break_thread();
7212890SJoyce.McIntosh@Sun.COM 
7312890SJoyce.McIntosh@Sun.COM /* levelII oplock break requests (smb_oplock_break_t) */
7412890SJoyce.McIntosh@Sun.COM static boolean_t	smb_oplock_initialized = B_FALSE;
7512890SJoyce.McIntosh@Sun.COM static kmem_cache_t	*smb_oplock_break_cache = NULL;
7612890SJoyce.McIntosh@Sun.COM static smb_llist_t	smb_oplock_breaks;
7712890SJoyce.McIntosh@Sun.COM static smb_thread_t	smb_oplock_thread;
7812890SJoyce.McIntosh@Sun.COM 
796600Sas200622 
806600Sas200622 /*
8112890SJoyce.McIntosh@Sun.COM  * smb_oplock_init
826600Sas200622  *
8312890SJoyce.McIntosh@Sun.COM  * This function is not multi-thread safe. The caller must make sure only one
8412890SJoyce.McIntosh@Sun.COM  * thread makes the call.
8512890SJoyce.McIntosh@Sun.COM  */
8612890SJoyce.McIntosh@Sun.COM int
smb_oplock_init(void)8712890SJoyce.McIntosh@Sun.COM smb_oplock_init(void)
8812890SJoyce.McIntosh@Sun.COM {
8912890SJoyce.McIntosh@Sun.COM 	int rc;
9012890SJoyce.McIntosh@Sun.COM 
9112890SJoyce.McIntosh@Sun.COM 	if (smb_oplock_initialized)
9212890SJoyce.McIntosh@Sun.COM 		return (0);
9312890SJoyce.McIntosh@Sun.COM 
9412890SJoyce.McIntosh@Sun.COM 	smb_oplock_break_cache = kmem_cache_create("smb_oplock_break_cache",
9512890SJoyce.McIntosh@Sun.COM 	    sizeof (smb_oplock_break_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
9612890SJoyce.McIntosh@Sun.COM 
9712890SJoyce.McIntosh@Sun.COM 	smb_llist_constructor(&smb_oplock_breaks, sizeof (smb_oplock_break_t),
9812890SJoyce.McIntosh@Sun.COM 	    offsetof(smb_oplock_break_t, ob_lnd));
9912890SJoyce.McIntosh@Sun.COM 
10012890SJoyce.McIntosh@Sun.COM 	smb_thread_init(&smb_oplock_thread, "smb_thread_oplock_break",
101*13138SJose.Borrego@Sun.COM 	    smb_oplock_break_thread, NULL);
10212890SJoyce.McIntosh@Sun.COM 
10312890SJoyce.McIntosh@Sun.COM 	rc = smb_thread_start(&smb_oplock_thread);
10412890SJoyce.McIntosh@Sun.COM 	if (rc != 0) {
10512890SJoyce.McIntosh@Sun.COM 		smb_thread_destroy(&smb_oplock_thread);
10612890SJoyce.McIntosh@Sun.COM 		smb_llist_destructor(&smb_oplock_breaks);
10712890SJoyce.McIntosh@Sun.COM 		kmem_cache_destroy(smb_oplock_break_cache);
10812890SJoyce.McIntosh@Sun.COM 		return (rc);
10912890SJoyce.McIntosh@Sun.COM 	}
11012890SJoyce.McIntosh@Sun.COM 
11112890SJoyce.McIntosh@Sun.COM 	smb_oplock_initialized = B_TRUE;
11212890SJoyce.McIntosh@Sun.COM 	return (0);
11312890SJoyce.McIntosh@Sun.COM }
11412890SJoyce.McIntosh@Sun.COM 
11512890SJoyce.McIntosh@Sun.COM /*
11612890SJoyce.McIntosh@Sun.COM  * smb_oplock_fini
11712890SJoyce.McIntosh@Sun.COM  * This function is not multi-thread safe. The caller must make sure only one
11812890SJoyce.McIntosh@Sun.COM  * thread makes the call.
1196600Sas200622  */
12012890SJoyce.McIntosh@Sun.COM void
smb_oplock_fini(void)12112890SJoyce.McIntosh@Sun.COM smb_oplock_fini(void)
12212890SJoyce.McIntosh@Sun.COM {
12312890SJoyce.McIntosh@Sun.COM 	smb_oplock_break_t	*ob;
12412890SJoyce.McIntosh@Sun.COM 
12512890SJoyce.McIntosh@Sun.COM 	if (!smb_oplock_initialized)
12612890SJoyce.McIntosh@Sun.COM 		return;
12712890SJoyce.McIntosh@Sun.COM 
12812890SJoyce.McIntosh@Sun.COM 	smb_thread_stop(&smb_oplock_thread);
12912890SJoyce.McIntosh@Sun.COM 	smb_thread_destroy(&smb_oplock_thread);
13012890SJoyce.McIntosh@Sun.COM 
13112890SJoyce.McIntosh@Sun.COM 	while ((ob = smb_llist_head(&smb_oplock_breaks)) != NULL) {
13212890SJoyce.McIntosh@Sun.COM 		SMB_OPLOCK_BREAK_VALID(ob);
13312890SJoyce.McIntosh@Sun.COM 		smb_llist_remove(&smb_oplock_breaks, ob);
13412890SJoyce.McIntosh@Sun.COM 		smb_oplock_delete_break(ob);
13512890SJoyce.McIntosh@Sun.COM 	}
13612890SJoyce.McIntosh@Sun.COM 	smb_llist_destructor(&smb_oplock_breaks);
13712890SJoyce.McIntosh@Sun.COM 
13812890SJoyce.McIntosh@Sun.COM 	kmem_cache_destroy(smb_oplock_break_cache);
13912890SJoyce.McIntosh@Sun.COM }
14012890SJoyce.McIntosh@Sun.COM 
14112890SJoyce.McIntosh@Sun.COM /*
14212890SJoyce.McIntosh@Sun.COM  * smb_oplock_install_fem
14312890SJoyce.McIntosh@Sun.COM  * Install fem monitor for cross protocol oplock breaking.
14412890SJoyce.McIntosh@Sun.COM  */
14512890SJoyce.McIntosh@Sun.COM static int
smb_oplock_install_fem(smb_node_t * node)14612890SJoyce.McIntosh@Sun.COM smb_oplock_install_fem(smb_node_t *node)
14712890SJoyce.McIntosh@Sun.COM {
14812890SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
14912890SJoyce.McIntosh@Sun.COM 
15012890SJoyce.McIntosh@Sun.COM 	if (node->n_oplock.ol_fem == B_FALSE) {
15112890SJoyce.McIntosh@Sun.COM 		if (smb_fem_oplock_install(node) != 0) {
15212890SJoyce.McIntosh@Sun.COM 			cmn_err(CE_NOTE, "No oplock granted: "
15312890SJoyce.McIntosh@Sun.COM 			    "failed to install fem monitor %s",
15412890SJoyce.McIntosh@Sun.COM 			    node->vp->v_path);
15512890SJoyce.McIntosh@Sun.COM 			return (-1);
15612890SJoyce.McIntosh@Sun.COM 		}
15712890SJoyce.McIntosh@Sun.COM 		node->n_oplock.ol_fem = B_TRUE;
15812890SJoyce.McIntosh@Sun.COM 	}
15912890SJoyce.McIntosh@Sun.COM 	return (0);
16012890SJoyce.McIntosh@Sun.COM }
16112890SJoyce.McIntosh@Sun.COM 
16212890SJoyce.McIntosh@Sun.COM /*
16312890SJoyce.McIntosh@Sun.COM  * smb_oplock_uninstall_fem
16412890SJoyce.McIntosh@Sun.COM  * Uninstall fem monitor for cross protocol oplock breaking.
16512890SJoyce.McIntosh@Sun.COM  */
16612890SJoyce.McIntosh@Sun.COM static void
smb_oplock_uninstall_fem(smb_node_t * node)16712890SJoyce.McIntosh@Sun.COM smb_oplock_uninstall_fem(smb_node_t *node)
16812890SJoyce.McIntosh@Sun.COM {
16912890SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
17012890SJoyce.McIntosh@Sun.COM 
17112890SJoyce.McIntosh@Sun.COM 	if (node->n_oplock.ol_fem) {
17212890SJoyce.McIntosh@Sun.COM 		if (smb_fem_oplock_uninstall(node) == 0) {
17312890SJoyce.McIntosh@Sun.COM 			node->n_oplock.ol_fem = B_FALSE;
17412890SJoyce.McIntosh@Sun.COM 		} else {
17512890SJoyce.McIntosh@Sun.COM 			cmn_err(CE_NOTE,
17612890SJoyce.McIntosh@Sun.COM 			    "failed to uninstall fem monitor %s",
17712890SJoyce.McIntosh@Sun.COM 			    node->vp->v_path);
17812890SJoyce.McIntosh@Sun.COM 		}
17912890SJoyce.McIntosh@Sun.COM 	}
18012890SJoyce.McIntosh@Sun.COM }
1816600Sas200622 
1826600Sas200622 /*
1836600Sas200622  * smb_oplock_acquire
1846600Sas200622  *
18512890SJoyce.McIntosh@Sun.COM  * Attempt to acquire an oplock. Clients will request EXCLUSIVE or BATCH,
18612890SJoyce.McIntosh@Sun.COM  * but might only be granted LEVEL_II or NONE.
1876600Sas200622  *
18812890SJoyce.McIntosh@Sun.COM  * If oplocks are not supported on the tree, or node, grant NONE.
18912890SJoyce.McIntosh@Sun.COM  * If nobody else has the file open, grant the requested level.
19012890SJoyce.McIntosh@Sun.COM  * If any of the following are true, grant NONE:
19112890SJoyce.McIntosh@Sun.COM  * - there is an exclusive oplock on the node
19212890SJoyce.McIntosh@Sun.COM  * - op->op_oplock_levelII is B_FALSE (LEVEL_II not supported by open cmd.
19312890SJoyce.McIntosh@Sun.COM  * - LEVEL_II oplocks are not supported for the session
19412890SJoyce.McIntosh@Sun.COM  * - a BATCH oplock is requested on a named stream
19512890SJoyce.McIntosh@Sun.COM  * - there are any range locks on the node
19612890SJoyce.McIntosh@Sun.COM  * Otherwise, grant LEVEL_II.
1976600Sas200622  *
19812890SJoyce.McIntosh@Sun.COM  * ol->ol_xthread is set to the current thread to lock the oplock against
19912890SJoyce.McIntosh@Sun.COM  * other operations until the acquire response is on the wire. When the
20012890SJoyce.McIntosh@Sun.COM  * acquire response is on the wire, smb_oplock_broadcast() is called to
20112890SJoyce.McIntosh@Sun.COM  * reset ol->ol_xthread and wake any waiting threads.
2026600Sas200622  */
2038934SJose.Borrego@Sun.COM void
smb_oplock_acquire(smb_request_t * sr,smb_node_t * node,smb_ofile_t * ofile)20412890SJoyce.McIntosh@Sun.COM smb_oplock_acquire(smb_request_t *sr, smb_node_t *node, smb_ofile_t *ofile)
2056600Sas200622 {
20612890SJoyce.McIntosh@Sun.COM 	smb_oplock_t		*ol;
20712890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
20812890SJoyce.McIntosh@Sun.COM 	list_t			*grants;
20912890SJoyce.McIntosh@Sun.COM 	smb_arg_open_t		*op;
21012890SJoyce.McIntosh@Sun.COM 	smb_tree_t		*tree;
21112890SJoyce.McIntosh@Sun.COM 	smb_session_t		*session;
2128934SJose.Borrego@Sun.COM 
2138934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
21412890SJoyce.McIntosh@Sun.COM 	SMB_OFILE_VALID(ofile);
2158934SJose.Borrego@Sun.COM 
21612890SJoyce.McIntosh@Sun.COM 	ASSERT(node == SMB_OFILE_GET_NODE(ofile));
2178934SJose.Borrego@Sun.COM 
21812890SJoyce.McIntosh@Sun.COM 	op = &sr->sr_open;
21912890SJoyce.McIntosh@Sun.COM 	tree = SMB_OFILE_GET_TREE(ofile);
22012890SJoyce.McIntosh@Sun.COM 	session = SMB_OFILE_GET_SESSION(ofile);
2218934SJose.Borrego@Sun.COM 
22212890SJoyce.McIntosh@Sun.COM 	if (!smb_tree_has_feature(tree, SMB_TREE_OPLOCKS) ||
22312890SJoyce.McIntosh@Sun.COM 	    (op->op_oplock_level == SMB_OPLOCK_NONE) ||
22412890SJoyce.McIntosh@Sun.COM 	    ((op->op_oplock_level == SMB_OPLOCK_BATCH) &&
22512890SJoyce.McIntosh@Sun.COM 	    SMB_IS_STREAM(node))) {
2268934SJose.Borrego@Sun.COM 		op->op_oplock_level = SMB_OPLOCK_NONE;
2278934SJose.Borrego@Sun.COM 		return;
2288934SJose.Borrego@Sun.COM 	}
2298934SJose.Borrego@Sun.COM 
2308934SJose.Borrego@Sun.COM 	ol = &node->n_oplock;
23112890SJoyce.McIntosh@Sun.COM 	grants = &ol->ol_grants;
2328934SJose.Borrego@Sun.COM 
23312890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
23412890SJoyce.McIntosh@Sun.COM 	smb_oplock_wait(node);
23512890SJoyce.McIntosh@Sun.COM 
23612890SJoyce.McIntosh@Sun.COM 	nbl_start_crit(node->vp, RW_READER);
2376600Sas200622 
23812890SJoyce.McIntosh@Sun.COM 	if ((node->n_open_count > 1) ||
23912890SJoyce.McIntosh@Sun.COM 	    (node->n_opening_count > 1) ||
24012890SJoyce.McIntosh@Sun.COM 	    smb_vop_other_opens(node->vp, ofile->f_mode)) {
24112890SJoyce.McIntosh@Sun.COM 		if ((!op->op_oplock_levelII) ||
24212890SJoyce.McIntosh@Sun.COM 		    (!smb_session_levelII_oplocks(session)) ||
24312890SJoyce.McIntosh@Sun.COM 		    (smb_oplock_exclusive_grant(grants) != NULL) ||
24412890SJoyce.McIntosh@Sun.COM 		    (smb_range_check(sr, node, 0, UINT64_MAX, B_TRUE) != 0)) {
24512890SJoyce.McIntosh@Sun.COM 			op->op_oplock_level = SMB_OPLOCK_NONE;
24612890SJoyce.McIntosh@Sun.COM 			nbl_end_crit(node->vp);
24712890SJoyce.McIntosh@Sun.COM 			mutex_exit(&ol->ol_mutex);
2489021Samw@Sun.COM 			return;
2499021Samw@Sun.COM 		}
25012890SJoyce.McIntosh@Sun.COM 
25112890SJoyce.McIntosh@Sun.COM 		op->op_oplock_level = SMB_OPLOCK_LEVEL_II;
25212890SJoyce.McIntosh@Sun.COM 	}
25312890SJoyce.McIntosh@Sun.COM 
25412890SJoyce.McIntosh@Sun.COM 	nbl_end_crit(node->vp);
25512890SJoyce.McIntosh@Sun.COM 
25612914SJoyce.McIntosh@Sun.COM 	og = smb_oplock_set_grant(ofile, op->op_oplock_level);
25712890SJoyce.McIntosh@Sun.COM 	if (smb_oplock_insert_grant(node, og) != 0) {
25812914SJoyce.McIntosh@Sun.COM 		smb_oplock_clear_grant(og);
25912890SJoyce.McIntosh@Sun.COM 		op->op_oplock_level = SMB_OPLOCK_NONE;
26012890SJoyce.McIntosh@Sun.COM 		mutex_exit(&ol->ol_mutex);
26112890SJoyce.McIntosh@Sun.COM 		return;
26212890SJoyce.McIntosh@Sun.COM 	}
26312890SJoyce.McIntosh@Sun.COM 
26412890SJoyce.McIntosh@Sun.COM 	ol->ol_xthread = curthread;
26512890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
26612890SJoyce.McIntosh@Sun.COM }
26712890SJoyce.McIntosh@Sun.COM 
26812890SJoyce.McIntosh@Sun.COM /*
26912890SJoyce.McIntosh@Sun.COM  * smb_oplock_break
27012890SJoyce.McIntosh@Sun.COM  *
27112890SJoyce.McIntosh@Sun.COM  * Break granted oplocks according to the following rules:
27212890SJoyce.McIntosh@Sun.COM  *
27312890SJoyce.McIntosh@Sun.COM  * If there's an exclusive oplock granted on the node
27412890SJoyce.McIntosh@Sun.COM  *  - if the BREAK_BATCH flags is specified and the oplock is not
27512890SJoyce.McIntosh@Sun.COM  *    a batch oplock, no break is required.
27612890SJoyce.McIntosh@Sun.COM  *  - if the session doesn't support LEVEL II oplocks, and 'brk' is
27712890SJoyce.McIntosh@Sun.COM  *    BREAK_TO_LEVEL_II, do a BREAK_TO_NONE.
27812890SJoyce.McIntosh@Sun.COM  *  - if the oplock is already breaking update the break level (if
27912890SJoyce.McIntosh@Sun.COM  *    the requested break is to a lesser level), otherwise send an
28012890SJoyce.McIntosh@Sun.COM  *    oplock break.
28112890SJoyce.McIntosh@Sun.COM  *    Wait for acknowledgement of the break (unless NOWAIT flag is set)
28212890SJoyce.McIntosh@Sun.COM  *
28312890SJoyce.McIntosh@Sun.COM  * Otherwise:
28412890SJoyce.McIntosh@Sun.COM  * If there are level II oplocks granted on the node, and the flags
28512890SJoyce.McIntosh@Sun.COM  * indicate that they should be broken (BREAK_TO_NONE specified,
28612890SJoyce.McIntosh@Sun.COM  * BREAK_EXCLUSIVE, BREAK_BATCH not specified) queue the levelII
28712890SJoyce.McIntosh@Sun.COM  * break request for asynchronous processing.
28812890SJoyce.McIntosh@Sun.COM  *
28912890SJoyce.McIntosh@Sun.COM  * Returns:
29012890SJoyce.McIntosh@Sun.COM  *       0 - oplock broken (or no break required)
29112890SJoyce.McIntosh@Sun.COM  *  EAGAIN - oplock break request sent and would block
29212890SJoyce.McIntosh@Sun.COM  *           awaiting the reponse but NOWAIT was specified
29312890SJoyce.McIntosh@Sun.COM  */
29412890SJoyce.McIntosh@Sun.COM int
smb_oplock_break(smb_request_t * sr,smb_node_t * node,uint32_t flags)29512890SJoyce.McIntosh@Sun.COM smb_oplock_break(smb_request_t *sr, smb_node_t *node, uint32_t flags)
29612890SJoyce.McIntosh@Sun.COM {
29712890SJoyce.McIntosh@Sun.COM 	smb_oplock_t		*ol;
29812890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
29912890SJoyce.McIntosh@Sun.COM 	list_t			*grants;
30012890SJoyce.McIntosh@Sun.COM 	uint32_t		timeout;
30112890SJoyce.McIntosh@Sun.COM 	uint8_t			brk;
30212890SJoyce.McIntosh@Sun.COM 
30312890SJoyce.McIntosh@Sun.COM 	SMB_NODE_VALID(node);
30412890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
30512890SJoyce.McIntosh@Sun.COM 	grants = &ol->ol_grants;
30612890SJoyce.McIntosh@Sun.COM 
30712890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
30812890SJoyce.McIntosh@Sun.COM 	smb_oplock_wait(node);
30912890SJoyce.McIntosh@Sun.COM 
31012890SJoyce.McIntosh@Sun.COM 	og = list_head(grants);
31112890SJoyce.McIntosh@Sun.COM 	if (og == NULL) {
31212890SJoyce.McIntosh@Sun.COM 		mutex_exit(&ol->ol_mutex);
31312890SJoyce.McIntosh@Sun.COM 		return (0);
31412890SJoyce.McIntosh@Sun.COM 	}
31512890SJoyce.McIntosh@Sun.COM 
31612890SJoyce.McIntosh@Sun.COM 	SMB_OPLOCK_GRANT_VALID(og);
31712890SJoyce.McIntosh@Sun.COM 
31812890SJoyce.McIntosh@Sun.COM 	/* break levelII oplocks */
31912890SJoyce.McIntosh@Sun.COM 	if (og->og_level == SMB_OPLOCK_LEVEL_II) {
32012890SJoyce.McIntosh@Sun.COM 		mutex_exit(&ol->ol_mutex);
32112890SJoyce.McIntosh@Sun.COM 
32212890SJoyce.McIntosh@Sun.COM 		if ((flags & SMB_OPLOCK_BREAK_TO_NONE) &&
32312890SJoyce.McIntosh@Sun.COM 		    !(flags & SMB_OPLOCK_BREAK_EXCLUSIVE) &&
32412890SJoyce.McIntosh@Sun.COM 		    !(flags & SMB_OPLOCK_BREAK_BATCH))  {
32512890SJoyce.McIntosh@Sun.COM 			smb_oplock_break_levelII(node);
32612890SJoyce.McIntosh@Sun.COM 		}
32712890SJoyce.McIntosh@Sun.COM 		return (0);
32812890SJoyce.McIntosh@Sun.COM 	}
32912890SJoyce.McIntosh@Sun.COM 
33012890SJoyce.McIntosh@Sun.COM 	/* break exclusive oplock */
33112890SJoyce.McIntosh@Sun.COM 	if ((flags & SMB_OPLOCK_BREAK_BATCH) &&
33212890SJoyce.McIntosh@Sun.COM 	    (og->og_level != SMB_OPLOCK_BATCH)) {
33312890SJoyce.McIntosh@Sun.COM 		mutex_exit(&ol->ol_mutex);
33412890SJoyce.McIntosh@Sun.COM 		return (0);
33512890SJoyce.McIntosh@Sun.COM 	}
33612890SJoyce.McIntosh@Sun.COM 
33712890SJoyce.McIntosh@Sun.COM 	if ((flags & SMB_OPLOCK_BREAK_TO_LEVEL_II) &&
33812890SJoyce.McIntosh@Sun.COM 	    smb_session_levelII_oplocks(og->og_session)) {
33912890SJoyce.McIntosh@Sun.COM 		brk = SMB_OPLOCK_BREAK_TO_LEVEL_II;
34012890SJoyce.McIntosh@Sun.COM 	} else {
34112890SJoyce.McIntosh@Sun.COM 		brk = SMB_OPLOCK_BREAK_TO_NONE;
34212890SJoyce.McIntosh@Sun.COM 	}
34312890SJoyce.McIntosh@Sun.COM 
34412890SJoyce.McIntosh@Sun.COM 	switch (ol->ol_break) {
34512890SJoyce.McIntosh@Sun.COM 	case SMB_OPLOCK_NO_BREAK:
34612890SJoyce.McIntosh@Sun.COM 		ol->ol_break = brk;
34712890SJoyce.McIntosh@Sun.COM 		smb_session_oplock_break(og->og_session,
34812890SJoyce.McIntosh@Sun.COM 		    og->og_tid, og->og_fid, brk);
3499021Samw@Sun.COM 		break;
35012890SJoyce.McIntosh@Sun.COM 	case SMB_OPLOCK_BREAK_TO_LEVEL_II:
35112890SJoyce.McIntosh@Sun.COM 		if (brk == SMB_OPLOCK_BREAK_TO_NONE)
35212890SJoyce.McIntosh@Sun.COM 			ol->ol_break = SMB_OPLOCK_BREAK_TO_NONE;
35312890SJoyce.McIntosh@Sun.COM 		break;
35412890SJoyce.McIntosh@Sun.COM 	case SMB_OPLOCK_BREAK_TO_NONE:
35512890SJoyce.McIntosh@Sun.COM 	default:
35612890SJoyce.McIntosh@Sun.COM 		break;
35712890SJoyce.McIntosh@Sun.COM 	}
35812890SJoyce.McIntosh@Sun.COM 
35912890SJoyce.McIntosh@Sun.COM 	if (flags & SMB_OPLOCK_BREAK_NOWAIT) {
36012890SJoyce.McIntosh@Sun.COM 		mutex_exit(&ol->ol_mutex);
36112890SJoyce.McIntosh@Sun.COM 		return (EAGAIN);
36212890SJoyce.McIntosh@Sun.COM 	}
36312890SJoyce.McIntosh@Sun.COM 
36412890SJoyce.McIntosh@Sun.COM 	if (sr && (sr->session == og->og_session) &&
36512890SJoyce.McIntosh@Sun.COM 	    (sr->smb_uid == og->og_uid)) {
36612890SJoyce.McIntosh@Sun.COM 		timeout = smb_oplock_min_timeout;
36712890SJoyce.McIntosh@Sun.COM 	} else {
36812890SJoyce.McIntosh@Sun.COM 		timeout = smb_oplock_timeout;
36912890SJoyce.McIntosh@Sun.COM 	}
37012890SJoyce.McIntosh@Sun.COM 
37112890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
37212890SJoyce.McIntosh@Sun.COM 	smb_oplock_wait_ack(node, timeout);
37312890SJoyce.McIntosh@Sun.COM 	return (0);
37412890SJoyce.McIntosh@Sun.COM }
37512890SJoyce.McIntosh@Sun.COM 
37612890SJoyce.McIntosh@Sun.COM /*
37712890SJoyce.McIntosh@Sun.COM  * smb_oplock_break_levelII
37812890SJoyce.McIntosh@Sun.COM  *
37912890SJoyce.McIntosh@Sun.COM  * LevelII (shared) oplock breaks are processed asynchronously.
38012890SJoyce.McIntosh@Sun.COM  * Unlike exclusive oplock breaks, the thread initiating the break
38112890SJoyce.McIntosh@Sun.COM  * is NOT blocked while the request is processed.
38212890SJoyce.McIntosh@Sun.COM  *
38312890SJoyce.McIntosh@Sun.COM  * Create an oplock_break_request and add it to the list for async
38412890SJoyce.McIntosh@Sun.COM  * processing.
38512890SJoyce.McIntosh@Sun.COM  */
38612890SJoyce.McIntosh@Sun.COM void
smb_oplock_break_levelII(smb_node_t * node)38712890SJoyce.McIntosh@Sun.COM smb_oplock_break_levelII(smb_node_t *node)
38812890SJoyce.McIntosh@Sun.COM {
38912890SJoyce.McIntosh@Sun.COM 	smb_oplock_break_t	*ob;
39012890SJoyce.McIntosh@Sun.COM 
39112890SJoyce.McIntosh@Sun.COM 	ob = smb_oplock_create_break(node);
39212890SJoyce.McIntosh@Sun.COM 
39312890SJoyce.McIntosh@Sun.COM 	smb_llist_enter(&smb_oplock_breaks, RW_WRITER);
39412890SJoyce.McIntosh@Sun.COM 	smb_llist_insert_tail(&smb_oplock_breaks, ob);
39512890SJoyce.McIntosh@Sun.COM 	smb_llist_exit(&smb_oplock_breaks);
39612890SJoyce.McIntosh@Sun.COM 
39712890SJoyce.McIntosh@Sun.COM 	smb_thread_signal(&smb_oplock_thread);
39812890SJoyce.McIntosh@Sun.COM }
39912890SJoyce.McIntosh@Sun.COM 
40012890SJoyce.McIntosh@Sun.COM /*
40112890SJoyce.McIntosh@Sun.COM  * smb_oplock_break_thread
40212890SJoyce.McIntosh@Sun.COM  *
40312890SJoyce.McIntosh@Sun.COM  * The smb_oplock_thread is woken when an oplock break request is
40412890SJoyce.McIntosh@Sun.COM  * added to the list of pending levelII oplock break requests.
40512890SJoyce.McIntosh@Sun.COM  * Gets the oplock break request from the list, processes it and
40612890SJoyce.McIntosh@Sun.COM  * deletes it.
40712890SJoyce.McIntosh@Sun.COM  */
40812890SJoyce.McIntosh@Sun.COM /*ARGSUSED*/
40912890SJoyce.McIntosh@Sun.COM static void
smb_oplock_break_thread(smb_thread_t * thread,void * arg)41012890SJoyce.McIntosh@Sun.COM smb_oplock_break_thread(smb_thread_t *thread, void *arg)
41112890SJoyce.McIntosh@Sun.COM {
41212890SJoyce.McIntosh@Sun.COM 	smb_oplock_break_t	*ob;
41312890SJoyce.McIntosh@Sun.COM 
41412890SJoyce.McIntosh@Sun.COM 	while (smb_thread_continue(thread)) {
41512890SJoyce.McIntosh@Sun.COM 		while ((ob = smb_oplock_get_break()) != NULL) {
41612890SJoyce.McIntosh@Sun.COM 			smb_oplock_process_levelII_break(ob->ob_node);
41712890SJoyce.McIntosh@Sun.COM 			smb_oplock_delete_break(ob);
41812890SJoyce.McIntosh@Sun.COM 		}
41912890SJoyce.McIntosh@Sun.COM 	}
42012890SJoyce.McIntosh@Sun.COM }
42112890SJoyce.McIntosh@Sun.COM 
42212890SJoyce.McIntosh@Sun.COM /*
42312890SJoyce.McIntosh@Sun.COM  * smb_oplock_get_break
42412890SJoyce.McIntosh@Sun.COM  *
42512890SJoyce.McIntosh@Sun.COM  * Remove and return the next oplock break request from the list
42612890SJoyce.McIntosh@Sun.COM  */
42712890SJoyce.McIntosh@Sun.COM static smb_oplock_break_t *
smb_oplock_get_break(void)42812890SJoyce.McIntosh@Sun.COM smb_oplock_get_break(void)
42912890SJoyce.McIntosh@Sun.COM {
43012890SJoyce.McIntosh@Sun.COM 	smb_oplock_break_t	*ob;
43112890SJoyce.McIntosh@Sun.COM 
43212890SJoyce.McIntosh@Sun.COM 	smb_llist_enter(&smb_oplock_breaks, RW_WRITER);
43312890SJoyce.McIntosh@Sun.COM 	if ((ob = smb_llist_head(&smb_oplock_breaks)) != NULL) {
43412890SJoyce.McIntosh@Sun.COM 		SMB_OPLOCK_BREAK_VALID(ob);
43512890SJoyce.McIntosh@Sun.COM 		smb_llist_remove(&smb_oplock_breaks, ob);
43612890SJoyce.McIntosh@Sun.COM 	}
43712890SJoyce.McIntosh@Sun.COM 	smb_llist_exit(&smb_oplock_breaks);
43812890SJoyce.McIntosh@Sun.COM 	return (ob);
43912890SJoyce.McIntosh@Sun.COM }
44012890SJoyce.McIntosh@Sun.COM 
44112890SJoyce.McIntosh@Sun.COM /*
44212890SJoyce.McIntosh@Sun.COM  * smb_oplock_process_levelII_break
44312890SJoyce.McIntosh@Sun.COM  */
44412890SJoyce.McIntosh@Sun.COM void
smb_oplock_process_levelII_break(smb_node_t * node)44512890SJoyce.McIntosh@Sun.COM smb_oplock_process_levelII_break(smb_node_t *node)
44612890SJoyce.McIntosh@Sun.COM {
44712890SJoyce.McIntosh@Sun.COM 	smb_oplock_t		*ol;
44812890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
44912890SJoyce.McIntosh@Sun.COM 	list_t			*grants;
45012890SJoyce.McIntosh@Sun.COM 
45112890SJoyce.McIntosh@Sun.COM 	if (!smb_oplock_levelII)
45212890SJoyce.McIntosh@Sun.COM 		return;
45312890SJoyce.McIntosh@Sun.COM 
45412890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
45512890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
45612890SJoyce.McIntosh@Sun.COM 	smb_oplock_wait(node);
45712890SJoyce.McIntosh@Sun.COM 	grants = &node->n_oplock.ol_grants;
45812890SJoyce.McIntosh@Sun.COM 
45912890SJoyce.McIntosh@Sun.COM 	while ((og = list_head(grants)) != NULL) {
46012890SJoyce.McIntosh@Sun.COM 		SMB_OPLOCK_GRANT_VALID(og);
46112890SJoyce.McIntosh@Sun.COM 
46212890SJoyce.McIntosh@Sun.COM 		if (SMB_OPLOCK_IS_EXCLUSIVE(og->og_level))
46312890SJoyce.McIntosh@Sun.COM 			break;
46412890SJoyce.McIntosh@Sun.COM 
46512890SJoyce.McIntosh@Sun.COM 		smb_session_oplock_break(og->og_session,
46612890SJoyce.McIntosh@Sun.COM 		    og->og_tid, og->og_fid, SMB_OPLOCK_BREAK_TO_NONE);
46712890SJoyce.McIntosh@Sun.COM 		smb_oplock_remove_grant(node, og);
46812914SJoyce.McIntosh@Sun.COM 		smb_oplock_clear_grant(og);
46912890SJoyce.McIntosh@Sun.COM 	}
47012890SJoyce.McIntosh@Sun.COM 
47112890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
47212890SJoyce.McIntosh@Sun.COM }
47312890SJoyce.McIntosh@Sun.COM 
47412890SJoyce.McIntosh@Sun.COM /*
47512890SJoyce.McIntosh@Sun.COM  * smb_oplock_wait_ack
47612890SJoyce.McIntosh@Sun.COM  *
47712890SJoyce.McIntosh@Sun.COM  * Timed wait for an oplock break acknowledgement (or oplock release).
47812890SJoyce.McIntosh@Sun.COM  */
47912890SJoyce.McIntosh@Sun.COM static void
smb_oplock_wait_ack(smb_node_t * node,uint32_t timeout)48012890SJoyce.McIntosh@Sun.COM smb_oplock_wait_ack(smb_node_t *node, uint32_t timeout)
48112890SJoyce.McIntosh@Sun.COM {
48212890SJoyce.McIntosh@Sun.COM 	smb_oplock_t	*ol;
48312890SJoyce.McIntosh@Sun.COM 	clock_t		time;
48412890SJoyce.McIntosh@Sun.COM 
48512890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
48612890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
48712890SJoyce.McIntosh@Sun.COM 	time = MSEC_TO_TICK(timeout) + ddi_get_lbolt();
48812890SJoyce.McIntosh@Sun.COM 
48912890SJoyce.McIntosh@Sun.COM 	while (ol->ol_break != SMB_OPLOCK_NO_BREAK) {
49012890SJoyce.McIntosh@Sun.COM 		if (cv_timedwait(&ol->ol_cv, &ol->ol_mutex, time) < 0) {
49112890SJoyce.McIntosh@Sun.COM 			smb_oplock_timedout(node);
49212890SJoyce.McIntosh@Sun.COM 			cv_broadcast(&ol->ol_cv);
49312890SJoyce.McIntosh@Sun.COM 			break;
49412890SJoyce.McIntosh@Sun.COM 		}
49512890SJoyce.McIntosh@Sun.COM 	}
49612890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
49712890SJoyce.McIntosh@Sun.COM }
49812890SJoyce.McIntosh@Sun.COM 
49912890SJoyce.McIntosh@Sun.COM /*
50012890SJoyce.McIntosh@Sun.COM  * smb_oplock_timedout
50112890SJoyce.McIntosh@Sun.COM  *
50212890SJoyce.McIntosh@Sun.COM  * An oplock break has not been acknowledged within timeout
50312890SJoyce.McIntosh@Sun.COM  * 'smb_oplock_timeout'.
50412890SJoyce.McIntosh@Sun.COM  * Set oplock grant to the desired break level.
50512890SJoyce.McIntosh@Sun.COM  */
50612890SJoyce.McIntosh@Sun.COM static void
smb_oplock_timedout(smb_node_t * node)50712890SJoyce.McIntosh@Sun.COM smb_oplock_timedout(smb_node_t *node)
50812890SJoyce.McIntosh@Sun.COM {
50912890SJoyce.McIntosh@Sun.COM 	smb_oplock_t		*ol;
51012890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
51112890SJoyce.McIntosh@Sun.COM 	list_t			*grants;
51212890SJoyce.McIntosh@Sun.COM 
51312890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
51412890SJoyce.McIntosh@Sun.COM 	grants = &ol->ol_grants;
51512890SJoyce.McIntosh@Sun.COM 
51612890SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&ol->ol_mutex));
51712890SJoyce.McIntosh@Sun.COM 
51812890SJoyce.McIntosh@Sun.COM 	og = smb_oplock_exclusive_grant(grants);
51912890SJoyce.McIntosh@Sun.COM 	if (og) {
52012890SJoyce.McIntosh@Sun.COM 		switch (ol->ol_break) {
52112890SJoyce.McIntosh@Sun.COM 		case SMB_OPLOCK_BREAK_TO_NONE:
52212890SJoyce.McIntosh@Sun.COM 			og->og_level = SMB_OPLOCK_NONE;
52312890SJoyce.McIntosh@Sun.COM 			smb_oplock_remove_grant(node, og);
52412914SJoyce.McIntosh@Sun.COM 			smb_oplock_clear_grant(og);
52512890SJoyce.McIntosh@Sun.COM 			break;
52612890SJoyce.McIntosh@Sun.COM 		case SMB_OPLOCK_BREAK_TO_LEVEL_II:
52712890SJoyce.McIntosh@Sun.COM 			og->og_level = SMB_OPLOCK_LEVEL_II;
52812890SJoyce.McIntosh@Sun.COM 			break;
52912890SJoyce.McIntosh@Sun.COM 		default:
53012890SJoyce.McIntosh@Sun.COM 			SMB_PANIC();
53112890SJoyce.McIntosh@Sun.COM 		}
53212890SJoyce.McIntosh@Sun.COM 	}
53312890SJoyce.McIntosh@Sun.COM 	ol->ol_break = SMB_OPLOCK_NO_BREAK;
53412890SJoyce.McIntosh@Sun.COM }
53512890SJoyce.McIntosh@Sun.COM 
53612890SJoyce.McIntosh@Sun.COM /*
53712890SJoyce.McIntosh@Sun.COM  * smb_oplock_release
53812890SJoyce.McIntosh@Sun.COM  *
53912890SJoyce.McIntosh@Sun.COM  * Release the oplock granted on ofile 'of'.
54012890SJoyce.McIntosh@Sun.COM  * Wake any threads waiting for an oplock break acknowledgement for
54112890SJoyce.McIntosh@Sun.COM  * this oplock.
54212890SJoyce.McIntosh@Sun.COM  * This is called when the ofile is being closed.
54312890SJoyce.McIntosh@Sun.COM  */
54412890SJoyce.McIntosh@Sun.COM void
smb_oplock_release(smb_node_t * node,smb_ofile_t * of)54512890SJoyce.McIntosh@Sun.COM smb_oplock_release(smb_node_t *node, smb_ofile_t *of)
54612890SJoyce.McIntosh@Sun.COM {
54712890SJoyce.McIntosh@Sun.COM 	smb_oplock_t		*ol;
54812890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
54912890SJoyce.McIntosh@Sun.COM 
55012890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
55112890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
55212890SJoyce.McIntosh@Sun.COM 	smb_oplock_wait(node);
55312890SJoyce.McIntosh@Sun.COM 
55412914SJoyce.McIntosh@Sun.COM 	og = smb_oplock_get_grant(ol, of);
55512890SJoyce.McIntosh@Sun.COM 	if (og) {
55612890SJoyce.McIntosh@Sun.COM 		smb_oplock_remove_grant(node, og);
55712914SJoyce.McIntosh@Sun.COM 		smb_oplock_clear_grant(og);
55812890SJoyce.McIntosh@Sun.COM 
55912890SJoyce.McIntosh@Sun.COM 		if (ol->ol_break != SMB_OPLOCK_NO_BREAK) {
56012890SJoyce.McIntosh@Sun.COM 			ol->ol_break = SMB_OPLOCK_NO_BREAK;
56112890SJoyce.McIntosh@Sun.COM 			cv_broadcast(&ol->ol_cv);
56212890SJoyce.McIntosh@Sun.COM 		}
56312890SJoyce.McIntosh@Sun.COM 	}
56412890SJoyce.McIntosh@Sun.COM 
56512890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
56612890SJoyce.McIntosh@Sun.COM }
56712890SJoyce.McIntosh@Sun.COM 
56812890SJoyce.McIntosh@Sun.COM /*
56912890SJoyce.McIntosh@Sun.COM  * smb_oplock_ack
57012890SJoyce.McIntosh@Sun.COM  *
57112890SJoyce.McIntosh@Sun.COM  * Process oplock acknowledgement received for ofile 'of'.
57212890SJoyce.McIntosh@Sun.COM  * - oplock.ol_break is the break level that was requested.
57312890SJoyce.McIntosh@Sun.COM  * - brk is the break level being acknowledged by the client.
57412890SJoyce.McIntosh@Sun.COM  *
57512890SJoyce.McIntosh@Sun.COM  * Update the oplock grant level to the lesser of ol_break and brk.
57612890SJoyce.McIntosh@Sun.COM  * If the grant is now SMB_OPLOCK_NONE, remove the grant from the
57712890SJoyce.McIntosh@Sun.COM  * oplock's grant list and delete it.
57812890SJoyce.McIntosh@Sun.COM  * If the requested break level (ol_break) was NONE and the brk is
57912890SJoyce.McIntosh@Sun.COM  * LEVEL_II, send another oplock break (NONE). Do not wait for an
58012890SJoyce.McIntosh@Sun.COM  * acknowledgement.
58112890SJoyce.McIntosh@Sun.COM  * Wake any threads waiting for the oplock break acknowledgement.
58212890SJoyce.McIntosh@Sun.COM  */
58312890SJoyce.McIntosh@Sun.COM void
smb_oplock_ack(smb_node_t * node,smb_ofile_t * of,uint8_t brk)58412890SJoyce.McIntosh@Sun.COM smb_oplock_ack(smb_node_t *node, smb_ofile_t *of, uint8_t brk)
58512890SJoyce.McIntosh@Sun.COM {
58612890SJoyce.McIntosh@Sun.COM 	smb_oplock_t		*ol;
58712890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
58812890SJoyce.McIntosh@Sun.COM 	boolean_t		brk_to_none = B_FALSE;
58912890SJoyce.McIntosh@Sun.COM 
59012890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
59112890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
59212890SJoyce.McIntosh@Sun.COM 	smb_oplock_wait(node);
59312890SJoyce.McIntosh@Sun.COM 
59412890SJoyce.McIntosh@Sun.COM 	if ((ol->ol_break == SMB_OPLOCK_NO_BREAK) ||
59512914SJoyce.McIntosh@Sun.COM 	    ((og = smb_oplock_get_grant(ol, of)) == NULL)) {
59612890SJoyce.McIntosh@Sun.COM 		mutex_exit(&ol->ol_mutex);
59712890SJoyce.McIntosh@Sun.COM 		return;
59812890SJoyce.McIntosh@Sun.COM 	}
59912890SJoyce.McIntosh@Sun.COM 
60012890SJoyce.McIntosh@Sun.COM 	switch (brk) {
60112890SJoyce.McIntosh@Sun.COM 	case SMB_OPLOCK_BREAK_TO_NONE:
60212890SJoyce.McIntosh@Sun.COM 		og->og_level = SMB_OPLOCK_NONE;
60312890SJoyce.McIntosh@Sun.COM 		break;
60412890SJoyce.McIntosh@Sun.COM 	case SMB_OPLOCK_BREAK_TO_LEVEL_II:
60512890SJoyce.McIntosh@Sun.COM 		if (ol->ol_break == SMB_OPLOCK_BREAK_TO_LEVEL_II) {
60612890SJoyce.McIntosh@Sun.COM 			og->og_level = SMB_OPLOCK_LEVEL_II;
60712890SJoyce.McIntosh@Sun.COM 		} else {
60812890SJoyce.McIntosh@Sun.COM 			/* SMB_OPLOCK_BREAK_TO_NONE */
60912890SJoyce.McIntosh@Sun.COM 			og->og_level = SMB_OPLOCK_NONE;
61012890SJoyce.McIntosh@Sun.COM 			brk_to_none = B_TRUE;
61112890SJoyce.McIntosh@Sun.COM 		}
6129021Samw@Sun.COM 		break;
6139021Samw@Sun.COM 	default:
6149021Samw@Sun.COM 		SMB_PANIC();
6159021Samw@Sun.COM 	}
6169021Samw@Sun.COM 
61712890SJoyce.McIntosh@Sun.COM 	if (og->og_level == SMB_OPLOCK_NONE) {
61812890SJoyce.McIntosh@Sun.COM 		smb_oplock_remove_grant(node, og);
61912914SJoyce.McIntosh@Sun.COM 		smb_oplock_clear_grant(og);
62012890SJoyce.McIntosh@Sun.COM 	}
6216600Sas200622 
62212890SJoyce.McIntosh@Sun.COM 	ol->ol_break = SMB_OPLOCK_NO_BREAK;
62312890SJoyce.McIntosh@Sun.COM 	cv_broadcast(&ol->ol_cv);
6246600Sas200622 
62512890SJoyce.McIntosh@Sun.COM 	if (brk_to_none) {
62612890SJoyce.McIntosh@Sun.COM 		smb_session_oplock_break(of->f_session,
62712890SJoyce.McIntosh@Sun.COM 		    of->f_tree->t_tid, of->f_fid,
62812890SJoyce.McIntosh@Sun.COM 		    SMB_OPLOCK_BREAK_TO_NONE);
62912890SJoyce.McIntosh@Sun.COM 	}
6306600Sas200622 
63112890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
6326600Sas200622 }
6336600Sas200622 
6346600Sas200622 /*
63512890SJoyce.McIntosh@Sun.COM  * smb_oplock_broadcast
6366600Sas200622  *
63712890SJoyce.McIntosh@Sun.COM  * ol->ol_xthread identifies the thread that was performing an oplock
63812890SJoyce.McIntosh@Sun.COM  * acquire. Other threads may be blocked awaiting completion of the
63912890SJoyce.McIntosh@Sun.COM  * acquire.
64012890SJoyce.McIntosh@Sun.COM  * If the calling thread is ol_ol_xthread, wake any waiting threads.
6416600Sas200622  */
6428934SJose.Borrego@Sun.COM void
smb_oplock_broadcast(smb_node_t * node)64312890SJoyce.McIntosh@Sun.COM smb_oplock_broadcast(smb_node_t *node)
6448934SJose.Borrego@Sun.COM {
6458934SJose.Borrego@Sun.COM 	smb_oplock_t	*ol;
6466600Sas200622 
6478934SJose.Borrego@Sun.COM 	SMB_NODE_VALID(node);
6488934SJose.Borrego@Sun.COM 	ol = &node->n_oplock;
6498934SJose.Borrego@Sun.COM 
65012890SJoyce.McIntosh@Sun.COM 	mutex_enter(&ol->ol_mutex);
6518934SJose.Borrego@Sun.COM 	if ((ol->ol_xthread != NULL) && (ol->ol_xthread == curthread)) {
6528934SJose.Borrego@Sun.COM 		ol->ol_xthread = NULL;
6539021Samw@Sun.COM 		cv_broadcast(&ol->ol_cv);
6546600Sas200622 	}
65512890SJoyce.McIntosh@Sun.COM 	mutex_exit(&ol->ol_mutex);
6568934SJose.Borrego@Sun.COM }
6576600Sas200622 
6588934SJose.Borrego@Sun.COM /*
6598934SJose.Borrego@Sun.COM  * smb_oplock_wait
6608934SJose.Borrego@Sun.COM  *
66112890SJoyce.McIntosh@Sun.COM  * Wait for the completion of an oplock acquire.
66212890SJoyce.McIntosh@Sun.COM  * If ol_xthread is not NULL and doesn't contain the pointer to the
66312890SJoyce.McIntosh@Sun.COM  * context of the calling thread, the caller will sleep until the
66412890SJoyce.McIntosh@Sun.COM  * ol_xthread is reset to NULL (via smb_oplock_broadcast()).
6658934SJose.Borrego@Sun.COM  */
6668934SJose.Borrego@Sun.COM static void
smb_oplock_wait(smb_node_t * node)6679021Samw@Sun.COM smb_oplock_wait(smb_node_t *node)
6688934SJose.Borrego@Sun.COM {
66912890SJoyce.McIntosh@Sun.COM 	smb_oplock_t	*ol;
67012890SJoyce.McIntosh@Sun.COM 
67112890SJoyce.McIntosh@Sun.COM 	ol = &node->n_oplock;
67212890SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&ol->ol_mutex));
6738934SJose.Borrego@Sun.COM 
6748934SJose.Borrego@Sun.COM 	if ((ol->ol_xthread != NULL) && (ol->ol_xthread != curthread)) {
6758934SJose.Borrego@Sun.COM 		while (ol->ol_xthread != NULL)
67612890SJoyce.McIntosh@Sun.COM 			cv_wait(&ol->ol_cv, &ol->ol_mutex);
6776600Sas200622 	}
6786600Sas200622 }
67912890SJoyce.McIntosh@Sun.COM 
68012890SJoyce.McIntosh@Sun.COM /*
68112914SJoyce.McIntosh@Sun.COM  * smb_oplock_set_grant
68212890SJoyce.McIntosh@Sun.COM  */
68312890SJoyce.McIntosh@Sun.COM static smb_oplock_grant_t *
smb_oplock_set_grant(smb_ofile_t * of,uint8_t level)68412914SJoyce.McIntosh@Sun.COM smb_oplock_set_grant(smb_ofile_t *of, uint8_t level)
68512890SJoyce.McIntosh@Sun.COM {
68612890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
68712890SJoyce.McIntosh@Sun.COM 
68812914SJoyce.McIntosh@Sun.COM 	og = &of->f_oplock_grant;
68912914SJoyce.McIntosh@Sun.COM 
69012890SJoyce.McIntosh@Sun.COM 	og->og_magic = SMB_OPLOCK_GRANT_MAGIC;
69112890SJoyce.McIntosh@Sun.COM 	og->og_level = level;
69212890SJoyce.McIntosh@Sun.COM 	og->og_ofile = of;
69312890SJoyce.McIntosh@Sun.COM 	og->og_fid = of->f_fid;
69412890SJoyce.McIntosh@Sun.COM 	og->og_tid = of->f_tree->t_tid;
69512890SJoyce.McIntosh@Sun.COM 	og->og_uid = of->f_user->u_uid;
69612890SJoyce.McIntosh@Sun.COM 	og->og_session = of->f_session;
69712890SJoyce.McIntosh@Sun.COM 	return (og);
69812890SJoyce.McIntosh@Sun.COM }
69912890SJoyce.McIntosh@Sun.COM 
70012890SJoyce.McIntosh@Sun.COM /*
70112914SJoyce.McIntosh@Sun.COM  * smb_oplock_clear_grant
70212890SJoyce.McIntosh@Sun.COM  */
70312890SJoyce.McIntosh@Sun.COM void
smb_oplock_clear_grant(smb_oplock_grant_t * og)70412914SJoyce.McIntosh@Sun.COM smb_oplock_clear_grant(smb_oplock_grant_t *og)
70512890SJoyce.McIntosh@Sun.COM {
70612914SJoyce.McIntosh@Sun.COM 	bzero(og, sizeof (smb_oplock_grant_t));
70712890SJoyce.McIntosh@Sun.COM }
70812890SJoyce.McIntosh@Sun.COM 
70912890SJoyce.McIntosh@Sun.COM /*
71012890SJoyce.McIntosh@Sun.COM  * smb_oplock_insert_grant
71112890SJoyce.McIntosh@Sun.COM  *
71212890SJoyce.McIntosh@Sun.COM  * If there are no grants in the oplock's list install the fem
71312890SJoyce.McIntosh@Sun.COM  * monitor.
71412890SJoyce.McIntosh@Sun.COM  * Insert the grant into the list and increment the grant count.
71512890SJoyce.McIntosh@Sun.COM  */
71612890SJoyce.McIntosh@Sun.COM static int
smb_oplock_insert_grant(smb_node_t * node,smb_oplock_grant_t * og)71712890SJoyce.McIntosh@Sun.COM smb_oplock_insert_grant(smb_node_t *node, smb_oplock_grant_t *og)
71812890SJoyce.McIntosh@Sun.COM {
71912890SJoyce.McIntosh@Sun.COM 	smb_oplock_t *ol = &node->n_oplock;
72012890SJoyce.McIntosh@Sun.COM 
72112890SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&ol->ol_mutex));
72212890SJoyce.McIntosh@Sun.COM 
72312890SJoyce.McIntosh@Sun.COM 	if (ol->ol_count == 0) {
72412890SJoyce.McIntosh@Sun.COM 		if (smb_oplock_install_fem(node) != 0)
72512890SJoyce.McIntosh@Sun.COM 			return (-1);
72612890SJoyce.McIntosh@Sun.COM 	}
72712890SJoyce.McIntosh@Sun.COM 
72812890SJoyce.McIntosh@Sun.COM 	list_insert_tail(&ol->ol_grants, og);
72912890SJoyce.McIntosh@Sun.COM 	++ol->ol_count;
73012890SJoyce.McIntosh@Sun.COM 	return (0);
73112890SJoyce.McIntosh@Sun.COM }
73212890SJoyce.McIntosh@Sun.COM 
73312890SJoyce.McIntosh@Sun.COM /*
73412890SJoyce.McIntosh@Sun.COM  * smb_oplock_remove_grant
73512890SJoyce.McIntosh@Sun.COM  *
73612890SJoyce.McIntosh@Sun.COM  * Remove the oplock grant from the list, decrement the grant count
73712890SJoyce.McIntosh@Sun.COM  * and, if there are no other grants in the list, uninstall the fem
73812890SJoyce.McIntosh@Sun.COM  * monitor.
73912890SJoyce.McIntosh@Sun.COM  */
74012890SJoyce.McIntosh@Sun.COM static void
smb_oplock_remove_grant(smb_node_t * node,smb_oplock_grant_t * og)74112890SJoyce.McIntosh@Sun.COM smb_oplock_remove_grant(smb_node_t *node, smb_oplock_grant_t *og)
74212890SJoyce.McIntosh@Sun.COM {
74312890SJoyce.McIntosh@Sun.COM 	smb_oplock_t *ol = &node->n_oplock;
74412890SJoyce.McIntosh@Sun.COM 
74512890SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&ol->ol_mutex));
74612890SJoyce.McIntosh@Sun.COM 	ASSERT(ol->ol_count > 0);
74712890SJoyce.McIntosh@Sun.COM 
74812890SJoyce.McIntosh@Sun.COM 	list_remove(&ol->ol_grants, og);
74912890SJoyce.McIntosh@Sun.COM 	if (--ol->ol_count == 0)
75012890SJoyce.McIntosh@Sun.COM 		smb_oplock_uninstall_fem(node);
75112890SJoyce.McIntosh@Sun.COM }
75212890SJoyce.McIntosh@Sun.COM 
75312890SJoyce.McIntosh@Sun.COM /*
75412890SJoyce.McIntosh@Sun.COM  * smb_oplock_exclusive_grant
75512890SJoyce.McIntosh@Sun.COM  *
75612890SJoyce.McIntosh@Sun.COM  * If an exclusive (EXCLUSIVE or BATCH) oplock grant exists,
75712890SJoyce.McIntosh@Sun.COM  * return it. Otherwise return NULL.
75812890SJoyce.McIntosh@Sun.COM  */
75912890SJoyce.McIntosh@Sun.COM static smb_oplock_grant_t *
smb_oplock_exclusive_grant(list_t * grants)76012890SJoyce.McIntosh@Sun.COM smb_oplock_exclusive_grant(list_t *grants)
76112890SJoyce.McIntosh@Sun.COM {
76212890SJoyce.McIntosh@Sun.COM 	smb_oplock_grant_t	*og;
76312890SJoyce.McIntosh@Sun.COM 
76412890SJoyce.McIntosh@Sun.COM 	og = list_head(grants);
76512890SJoyce.McIntosh@Sun.COM 	if (og) {
76612890SJoyce.McIntosh@Sun.COM 		SMB_OPLOCK_GRANT_VALID(og);
76712890SJoyce.McIntosh@Sun.COM 		if (SMB_OPLOCK_IS_EXCLUSIVE(og->og_level))
76812890SJoyce.McIntosh@Sun.COM 			return (og);
76912890SJoyce.McIntosh@Sun.COM 	}
77012890SJoyce.McIntosh@Sun.COM 	return (NULL);
77112890SJoyce.McIntosh@Sun.COM }
77212890SJoyce.McIntosh@Sun.COM 
77312890SJoyce.McIntosh@Sun.COM /*
77412914SJoyce.McIntosh@Sun.COM  * smb_oplock_get_grant
77512890SJoyce.McIntosh@Sun.COM  *
77612890SJoyce.McIntosh@Sun.COM  * Find oplock grant corresponding to the specified ofile.
77712890SJoyce.McIntosh@Sun.COM  */
77812890SJoyce.McIntosh@Sun.COM static smb_oplock_grant_t *
smb_oplock_get_grant(smb_oplock_t * ol,smb_ofile_t * ofile)77912914SJoyce.McIntosh@Sun.COM smb_oplock_get_grant(smb_oplock_t *ol, smb_ofile_t *ofile)
78012890SJoyce.McIntosh@Sun.COM {
78112914SJoyce.McIntosh@Sun.COM 	ASSERT(MUTEX_HELD(&ol->ol_mutex));
78212890SJoyce.McIntosh@Sun.COM 
78312914SJoyce.McIntosh@Sun.COM 	if (SMB_OFILE_OPLOCK_GRANTED(ofile))
78412914SJoyce.McIntosh@Sun.COM 		return (&ofile->f_oplock_grant);
78512914SJoyce.McIntosh@Sun.COM 	else
78612914SJoyce.McIntosh@Sun.COM 		return (NULL);
78712890SJoyce.McIntosh@Sun.COM }
78812890SJoyce.McIntosh@Sun.COM 
78912890SJoyce.McIntosh@Sun.COM /*
79012890SJoyce.McIntosh@Sun.COM  * smb_oplock_create_break
79112890SJoyce.McIntosh@Sun.COM  */
79212890SJoyce.McIntosh@Sun.COM static smb_oplock_break_t *
smb_oplock_create_break(smb_node_t * node)79312890SJoyce.McIntosh@Sun.COM smb_oplock_create_break(smb_node_t *node)
79412890SJoyce.McIntosh@Sun.COM {
79512890SJoyce.McIntosh@Sun.COM 	smb_oplock_break_t	*ob;
79612890SJoyce.McIntosh@Sun.COM 
79712890SJoyce.McIntosh@Sun.COM 	ob = kmem_cache_alloc(smb_oplock_break_cache, KM_SLEEP);
79812890SJoyce.McIntosh@Sun.COM 
79912890SJoyce.McIntosh@Sun.COM 	smb_node_ref(node);
80012890SJoyce.McIntosh@Sun.COM 	ob->ob_magic = SMB_OPLOCK_BREAK_MAGIC;
80112890SJoyce.McIntosh@Sun.COM 	ob->ob_node = node;
80212890SJoyce.McIntosh@Sun.COM 
80312890SJoyce.McIntosh@Sun.COM 	return (ob);
80412890SJoyce.McIntosh@Sun.COM }
80512890SJoyce.McIntosh@Sun.COM 
80612890SJoyce.McIntosh@Sun.COM /*
80712890SJoyce.McIntosh@Sun.COM  * smb_oplock_delete_break
80812890SJoyce.McIntosh@Sun.COM  */
80912890SJoyce.McIntosh@Sun.COM static void
smb_oplock_delete_break(smb_oplock_break_t * ob)81012890SJoyce.McIntosh@Sun.COM smb_oplock_delete_break(smb_oplock_break_t *ob)
81112890SJoyce.McIntosh@Sun.COM {
81212890SJoyce.McIntosh@Sun.COM 	smb_node_release(ob->ob_node);
81312890SJoyce.McIntosh@Sun.COM 	kmem_cache_free(smb_oplock_break_cache, ob);
81412890SJoyce.McIntosh@Sun.COM }
815