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. 14a5a9a6bbSGordon Ross * Copyright 2022 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 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; 167*9788d6deSGordon 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) { 24694047d49SGordon Ross /* 24794047d49SGordon Ross * Using smb2_cmd_code to indicate what to call. 24894047d49SGordon Ross * work func. will call smb_oplock_send_brk 24994047d49SGordon Ross */ 2507f5d80fdSGordon Ross sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; 2517f5d80fdSGordon Ross smb2sr_append_postwork(ack_sr, sr); 252*9788d6deSGordon Ross return; 253*9788d6deSGordon Ross } 254*9788d6deSGordon Ross 2557f5d80fdSGordon Ross /* Will call smb_oplock_send_break */ 2567f5d80fdSGordon Ross sr->smb2_status = STATUS_CANT_GRANT; 257*9788d6deSGordon Ross tqid = taskq_dispatch(sv->sv_worker_pool, 2587f5d80fdSGordon Ross smb_oplock_async_break, sr, TQ_SLEEP); 259*9788d6deSGordon 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 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; 283*9788d6deSGordon 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 */ 382*9788d6deSGordon Ross tqid = taskq_dispatch(sv->sv_worker_pool, 38394047d49SGordon Ross smb_oplock_async_break, sr, TQ_SLEEP); 384*9788d6deSGordon 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 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 43994047d49SGordon Ross /* 44072b35b05SGordon Ross * Send an oplock (or lease) break to the client. 44172b35b05SGordon Ross * If we can't, then do a local break. 44294047d49SGordon 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. 44794047d49SGordon 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. 45194047d49SGordon Ross */ 45294047d49SGordon Ross void 45372b35b05SGordon Ross smb_oplock_send_break(smb_request_t *sr) 45494047d49SGordon Ross { 45572b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 45694047d49SGordon Ross 45772b35b05SGordon 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); 46194047d49SGordon Ross else 46272b35b05SGordon Ross smb1_oplock_send_break(sr); 46394047d49SGordon Ross } 46494047d49SGordon Ross 46594047d49SGordon 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. 46894047d49SGordon 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. 47294047d49SGordon 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. 47694047d49SGordon Ross */ 47772b35b05SGordon Ross static const smb_ofile_t invalid_ofile; 47872b35b05SGordon Ross static void 47972b35b05SGordon Ross smb_oplock_hdl_moved(smb_ofile_t *ofile) 48072b35b05SGordon Ross { 48172b35b05SGordon Ross smb_lease_t *ls = ofile->f_lease; 48294047d49SGordon 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; 48694047d49SGordon 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; 49094047d49SGordon Ross } 49194047d49SGordon Ross 49294047d49SGordon Ross /* 4936f8336c5SGordon Ross * See: NT_STATUS_OPLOCK_HANDLE_CLOSED above and 4946f8336c5SGordon Ross * smb_ofile_close, smb_oplock_break_CLOSE. 49594047d49SGordon Ross * 49694047d49SGordon Ross * The FS-level oplock layer calls this to update the 49794047d49SGordon Ross * SMB-level state when a handle loses its oplock. 49894047d49SGordon Ross */ 49994047d49SGordon Ross static void 50072b35b05SGordon Ross smb_oplock_hdl_closed(smb_ofile_t *ofile) 50194047d49SGordon Ross { 50294047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 50394047d49SGordon Ross 50494047d49SGordon Ross if (lease != NULL) { 50594047d49SGordon Ross if (lease->ls_oplock_ofile == ofile) { 5066f8336c5SGordon Ross /* 5076f8336c5SGordon Ross * smb2_lease_ofile_close should have 5086f8336c5SGordon Ross * moved the oplock to another ofile. 5096f8336c5SGordon Ross */ 5106f8336c5SGordon Ross ASSERT(0); 51194047d49SGordon Ross lease->ls_oplock_ofile = NULL; 51294047d49SGordon Ross } 51394047d49SGordon Ross } 51494047d49SGordon Ross ofile->f_oplock.og_state = 0; 5157f6a299eSGordon Ross ofile->f_oplock.og_breakto = 0; 5167f6a299eSGordon Ross ofile->f_oplock.og_breaking = B_FALSE; 51794047d49SGordon 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. 53772b35b05SGordon Ross */ 53872b35b05SGordon Ross static void 53972b35b05SGordon Ross smb_oplock_hdl_update(smb_request_t *sr) 54072b35b05SGordon 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 55172b35b05SGordon 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; 55572b35b05SGordon Ross if (lease != NULL) { 5567f6a299eSGordon Ross // If no unacknowledged break, update epoch. 5577f6a299eSGordon Ross if (lease->ls_breakto == lease->ls_state) 55872b35b05SGordon Ross lease->ls_epoch++; 5597f6a299eSGordon Ross 5607f6a299eSGordon Ross lease->ls_breakto = NewLevel; 5617f6a299eSGordon Ross lease->ls_breaking = B_TRUE; 56272b35b05SGordon Ross } 56372b35b05SGordon Ross 5647f6a299eSGordon Ross if (!AckReq) { 56572b35b05SGordon Ross /* 56672b35b05SGordon Ross * Not expecting an Ack from the client. 56772b35b05SGordon Ross * Update state immediately. 56872b35b05SGordon 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; 57472b35b05SGordon 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 58972b35b05SGordon Ross smb_oplock_close(smb_ofile_t *ofile) 59072b35b05SGordon Ross { 59172b35b05SGordon Ross smb_node_t *node = ofile->f_node; 59272b35b05SGordon Ross 59372b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 59472b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex); 59572b35b05SGordon Ross 59672b35b05SGordon Ross if (ofile->f_oplock_closing == B_FALSE) { 59772b35b05SGordon Ross ofile->f_oplock_closing = B_TRUE; 59872b35b05SGordon Ross 59972b35b05SGordon Ross if (ofile->f_lease != NULL) 60072b35b05SGordon Ross smb2_lease_ofile_close(ofile); 60172b35b05SGordon Ross 60272b35b05SGordon Ross smb_oplock_break_CLOSE(node, ofile); 60372b35b05SGordon 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); 60872b35b05SGordon Ross } 60972b35b05SGordon Ross 61072b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex); 61172b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 61272b35b05SGordon Ross } 61372b35b05SGordon Ross 61472b35b05SGordon Ross /* 61572b35b05SGordon Ross * Called by smb_request_cancel() via sr->cancel_method 61672b35b05SGordon Ross * Arg is the smb_node_t with the breaking oplock. 61772b35b05SGordon Ross */ 61872b35b05SGordon Ross static void 61972b35b05SGordon Ross smb_oplock_wait_ack_cancel(smb_request_t *sr) 62072b35b05SGordon 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 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); 65872b35b05SGordon Ross 6597f6a299eSGordon Ross /* 6607f6a299eSGordon Ross * Wait on either lease state or oplock state 6617f6a299eSGordon Ross */ 66272b35b05SGordon 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 67072b35b05SGordon 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 6767f6a299eSGordon 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 */ 69872b35b05SGordon Ross status = NT_STATUS_CANNOT_BREAK_OPLOCK; 69972b35b05SGordon Ross break; 70072b35b05SGordon Ross } 70172b35b05SGordon Ross 70272b35b05SGordon Ross /* 70372b35b05SGordon Ross * Check if we were woken by smb_request_cancel, 70472b35b05SGordon Ross * which sets state SMB_REQ_STATE_CANCEL_PENDING 70572b35b05SGordon Ross * and signals the CV. The mutex enter/exit is 70672b35b05SGordon Ross * just to ensure cache visibility of sr_state 70772b35b05SGordon Ross * that was updated in smb_request_cancel. 70872b35b05SGordon Ross */ 70972b35b05SGordon Ross mutex_enter(&sr->sr_mutex); 71072b35b05SGordon Ross srstate = sr->sr_state; 71172b35b05SGordon Ross mutex_exit(&sr->sr_mutex); 71272b35b05SGordon Ross if (srstate != SMB_REQ_STATE_WAITING_OLBRK) { 71372b35b05SGordon Ross break; 71472b35b05SGordon Ross } 71572b35b05SGordon Ross } 71672b35b05SGordon Ross mutex_exit(&ol->ol_mutex); 71772b35b05SGordon Ross 71872b35b05SGordon Ross /* 71972b35b05SGordon Ross * Clear cancellation callback and see if it fired. 72072b35b05SGordon Ross */ 72172b35b05SGordon Ross mutex_enter(&sr->sr_mutex); 72272b35b05SGordon Ross sr->cancel_method = NULL; 72372b35b05SGordon Ross sr->cancel_arg2 = NULL; 72472b35b05SGordon Ross switch (sr->sr_state) { 72572b35b05SGordon Ross case SMB_REQ_STATE_WAITING_OLBRK: 72672b35b05SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE; 72772b35b05SGordon Ross /* status from above */ 72872b35b05SGordon Ross break; 72972b35b05SGordon Ross case SMB_REQ_STATE_CANCEL_PENDING: 73072b35b05SGordon Ross sr->sr_state = SMB_REQ_STATE_CANCELLED; 73172b35b05SGordon Ross status = NT_STATUS_CANCELLED; 73272b35b05SGordon Ross break; 73372b35b05SGordon Ross default: 73472b35b05SGordon Ross status = NT_STATUS_INTERNAL_ERROR; 73572b35b05SGordon Ross break; 73672b35b05SGordon Ross } 73772b35b05SGordon Ross mutex_exit(&sr->sr_mutex); 73872b35b05SGordon Ross 73972b35b05SGordon Ross return (status); 74072b35b05SGordon Ross } 74172b35b05SGordon Ross 74272b35b05SGordon Ross /* 743525641e8SGordon Ross * Called by smb_request_cancel() via sr->cancel_method 744525641e8SGordon Ross * Arg is the smb_node_t with the breaking oplock. 745525641e8SGordon Ross */ 746525641e8SGordon Ross static void 747525641e8SGordon Ross smb_oplock_wait_break_cancel(smb_request_t *sr) 748525641e8SGordon Ross { 749525641e8SGordon Ross smb_node_t *node = sr->cancel_arg2; 750525641e8SGordon Ross smb_oplock_t *ol; 751525641e8SGordon Ross 752525641e8SGordon Ross SMB_NODE_VALID(node); 753525641e8SGordon Ross ol = &node->n_oplock; 754525641e8SGordon Ross 755525641e8SGordon Ross mutex_enter(&ol->ol_mutex); 756525641e8SGordon Ross cv_broadcast(&ol->WaitingOpenCV); 757525641e8SGordon Ross mutex_exit(&ol->ol_mutex); 758525641e8SGordon Ross } 759525641e8SGordon Ross 760525641e8SGordon Ross /* 76194047d49SGordon Ross * Wait up to "timeout" mSec. for the current oplock "breaking" flags 76294047d49SGordon Ross * to be cleared (by smb_oplock_ack_break or smb_oplock_break_CLOSE). 76394047d49SGordon Ross * 76494047d49SGordon Ross * Callers of the above public oplock functions: 76594047d49SGordon Ross * smb_oplock_request() 76694047d49SGordon Ross * smb_oplock_ack_break() 76794047d49SGordon Ross * smb_oplock_break_OPEN() ... 76894047d49SGordon Ross * check for return status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS 76994047d49SGordon Ross * and call this function to wait for the break to complete. 77094047d49SGordon Ross * 77194047d49SGordon Ross * Most callers should use this default timeout, which they get 77294047d49SGordon Ross * by passing zero as the timeout arg. This include places where 77394047d49SGordon Ross * we're about to do something that invalidates some cache. 77494047d49SGordon Ross */ 77594047d49SGordon Ross uint32_t 776525641e8SGordon Ross smb_oplock_wait_break(smb_request_t *sr, smb_node_t *node, int timeout) 777525641e8SGordon Ross { 778525641e8SGordon Ross smb_oplock_t *ol; 779525641e8SGordon Ross clock_t time, rv; 780525641e8SGordon Ross uint32_t status = 0; 781525641e8SGordon Ross smb_req_state_t srstate; 782525641e8SGordon Ross 783525641e8SGordon Ross SMB_NODE_VALID(node); 784525641e8SGordon Ross ol = &node->n_oplock; 785525641e8SGordon Ross 786525641e8SGordon Ross if (timeout == 0) 787525641e8SGordon Ross timeout = smb_oplock_timeout_def; 788525641e8SGordon Ross time = MSEC_TO_TICK(timeout) + ddi_get_lbolt(); 789525641e8SGordon Ross 790525641e8SGordon Ross mutex_enter(&sr->sr_mutex); 791525641e8SGordon Ross if (sr->sr_state != SMB_REQ_STATE_ACTIVE) { 792525641e8SGordon Ross mutex_exit(&sr->sr_mutex); 793525641e8SGordon Ross return (NT_STATUS_CANCELLED); 794525641e8SGordon Ross } 795525641e8SGordon Ross sr->sr_state = SMB_REQ_STATE_WAITING_OLBRK; 796525641e8SGordon Ross sr->cancel_method = smb_oplock_wait_break_cancel; 797525641e8SGordon Ross sr->cancel_arg2 = node; 798525641e8SGordon Ross mutex_exit(&sr->sr_mutex); 799525641e8SGordon Ross 800525641e8SGordon Ross mutex_enter(&ol->ol_mutex); 801525641e8SGordon Ross while ((ol->ol_state & BREAK_ANY) != 0) { 802525641e8SGordon Ross ol->waiters++; 803525641e8SGordon Ross rv = cv_timedwait(&ol->WaitingOpenCV, 804525641e8SGordon Ross &ol->ol_mutex, time); 805525641e8SGordon Ross ol->waiters--; 806525641e8SGordon Ross if (rv < 0) { 807525641e8SGordon Ross /* cv_timewait timeout */ 808525641e8SGordon Ross status = NT_STATUS_CANNOT_BREAK_OPLOCK; 809525641e8SGordon Ross break; 810525641e8SGordon Ross } 811525641e8SGordon Ross 812525641e8SGordon Ross /* 813525641e8SGordon Ross * Check if we were woken by smb_request_cancel, 814525641e8SGordon Ross * which sets state SMB_REQ_STATE_CANCEL_PENDING 81572b35b05SGordon Ross * and signals the CV. The mutex enter/exit is 81672b35b05SGordon Ross * just to ensure cache visibility of sr_state 81772b35b05SGordon Ross * that was updated in smb_request_cancel. 818525641e8SGordon Ross */ 819525641e8SGordon Ross mutex_enter(&sr->sr_mutex); 820525641e8SGordon Ross srstate = sr->sr_state; 821525641e8SGordon Ross mutex_exit(&sr->sr_mutex); 822525641e8SGordon Ross if (srstate != SMB_REQ_STATE_WAITING_OLBRK) { 823525641e8SGordon Ross break; 824525641e8SGordon Ross } 825525641e8SGordon Ross } 826525641e8SGordon Ross 827525641e8SGordon Ross mutex_exit(&ol->ol_mutex); 828525641e8SGordon Ross 829525641e8SGordon Ross mutex_enter(&sr->sr_mutex); 830525641e8SGordon Ross sr->cancel_method = NULL; 831525641e8SGordon Ross sr->cancel_arg2 = NULL; 832525641e8SGordon Ross switch (sr->sr_state) { 833525641e8SGordon Ross case SMB_REQ_STATE_WAITING_OLBRK: 834525641e8SGordon Ross sr->sr_state = SMB_REQ_STATE_ACTIVE; 835525641e8SGordon Ross /* status from above */ 836525641e8SGordon Ross break; 837525641e8SGordon Ross case SMB_REQ_STATE_CANCEL_PENDING: 838525641e8SGordon Ross sr->sr_state = SMB_REQ_STATE_CANCELLED; 839525641e8SGordon Ross status = NT_STATUS_CANCELLED; 840525641e8SGordon Ross break; 841525641e8SGordon Ross default: 842525641e8SGordon Ross status = NT_STATUS_INTERNAL_ERROR; 843525641e8SGordon Ross break; 844525641e8SGordon Ross } 845525641e8SGordon Ross mutex_exit(&sr->sr_mutex); 846525641e8SGordon Ross 847525641e8SGordon Ross return (status); 848525641e8SGordon Ross } 849525641e8SGordon Ross 850525641e8SGordon Ross /* 851525641e8SGordon Ross * Simplified version used in smb_fem.c, like above, 852525641e8SGordon Ross * but no smb_request_cancel stuff. 853525641e8SGordon Ross */ 854525641e8SGordon Ross uint32_t 855525641e8SGordon Ross smb_oplock_wait_break_fem(smb_node_t *node, int timeout) /* mSec. */ 85694047d49SGordon Ross { 85794047d49SGordon Ross smb_oplock_t *ol; 85894047d49SGordon Ross clock_t time, rv; 85994047d49SGordon Ross uint32_t status = 0; 86094047d49SGordon Ross 86194047d49SGordon Ross if (timeout == 0) 86294047d49SGordon Ross timeout = smb_oplock_timeout_def; 86394047d49SGordon Ross 86494047d49SGordon Ross SMB_NODE_VALID(node); 86594047d49SGordon Ross ol = &node->n_oplock; 86694047d49SGordon Ross 86794047d49SGordon Ross mutex_enter(&ol->ol_mutex); 86894047d49SGordon Ross time = MSEC_TO_TICK(timeout) + ddi_get_lbolt(); 86994047d49SGordon Ross 87094047d49SGordon Ross while ((ol->ol_state & BREAK_ANY) != 0) { 87194047d49SGordon Ross ol->waiters++; 87294047d49SGordon Ross rv = cv_timedwait(&ol->WaitingOpenCV, 87394047d49SGordon Ross &ol->ol_mutex, time); 87494047d49SGordon Ross ol->waiters--; 87594047d49SGordon Ross if (rv < 0) { 87694047d49SGordon Ross status = NT_STATUS_CANNOT_BREAK_OPLOCK; 87794047d49SGordon Ross break; 87894047d49SGordon Ross } 87994047d49SGordon Ross } 88094047d49SGordon Ross 88194047d49SGordon Ross mutex_exit(&ol->ol_mutex); 88294047d49SGordon Ross 88394047d49SGordon Ross return (status); 88494047d49SGordon Ross } 885