194047d49SGordon Ross /*
294047d49SGordon Ross * This file and its contents are supplied under the terms of the
394047d49SGordon Ross * Common Development and Distribution License ("CDDL"), version 1.0.
494047d49SGordon Ross * You may only use this file in accordance with the terms of version
594047d49SGordon Ross * 1.0 of the CDDL.
694047d49SGordon Ross *
794047d49SGordon Ross * A full copy of the text of the CDDL should have accompanied this
894047d49SGordon Ross * source. A copy of the CDDL is also available via the Internet at
994047d49SGordon Ross * http://www.illumos.org/license/CDDL.
1094047d49SGordon Ross */
1194047d49SGordon Ross
1294047d49SGordon Ross /*
13a9931e68SGordon Ross * Copyright 2021 Tintri by DDN, Inc. All rights reserved.
14e515d096SGordon Ross * Copyright 2021-2023 RackTop Systems, Inc.
1594047d49SGordon Ross */
1694047d49SGordon Ross
1794047d49SGordon Ross /*
1894047d49SGordon Ross * (SMB1/SMB2) Server-level Oplock support.
1994047d49SGordon Ross *
2094047d49SGordon Ross * Conceptually, this is a separate layer on top of the
2194047d49SGordon Ross * file system (FS) layer oplock code in smb_cmn_oplock.c.
2294047d49SGordon Ross * If these layers were more distinct, the FS layer would
2394047d49SGordon Ross * need to use call-back functions (installed from here)
2494047d49SGordon Ross * to "indicate an oplock break to the server" (see below).
2594047d49SGordon Ross * As these layers are all in the same kernel module, the
2694047d49SGordon Ross * delivery of these break indications just uses a direct
2794047d49SGordon Ross * function call to smb_oplock_ind_break() below.
2894047d49SGordon Ross *
2994047d49SGordon Ross * This layer is responsible for handling the break indication,
3094047d49SGordon Ross * which often requires scheduling a taskq job in the server,
3194047d49SGordon Ross * and sending an oplock break mesage to the client using
3294047d49SGordon Ross * the appropriate protocol for the open handle affected.
3394047d49SGordon Ross *
3494047d49SGordon Ross * The details of composing an oplock break message, the
3594047d49SGordon Ross * protocol-specific details of requesting an oplock, and
3694047d49SGordon Ross * returning that oplock to the client are in the files:
3794047d49SGordon Ross * smb_oplock.c, smb2_oplock.c, smb2_lease.c
3894047d49SGordon Ross */
3994047d49SGordon Ross
4094047d49SGordon Ross #include <smbsrv/smb2_kproto.h>
4194047d49SGordon Ross #include <smbsrv/smb_oplock.h>
4294047d49SGordon Ross
4394047d49SGordon Ross /*
4494047d49SGordon Ross * Verify relationship between BREAK_TO_... and CACHE bits,
4594047d49SGordon Ross * used when setting the BREAK_TO_... below.
4694047d49SGordon Ross */
4794047d49SGordon Ross #if BREAK_TO_READ_CACHING != (READ_CACHING << BREAK_SHIFT)
4894047d49SGordon Ross #error "BREAK_TO_READ_CACHING"
4994047d49SGordon Ross #endif
5094047d49SGordon Ross #if BREAK_TO_HANDLE_CACHING != (HANDLE_CACHING << BREAK_SHIFT)
5194047d49SGordon Ross #error "BREAK_TO_HANDLE_CACHING"
5294047d49SGordon Ross #endif
5394047d49SGordon Ross #if BREAK_TO_WRITE_CACHING != (WRITE_CACHING << BREAK_SHIFT)
5494047d49SGordon Ross #error "BREAK_TO_WRITE_CACHING"
5594047d49SGordon Ross #endif
5694047d49SGordon Ross #define CACHE_RWH (READ_CACHING | WRITE_CACHING | HANDLE_CACHING)
5794047d49SGordon Ross
5894047d49SGordon Ross /*
5994047d49SGordon Ross * This is the timeout used in the thread that sends an
6094047d49SGordon Ross * oplock break and waits for the client to respond
6194047d49SGordon Ross * before it breaks the oplock locally.
6294047d49SGordon Ross */
6394047d49SGordon Ross int smb_oplock_timeout_ack = 30000; /* mSec. */
6494047d49SGordon Ross
6594047d49SGordon Ross /*
6694047d49SGordon Ross * This is the timeout used in threads that have just
6794047d49SGordon Ross * finished some sort of oplock request and now must
6894047d49SGordon Ross * wait for (possibly multiple) breaks to complete.
6994047d49SGordon Ross * This value must be at least a couple seconds LONGER
7094047d49SGordon Ross * than the ack timeout above so that I/O callers won't
7194047d49SGordon Ross * give up waiting before the local ack timeout.
7294047d49SGordon Ross */
7394047d49SGordon Ross int smb_oplock_timeout_def = 45000; /* mSec. */
7494047d49SGordon Ross
7594047d49SGordon Ross static void smb_oplock_async_break(void *);
7672b35b05SGordon Ross static void smb_oplock_hdl_update(smb_request_t *sr);
7772b35b05SGordon Ross static void smb_oplock_hdl_moved(smb_ofile_t *);
7872b35b05SGordon Ross static void smb_oplock_hdl_closed(smb_ofile_t *);
79525641e8SGordon Ross static void smb_oplock_wait_break_cancel(smb_request_t *sr);
8094047d49SGordon Ross
8194047d49SGordon Ross
8294047d49SGordon Ross /*
8394047d49SGordon Ross * 2.1.5.17.3 Indicating an Oplock Break to the Server
8494047d49SGordon Ross *
8594047d49SGordon Ross * The inputs for indicating an oplock break to the server are:
8694047d49SGordon Ross *
8794047d49SGordon Ross * BreakingOplockOpen: The Open used to request the oplock
8894047d49SGordon Ross * that is now breaking.
8994047d49SGordon Ross * NewOplockLevel: The type of oplock the requested oplock
9094047d49SGordon Ross * has been broken to. Valid values are as follows:
9194047d49SGordon Ross * LEVEL_NONE (that is, no oplock)
9294047d49SGordon Ross * LEVEL_TWO
9394047d49SGordon Ross * A combination of one or more of the following flags:
9494047d49SGordon Ross * READ_CACHING
9594047d49SGordon Ross * HANDLE_CACHING
9694047d49SGordon Ross * WRITE_CACHING
9794047d49SGordon Ross * AcknowledgeRequired: A Boolean value; TRUE if the server
9894047d49SGordon Ross * MUST acknowledge the oplock break, FALSE if not,
9994047d49SGordon Ross * as specified in section 2.1.5.18.
10094047d49SGordon Ross * OplockCompletionStatus: The NTSTATUS code to return to the server.
10194047d49SGordon Ross *
10294047d49SGordon Ross * This algorithm simply represents the completion of an oplock request,
10394047d49SGordon Ross * as specified in section 2.1.5.17.1 or section 2.1.5.17.2. The server
10494047d49SGordon Ross * is expected to associate the return status from this algorithm with
10594047d49SGordon Ross * BreakingOplockOpen, which is the Open passed in when it requested
10694047d49SGordon Ross * the oplock that is now breaking.
10794047d49SGordon Ross *
10894047d49SGordon Ross * It is important to note that because several oplocks can be outstanding
10994047d49SGordon Ross * in parallel, although this algorithm represents the completion of an
11094047d49SGordon Ross * oplock request, it might not result in the completion of the algorithm
11194047d49SGordon Ross * that called it. In particular, calling this algorithm will result in
11294047d49SGordon Ross * completion of the caller only if BreakingOplockOpen is the same as the
11394047d49SGordon Ross * Open with which the calling algorithm was itself called. To mitigate
11494047d49SGordon Ross * confusion, each algorithm that refers to this section will specify
11594047d49SGordon Ross * whether that algorithm's operation terminates at that point or not.
11694047d49SGordon Ross *
11794047d49SGordon Ross * The object store MUST return OplockCompletionStatus,
11894047d49SGordon Ross * AcknowledgeRequired, and NewOplockLevel to the server (the algorithm is
11994047d49SGordon Ross * as specified in section 2.1.5.17.1 and section 2.1.5.17.2).
12094047d49SGordon Ross *
12194047d49SGordon Ross * Implementation:
12294047d49SGordon Ross *
12394047d49SGordon Ross * We use two versions of this function:
12494047d49SGordon Ross * smb_oplock_ind_break_in_ack
12594047d49SGordon Ross * smb_oplock_ind_break
12694047d49SGordon Ross *
12794047d49SGordon Ross * The first is used when we're handling an Oplock Break Ack.
12894047d49SGordon Ross * The second is used when other operations cause a break,
12994047d49SGordon Ross * generally in one of the smb_oplock_break_... functions.
13094047d49SGordon Ross *
13194047d49SGordon Ross * Note that these are call-back functions that may be called with the
13294047d49SGordon Ross * node ofile list rwlock held and the node oplock mutex entered, so
13394047d49SGordon Ross * these should ONLY schedule oplock break work, and MUST NOT attempt
13494047d49SGordon Ross * any actions that might require either of those locks.
13594047d49SGordon Ross */
13694047d49SGordon Ross
13794047d49SGordon Ross /*
13894047d49SGordon Ross * smb_oplock_ind_break_in_ack
13994047d49SGordon Ross *
14094047d49SGordon Ross * Variant of smb_oplock_ind_break() for the oplock Ack handler.
14194047d49SGordon Ross * When we need to indicate another oplock break from within the
14294047d49SGordon Ross * Ack handler (during the Ack. of some previous oplock break)
14394047d49SGordon Ross * we need to make sure this new break indication goes out only
14494047d49SGordon Ross * AFTER the reply to the current break ack. is sent out.
14594047d49SGordon Ross *
14694047d49SGordon Ross * In this case, we always have an SR (the break ack) so we can
14794047d49SGordon Ross * append the "ind break" work to the current SR and let the
14894047d49SGordon Ross * request hander thread do this work after the reply is sent.
14994047d49SGordon Ross * Note: this is always an SMB2 or later request, because this
15094047d49SGordon Ross * only happens for "granular" oplocks, which are SMB2-only.
15194047d49SGordon Ross *
15294047d49SGordon Ross * This is mostly the same as smb_oplock_ind_break() except:
15394047d49SGordon Ross * - The only CompletionStatus possible is STATUS_CANT_GRANT.
15494047d49SGordon Ross * - Instead of taskq_dispatch this appends the new SR to
1557f5d80fdSGordon Ross * the "post work" queue on the current SR (if possible).
15694047d49SGordon Ross *
15794047d49SGordon Ross * Note called with the node ofile list rwlock held and
15894047d49SGordon Ross * the oplock mutex entered.
15994047d49SGordon Ross */
16094047d49SGordon Ross void
smb_oplock_ind_break_in_ack(smb_request_t * ack_sr,smb_ofile_t * ofile,uint32_t NewLevel,boolean_t AckRequired)16194047d49SGordon Ross smb_oplock_ind_break_in_ack(smb_request_t *ack_sr, smb_ofile_t *ofile,
16294047d49SGordon Ross uint32_t NewLevel, boolean_t AckRequired)
16394047d49SGordon Ross {
1647f5d80fdSGordon Ross smb_server_t *sv = ofile->f_server;
1657f5d80fdSGordon Ross smb_node_t *node = ofile->f_node;
1667f5d80fdSGordon Ross smb_request_t *sr = NULL;
1679788d6deSGordon Ross taskqid_t tqid;
1687f5d80fdSGordon Ross boolean_t use_postwork = B_TRUE;
1697f5d80fdSGordon Ross
1707f5d80fdSGordon Ross ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
1717f5d80fdSGordon Ross ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
17294047d49SGordon Ross
17394047d49SGordon Ross /*
17494047d49SGordon Ross * This should happen only with SMB2 or later,
17594047d49SGordon Ross * but in case that ever changes...
17694047d49SGordon Ross */
17794047d49SGordon Ross if (ack_sr->session->dialect < SMB_VERS_2_BASE) {
17894047d49SGordon Ross smb_oplock_ind_break(ofile, NewLevel,
17994047d49SGordon Ross AckRequired, STATUS_CANT_GRANT);
18094047d49SGordon Ross return;
18194047d49SGordon Ross }
18294047d49SGordon Ross
18394047d49SGordon Ross /*
18494047d49SGordon Ross * We're going to schedule a request that will have a
18594047d49SGordon Ross * reference to this ofile. Get the hold first.
18694047d49SGordon Ross */
18772b35b05SGordon Ross if (!smb_ofile_hold_olbrk(ofile)) {
18894047d49SGordon Ross /* It's closing (or whatever). Nothing to do. */
18994047d49SGordon Ross return;
19094047d49SGordon Ross }
19194047d49SGordon Ross
19294047d49SGordon Ross /*
19394047d49SGordon Ross * When called from Ack processing, we want to use a
1947f5d80fdSGordon Ross * request on the session doing the ack, so we can
1957f5d80fdSGordon Ross * append "post work" to that session. If we can't
19694047d49SGordon Ross * allocate a request on that session (because it's
1977f5d80fdSGordon Ross * now disconnecting) use a request from the server
1987f5d80fdSGordon Ross * session like smb_oplock_ind_break does, and then
1997f5d80fdSGordon Ross * use taskq_dispatch instead of postwork.
20094047d49SGordon Ross */
2017f5d80fdSGordon Ross sr = smb_request_alloc(ack_sr->session, 0);
2027f5d80fdSGordon Ross if (sr == NULL) {
2037f5d80fdSGordon Ross use_postwork = B_FALSE;
2047f5d80fdSGordon Ross sr = smb_request_alloc(sv->sv_session, 0);
2057f5d80fdSGordon Ross }
2067f5d80fdSGordon Ross if (sr == NULL) {
2077f5d80fdSGordon Ross /*
2087f5d80fdSGordon Ross * Server must be shutting down. We took a
2097f5d80fdSGordon Ross * hold on the ofile that must be released,
2107f5d80fdSGordon Ross * but we can't release here because we're
2117f5d80fdSGordon Ross * called with the node ofile list entered.
2127f5d80fdSGordon Ross * See smb_ofile_release_LL.
2137f5d80fdSGordon Ross */
2147f5d80fdSGordon Ross smb_llist_post(&node->n_ofile_list, ofile,
2157f5d80fdSGordon Ross smb_ofile_release_LL);
21694047d49SGordon Ross return;
21794047d49SGordon Ross }
21894047d49SGordon Ross
2197f5d80fdSGordon Ross sr->sr_state = SMB_REQ_STATE_SUBMITTED;
2207f5d80fdSGordon Ross sr->smb2_async = B_TRUE;
2217f5d80fdSGordon Ross sr->user_cr = zone_kcred();
2227f5d80fdSGordon Ross sr->fid_ofile = ofile;
2235bcbb01cSGordon Ross if (ofile->f_tree != NULL) {
2247f5d80fdSGordon Ross sr->tid_tree = ofile->f_tree;
2257f5d80fdSGordon Ross smb_tree_hold_internal(sr->tid_tree);
2265bcbb01cSGordon Ross }
2275bcbb01cSGordon Ross if (ofile->f_user != NULL) {
2287f5d80fdSGordon Ross sr->uid_user = ofile->f_user;
2297f5d80fdSGordon Ross smb_user_hold_internal(sr->uid_user);
2305bcbb01cSGordon Ross }
2317f6a299eSGordon Ross if (ofile->f_lease != NULL)
2327f6a299eSGordon Ross NewLevel |= OPLOCK_LEVEL_GRANULAR;
2337f6a299eSGordon Ross
2347f5d80fdSGordon Ross sr->arg.olbrk.NewLevel = NewLevel;
2357f5d80fdSGordon Ross sr->arg.olbrk.AckRequired = AckRequired;
23694047d49SGordon Ross
2377f6a299eSGordon Ross /*
2387f6a299eSGordon Ross * Could do this in _hdl_update but this way it's
2397f6a299eSGordon Ross * visible in the dtrace fbt entry probe.
2407f6a299eSGordon Ross */
2417f6a299eSGordon Ross sr->arg.olbrk.OldLevel = ofile->f_oplock.og_breakto;
2427f6a299eSGordon Ross
24372b35b05SGordon Ross smb_oplock_hdl_update(sr);
24472b35b05SGordon Ross
2457f5d80fdSGordon Ross if (use_postwork) {
2467f5d80fdSGordon Ross /*
2477f5d80fdSGordon Ross * Using smb2_cmd_code to indicate what to call.
2487f5d80fdSGordon Ross * work func. will call smb_oplock_send_brk
2497f5d80fdSGordon Ross */
2507f5d80fdSGordon Ross sr->smb2_cmd_code = SMB2_OPLOCK_BREAK;
2517f5d80fdSGordon Ross smb2sr_append_postwork(ack_sr, sr);
2529788d6deSGordon Ross return;
2537f5d80fdSGordon Ross }
2549788d6deSGordon Ross
2559788d6deSGordon Ross /* Will call smb_oplock_send_break */
2569788d6deSGordon Ross sr->smb2_status = STATUS_CANT_GRANT;
257e515d096SGordon Ross tqid = taskq_dispatch(sv->sv_notify_pool,
2589788d6deSGordon Ross smb_oplock_async_break, sr, TQ_SLEEP);
2599788d6deSGordon Ross VERIFY(tqid != TASKQID_INVALID);
26094047d49SGordon Ross }
26194047d49SGordon Ross
26294047d49SGordon Ross /*
26394047d49SGordon Ross * smb_oplock_ind_break
26494047d49SGordon Ross *
26594047d49SGordon Ross * This is the function described in [MS-FSA] 2.1.5.17.3
26694047d49SGordon Ross * which is called many places in the oplock break code.
26794047d49SGordon Ross *
26894047d49SGordon Ross * Schedule a request & taskq job to do oplock break work
26994047d49SGordon Ross * as requested by the FS-level code (smb_cmn_oplock.c).
27094047d49SGordon Ross *
27172b35b05SGordon Ross * See also: smb_oplock_ind_break_in_ack
27272b35b05SGordon Ross *
27394047d49SGordon Ross * Note called with the node ofile list rwlock held and
27494047d49SGordon Ross * the oplock mutex entered.
27594047d49SGordon Ross */
27694047d49SGordon Ross void
smb_oplock_ind_break(smb_ofile_t * ofile,uint32_t NewLevel,boolean_t AckRequired,uint32_t CompletionStatus)27794047d49SGordon Ross smb_oplock_ind_break(smb_ofile_t *ofile, uint32_t NewLevel,
27894047d49SGordon Ross boolean_t AckRequired, uint32_t CompletionStatus)
27994047d49SGordon Ross {
28094047d49SGordon Ross smb_server_t *sv = ofile->f_server;
281525641e8SGordon Ross smb_node_t *node = ofile->f_node;
28294047d49SGordon Ross smb_request_t *sr = NULL;
2839788d6deSGordon Ross taskqid_t tqid;
28494047d49SGordon Ross
2857f5d80fdSGordon Ross ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
2867f5d80fdSGordon Ross ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
2877f5d80fdSGordon Ross
28894047d49SGordon Ross /*
28994047d49SGordon Ross * See notes at smb_oplock_async_break re. CompletionStatus
29094047d49SGordon Ross * Check for any invalid codes here, so assert happens in
29194047d49SGordon Ross * the thread passing an unexpected value.
29294047d49SGordon Ross * The real work happens in a taskq job.
29394047d49SGordon Ross */
29494047d49SGordon Ross switch (CompletionStatus) {
29594047d49SGordon Ross
29694047d49SGordon Ross case NT_STATUS_SUCCESS:
29794047d49SGordon Ross case STATUS_CANT_GRANT:
29894047d49SGordon Ross /* Send break via taskq job. */
29994047d49SGordon Ross break;
30094047d49SGordon Ross
30194047d49SGordon Ross case STATUS_NEW_HANDLE:
30272b35b05SGordon Ross smb_oplock_hdl_moved(ofile);
3036f8336c5SGordon Ross return;
3046f8336c5SGordon Ross
30594047d49SGordon Ross case NT_STATUS_OPLOCK_HANDLE_CLOSED:
30672b35b05SGordon Ross smb_oplock_hdl_closed(ofile);
30794047d49SGordon Ross return;
30894047d49SGordon Ross
30994047d49SGordon Ross default:
31094047d49SGordon Ross ASSERT(0);
31194047d49SGordon Ross return;
31294047d49SGordon Ross }
31394047d49SGordon Ross
31494047d49SGordon Ross /*
31594047d49SGordon Ross * We're going to schedule a request that will have a
31694047d49SGordon Ross * reference to this ofile. Get the hold first.
31794047d49SGordon Ross */
31872b35b05SGordon Ross if (!smb_ofile_hold_olbrk(ofile)) {
31994047d49SGordon Ross /* It's closing (or whatever). Nothing to do. */
32094047d49SGordon Ross return;
32194047d49SGordon Ross }
32294047d49SGordon Ross
32394047d49SGordon Ross /*
32494047d49SGordon Ross * We need a request allocated on the session that owns
32594047d49SGordon Ross * this ofile in order to safely send on that session.
32694047d49SGordon Ross *
32794047d49SGordon Ross * Note that while we hold a ref. on the ofile, it's
32894047d49SGordon Ross * f_session will not change. An ofile in state
32994047d49SGordon Ross * _ORPHANED will have f_session == NULL, but the
33094047d49SGordon Ross * f_session won't _change_ while we have a ref,
33194047d49SGordon Ross * and won't be torn down under our feet.
3325bcbb01cSGordon Ross * Same for f_tree and f_user
33394047d49SGordon Ross *
33494047d49SGordon Ross * If f_session is NULL, or it's in a state that doesn't
33594047d49SGordon Ross * allow new requests, use the special "server" session.
33694047d49SGordon Ross */
33794047d49SGordon Ross if (ofile->f_session != NULL)
33894047d49SGordon Ross sr = smb_request_alloc(ofile->f_session, 0);
33994047d49SGordon Ross if (sr == NULL)
34094047d49SGordon Ross sr = smb_request_alloc(sv->sv_session, 0);
341525641e8SGordon Ross if (sr == NULL) {
342525641e8SGordon Ross /*
343525641e8SGordon Ross * Server must be shutting down. We took a
344525641e8SGordon Ross * hold on the ofile that must be released,
345525641e8SGordon Ross * but we can't release here because we're
346525641e8SGordon Ross * called with the node ofile list entered.
347525641e8SGordon Ross * See smb_ofile_release_LL.
348525641e8SGordon Ross */
349525641e8SGordon Ross smb_llist_post(&node->n_ofile_list, ofile,
350525641e8SGordon Ross smb_ofile_release_LL);
351525641e8SGordon Ross return;
352525641e8SGordon Ross }
35394047d49SGordon Ross
35494047d49SGordon Ross sr->sr_state = SMB_REQ_STATE_SUBMITTED;
35594047d49SGordon Ross sr->smb2_async = B_TRUE;
35694047d49SGordon Ross sr->user_cr = zone_kcred();
35794047d49SGordon Ross sr->fid_ofile = ofile;
3585bcbb01cSGordon Ross if (ofile->f_tree != NULL) {
3595bcbb01cSGordon Ross sr->tid_tree = ofile->f_tree;
3605bcbb01cSGordon Ross smb_tree_hold_internal(sr->tid_tree);
3615bcbb01cSGordon Ross }
3625bcbb01cSGordon Ross if (ofile->f_user != NULL) {
3635bcbb01cSGordon Ross sr->uid_user = ofile->f_user;
3645bcbb01cSGordon Ross smb_user_hold_internal(sr->uid_user);
3655bcbb01cSGordon Ross }
3667f6a299eSGordon Ross if (ofile->f_lease != NULL)
3677f6a299eSGordon Ross NewLevel |= OPLOCK_LEVEL_GRANULAR;
3687f6a299eSGordon Ross
36994047d49SGordon Ross sr->arg.olbrk.NewLevel = NewLevel;
37094047d49SGordon Ross sr->arg.olbrk.AckRequired = AckRequired;
37194047d49SGordon Ross sr->smb2_status = CompletionStatus;
37294047d49SGordon Ross
3737f6a299eSGordon Ross /*
3747f6a299eSGordon Ross * Could do this in _hdl_update but this way it's
3757f6a299eSGordon Ross * visible in the dtrace fbt entry probe.
3767f6a299eSGordon Ross */
3777f6a299eSGordon Ross sr->arg.olbrk.OldLevel = ofile->f_oplock.og_breakto;
3787f6a299eSGordon Ross
37972b35b05SGordon Ross smb_oplock_hdl_update(sr);
38072b35b05SGordon Ross
3817f6a299eSGordon Ross /* Will call smb_oplock_send_break */
382e515d096SGordon Ross tqid = taskq_dispatch(sv->sv_notify_pool,
38394047d49SGordon Ross smb_oplock_async_break, sr, TQ_SLEEP);
3849788d6deSGordon Ross VERIFY(tqid != TASKQID_INVALID);
38594047d49SGordon Ross }
38694047d49SGordon Ross
38794047d49SGordon Ross /*
38894047d49SGordon Ross * smb_oplock_async_break
38994047d49SGordon Ross *
39094047d49SGordon Ross * Called via the taskq to handle an asynchronous oplock break.
39194047d49SGordon Ross * We have a hold on the ofile, which will be released in
39294047d49SGordon Ross * smb_request_free (via sr->fid_ofile)
39394047d49SGordon Ross *
39472b35b05SGordon Ross * Note we may have: sr->uid_user == NULL, sr->tid_tree == NULL.
39594047d49SGordon Ross */
39694047d49SGordon Ross static void
smb_oplock_async_break(void * arg)39794047d49SGordon Ross smb_oplock_async_break(void *arg)
39894047d49SGordon Ross {
39994047d49SGordon Ross smb_request_t *sr = arg;
40094047d49SGordon Ross uint32_t CompletionStatus;
40194047d49SGordon Ross
40294047d49SGordon Ross SMB_REQ_VALID(sr);
40394047d49SGordon Ross
40494047d49SGordon Ross CompletionStatus = sr->smb2_status;
40594047d49SGordon Ross sr->smb2_status = NT_STATUS_SUCCESS;
40694047d49SGordon Ross
40794047d49SGordon Ross mutex_enter(&sr->sr_mutex);
40894047d49SGordon Ross sr->sr_worker = curthread;
40994047d49SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE;
41094047d49SGordon Ross mutex_exit(&sr->sr_mutex);
41194047d49SGordon Ross
41294047d49SGordon Ross /*
41394047d49SGordon Ross * Note that the CompletionStatus from the FS level
41494047d49SGordon Ross * (smb_cmn_oplock.c) encodes what kind of action we
41594047d49SGordon Ross * need to take at the SMB level.
41694047d49SGordon Ross */
41794047d49SGordon Ross switch (CompletionStatus) {
41894047d49SGordon Ross
41994047d49SGordon Ross case STATUS_CANT_GRANT:
42094047d49SGordon Ross case NT_STATUS_SUCCESS:
42172b35b05SGordon Ross smb_oplock_send_break(sr);
42294047d49SGordon Ross break;
42394047d49SGordon Ross
42494047d49SGordon Ross default:
42594047d49SGordon Ross /* Checked by caller. */
42694047d49SGordon Ross ASSERT(0);
42794047d49SGordon Ross break;
42894047d49SGordon Ross }
42994047d49SGordon Ross
4308d94f651SGordon Ross if (sr->dh_nvl_dirty) {
4318d94f651SGordon Ross sr->dh_nvl_dirty = B_FALSE;
4328d94f651SGordon Ross smb2_dh_update_nvfile(sr);
4338d94f651SGordon Ross }
4348d94f651SGordon Ross
43594047d49SGordon Ross sr->sr_state = SMB_REQ_STATE_COMPLETED;
43694047d49SGordon Ross smb_request_free(sr);
43794047d49SGordon Ross }
43894047d49SGordon Ross
43972b35b05SGordon Ross /*
44072b35b05SGordon Ross * Send an oplock (or lease) break to the client.
44172b35b05SGordon Ross * If we can't, then do a local break.
44272b35b05SGordon Ross *
44372b35b05SGordon Ross * This is called either from smb_oplock_async_break via a
44472b35b05SGordon Ross * taskq job scheduled in smb_oplock_ind_break, or from the
44572b35b05SGordon Ross * smb2sr_append_postwork() mechanism when we're doing a
44672b35b05SGordon Ross * "break in ack", via smb_oplock_ind_break_in_ack.
44772b35b05SGordon Ross *
44872b35b05SGordon Ross * We don't always have an sr->session here, so
44972b35b05SGordon Ross * determine the oplock type (lease etc) from
45072b35b05SGordon Ross * f_lease and f_oplock.og_dialect etc.
45172b35b05SGordon Ross */
45272b35b05SGordon Ross void
smb_oplock_send_break(smb_request_t * sr)45372b35b05SGordon Ross smb_oplock_send_break(smb_request_t *sr)
4546f8336c5SGordon Ross {
45572b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
45672b35b05SGordon Ross
4576f8336c5SGordon Ross if (ofile->f_lease != NULL)
45872b35b05SGordon Ross smb2_lease_send_break(sr);
45972b35b05SGordon Ross else if (ofile->f_oplock.og_dialect >= SMB_VERS_2_BASE)
46072b35b05SGordon Ross smb2_oplock_send_break(sr);
4616f8336c5SGordon Ross else
46272b35b05SGordon Ross smb1_oplock_send_break(sr);
46372b35b05SGordon Ross }
4646f8336c5SGordon Ross
46572b35b05SGordon Ross /*
46672b35b05SGordon Ross * Called by smb_oplock_ind_break for the case STATUS_NEW_HANDLE,
46772b35b05SGordon Ross * which is an alias for NT_STATUS_OPLOCK_SWITCHED_TO_NEW_HANDLE.
46872b35b05SGordon Ross *
46972b35b05SGordon Ross * The FS-level oplock layer calls this to update the SMB-level state
47072b35b05SGordon Ross * when the oplock for some lease is about to move to a different
47172b35b05SGordon Ross * ofile on the lease.
47272b35b05SGordon Ross *
47372b35b05SGordon Ross * To avoid later confusion, clear og_state on this ofile now.
47472b35b05SGordon Ross * Without this, smb_oplock_move() may issue debug complaints
47572b35b05SGordon Ross * about moving oplock state onto a non-empty oplock.
47672b35b05SGordon Ross */
47772b35b05SGordon Ross static const smb_ofile_t invalid_ofile;
47872b35b05SGordon Ross static void
smb_oplock_hdl_moved(smb_ofile_t * ofile)47972b35b05SGordon Ross smb_oplock_hdl_moved(smb_ofile_t *ofile)
48072b35b05SGordon Ross {
48172b35b05SGordon Ross smb_lease_t *ls = ofile->f_lease;
48272b35b05SGordon Ross
48372b35b05SGordon Ross ASSERT(ls != NULL);
48472b35b05SGordon Ross if (ls != NULL && ls->ls_oplock_ofile == ofile)
48572b35b05SGordon Ross ls->ls_oplock_ofile = (smb_ofile_t *)&invalid_ofile;
48672b35b05SGordon Ross
48772b35b05SGordon Ross ofile->f_oplock.og_state = 0;
4887f6a299eSGordon Ross ofile->f_oplock.og_breakto = 0;
4897f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
4906f8336c5SGordon Ross }
4916f8336c5SGordon Ross
49272b35b05SGordon Ross /*
49372b35b05SGordon Ross * See: NT_STATUS_OPLOCK_HANDLE_CLOSED above and
49472b35b05SGordon Ross * smb_ofile_close, smb_oplock_break_CLOSE.
49572b35b05SGordon Ross *
49672b35b05SGordon Ross * The FS-level oplock layer calls this to update the
49772b35b05SGordon Ross * SMB-level state when a handle loses its oplock.
49872b35b05SGordon Ross */
49972b35b05SGordon Ross static void
smb_oplock_hdl_closed(smb_ofile_t * ofile)50072b35b05SGordon Ross smb_oplock_hdl_closed(smb_ofile_t *ofile)
50172b35b05SGordon Ross {
50272b35b05SGordon Ross smb_lease_t *lease = ofile->f_lease;
50372b35b05SGordon Ross
50472b35b05SGordon Ross if (lease != NULL) {
50572b35b05SGordon Ross if (lease->ls_oplock_ofile == ofile) {
50672b35b05SGordon Ross /*
50772b35b05SGordon Ross * smb2_lease_ofile_close should have
50872b35b05SGordon Ross * moved the oplock to another ofile.
50972b35b05SGordon Ross */
51072b35b05SGordon Ross ASSERT(0);
51172b35b05SGordon Ross lease->ls_oplock_ofile = NULL;
51272b35b05SGordon Ross }
51372b35b05SGordon Ross }
51472b35b05SGordon Ross ofile->f_oplock.og_state = 0;
5157f6a299eSGordon Ross ofile->f_oplock.og_breakto = 0;
5167f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
51772b35b05SGordon Ross }
51894047d49SGordon Ross
51994047d49SGordon Ross /*
52072b35b05SGordon Ross * smb_oplock_hdl_update
52172b35b05SGordon Ross *
52272b35b05SGordon Ross * Called by smb_oplock_ind_break (and ...in_ack) just before we
52372b35b05SGordon Ross * schedule smb_oplock_async_break / mb_oplock_send_break taskq job,
52472b35b05SGordon Ross * so we can make any state changes that should happen immediately.
52572b35b05SGordon Ross *
52672b35b05SGordon Ross * Here, keep track of what we will send to the client.
5277f6a299eSGordon Ross * Saves old state in arg.olbck.OldLevel
5287f6a299eSGordon Ross *
5297f6a299eSGordon Ross * Note that because we may be in the midst of processing an
5307f6a299eSGordon Ross * smb_oplock_ack_break call here, the _breaking flag will be
5317f6a299eSGordon Ross * temporarily false, and is set true again if this ack causes
5327f6a299eSGordon Ross * another break. This makes it tricky to know when to update
5337f6a299eSGordon Ross * the epoch, which is not supposed to increment when there's
5347f6a299eSGordon Ross * already an unacknowledged break out to the client.
5357f6a299eSGordon Ross * We can recognize that by comparing ls_state vs ls_breakto.
5367f6a299eSGordon Ross * If no unacknowledged break, ls_state == ls_breakto.
53794047d49SGordon Ross */
53872b35b05SGordon Ross static void
smb_oplock_hdl_update(smb_request_t * sr)53972b35b05SGordon Ross smb_oplock_hdl_update(smb_request_t *sr)
54094047d49SGordon Ross {
54172b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
54272b35b05SGordon Ross smb_lease_t *lease = ofile->f_lease;
54372b35b05SGordon Ross uint32_t NewLevel = sr->arg.olbrk.NewLevel;
54472b35b05SGordon Ross boolean_t AckReq = sr->arg.olbrk.AckRequired;
54572b35b05SGordon Ross
54672b35b05SGordon Ross #ifdef DEBUG
54772b35b05SGordon Ross smb_node_t *node = ofile->f_node;
54872b35b05SGordon Ross ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock));
54972b35b05SGordon Ross ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex));
55072b35b05SGordon Ross #endif
55194047d49SGordon Ross
5527f6a299eSGordon Ross /* Caller sets arg.olbrk.OldLevel */
5537f6a299eSGordon Ross ofile->f_oplock.og_breakto = NewLevel;
5547f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_TRUE;
5556f8336c5SGordon Ross if (lease != NULL) {
5567f6a299eSGordon Ross // If no unacknowledged break, update epoch.
5577f6a299eSGordon Ross if (lease->ls_breakto == lease->ls_state)
5587f6a299eSGordon Ross lease->ls_epoch++;
55994047d49SGordon Ross
5607f6a299eSGordon Ross lease->ls_breakto = NewLevel;
5617f6a299eSGordon Ross lease->ls_breaking = B_TRUE;
5627f6a299eSGordon Ross }
56394047d49SGordon Ross
5647f6a299eSGordon Ross if (!AckReq) {
56594047d49SGordon Ross /*
56672b35b05SGordon Ross * Not expecting an Ack from the client.
56772b35b05SGordon Ross * Update state immediately.
56894047d49SGordon Ross */
56972b35b05SGordon Ross ofile->f_oplock.og_state = NewLevel;
5707f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
57172b35b05SGordon Ross if (lease != NULL) {
5727f6a299eSGordon Ross lease->ls_state = NewLevel;
5737f6a299eSGordon Ross lease->ls_breaking = B_FALSE;
57494047d49SGordon Ross }
57572b35b05SGordon Ross if (ofile->dh_persist) {
57672b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile);
57772b35b05SGordon Ross }
57872b35b05SGordon Ross }
57972b35b05SGordon Ross }
58072b35b05SGordon Ross
58172b35b05SGordon Ross /*
58272b35b05SGordon Ross * Helper for smb_ofile_close
58372b35b05SGordon Ross *
58472b35b05SGordon Ross * Note that a client may close an ofile in response to an
58572b35b05SGordon Ross * oplock break or lease break intead of doing an Ack break,
58672b35b05SGordon Ross * so this must wake anything that might be waiting on an ack.
58772b35b05SGordon Ross */
58872b35b05SGordon Ross void
smb_oplock_close(smb_ofile_t * ofile)58972b35b05SGordon Ross smb_oplock_close(smb_ofile_t *ofile)
59072b35b05SGordon Ross {
59172b35b05SGordon Ross smb_node_t *node = ofile->f_node;
59294047d49SGordon Ross
59372b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER);
59472b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex);
59594047d49SGordon Ross
59672b35b05SGordon Ross if (ofile->f_oplock_closing == B_FALSE) {
59772b35b05SGordon Ross ofile->f_oplock_closing = B_TRUE;
59894047d49SGordon Ross
59972b35b05SGordon Ross if (ofile->f_lease != NULL)
60072b35b05SGordon Ross smb2_lease_ofile_close(ofile);
60194047d49SGordon Ross
60272b35b05SGordon Ross smb_oplock_break_CLOSE(node, ofile);
60394047d49SGordon Ross
60472b35b05SGordon Ross ofile->f_oplock.og_state = 0;
6057f6a299eSGordon Ross ofile->f_oplock.og_breakto = 0;
6067f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE;
60772b35b05SGordon Ross cv_broadcast(&ofile->f_oplock.og_ack_cv);
60894047d49SGordon Ross }
60994047d49SGordon Ross
61072b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex);
61172b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list);
61294047d49SGordon Ross }
61394047d49SGordon Ross
61494047d49SGordon Ross /*
61572b35b05SGordon Ross * Called by smb_request_cancel() via sr->cancel_method
61672b35b05SGordon Ross * Arg is the smb_node_t with the breaking oplock.
61794047d49SGordon Ross */
61894047d49SGordon Ross static void
smb_oplock_wait_ack_cancel(smb_request_t * sr)61972b35b05SGordon Ross smb_oplock_wait_ack_cancel(smb_request_t *sr)
62094047d49SGordon Ross {
62172b35b05SGordon Ross kcondvar_t *cvp = sr->cancel_arg2;
62272b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
62372b35b05SGordon Ross smb_node_t *node = ofile->f_node;
62472b35b05SGordon Ross
62572b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex);
62672b35b05SGordon Ross cv_broadcast(cvp);
62772b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex);
62872b35b05SGordon Ross }
62972b35b05SGordon Ross
63072b35b05SGordon Ross /*
63172b35b05SGordon Ross * Wait for an oplock break ACK to arrive. This is called after
63272b35b05SGordon Ross * we've sent an oplock break or lease break to the client where
63372b35b05SGordon Ross * an "Ack break" is expected back. If we get an Ack, that will
6347f6a299eSGordon Ross * wake us up via smb2_oplock_break_ack or smb2_lease_break_ack.
6357f6a299eSGordon Ross *
6367f6a299eSGordon Ross * Wait until state reduced to NewLevel (or less).
6377f6a299eSGordon Ross * Note that in multi-break cases, we might wait here for just
6387f6a299eSGordon Ross * one ack when another has become pending, in which case the
6397f6a299eSGordon Ross * og_breakto might be a subset of NewLevel. Wait until the
6407f6a299eSGordon Ross * state field is no longer a superset of NewLevel.
64172b35b05SGordon Ross */
64272b35b05SGordon Ross uint32_t
smb_oplock_wait_ack(smb_request_t * sr,uint32_t NewLevel)6437f6a299eSGordon Ross smb_oplock_wait_ack(smb_request_t *sr, uint32_t NewLevel)
64472b35b05SGordon Ross {
64572b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile;
64672b35b05SGordon Ross smb_lease_t *lease = ofile->f_lease;
64772b35b05SGordon Ross smb_node_t *node = ofile->f_node;
64872b35b05SGordon Ross smb_oplock_t *ol = &node->n_oplock;
6497f6a299eSGordon Ross uint32_t *state_p;
6507f6a299eSGordon Ross kcondvar_t *cv_p;
65172b35b05SGordon Ross clock_t time, rv;
65272b35b05SGordon Ross uint32_t status = 0;
65372b35b05SGordon Ross smb_req_state_t srstate;
6547f6a299eSGordon Ross uint32_t wait_mask;
65572b35b05SGordon Ross
65672b35b05SGordon Ross time = ddi_get_lbolt() +
65772b35b05SGordon Ross MSEC_TO_TICK(smb_oplock_timeout_ack);
65894047d49SGordon Ross
6597f6a299eSGordon Ross /*
6607f6a299eSGordon Ross * Wait on either lease state or oplock state
6617f6a299eSGordon Ross */
66294047d49SGordon Ross if (lease != NULL) {
6637f6a299eSGordon Ross state_p = &lease->ls_state;
6647f6a299eSGordon Ross cv_p = &lease->ls_ack_cv;
66572b35b05SGordon Ross } else {
6667f6a299eSGordon Ross state_p = &ofile->f_oplock.og_state;
6677f6a299eSGordon Ross cv_p = &ofile->f_oplock.og_ack_cv;
66872b35b05SGordon Ross }
66972b35b05SGordon Ross
6707f6a299eSGordon Ross /*
6717f6a299eSGordon Ross * These are all the bits that we wait to be cleared.
6727f6a299eSGordon Ross */
6737f6a299eSGordon Ross wait_mask = ~NewLevel & (CACHE_RWH |
6747f6a299eSGordon Ross LEVEL_TWO | LEVEL_ONE | LEVEL_BATCH);
6757f6a299eSGordon Ross
67672b35b05SGordon Ross /*
67772b35b05SGordon Ross * Setup cancellation callback
67872b35b05SGordon Ross */
67972b35b05SGordon Ross mutex_enter(&sr->sr_mutex);
68072b35b05SGordon Ross if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
68172b35b05SGordon Ross mutex_exit(&sr->sr_mutex);
68272b35b05SGordon Ross return (NT_STATUS_CANCELLED);
68372b35b05SGordon Ross }
68472b35b05SGordon Ross sr->sr_state = SMB_REQ_STATE_WAITING_OLBRK;
68572b35b05SGordon Ross sr->cancel_method = smb_oplock_wait_ack_cancel;
6867f6a299eSGordon Ross sr->cancel_arg2 = cv_p;
68772b35b05SGordon Ross mutex_exit(&sr->sr_mutex);
68872b35b05SGordon Ross
68972b35b05SGordon Ross /*
69072b35b05SGordon Ross * Enter the wait loop
69172b35b05SGordon Ross */
69272b35b05SGordon Ross mutex_enter(&ol->ol_mutex);
6937f6a299eSGordon Ross
6947f6a299eSGordon Ross while ((*state_p & wait_mask) != 0) {
6957f6a299eSGordon Ross rv = cv_timedwait(cv_p, &ol->ol_mutex, time);
69672b35b05SGordon Ross if (rv < 0) {
69772b35b05SGordon Ross /* cv_timewait timeout */
6983b24312dSGordon Ross char *fname;
6993b24312dSGordon Ross char *opname;
7003b24312dSGordon Ross int rc;
7013b24312dSGordon Ross
7023b24312dSGordon Ross /*
7033b24312dSGordon Ross * Get the path name of the open file
7043b24312dSGordon Ross */
7053b24312dSGordon Ross fname = smb_srm_zalloc(sr, MAXPATHLEN);
7063b24312dSGordon Ross rc = smb_node_getpath(node, NULL, fname, MAXPATHLEN);
7073b24312dSGordon Ross if (rc != 0) {
7083b24312dSGordon Ross /* Not expected. Just show last part. */
7093b24312dSGordon Ross (void) snprintf(fname, MAXPATHLEN, "(?)/%s",
7103b24312dSGordon Ross node->od_name);
7113b24312dSGordon Ross }
7123b24312dSGordon Ross
7133b24312dSGordon Ross /*
7143b24312dSGordon Ross * Get an operation name reflecting which kind of
7153b24312dSGordon Ross * lease or oplock break got us here, so the log
7163b24312dSGordon Ross * message will say "lease break" or whatever.
7173b24312dSGordon Ross */
7183b24312dSGordon Ross if (lease != NULL) {
7193b24312dSGordon Ross opname = "lease";
7203b24312dSGordon Ross } else if (ofile->f_oplock.og_dialect >=
7213b24312dSGordon Ross SMB_VERS_2_BASE) {
7223b24312dSGordon Ross opname = "oplock2";
7233b24312dSGordon Ross } else {
7243b24312dSGordon Ross opname = "oplock1";
7253b24312dSGordon Ross }
7263b24312dSGordon Ross
7273b24312dSGordon Ross cmn_err(CE_NOTE, "!client %s %s break timeout for %s",
7283b24312dSGordon Ross sr->session->ip_addr_str, opname, fname);
7293b24312dSGordon Ross
73072b35b05SGordon Ross status = NT_STATUS_CANNOT_BREAK_OPLOCK;
73172b35b05SGordon Ross break;
73272b35b05SGordon Ross }
73372b35b05SGordon Ross
73472b35b05SGordon Ross /*
73572b35b05SGordon Ross * Check if we were woken by smb_request_cancel,
73672b35b05SGordon Ross * which sets state SMB_REQ_STATE_CANCEL_PENDING
73772b35b05SGordon Ross * and signals the CV. The mutex enter/exit is
73872b35b05SGordon Ross * just to ensure cache visibility of sr_state
73972b35b05SGordon Ross * that was updated in smb_request_cancel.
74072b35b05SGordon Ross */
74172b35b05SGordon Ross mutex_enter(&sr->sr_mutex);
74272b35b05SGordon Ross srstate = sr->sr_state;
74372b35b05SGordon Ross mutex_exit(&sr->sr_mutex);
74472b35b05SGordon Ross if (srstate != SMB_REQ_STATE_WAITING_OLBRK) {
74572b35b05SGordon Ross break;
74694047d49SGordon Ross }
74794047d49SGordon Ross }
74872b35b05SGordon Ross mutex_exit(&ol->ol_mutex);
74972b35b05SGordon Ross
75072b35b05SGordon Ross /*
751*66b505f1SGordon Ross * See if it completed or if it was cancelled.
75272b35b05SGordon Ross */
75372b35b05SGordon Ross mutex_enter(&sr->sr_mutex);
754*66b505f1SGordon Ross switch_state:
75572b35b05SGordon Ross switch (sr->sr_state) {
75672b35b05SGordon Ross case SMB_REQ_STATE_WAITING_OLBRK:
757*66b505f1SGordon Ross /* Normal wakeup. Keep status from above. */
75872b35b05SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE;
75972b35b05SGordon Ross /* status from above */
76072b35b05SGordon Ross break;
76172b35b05SGordon Ross case SMB_REQ_STATE_CANCEL_PENDING:
762*66b505f1SGordon Ross /* cancel_method running. wait. */
763*66b505f1SGordon Ross cv_wait(&sr->sr_st_cv, &sr->sr_mutex);
764*66b505f1SGordon Ross goto switch_state;
765*66b505f1SGordon Ross case SMB_REQ_STATE_CANCELLED:
76672b35b05SGordon Ross status = NT_STATUS_CANCELLED;
76772b35b05SGordon Ross break;
76872b35b05SGordon Ross default:
76972b35b05SGordon Ross status = NT_STATUS_INTERNAL_ERROR;
77072b35b05SGordon Ross break;
77172b35b05SGordon Ross }
772*66b505f1SGordon Ross sr->cancel_method = NULL;
773*66b505f1SGordon Ross sr->cancel_arg2 = NULL;
77472b35b05SGordon Ross mutex_exit(&sr->sr_mutex);
77572b35b05SGordon Ross
77672b35b05SGordon Ross return (status);
77794047d49SGordon Ross }
77894047d49SGordon Ross
779525641e8SGordon Ross /*
780525641e8SGordon Ross * Called by smb_request_cancel() via sr->cancel_method
781525641e8SGordon Ross * Arg is the smb_node_t with the breaking oplock.
782525641e8SGordon Ross */
783525641e8SGordon Ross static void
smb_oplock_wait_break_cancel(smb_request_t * sr)784525641e8SGordon Ross smb_oplock_wait_break_cancel(smb_request_t *sr)
785525641e8SGordon Ross {
786525641e8SGordon Ross smb_node_t *node = sr->cancel_arg2;
787525641e8SGordon Ross smb_oplock_t *ol;
788525641e8SGordon Ross
789525641e8SGordon Ross SMB_NODE_VALID(node);
790525641e8SGordon Ross ol = &node->n_oplock;
791525641e8SGordon Ross
792525641e8SGordon Ross mutex_enter(&ol->ol_mutex);
793525641e8SGordon Ross cv_broadcast(&ol->WaitingOpenCV);
794525641e8SGordon Ross mutex_exit(&ol->ol_mutex);
795525641e8SGordon Ross }
796525641e8SGordon Ross
79794047d49SGordon Ross /*
79894047d49SGordon Ross * Wait up to "timeout" mSec. for the current oplock "breaking" flags
79994047d49SGordon Ross * to be cleared (by smb_oplock_ack_break or smb_oplock_break_CLOSE).
80094047d49SGordon Ross *
80194047d49SGordon Ross * Callers of the above public oplock functions:
80294047d49SGordon Ross * smb_oplock_request()
80394047d49SGordon Ross * smb_oplock_ack_break()
80494047d49SGordon Ross * smb_oplock_break_OPEN() ...
80594047d49SGordon Ross * check for return status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS
80694047d49SGordon Ross * and call this function to wait for the break to complete.
80794047d49SGordon Ross *
80894047d49SGordon Ross * Most callers should use this default timeout, which they get
80994047d49SGordon Ross * by passing zero as the timeout arg. This include places where
81094047d49SGordon Ross * we're about to do something that invalidates some cache.
81194047d49SGordon Ross */
81294047d49SGordon Ross uint32_t
smb_oplock_wait_break(smb_request_t * sr,smb_node_t * node,int timeout)813525641e8SGordon Ross smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout)
814525641e8SGordon Ross {
815525641e8SGordon Ross smb_oplock_t *ol;
816525641e8SGordon Ross clock_t time, rv;
817525641e8SGordon Ross uint32_t status = 0;
818525641e8SGordon Ross smb_req_state_t srstate;
819525641e8SGordon Ross
820525641e8SGordon Ross SMB_NODE_VALID(node);
821525641e8SGordon Ross ol = &node->n_oplock;
822525641e8SGordon Ross
823525641e8SGordon Ross if (timeout == 0)
824525641e8SGordon Ross timeout = smb_oplock_timeout_def;
825525641e8SGordon Ross time = MSEC_TO_TICK(timeout) + ddi_get_lbolt();
826525641e8SGordon Ross
827*66b505f1SGordon Ross /*
828*66b505f1SGordon Ross * Setup cancellation callback
829*66b505f1SGordon Ross */
830525641e8SGordon Ross mutex_enter(&sr->sr_mutex);
831525641e8SGordon Ross if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
832525641e8SGordon Ross mutex_exit(&sr->sr_mutex);
833525641e8SGordon Ross return (NT_STATUS_CANCELLED);
834525641e8SGordon Ross }
835525641e8SGordon Ross sr->sr_state = SMB_REQ_STATE_WAITING_OLBRK;
836525641e8SGordon Ross sr->cancel_method = smb_oplock_wait_break_cancel;
837525641e8SGordon Ross sr->cancel_arg2 = node;
838525641e8SGordon Ross mutex_exit(&sr->sr_mutex);
839525641e8SGordon Ross
840525641e8SGordon Ross mutex_enter(&ol->ol_mutex);
841525641e8SGordon Ross while ((ol->ol_state & BREAK_ANY) != 0) {
842525641e8SGordon Ross ol->waiters++;
843525641e8SGordon Ross rv = cv_timedwait(&ol->WaitingOpenCV,
844525641e8SGordon Ross &ol->ol_mutex, time);
845525641e8SGordon Ross ol->waiters--;
846525641e8SGordon Ross if (rv < 0) {
847525641e8SGordon Ross /* cv_timewait timeout */
848525641e8SGordon Ross status = NT_STATUS_CANNOT_BREAK_OPLOCK;
849525641e8SGordon Ross break;
850525641e8SGordon Ross }
851525641e8SGordon Ross
852525641e8SGordon Ross /*
853525641e8SGordon Ross * Check if we were woken by smb_request_cancel,
854525641e8SGordon Ross * which sets state SMB_REQ_STATE_CANCEL_PENDING
85572b35b05SGordon Ross * and signals the CV. The mutex enter/exit is
85672b35b05SGordon Ross * just to ensure cache visibility of sr_state
85772b35b05SGordon Ross * that was updated in smb_request_cancel.
858525641e8SGordon Ross */
859525641e8SGordon Ross mutex_enter(&sr->sr_mutex);
860525641e8SGordon Ross srstate = sr->sr_state;
861525641e8SGordon Ross mutex_exit(&sr->sr_mutex);
862525641e8SGordon Ross if (srstate != SMB_REQ_STATE_WAITING_OLBRK) {
863525641e8SGordon Ross break;
864525641e8SGordon Ross }
865525641e8SGordon Ross }
866525641e8SGordon Ross mutex_exit(&ol->ol_mutex);
867525641e8SGordon Ross
868*66b505f1SGordon Ross /*
869*66b505f1SGordon Ross * Check whether it completed or was cancelled.
870*66b505f1SGordon Ross */
871525641e8SGordon Ross mutex_enter(&sr->sr_mutex);
872*66b505f1SGordon Ross switch_state:
873525641e8SGordon Ross switch (sr->sr_state) {
874525641e8SGordon Ross case SMB_REQ_STATE_WAITING_OLBRK:
875*66b505f1SGordon Ross /* Normal wakeup. Keep status from above. */
876525641e8SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE;
877525641e8SGordon Ross break;
878525641e8SGordon Ross case SMB_REQ_STATE_CANCEL_PENDING:
879*66b505f1SGordon Ross /* cancel_method running. wait. */
880*66b505f1SGordon Ross cv_wait(&sr->sr_st_cv, &sr->sr_mutex);
881*66b505f1SGordon Ross goto switch_state;
882*66b505f1SGordon Ross case SMB_REQ_STATE_CANCELLED:
883525641e8SGordon Ross status = NT_STATUS_CANCELLED;
884525641e8SGordon Ross break;
885525641e8SGordon Ross default:
886525641e8SGordon Ross status = NT_STATUS_INTERNAL_ERROR;
887525641e8SGordon Ross break;
888525641e8SGordon Ross }
889*66b505f1SGordon Ross sr->cancel_method = NULL;
890*66b505f1SGordon Ross sr->cancel_arg2 = NULL;
891525641e8SGordon Ross mutex_exit(&sr->sr_mutex);
892525641e8SGordon Ross
893525641e8SGordon Ross return (status);
894525641e8SGordon Ross }
895525641e8SGordon Ross
896525641e8SGordon Ross /*
897525641e8SGordon Ross * Simplified version used in smb_fem.c, like above,
898525641e8SGordon Ross * but no smb_request_cancel stuff.
899525641e8SGordon Ross */
900525641e8SGordon Ross uint32_t
smb_oplock_wait_break_fem(smb_node_t * node,int timeout)901525641e8SGordon Ross smb_oplock_wait_break_fem(smb_node_t *node, int timeout) /* mSec. */
90294047d49SGordon Ross {
90394047d49SGordon Ross smb_oplock_t *ol;
90494047d49SGordon Ross clock_t time, rv;
90594047d49SGordon Ross uint32_t status = 0;
90694047d49SGordon Ross
90794047d49SGordon Ross if (timeout == 0)
90894047d49SGordon Ross timeout = smb_oplock_timeout_def;
90994047d49SGordon Ross
91094047d49SGordon Ross SMB_NODE_VALID(node);
91194047d49SGordon Ross ol = &node->n_oplock;
91294047d49SGordon Ross
91394047d49SGordon Ross mutex_enter(&ol->ol_mutex);
91494047d49SGordon Ross time = MSEC_TO_TICK(timeout) + ddi_get_lbolt();
91594047d49SGordon Ross
91694047d49SGordon Ross while ((ol->ol_state & BREAK_ANY) != 0) {
91794047d49SGordon Ross ol->waiters++;
91894047d49SGordon Ross rv = cv_timedwait(&ol->WaitingOpenCV,
91994047d49SGordon Ross &ol->ol_mutex, time);
92094047d49SGordon Ross ol->waiters--;
92194047d49SGordon Ross if (rv < 0) {
92294047d49SGordon Ross status = NT_STATUS_CANNOT_BREAK_OPLOCK;
92394047d49SGordon Ross break;
92494047d49SGordon Ross }
92594047d49SGordon Ross }
92694047d49SGordon Ross
92794047d49SGordon Ross mutex_exit(&ol->ol_mutex);
92894047d49SGordon Ross
92994047d49SGordon Ross return (status);
93094047d49SGordon Ross }
931