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 * Dispatch function for SMB2_OPLOCK_BREAK 1994047d49SGordon Ross */ 2094047d49SGordon Ross 2194047d49SGordon Ross #include <smbsrv/smb2_kproto.h> 2294047d49SGordon Ross #include <smbsrv/smb_oplock.h> 2394047d49SGordon Ross 2494047d49SGordon Ross /* StructSize for the two "break" message formats. */ 2594047d49SGordon Ross #define SSZ_OPLOCK 24 2694047d49SGordon Ross #define SSZ_LEASE_ACK 36 2794047d49SGordon Ross #define SSZ_LEASE_BRK 44 2894047d49SGordon Ross 2994047d49SGordon Ross #define NODE_FLAGS_DELETING (NODE_FLAGS_DELETE_ON_CLOSE |\ 3094047d49SGordon Ross NODE_FLAGS_DELETE_COMMITTED) 3194047d49SGordon Ross 3294047d49SGordon Ross static const char lease_zero[UUID_LEN] = { 0 }; 3394047d49SGordon Ross 3494047d49SGordon Ross static kmem_cache_t *smb_lease_cache = NULL; 3594047d49SGordon Ross 3694047d49SGordon Ross void 3794047d49SGordon Ross smb2_lease_init() 3894047d49SGordon Ross { 3994047d49SGordon Ross if (smb_lease_cache != NULL) 4094047d49SGordon Ross return; 4194047d49SGordon Ross 4294047d49SGordon Ross smb_lease_cache = kmem_cache_create("smb_lease_cache", 4394047d49SGordon Ross sizeof (smb_lease_t), 8, NULL, NULL, NULL, NULL, NULL, 0); 4494047d49SGordon Ross } 4594047d49SGordon Ross 4694047d49SGordon Ross void 4794047d49SGordon Ross smb2_lease_fini() 4894047d49SGordon Ross { 4994047d49SGordon Ross if (smb_lease_cache != NULL) { 5094047d49SGordon Ross kmem_cache_destroy(smb_lease_cache); 5194047d49SGordon Ross smb_lease_cache = NULL; 5294047d49SGordon Ross } 5394047d49SGordon Ross } 5494047d49SGordon Ross 5594047d49SGordon Ross static void 5694047d49SGordon Ross smb2_lease_hold(smb_lease_t *ls) 5794047d49SGordon Ross { 5894047d49SGordon Ross mutex_enter(&ls->ls_mutex); 5994047d49SGordon Ross ls->ls_refcnt++; 6094047d49SGordon Ross mutex_exit(&ls->ls_mutex); 6194047d49SGordon Ross } 6294047d49SGordon Ross 63*72b35b05SGordon Ross static void 64*72b35b05SGordon Ross lease_destroy(smb_lease_t *ls) 65*72b35b05SGordon Ross { 66*72b35b05SGordon Ross smb_node_release(ls->ls_node); 67*72b35b05SGordon Ross mutex_destroy(&ls->ls_mutex); 68*72b35b05SGordon Ross kmem_cache_free(smb_lease_cache, ls); 69*72b35b05SGordon Ross } 70*72b35b05SGordon Ross 7194047d49SGordon Ross void 7294047d49SGordon Ross smb2_lease_rele(smb_lease_t *ls) 7394047d49SGordon Ross { 7494047d49SGordon Ross smb_llist_t *bucket; 75*72b35b05SGordon Ross boolean_t destroy = B_FALSE; 7694047d49SGordon Ross 7794047d49SGordon Ross mutex_enter(&ls->ls_mutex); 7894047d49SGordon Ross ls->ls_refcnt--; 7994047d49SGordon Ross if (ls->ls_refcnt != 0) { 8094047d49SGordon Ross mutex_exit(&ls->ls_mutex); 8194047d49SGordon Ross return; 8294047d49SGordon Ross } 8394047d49SGordon Ross mutex_exit(&ls->ls_mutex); 8494047d49SGordon Ross 8594047d49SGordon Ross /* 8694047d49SGordon Ross * Get the list lock, then re-check the refcnt 8794047d49SGordon Ross * and if it's still zero, unlink & destroy. 8894047d49SGordon Ross */ 8994047d49SGordon Ross bucket = ls->ls_bucket; 9094047d49SGordon Ross smb_llist_enter(bucket, RW_WRITER); 9194047d49SGordon Ross 9294047d49SGordon Ross mutex_enter(&ls->ls_mutex); 9394047d49SGordon Ross if (ls->ls_refcnt == 0) { 94*72b35b05SGordon Ross smb_llist_remove(bucket, ls); 95*72b35b05SGordon Ross destroy = B_TRUE; 9694047d49SGordon Ross } 97*72b35b05SGordon Ross mutex_exit(&ls->ls_mutex); 9894047d49SGordon Ross 9994047d49SGordon Ross smb_llist_exit(bucket); 100*72b35b05SGordon Ross 101*72b35b05SGordon Ross if (destroy) { 102*72b35b05SGordon Ross lease_destroy(ls); 103*72b35b05SGordon Ross } 10494047d49SGordon Ross } 10594047d49SGordon Ross 10694047d49SGordon Ross /* 10794047d49SGordon Ross * Compute a hash from a uuid 10894047d49SGordon Ross * Based on mod_hash_bystr() 10994047d49SGordon Ross */ 11094047d49SGordon Ross static uint_t 11194047d49SGordon Ross smb_hash_uuid(const uint8_t *uuid) 11294047d49SGordon Ross { 11394047d49SGordon Ross char *k = (char *)uuid; 11494047d49SGordon Ross uint_t hash = 0; 11594047d49SGordon Ross uint_t g; 11694047d49SGordon Ross int i; 11794047d49SGordon Ross 11894047d49SGordon Ross ASSERT(k); 11994047d49SGordon Ross for (i = 0; i < UUID_LEN; i++) { 12094047d49SGordon Ross hash = (hash << 4) + k[i]; 12194047d49SGordon Ross if ((g = (hash & 0xf0000000)) != 0) { 12294047d49SGordon Ross hash ^= (g >> 24); 12394047d49SGordon Ross hash ^= g; 12494047d49SGordon Ross } 12594047d49SGordon Ross } 12694047d49SGordon Ross return (hash); 12794047d49SGordon Ross } 12894047d49SGordon Ross 12994047d49SGordon Ross /* 13094047d49SGordon Ross * Add or update a lease table entry for a new ofile. 13194047d49SGordon Ross * (in the per-session lease table) 13294047d49SGordon Ross * See [MS-SMB2] 3.3.5.9.8 13394047d49SGordon Ross * Handling the SMB2_CREATE_REQUEST_LEASE Create Context 13494047d49SGordon Ross */ 13594047d49SGordon Ross uint32_t 1368d94f651SGordon Ross smb2_lease_create(smb_request_t *sr, uint8_t *clnt) 13794047d49SGordon Ross { 13894047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 13994047d49SGordon Ross uint8_t *key = op->lease_key; 14094047d49SGordon Ross smb_ofile_t *of = sr->fid_ofile; 14194047d49SGordon Ross smb_hash_t *ht = sr->sr_server->sv_lease_ht; 14294047d49SGordon Ross smb_llist_t *bucket; 14394047d49SGordon Ross smb_lease_t *lease; 14494047d49SGordon Ross smb_lease_t *newlease; 14594047d49SGordon Ross size_t hashkey; 14694047d49SGordon Ross uint32_t status = NT_STATUS_INVALID_PARAMETER; 14794047d49SGordon Ross 14894047d49SGordon Ross if (bcmp(key, lease_zero, UUID_LEN) == 0) 14994047d49SGordon Ross return (status); 15094047d49SGordon Ross 15194047d49SGordon Ross /* 15294047d49SGordon Ross * Find or create, and add a ref for the new ofile. 15394047d49SGordon Ross */ 15494047d49SGordon Ross hashkey = smb_hash_uuid(key); 15594047d49SGordon Ross hashkey &= (ht->num_buckets - 1); 15694047d49SGordon Ross bucket = &ht->buckets[hashkey].b_list; 15794047d49SGordon Ross 15894047d49SGordon Ross newlease = kmem_cache_alloc(smb_lease_cache, KM_SLEEP); 15994047d49SGordon Ross bzero(newlease, sizeof (smb_lease_t)); 16094047d49SGordon Ross mutex_init(&newlease->ls_mutex, NULL, MUTEX_DEFAULT, NULL); 16194047d49SGordon Ross newlease->ls_bucket = bucket; 16294047d49SGordon Ross newlease->ls_node = of->f_node; 163*72b35b05SGordon Ross smb_node_ref(newlease->ls_node); 16494047d49SGordon Ross newlease->ls_refcnt = 1; 16594047d49SGordon Ross newlease->ls_epoch = op->lease_epoch; 16694047d49SGordon Ross newlease->ls_version = op->lease_version; 16794047d49SGordon Ross bcopy(key, newlease->ls_key, UUID_LEN); 16894047d49SGordon Ross bcopy(clnt, newlease->ls_clnt, UUID_LEN); 16994047d49SGordon Ross 17094047d49SGordon Ross smb_llist_enter(bucket, RW_WRITER); 17194047d49SGordon Ross for (lease = smb_llist_head(bucket); lease != NULL; 17294047d49SGordon Ross lease = smb_llist_next(bucket, lease)) { 17394047d49SGordon Ross /* 17494047d49SGordon Ross * Looking for this lease ID, on a node 17594047d49SGordon Ross * that's not being deleted. 17694047d49SGordon Ross */ 17794047d49SGordon Ross if (bcmp(lease->ls_key, key, UUID_LEN) == 0 && 17894047d49SGordon Ross bcmp(lease->ls_clnt, clnt, UUID_LEN) == 0 && 17994047d49SGordon Ross (lease->ls_node->flags & NODE_FLAGS_DELETING) == 0) 18094047d49SGordon Ross break; 18194047d49SGordon Ross } 18294047d49SGordon Ross if (lease != NULL) { 18394047d49SGordon Ross /* 18494047d49SGordon Ross * Found existing lease. Make sure it refers to 18594047d49SGordon Ross * the same node... 18694047d49SGordon Ross */ 18794047d49SGordon Ross if (lease->ls_node == of->f_node) { 18894047d49SGordon Ross smb2_lease_hold(lease); 18994047d49SGordon Ross } else { 19094047d49SGordon Ross /* Same lease ID, different node! */ 19194047d49SGordon Ross #ifdef DEBUG 19294047d49SGordon Ross cmn_err(CE_NOTE, "new lease on node %p (%s) " 19394047d49SGordon Ross "conflicts with existing node %p (%s)", 19494047d49SGordon Ross (void *) of->f_node, 19594047d49SGordon Ross of->f_node->od_name, 19694047d49SGordon Ross (void *) lease->ls_node, 19794047d49SGordon Ross lease->ls_node->od_name); 19894047d49SGordon Ross #endif 199a5a9a6bbSGordon Ross DTRACE_PROBE2(dup_lease, smb_request_t *, sr, 200a5a9a6bbSGordon Ross smb_lease_t *, lease); 20194047d49SGordon Ross lease = NULL; /* error */ 20294047d49SGordon Ross } 20394047d49SGordon Ross } else { 20494047d49SGordon Ross lease = newlease; 20594047d49SGordon Ross smb_llist_insert_head(bucket, lease); 20694047d49SGordon Ross newlease = NULL; /* don't free */ 20794047d49SGordon Ross } 20894047d49SGordon Ross smb_llist_exit(bucket); 20994047d49SGordon Ross 21094047d49SGordon Ross if (newlease != NULL) { 211*72b35b05SGordon Ross lease_destroy(newlease); 21294047d49SGordon Ross } 21394047d49SGordon Ross 21494047d49SGordon Ross if (lease != NULL) { 21594047d49SGordon Ross of->f_lease = lease; 21694047d49SGordon Ross status = NT_STATUS_SUCCESS; 21794047d49SGordon Ross } 21894047d49SGordon Ross 21994047d49SGordon Ross return (status); 22094047d49SGordon Ross } 22194047d49SGordon Ross 22294047d49SGordon Ross /* 22394047d49SGordon Ross * Find the lease for a given: client_uuid, lease_key 22494047d49SGordon Ross * Returns the lease with a new ref. 22594047d49SGordon Ross */ 226*72b35b05SGordon Ross static smb_lease_t * 227*72b35b05SGordon Ross lease_lookup(smb_request_t *sr, uint8_t *lease_key) 22894047d49SGordon Ross { 229*72b35b05SGordon Ross smb_server_t *sv = sr->sr_server; 230*72b35b05SGordon Ross uint8_t *clnt_uuid = sr->session->clnt_uuid; 23194047d49SGordon Ross smb_hash_t *ht = sv->sv_lease_ht; 23294047d49SGordon Ross smb_llist_t *bucket; 23394047d49SGordon Ross smb_lease_t *lease; 23494047d49SGordon Ross size_t hashkey; 23594047d49SGordon Ross 23694047d49SGordon Ross hashkey = smb_hash_uuid(lease_key); 23794047d49SGordon Ross hashkey &= (ht->num_buckets - 1); 23894047d49SGordon Ross bucket = &ht->buckets[hashkey].b_list; 23994047d49SGordon Ross 24094047d49SGordon Ross smb_llist_enter(bucket, RW_READER); 24194047d49SGordon Ross lease = smb_llist_head(bucket); 24294047d49SGordon Ross while (lease != NULL) { 24394047d49SGordon Ross if (bcmp(lease->ls_key, lease_key, UUID_LEN) == 0 && 24494047d49SGordon Ross bcmp(lease->ls_clnt, clnt_uuid, UUID_LEN) == 0) { 24594047d49SGordon Ross smb2_lease_hold(lease); 24694047d49SGordon Ross break; 24794047d49SGordon Ross } 24894047d49SGordon Ross lease = smb_llist_next(bucket, lease); 24994047d49SGordon Ross } 25094047d49SGordon Ross smb_llist_exit(bucket); 25194047d49SGordon Ross 25294047d49SGordon Ross return (lease); 25394047d49SGordon Ross } 25494047d49SGordon Ross 25594047d49SGordon Ross /* 256*72b35b05SGordon Ross * Find the oplock smb_ofile_t for the specified lease. 257*72b35b05SGordon Ross * If no such ofile, NT_STATUS_UNSUCCESSFUL. 25894047d49SGordon Ross * On success, ofile (held) in sr->fid_ofile. 25994047d49SGordon Ross */ 26094047d49SGordon Ross static uint32_t 261*72b35b05SGordon Ross lease_find_oplock(smb_request_t *sr, smb_lease_t *lease) 26294047d49SGordon Ross { 263*72b35b05SGordon Ross smb_node_t *node = lease->ls_node; 26494047d49SGordon Ross smb_ofile_t *o; 265*72b35b05SGordon Ross uint32_t status = NT_STATUS_UNSUCCESSFUL; 26694047d49SGordon Ross 267*72b35b05SGordon Ross ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 268*72b35b05SGordon Ross ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 269*72b35b05SGordon Ross ASSERT(sr->fid_ofile == NULL); 27094047d49SGordon Ross 271*72b35b05SGordon Ross FOREACH_NODE_OFILE(node, o) { 272*72b35b05SGordon Ross if (o->f_lease != lease) 273*72b35b05SGordon Ross continue; 274*72b35b05SGordon Ross if (o != lease->ls_oplock_ofile) 275*72b35b05SGordon Ross continue; 27694047d49SGordon Ross /* 277*72b35b05SGordon Ross * Found the ofile holding the oplock 278*72b35b05SGordon Ross * This hold released in smb_request_free 27994047d49SGordon Ross */ 280*72b35b05SGordon Ross if (smb_ofile_hold_olbrk(o)) { 28194047d49SGordon Ross sr->fid_ofile = o; 28294047d49SGordon Ross status = NT_STATUS_SUCCESS; 28394047d49SGordon Ross break; 28494047d49SGordon Ross } 28594047d49SGordon Ross } 28694047d49SGordon Ross 28794047d49SGordon Ross return (status); 28894047d49SGordon Ross } 28994047d49SGordon Ross 29094047d49SGordon Ross /* 29194047d49SGordon Ross * This is called by smb2_oplock_break_ack when the struct size 29294047d49SGordon Ross * indicates this is a lease break (SZ_LEASE). See: 29394047d49SGordon Ross * [MS-SMB2] 3.3.5.22.2 Processing a Lease Acknowledgment 294*72b35b05SGordon Ross * This is an "Ack" from the client. 29594047d49SGordon Ross */ 29694047d49SGordon Ross smb_sdrc_t 29794047d49SGordon Ross smb2_lease_break_ack(smb_request_t *sr) 29894047d49SGordon Ross { 299*72b35b05SGordon Ross smb_arg_olbrk_t *olbrk = &sr->arg.olbrk; 30094047d49SGordon Ross smb_lease_t *lease; 301*72b35b05SGordon Ross smb_node_t *node; 30294047d49SGordon Ross smb_ofile_t *ofile; 30394047d49SGordon Ross uint32_t LeaseState; 304*72b35b05SGordon Ross uint32_t BreakTo; 30594047d49SGordon Ross uint32_t status; 30694047d49SGordon Ross int rc = 0; 30794047d49SGordon Ross 30894047d49SGordon Ross if (sr->session->dialect < SMB_VERS_2_1) 30994047d49SGordon Ross return (SDRC_ERROR); 31094047d49SGordon Ross 31194047d49SGordon Ross /* 31294047d49SGordon Ross * Decode an SMB2 Lease Acknowldgement 31394047d49SGordon Ross * [MS-SMB2] 2.2.24.2 31494047d49SGordon Ross * Note: Struct size decoded by caller. 31594047d49SGordon Ross */ 31694047d49SGordon Ross rc = smb_mbc_decodef( 31794047d49SGordon Ross &sr->smb_data, "6.#cl8.", 31894047d49SGordon Ross /* reserved 6. */ 31994047d49SGordon Ross UUID_LEN, /* # */ 320*72b35b05SGordon Ross olbrk->LeaseKey, /* c */ 321*72b35b05SGordon Ross &olbrk->NewLevel); /* l */ 32294047d49SGordon Ross /* duration 8. */ 32394047d49SGordon Ross if (rc != 0) 32494047d49SGordon Ross return (SDRC_ERROR); 325*72b35b05SGordon Ross LeaseState = olbrk->NewLevel; 32694047d49SGordon Ross 327*72b35b05SGordon Ross /* 328*72b35b05SGordon Ross * Find the lease via the given key. 329*72b35b05SGordon Ross */ 330*72b35b05SGordon Ross lease = lease_lookup(sr, olbrk->LeaseKey); 331*72b35b05SGordon Ross if (lease == NULL) { 332*72b35b05SGordon Ross /* 333*72b35b05SGordon Ross * It's unusual to skip the dtrace start/done 334*72b35b05SGordon Ross * probes like this, but trying to run them 335*72b35b05SGordon Ross * with no lease->node would be complex and 336*72b35b05SGordon Ross * would not show anything particularly useful. 337*72b35b05SGordon Ross * Do the start probe after we find the ofile. 338*72b35b05SGordon Ross */ 339*72b35b05SGordon Ross status = NT_STATUS_OBJECT_NAME_NOT_FOUND; 340*72b35b05SGordon Ross smb2sr_put_error(sr, status); 341*72b35b05SGordon Ross return (SDRC_SUCCESS); 342*72b35b05SGordon Ross } 343*72b35b05SGordon Ross // Note: lease ref; smb_lease_rele() below. 344*72b35b05SGordon Ross node = lease->ls_node; 345*72b35b05SGordon Ross 346*72b35b05SGordon Ross /* 347*72b35b05SGordon Ross * Find the leased oplock. Hold locks so it can't move 348*72b35b05SGordon Ross * until we're done with ACK-break processing. 349*72b35b05SGordon Ross */ 350*72b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 351*72b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex); 352*72b35b05SGordon Ross 353*72b35b05SGordon Ross status = lease_find_oplock(sr, lease); 354*72b35b05SGordon Ross /* Normally have sr->fid_ofile now. */ 35594047d49SGordon Ross 35694047d49SGordon Ross DTRACE_SMB2_START(op__OplockBreak, smb_request_t *, sr); 357*72b35b05SGordon Ross 358*72b35b05SGordon Ross if (status != 0) { 359*72b35b05SGordon Ross /* Leased oplock not found. Must have closed. */ 36094047d49SGordon Ross goto errout; 361*72b35b05SGordon Ross } 36294047d49SGordon Ross 363*72b35b05SGordon Ross /* Success, so have sr->fid_ofile */ 36494047d49SGordon Ross ofile = sr->fid_ofile; 36594047d49SGordon Ross 3666f8336c5SGordon Ross if (lease->ls_breaking == 0) { 367*72b35b05SGordon Ross /* 368*72b35b05SGordon Ross * This ACK is either unsolicited or too late, 369*72b35b05SGordon Ross * eg. we timed out the ACK and did it locally. 370*72b35b05SGordon Ross */ 3716f8336c5SGordon Ross status = NT_STATUS_UNSUCCESSFUL; 3726f8336c5SGordon Ross goto errout; 3736f8336c5SGordon Ross } 3746f8336c5SGordon Ross 37594047d49SGordon Ross /* 37694047d49SGordon Ross * If the new LeaseState has any bits in excess of 37794047d49SGordon Ross * the lease state we sent in the break, error... 37894047d49SGordon Ross */ 379*72b35b05SGordon Ross BreakTo = (lease->ls_breaking >> BREAK_SHIFT) & 38094047d49SGordon Ross OPLOCK_LEVEL_CACHE_MASK; 381*72b35b05SGordon Ross if ((LeaseState & ~BreakTo) != 0) { 38294047d49SGordon Ross status = NT_STATUS_REQUEST_NOT_ACCEPTED; 38394047d49SGordon Ross goto errout; 38494047d49SGordon Ross } 38594047d49SGordon Ross 386*72b35b05SGordon Ross /* 387*72b35b05SGordon Ross * Process the lease break ack. 388*72b35b05SGordon Ross * 389*72b35b05SGordon Ross * Clear breaking flags before we ack, 390*72b35b05SGordon Ross * because ack might set those. 391*72b35b05SGordon Ross */ 392*72b35b05SGordon Ross ofile->f_oplock.og_breaking = 0; 39394047d49SGordon Ross lease->ls_breaking = 0; 394*72b35b05SGordon Ross cv_broadcast(&lease->ls_ack_cv); 39594047d49SGordon Ross 39694047d49SGordon Ross LeaseState |= OPLOCK_LEVEL_GRANULAR; 39794047d49SGordon Ross status = smb_oplock_ack_break(sr, ofile, &LeaseState); 39894047d49SGordon Ross 399*72b35b05SGordon Ross ofile->f_oplock.og_state = LeaseState; 400*72b35b05SGordon Ross lease->ls_state = LeaseState & CACHE_RWH; 401*72b35b05SGordon Ross /* ls_epoch does not change here */ 402*72b35b05SGordon Ross 403*72b35b05SGordon Ross if (ofile->dh_persist) 404*72b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile); 40594047d49SGordon Ross 40694047d49SGordon Ross errout: 40794047d49SGordon Ross sr->smb2_status = status; 40894047d49SGordon Ross DTRACE_SMB2_DONE(op__OplockBreak, smb_request_t *, sr); 409*72b35b05SGordon Ross 410*72b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex); 411*72b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 412*72b35b05SGordon Ross 413*72b35b05SGordon Ross smb2_lease_rele(lease); 414*72b35b05SGordon Ross 41594047d49SGordon Ross if (status) { 41694047d49SGordon Ross smb2sr_put_error(sr, status); 41794047d49SGordon Ross return (SDRC_SUCCESS); 41894047d49SGordon Ross } 41994047d49SGordon Ross 42094047d49SGordon Ross /* 42194047d49SGordon Ross * Encode an SMB2 Lease Ack. response 42294047d49SGordon Ross * [MS-SMB2] 2.2.25.2 42394047d49SGordon Ross */ 42494047d49SGordon Ross LeaseState &= OPLOCK_LEVEL_CACHE_MASK; 42594047d49SGordon Ross (void) smb_mbc_encodef( 42694047d49SGordon Ross &sr->reply, "w6.#cl8.", 42794047d49SGordon Ross SSZ_LEASE_ACK, /* w */ 42894047d49SGordon Ross /* reserved 6. */ 42994047d49SGordon Ross UUID_LEN, /* # */ 430*72b35b05SGordon Ross olbrk->LeaseKey, /* c */ 43194047d49SGordon Ross LeaseState); /* l */ 43294047d49SGordon Ross /* duration 8. */ 43394047d49SGordon Ross 43494047d49SGordon Ross return (SDRC_SUCCESS); 43594047d49SGordon Ross 43694047d49SGordon Ross } 43794047d49SGordon Ross 43894047d49SGordon Ross /* 43994047d49SGordon Ross * Compose an SMB2 Lease Break Notification packet, including 44094047d49SGordon Ross * the SMB2 header and everything, in sr->reply. 44194047d49SGordon Ross * The caller will send it and free the request. 44294047d49SGordon Ross * 44394047d49SGordon Ross * [MS-SMB2] 2.2.23.2 Lease Break Notification 44494047d49SGordon Ross */ 445*72b35b05SGordon Ross static void 446*72b35b05SGordon Ross smb2_lease_break_notification(smb_request_t *sr, 447*72b35b05SGordon Ross uint32_t OldLevel, uint32_t NewLevel, 448*72b35b05SGordon Ross uint16_t Epoch, boolean_t AckReq) 44994047d49SGordon Ross { 4506f8336c5SGordon Ross smb_lease_t *ls = sr->fid_ofile->f_lease; 451*72b35b05SGordon Ross uint16_t Flags = 0; 45294047d49SGordon Ross 45394047d49SGordon Ross /* 454*72b35b05SGordon Ross * Convert internal lease info to SMB2 45594047d49SGordon Ross */ 456*72b35b05SGordon Ross if (AckReq) 457*72b35b05SGordon Ross Flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED; 45894047d49SGordon Ross if (ls->ls_version < 2) 45994047d49SGordon Ross Epoch = 0; 460*72b35b05SGordon Ross OldLevel &= OPLOCK_LEVEL_CACHE_MASK; 461*72b35b05SGordon Ross NewLevel &= OPLOCK_LEVEL_CACHE_MASK; 46294047d49SGordon Ross 46394047d49SGordon Ross /* 46494047d49SGordon Ross * SMB2 Header 46594047d49SGordon Ross */ 46694047d49SGordon Ross sr->smb2_cmd_code = SMB2_OPLOCK_BREAK; 46794047d49SGordon Ross sr->smb2_hdr_flags = SMB2_FLAGS_SERVER_TO_REDIR; 46894047d49SGordon Ross sr->smb_tid = 0; 46994047d49SGordon Ross sr->smb_pid = 0; 47094047d49SGordon Ross sr->smb2_ssnid = 0; 47194047d49SGordon Ross sr->smb2_messageid = UINT64_MAX; 47294047d49SGordon Ross (void) smb2_encode_header(sr, B_FALSE); 47394047d49SGordon Ross 47494047d49SGordon Ross /* 47594047d49SGordon Ross * SMB2 Oplock Break, variable part 47694047d49SGordon Ross * 47794047d49SGordon Ross * [MS-SMB2] says the current lease state preceeds the 47894047d49SGordon Ross * new lease state, but that looks like an error... 47994047d49SGordon Ross */ 48094047d49SGordon Ross (void) smb_mbc_encodef( 48194047d49SGordon Ross &sr->reply, "wwl#cll4.4.4.", 48294047d49SGordon Ross SSZ_LEASE_BRK, /* w */ 48394047d49SGordon Ross Epoch, /* w */ 48494047d49SGordon Ross Flags, /* l */ 48594047d49SGordon Ross SMB_LEASE_KEY_SZ, /* # */ 48694047d49SGordon Ross ls->ls_key, /* c */ 487*72b35b05SGordon Ross OldLevel, /* cur.st l */ 488*72b35b05SGordon Ross NewLevel); /* new.st l */ 48994047d49SGordon Ross /* reserved (4.4.4.) */ 49094047d49SGordon Ross } 49194047d49SGordon Ross 492*72b35b05SGordon Ross /* 493*72b35b05SGordon Ross * Do our best to send a lease break message to the client. 494*72b35b05SGordon Ross * When we get to multi-channel, this is supposed to try 495*72b35b05SGordon Ross * every channel before giving up. For now, try every 496*72b35b05SGordon Ross * connected session with an ofile sharing this lease. 497*72b35b05SGordon Ross * 498*72b35b05SGordon Ross * If this ofile has a valid session, try that first. 499*72b35b05SGordon Ross * Otherwise look on the node list for other ofiles with 500*72b35b05SGordon Ross * the same lease and a connected session. 501*72b35b05SGordon Ross */ 502*72b35b05SGordon Ross static int 503*72b35b05SGordon Ross lease_send_any_cn(smb_request_t *sr) 504*72b35b05SGordon Ross { 505*72b35b05SGordon Ross smb_ofile_t *o; 506*72b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 507*72b35b05SGordon Ross smb_lease_t *lease = ofile->f_lease; 508*72b35b05SGordon Ross smb_node_t *node = ofile->f_node; 509*72b35b05SGordon Ross int rc = ENOTCONN; 510*72b35b05SGordon Ross 511*72b35b05SGordon Ross /* 512*72b35b05SGordon Ross * If the passed oplock ofile has a session, 513*72b35b05SGordon Ross * this IF expression will be true. 514*72b35b05SGordon Ross */ 515*72b35b05SGordon Ross if (sr->session == ofile->f_session) { 516*72b35b05SGordon Ross rc = smb_session_send(sr->session, 0, &sr->reply); 517*72b35b05SGordon Ross if (rc == 0) 518*72b35b05SGordon Ross return (rc); 519*72b35b05SGordon Ross } 520*72b35b05SGordon Ross 521*72b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 522*72b35b05SGordon Ross FOREACH_NODE_OFILE(node, o) { 523*72b35b05SGordon Ross if (o->f_lease != lease) 524*72b35b05SGordon Ross continue; 525*72b35b05SGordon Ross if (smb_ofile_hold(o)) { 526*72b35b05SGordon Ross /* Has a session. */ 527*72b35b05SGordon Ross rc = smb_session_send(o->f_session, 0, &sr->reply); 528*72b35b05SGordon Ross smb_llist_post(&node->n_ofile_list, o, 529*72b35b05SGordon Ross smb_ofile_release_LL); 530*72b35b05SGordon Ross } 531*72b35b05SGordon Ross if (rc == 0) 532*72b35b05SGordon Ross break; 533*72b35b05SGordon Ross } 534*72b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 535*72b35b05SGordon Ross 536*72b35b05SGordon Ross return (rc); 537*72b35b05SGordon Ross } 538*72b35b05SGordon Ross 539*72b35b05SGordon Ross /* 540*72b35b05SGordon Ross * See smb_llist_post on node->n_ofile_list below. 541*72b35b05SGordon Ross * Can't call smb_ofile_close with that list entered. 542*72b35b05SGordon Ross */ 543*72b35b05SGordon Ross static void 544*72b35b05SGordon Ross lease_ofile_close_rele(void *arg) 545*72b35b05SGordon Ross { 546*72b35b05SGordon Ross smb_ofile_t *of = (smb_ofile_t *)arg; 547*72b35b05SGordon Ross 548*72b35b05SGordon Ross smb_ofile_close(of, 0); 549*72b35b05SGordon Ross smb_ofile_release(of); 550*72b35b05SGordon Ross } 551*72b35b05SGordon Ross 552*72b35b05SGordon Ross /* 553*72b35b05SGordon Ross * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break 554*72b35b05SGordon Ross * If no connection, for each Open in Lease.LeaseOpens, 555*72b35b05SGordon Ross * the server MUST close the Open as specified in sec... 556*72b35b05SGordon Ross * for the following cases: 557*72b35b05SGordon Ross * - Open.IsDurable, Open.IsResilient, and 558*72b35b05SGordon Ross * Open.IsPersistent are all FALSE. 559*72b35b05SGordon Ross * - Open.IsDurable is TRUE and Lease.BreakToLeaseState 560*72b35b05SGordon Ross * does not contain SMB2_LEASE_HANDLE_CACHING and 561*72b35b05SGordon Ross */ 562*72b35b05SGordon Ross static void 563*72b35b05SGordon Ross lease_close_notconn(smb_request_t *sr, uint32_t NewLevel) 564*72b35b05SGordon Ross { 565*72b35b05SGordon Ross smb_ofile_t *o; 566*72b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 567*72b35b05SGordon Ross smb_lease_t *lease = ofile->f_lease; 568*72b35b05SGordon Ross smb_node_t *node = ofile->f_node; 569*72b35b05SGordon Ross 570*72b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 571*72b35b05SGordon Ross FOREACH_NODE_OFILE(node, o) { 572*72b35b05SGordon Ross if (o->f_lease != lease) 573*72b35b05SGordon Ross continue; 574*72b35b05SGordon Ross if (o->f_oplock_closing) 575*72b35b05SGordon Ross continue; 576*72b35b05SGordon Ross if (o->dh_persist) 577*72b35b05SGordon Ross continue; 578*72b35b05SGordon Ross if (o->dh_vers == SMB2_RESILIENT) 579*72b35b05SGordon Ross continue; 580*72b35b05SGordon Ross if (o->dh_vers == SMB2_NOT_DURABLE || 581*72b35b05SGordon Ross (NewLevel & OPLOCK_LEVEL_CACHE_HANDLE) == 0) { 582*72b35b05SGordon Ross if (smb_ofile_hold_olbrk(o)) { 583*72b35b05SGordon Ross smb_llist_post(&node->n_ofile_list, o, 584*72b35b05SGordon Ross lease_ofile_close_rele); 585*72b35b05SGordon Ross } 586*72b35b05SGordon Ross } 587*72b35b05SGordon Ross } 588*72b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 589*72b35b05SGordon Ross } 590*72b35b05SGordon Ross 591*72b35b05SGordon Ross /* 592*72b35b05SGordon Ross * Send a lease break over the wire, or if we can't, 593*72b35b05SGordon Ross * then process the lease break locally. 594*72b35b05SGordon Ross * 595*72b35b05SGordon Ross * [MS-SMB2] 3.3.4.7 Object Store Indicates a Lease Break 596*72b35b05SGordon Ross * 597*72b35b05SGordon Ross * This is mostly similar to smb2_oplock_send_break() 598*72b35b05SGordon Ross * See top comment there about the design. 599*72b35b05SGordon Ross * 600*72b35b05SGordon Ross * Differences beween a lease break and oplock break: 601*72b35b05SGordon Ross * 602*72b35b05SGordon Ross * Leases are an SMB-level mechanism whereby multiple open 603*72b35b05SGordon Ross * SMB file handles can share an oplock. All SMB handles 604*72b35b05SGordon Ross * on the lease enjoy the same caching rights. Down at the 605*72b35b05SGordon Ross * file-system level, just one oplock holds the cache rights 606*72b35b05SGordon Ross * for a lease, but (this is the tricky part) that oplock can 607*72b35b05SGordon Ross * MOVE among the SMB file handles sharing the lease. Such 608*72b35b05SGordon Ross * oplock moves can happen when a handle is closed (if that 609*72b35b05SGordon Ross * handle is the one with the oplock) or when a new open on 610*72b35b05SGordon Ross * the lease causes an upgrade of the caching rights. 611*72b35b05SGordon Ross * 612*72b35b05SGordon Ross * We have to deal here with lease movement because this call 613*72b35b05SGordon Ross * happens asynchronously after the smb_oplock_ind_break call, 614*72b35b05SGordon Ross * meaning that the oplock for the lease may have moved by the 615*72b35b05SGordon Ross * time this runs. In addition, the ofile holding the oplock 616*72b35b05SGordon Ross * might not be the best one to use to send a lease break. 617*72b35b05SGordon Ross * If the oplock is held by a handle that's "orphaned" and 618*72b35b05SGordon Ross * there are other handles on the lease with active sessions, 619*72b35b05SGordon Ross * we want to send the lease break on an active session. 620*72b35b05SGordon Ross * 621*72b35b05SGordon Ross * Also note: NewLevel (as provided by smb_oplock_ind_break etc.) 622*72b35b05SGordon Ross * does NOT include the GRANULAR flag. This level is expected to 623*72b35b05SGordon Ross * keep track of how each oplock was acquired (by lease or not) 624*72b35b05SGordon Ross * and put the GRANULAR flag back in when appropriate. 625*72b35b05SGordon Ross */ 626*72b35b05SGordon Ross void 627*72b35b05SGordon Ross smb2_lease_send_break(smb_request_t *sr) 628*72b35b05SGordon Ross { 629*72b35b05SGordon Ross smb_ofile_t *old_ofile; 630*72b35b05SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 631*72b35b05SGordon Ross smb_node_t *node = ofile->f_node; 632*72b35b05SGordon Ross smb_lease_t *lease = ofile->f_lease; 633*72b35b05SGordon Ross smb_arg_olbrk_t *olbrk = &sr->arg.olbrk; 634*72b35b05SGordon Ross boolean_t AckReq = olbrk->AckRequired; 635*72b35b05SGordon Ross uint32_t OldLevel = olbrk->OldLevel; 636*72b35b05SGordon Ross uint32_t NewLevel = olbrk->NewLevel; 637*72b35b05SGordon Ross uint32_t status; 638*72b35b05SGordon Ross int rc; 639*72b35b05SGordon Ross 640*72b35b05SGordon Ross NewLevel |= OPLOCK_LEVEL_GRANULAR; 641*72b35b05SGordon Ross 642*72b35b05SGordon Ross /* 643*72b35b05SGordon Ross * Build the break message in sr->reply. 644*72b35b05SGordon Ross * It's free'd in smb_request_free(). 645*72b35b05SGordon Ross * Always an SMB2 lease here. 646*72b35b05SGordon Ross */ 647*72b35b05SGordon Ross sr->reply.max_bytes = MLEN; 648*72b35b05SGordon Ross smb2_lease_break_notification(sr, 649*72b35b05SGordon Ross OldLevel, NewLevel, lease->ls_epoch, AckReq); 650*72b35b05SGordon Ross 651*72b35b05SGordon Ross /* 652*72b35b05SGordon Ross * Try to send the break message to the client, 653*72b35b05SGordon Ross * on any connection with this lease. 654*72b35b05SGordon Ross */ 655*72b35b05SGordon Ross rc = lease_send_any_cn(sr); 656*72b35b05SGordon Ross if (rc != 0) { 657*72b35b05SGordon Ross /* 658*72b35b05SGordon Ross * We were unable to send the oplock break request, 659*72b35b05SGordon Ross * presumably because the connection is gone. 660*72b35b05SGordon Ross * Close uninteresting handles. 661*72b35b05SGordon Ross */ 662*72b35b05SGordon Ross lease_close_notconn(sr, NewLevel); 663*72b35b05SGordon Ross /* Note: some handles may remain on the lease. */ 664*72b35b05SGordon Ross if (!AckReq) 665*72b35b05SGordon Ross return; 666*72b35b05SGordon Ross /* Do local Ack below. */ 667*72b35b05SGordon Ross } else { 668*72b35b05SGordon Ross /* 669*72b35b05SGordon Ross * OK, we were able to send the break message. 670*72b35b05SGordon Ross * If no ack. required, we're done. 671*72b35b05SGordon Ross */ 672*72b35b05SGordon Ross if (!AckReq) 673*72b35b05SGordon Ross return; 674*72b35b05SGordon Ross 675*72b35b05SGordon Ross /* 676*72b35b05SGordon Ross * We're expecting an ACK. Wait in this thread 677*72b35b05SGordon Ross * so we can log clients that don't respond. 678*72b35b05SGordon Ross */ 679*72b35b05SGordon Ross status = smb_oplock_wait_ack(sr); 680*72b35b05SGordon Ross if (status == 0) 681*72b35b05SGordon Ross return; 682*72b35b05SGordon Ross 683*72b35b05SGordon Ross cmn_err(CE_NOTE, "clnt %s oplock break timeout", 684*72b35b05SGordon Ross sr->session->ip_addr_str); 685*72b35b05SGordon Ross DTRACE_PROBE1(ack_timeout, smb_request_t *, sr); 686*72b35b05SGordon Ross 687*72b35b05SGordon Ross /* 688*72b35b05SGordon Ross * Will do local ack below. Note, after timeout, 689*72b35b05SGordon Ross * do a break to none or "no caching" regardless 690*72b35b05SGordon Ross * of what the passed in cache level was. 691*72b35b05SGordon Ross * That means: clear all except GRANULAR. 692*72b35b05SGordon Ross */ 693*72b35b05SGordon Ross NewLevel = OPLOCK_LEVEL_GRANULAR; 694*72b35b05SGordon Ross } 695*72b35b05SGordon Ross 696*72b35b05SGordon Ross /* 697*72b35b05SGordon Ross * Do the ack locally. 698*72b35b05SGordon Ross * 699*72b35b05SGordon Ross * Find the ofile with the leased oplock 700*72b35b05SGordon Ross * (may have moved before we took locks) 701*72b35b05SGordon Ross */ 702*72b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 703*72b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex); 704*72b35b05SGordon Ross 705*72b35b05SGordon Ross old_ofile = ofile; 706*72b35b05SGordon Ross sr->fid_ofile = NULL; 707*72b35b05SGordon Ross status = lease_find_oplock(sr, lease); 708*72b35b05SGordon Ross if (status != 0) { 709*72b35b05SGordon Ross /* put back old_ofile */ 710*72b35b05SGordon Ross sr->fid_ofile = old_ofile; 711*72b35b05SGordon Ross goto unlock_out; 712*72b35b05SGordon Ross } 713*72b35b05SGordon Ross smb_llist_post(&node->n_ofile_list, old_ofile, 714*72b35b05SGordon Ross smb_ofile_release_LL); 715*72b35b05SGordon Ross 716*72b35b05SGordon Ross ofile = sr->fid_ofile; 717*72b35b05SGordon Ross 718*72b35b05SGordon Ross /* 719*72b35b05SGordon Ross * Now continue like the non-lease code 720*72b35b05SGordon Ross */ 721*72b35b05SGordon Ross ofile->f_oplock.og_breaking = 0; 722*72b35b05SGordon Ross lease->ls_breaking = 0; 723*72b35b05SGordon Ross cv_broadcast(&lease->ls_ack_cv); 724*72b35b05SGordon Ross 725*72b35b05SGordon Ross status = smb_oplock_ack_break(sr, ofile, &NewLevel); 726*72b35b05SGordon Ross 727*72b35b05SGordon Ross ofile->f_oplock.og_state = NewLevel; 728*72b35b05SGordon Ross lease->ls_state = NewLevel & CACHE_RWH; 729*72b35b05SGordon Ross /* ls_epoch does not change here */ 730*72b35b05SGordon Ross 731*72b35b05SGordon Ross if (ofile->dh_persist) 732*72b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile); 733*72b35b05SGordon Ross 734*72b35b05SGordon Ross unlock_out: 735*72b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex); 736*72b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 737*72b35b05SGordon Ross 738*72b35b05SGordon Ross #ifdef DEBUG 739*72b35b05SGordon Ross if (status != 0) { 740*72b35b05SGordon Ross cmn_err(CE_NOTE, "clnt %s local oplock ack, status=0x%x", 741*72b35b05SGordon Ross sr->session->ip_addr_str, status); 742*72b35b05SGordon Ross } 743*72b35b05SGordon Ross #endif 744*72b35b05SGordon Ross } 745*72b35b05SGordon Ross 74694047d49SGordon Ross /* 74794047d49SGordon Ross * Client has an open handle and requests a lease. 74894047d49SGordon Ross * Convert SMB2 lease request info in to internal form, 74994047d49SGordon Ross * call common oplock code, convert result to SMB2. 75094047d49SGordon Ross * 75194047d49SGordon Ross * If necessary, "go async" here. 75294047d49SGordon Ross */ 75394047d49SGordon Ross void 75494047d49SGordon Ross smb2_lease_acquire(smb_request_t *sr) 75594047d49SGordon Ross { 75694047d49SGordon Ross smb_arg_open_t *op = &sr->arg.open; 75794047d49SGordon Ross smb_ofile_t *ofile = sr->fid_ofile; 75894047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 759*72b35b05SGordon Ross smb_node_t *node = ofile->f_node; 76094047d49SGordon Ross uint32_t status = NT_STATUS_OPLOCK_NOT_GRANTED; 76194047d49SGordon Ross uint32_t have, want; /* lease flags */ 76294047d49SGordon Ross boolean_t NewGrant = B_FALSE; 76394047d49SGordon Ross 76494047d49SGordon Ross /* Only disk trees get oplocks. */ 76594047d49SGordon Ross ASSERT((sr->tid_tree->t_res_type & STYPE_MASK) == STYPE_DISKTREE); 76694047d49SGordon Ross 76794047d49SGordon Ross /* 76894047d49SGordon Ross * Only plain files (for now). 76994047d49SGordon Ross * Later, test SMB2_CAP_DIRECTORY_LEASING 77094047d49SGordon Ross */ 77194047d49SGordon Ross if (!smb_node_is_file(ofile->f_node)) { 77294047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 77394047d49SGordon Ross return; 77494047d49SGordon Ross } 77594047d49SGordon Ross 77694047d49SGordon Ross if (!smb_tree_has_feature(sr->tid_tree, SMB_TREE_OPLOCKS)) { 77794047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 77894047d49SGordon Ross return; 77994047d49SGordon Ross } 78094047d49SGordon Ross 78194047d49SGordon Ross /* 78294047d49SGordon Ross * SMB2: Convert to internal form. 78394047d49SGordon Ross * Caller should have setup the lease. 78494047d49SGordon Ross */ 78594047d49SGordon Ross ASSERT(op->op_oplock_level == SMB2_OPLOCK_LEVEL_LEASE); 78694047d49SGordon Ross ASSERT(lease != NULL); 78794047d49SGordon Ross if (lease == NULL) { 78894047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_NONE; 78994047d49SGordon Ross return; 79094047d49SGordon Ross } 79194047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 79294047d49SGordon Ross (op->lease_state & CACHE_RWH); 79394047d49SGordon Ross 79494047d49SGordon Ross /* 79594047d49SGordon Ross * Tree options may force shared oplocks, 79694047d49SGordon Ross * in which case we reduce the request. 79794047d49SGordon Ross */ 79894047d49SGordon Ross if (smb_tree_has_feature(sr->tid_tree, SMB_TREE_FORCE_L2_OPLOCK)) { 79994047d49SGordon Ross op->op_oplock_state &= ~WRITE_CACHING; 80094047d49SGordon Ross } 80194047d49SGordon Ross 802*72b35b05SGordon Ross /* 803*72b35b05SGordon Ross * Using the "Locks Held" (LH) variant of smb_oplock_request 804*72b35b05SGordon Ross * below so things won't change underfoot. 805*72b35b05SGordon Ross */ 806*72b35b05SGordon Ross smb_llist_enter(&node->n_ofile_list, RW_READER); 807*72b35b05SGordon Ross mutex_enter(&node->n_oplock.ol_mutex); 808*72b35b05SGordon Ross 80994047d49SGordon Ross /* 81094047d49SGordon Ross * Disallow downgrade 81194047d49SGordon Ross * 81294047d49SGordon Ross * Note that open with a lease is not allowed to turn off 81394047d49SGordon Ross * any cache rights. If the client tries to "downgrade", 81494047d49SGordon Ross * any bits, just return the existing lease cache bits. 81594047d49SGordon Ross */ 81694047d49SGordon Ross have = lease->ls_state & CACHE_RWH; 81794047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 81894047d49SGordon Ross if ((have & ~want) != 0) { 81994047d49SGordon Ross op->op_oplock_state = have | 82094047d49SGordon Ross OPLOCK_LEVEL_GRANULAR; 82194047d49SGordon Ross goto done; 82294047d49SGordon Ross } 82394047d49SGordon Ross 82494047d49SGordon Ross /* 82594047d49SGordon Ross * Handle oplock requests in three parts: 82694047d49SGordon Ross * a: Requests with WRITE_CACHING 82794047d49SGordon Ross * b: Requests with HANDLE_CACHING 82894047d49SGordon Ross * c: Requests with READ_CACHING 82994047d49SGordon Ross * reducing the request before b and c. 83094047d49SGordon Ross * 83194047d49SGordon Ross * In each: first check if the lease grants the 83294047d49SGordon Ross * (possibly reduced) request, in which case we 83394047d49SGordon Ross * leave the lease unchanged and return what's 83494047d49SGordon Ross * granted by the lease. Otherwise, try to get 83594047d49SGordon Ross * the oplock, and if the succeeds, wait for any 83694047d49SGordon Ross * breaks, update the lease, and return. 83794047d49SGordon Ross */ 83894047d49SGordon Ross 83994047d49SGordon Ross /* 84094047d49SGordon Ross * Try exclusive (request is RW or RWH) 84194047d49SGordon Ross */ 84294047d49SGordon Ross if ((op->op_oplock_state & WRITE_CACHING) != 0) { 84394047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 84494047d49SGordon Ross if (have == want) 84594047d49SGordon Ross goto done; 84694047d49SGordon Ross 847*72b35b05SGordon Ross status = smb_oplock_request_LH(sr, ofile, 84894047d49SGordon Ross &op->op_oplock_state); 84994047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 85094047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 85194047d49SGordon Ross NewGrant = B_TRUE; 85294047d49SGordon Ross goto done; 85394047d49SGordon Ross } 85494047d49SGordon Ross 85594047d49SGordon Ross /* 85694047d49SGordon Ross * We did not get the exclusive oplock. 85794047d49SGordon Ross * 85894047d49SGordon Ross * There are odd rules about lease upgrade. 85994047d49SGordon Ross * If the existing lease grants R and the 86094047d49SGordon Ross * client fails to upgrade it to "RWH" 86194047d49SGordon Ross * (presumably due to handle conflicts) 86294047d49SGordon Ross * then just return the existing lease, 86394047d49SGordon Ross * even though upgrade to RH would work. 86494047d49SGordon Ross */ 86594047d49SGordon Ross if (have != 0) { 86694047d49SGordon Ross op->op_oplock_state = have | 86794047d49SGordon Ross OPLOCK_LEVEL_GRANULAR; 86894047d49SGordon Ross goto done; 86994047d49SGordon Ross } 87094047d49SGordon Ross 87194047d49SGordon Ross /* 87294047d49SGordon Ross * Keep trying without write. 87394047d49SGordon Ross * Need to re-init op_oplock_state 87494047d49SGordon Ross */ 87594047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 87694047d49SGordon Ross (op->lease_state & CACHE_RH); 87794047d49SGordon Ross } 87894047d49SGordon Ross 87994047d49SGordon Ross /* 88094047d49SGordon Ross * Try shared ("RH") 88194047d49SGordon Ross */ 88294047d49SGordon Ross if ((op->op_oplock_state & HANDLE_CACHING) != 0) { 88394047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 88494047d49SGordon Ross if (have == want) 88594047d49SGordon Ross goto done; 88694047d49SGordon Ross 887*72b35b05SGordon Ross status = smb_oplock_request_LH(sr, ofile, 88894047d49SGordon Ross &op->op_oplock_state); 88994047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 89094047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 89194047d49SGordon Ross NewGrant = B_TRUE; 89294047d49SGordon Ross goto done; 89394047d49SGordon Ross } 89494047d49SGordon Ross 89594047d49SGordon Ross /* 89694047d49SGordon Ross * We did not get "RH", probably because 89794047d49SGordon Ross * ther were (old style) Level II oplocks. 89894047d49SGordon Ross * Continue, try for just read. 899*72b35b05SGordon Ross * Again, re-init op_oplock_state 90094047d49SGordon Ross */ 90194047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR | 90294047d49SGordon Ross (op->lease_state & CACHE_R); 90394047d49SGordon Ross } 90494047d49SGordon Ross 90594047d49SGordon Ross /* 90694047d49SGordon Ross * Try shared ("R") 90794047d49SGordon Ross */ 90894047d49SGordon Ross if ((op->op_oplock_state & READ_CACHING) != 0) { 90994047d49SGordon Ross want = op->op_oplock_state & CACHE_RWH; 91094047d49SGordon Ross if (have == want) 91194047d49SGordon Ross goto done; 91294047d49SGordon Ross 913*72b35b05SGordon Ross status = smb_oplock_request_LH(sr, ofile, 91494047d49SGordon Ross &op->op_oplock_state); 91594047d49SGordon Ross if (status == NT_STATUS_SUCCESS || 91694047d49SGordon Ross status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 91794047d49SGordon Ross NewGrant = B_TRUE; 91894047d49SGordon Ross goto done; 91994047d49SGordon Ross } 92094047d49SGordon Ross 92194047d49SGordon Ross /* 92294047d49SGordon Ross * We did not get "R". 92394047d49SGordon Ross * Fall into "none". 92494047d49SGordon Ross */ 92594047d49SGordon Ross } 92694047d49SGordon Ross 92794047d49SGordon Ross /* 92894047d49SGordon Ross * None of the above were able to get an oplock. 92994047d49SGordon Ross * The lease has no caching rights, and we didn't 93094047d49SGordon Ross * add any in this request. Return it as-is. 93194047d49SGordon Ross */ 93294047d49SGordon Ross op->op_oplock_state = OPLOCK_LEVEL_GRANULAR; 93394047d49SGordon Ross 93494047d49SGordon Ross done: 935*72b35b05SGordon Ross /* 936*72b35b05SGordon Ross * Only success cases get here 937*72b35b05SGordon Ross */ 938*72b35b05SGordon Ross 939*72b35b05SGordon Ross /* 940*72b35b05SGordon Ross * Keep track of what we got (ofile->f_oplock.og_state etc) 941*72b35b05SGordon Ross * so we'll know what we had when sending a break later. 942*72b35b05SGordon Ross * Also keep a copy of some things in the lease. 943*72b35b05SGordon Ross * 944*72b35b05SGordon Ross * Not using og_dialect here, as ofile->f_lease tells us 945*72b35b05SGordon Ross * this has to be using granular oplocks. 946*72b35b05SGordon Ross */ 94794047d49SGordon Ross if (NewGrant) { 948*72b35b05SGordon Ross ofile->f_oplock.og_state = op->op_oplock_state; 949*72b35b05SGordon Ross ofile->f_oplock.og_breaking = 0; 95094047d49SGordon Ross 951*72b35b05SGordon Ross lease->ls_oplock_ofile = ofile; 952*72b35b05SGordon Ross lease->ls_state = op->op_oplock_state; 953*72b35b05SGordon Ross lease->ls_breaking = B_FALSE; 95494047d49SGordon Ross lease->ls_epoch++; 955*72b35b05SGordon Ross 956*72b35b05SGordon Ross if (ofile->dh_persist) { 957*72b35b05SGordon Ross smb2_dh_update_oplock(sr, ofile); 958*72b35b05SGordon Ross } 95994047d49SGordon Ross } 96094047d49SGordon Ross 96194047d49SGordon Ross /* 96294047d49SGordon Ross * Convert internal oplock state to SMB2 96394047d49SGordon Ross */ 96494047d49SGordon Ross op->op_oplock_level = SMB2_OPLOCK_LEVEL_LEASE; 96594047d49SGordon Ross op->lease_state = lease->ls_state & CACHE_RWH; 96694047d49SGordon Ross op->lease_flags = (lease->ls_breaking != 0) ? 96794047d49SGordon Ross SMB2_LEASE_FLAG_BREAK_IN_PROGRESS : 0; 96894047d49SGordon Ross op->lease_epoch = lease->ls_epoch; 96994047d49SGordon Ross op->lease_version = lease->ls_version; 970*72b35b05SGordon Ross 971*72b35b05SGordon Ross /* 972*72b35b05SGordon Ross * End of lock-held region 973*72b35b05SGordon Ross */ 974*72b35b05SGordon Ross mutex_exit(&node->n_oplock.ol_mutex); 975*72b35b05SGordon Ross smb_llist_exit(&node->n_ofile_list); 976*72b35b05SGordon Ross 977*72b35b05SGordon Ross /* 978*72b35b05SGordon Ross * After a new oplock grant, the status return 979*72b35b05SGordon Ross * may indicate we need to wait for breaks. 980*72b35b05SGordon Ross */ 981*72b35b05SGordon Ross if (status == NT_STATUS_OPLOCK_BREAK_IN_PROGRESS) { 982*72b35b05SGordon Ross (void) smb2sr_go_async(sr); 983*72b35b05SGordon Ross (void) smb_oplock_wait_break(sr, ofile->f_node, 0); 984*72b35b05SGordon Ross } 98594047d49SGordon Ross } 98694047d49SGordon Ross 98794047d49SGordon Ross /* 98894047d49SGordon Ross * This ofile has a lease and is about to close. 98994047d49SGordon Ross * Called by smb_ofile_close when there's a lease. 99094047d49SGordon Ross * 99194047d49SGordon Ross * With leases, just one ofile on a lease owns the oplock. 99294047d49SGordon Ross * If an ofile with a lease is closed and it's the one that 99394047d49SGordon Ross * owns the oplock, try to move the oplock to another ofile 99494047d49SGordon Ross * on the same lease. 995*72b35b05SGordon Ross * 996*72b35b05SGordon Ross * Would prefer that we could just use smb_ofile_hold_olbrk 997*72b35b05SGordon Ross * to select a suitable destination for the move, but this 998*72b35b05SGordon Ross * is called while holding the owning tree ofile list etc 999*72b35b05SGordon Ross * which can cause deadlock as described in illumos 13850 1000*72b35b05SGordon Ross * when smb_ofile_hold_olbrk has to wait. XXX todo 100194047d49SGordon Ross */ 100294047d49SGordon Ross void 100394047d49SGordon Ross smb2_lease_ofile_close(smb_ofile_t *ofile) 100494047d49SGordon Ross { 100594047d49SGordon Ross smb_node_t *node = ofile->f_node; 100694047d49SGordon Ross smb_lease_t *lease = ofile->f_lease; 100794047d49SGordon Ross smb_ofile_t *o; 100894047d49SGordon Ross 10091f0845f1SGordon Ross ASSERT(RW_READ_HELD(&node->n_ofile_list.ll_lock)); 10101f0845f1SGordon Ross ASSERT(MUTEX_HELD(&node->n_oplock.ol_mutex)); 10111f0845f1SGordon Ross 101294047d49SGordon Ross /* 101394047d49SGordon Ross * If this ofile was not the oplock owner for this lease, 101494047d49SGordon Ross * we can leave things as they are. 101594047d49SGordon Ross */ 101694047d49SGordon Ross if (lease->ls_oplock_ofile != ofile) 101794047d49SGordon Ross return; 101894047d49SGordon Ross 101994047d49SGordon Ross /* 102094047d49SGordon Ross * Find another ofile to which we can move the oplock. 1021*72b35b05SGordon Ross * First try for one that's open. Usually find one. 102294047d49SGordon Ross */ 102394047d49SGordon Ross FOREACH_NODE_OFILE(node, o) { 102494047d49SGordon Ross if (o == ofile) 102594047d49SGordon Ross continue; 102694047d49SGordon Ross if (o->f_lease != lease) 102794047d49SGordon Ross continue; 10286f8336c5SGordon Ross if (o->f_oplock_closing) 10291f0845f1SGordon Ross continue; 1030*72b35b05SGordon Ross 1031*72b35b05SGordon Ross mutex_enter(&o->f_mutex); 1032*72b35b05SGordon Ross if (o->f_state == SMB_OFILE_STATE_OPEN) { 1033*72b35b05SGordon Ross smb_oplock_move(node, ofile, o); 1034*72b35b05SGordon Ross lease->ls_oplock_ofile = o; 1035*72b35b05SGordon Ross mutex_exit(&o->f_mutex); 1036*72b35b05SGordon Ross return; 1037*72b35b05SGordon Ross } 1038*72b35b05SGordon Ross mutex_exit(&o->f_mutex); 103994047d49SGordon Ross } 1040*72b35b05SGordon Ross 1041*72b35b05SGordon Ross /* 1042*72b35b05SGordon Ross * Now try for one that's orphaned etc. 1043*72b35b05SGordon Ross */ 1044*72b35b05SGordon Ross FOREACH_NODE_OFILE(node, o) { 1045*72b35b05SGordon Ross if (o == ofile) 1046*72b35b05SGordon Ross continue; 1047*72b35b05SGordon Ross if (o->f_lease != lease) 1048*72b35b05SGordon Ross continue; 1049*72b35b05SGordon Ross if (o->f_oplock_closing) 1050*72b35b05SGordon Ross continue; 1051*72b35b05SGordon Ross 1052*72b35b05SGordon Ross /* 1053*72b35b05SGordon Ross * Allow most states as seen in smb_ofile_hold_olbrk 1054*72b35b05SGordon Ross * without waiting for "_reconnect" or "_saving". 1055*72b35b05SGordon Ross * Skip "_expired" because that's about to close. 1056*72b35b05SGordon Ross * This is OK because just swapping the oplock state 1057*72b35b05SGordon Ross * between two ofiles does not interfere with the 1058*72b35b05SGordon Ross * dh_save or reconnect code paths. 1059*72b35b05SGordon Ross */ 1060*72b35b05SGordon Ross mutex_enter(&o->f_mutex); 1061*72b35b05SGordon Ross switch (o->f_state) { 1062*72b35b05SGordon Ross case SMB_OFILE_STATE_OPEN: 1063*72b35b05SGordon Ross case SMB_OFILE_STATE_SAVE_DH: 1064*72b35b05SGordon Ross case SMB_OFILE_STATE_SAVING: 1065*72b35b05SGordon Ross case SMB_OFILE_STATE_ORPHANED: 1066*72b35b05SGordon Ross case SMB_OFILE_STATE_RECONNECT: 1067*72b35b05SGordon Ross smb_oplock_move(node, ofile, o); 1068*72b35b05SGordon Ross lease->ls_oplock_ofile = o; 1069*72b35b05SGordon Ross mutex_exit(&o->f_mutex); 1070*72b35b05SGordon Ross return; 1071*72b35b05SGordon Ross } 1072*72b35b05SGordon Ross mutex_exit(&o->f_mutex); 107394047d49SGordon Ross } 107494047d49SGordon Ross 1075*72b35b05SGordon Ross /* 1076*72b35b05SGordon Ross * Normal for last close on a lease. 1077*72b35b05SGordon Ross * Wakeup ACK waiters too. 1078*72b35b05SGordon Ross */ 1079*72b35b05SGordon Ross lease->ls_state = 0; 1080*72b35b05SGordon Ross lease->ls_breaking = 0; 1081*72b35b05SGordon Ross cv_broadcast(&lease->ls_ack_cv); 1082*72b35b05SGordon Ross 1083*72b35b05SGordon Ross lease->ls_oplock_ofile = NULL; 108494047d49SGordon Ross } 1085