1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2020 Tintri by DDN, Inc. All rights reserved. 14 */ 15 16 /* 17 * Dispatch function for SMB2_CANCEL 18 */ 19 20 #include <smbsrv/smb2_kproto.h> 21 22 static void smb2_cancel_async(smb_request_t *); 23 static void smb2_cancel_sync(smb_request_t *); 24 25 /* 26 * This handles an SMB2_CANCEL request when seen in the reader. 27 * (See smb2sr_newrq) Handle this immediately, rather than 28 * going through the normal taskq dispatch mechanism. 29 * Note that Cancel does NOT get a response. 30 * 31 * Any non-zero return causes disconnect. 32 * SMB2 header is already decoded. 33 */ 34 int 35 smb2_newrq_cancel(smb_request_t *sr) 36 { 37 38 /* 39 * If we get SMB2 cancel as part of a compound, 40 * that's a protocol violation. Drop 'em! 41 */ 42 if (sr->smb2_next_command != 0) 43 return (EINVAL); 44 45 DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr); 46 47 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) 48 smb2_cancel_async(sr); 49 else 50 smb2_cancel_sync(sr); 51 52 DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr); 53 54 return (0); 55 } 56 57 /* 58 * Dispatch handler for SMB2_CANCEL. 59 * Note that Cancel does NOT get a response. 60 */ 61 smb_sdrc_t 62 smb2_cancel(smb_request_t *sr) 63 { 64 65 /* 66 * If we get SMB2 cancel as part of a compound, 67 * that's a protocol violation. Drop 'em! 68 */ 69 if (sr->smb2_cmd_hdr != 0 || sr->smb2_next_command != 0) 70 return (SDRC_DROP_VC); 71 72 DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr); 73 74 if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { 75 smb2_cancel_async(sr); 76 } else { 77 smb2_cancel_sync(sr); 78 } 79 80 DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr); 81 82 return (SDRC_NO_REPLY); 83 } 84 85 /* 86 * SMB2 Cancel (sync) has an inherent race with the request being 87 * cancelled. The request may have been received but not yet 88 * executed by a worker thread, in which case we'll mark the 89 * request state as cancelled, and when a worker thread starts 90 * on this request we'll cancel everything in the compound. 91 */ 92 static void 93 smb2_cancel_sync(smb_request_t *sr) 94 { 95 struct smb_request *req; 96 struct smb_session *session = sr->session; 97 int cnt = 0; 98 99 if (sr->smb2_messageid == 0 || sr->smb2_messageid == UINT64_MAX) 100 return; 101 102 smb_slist_enter(&session->s_req_list); 103 for (req = smb_slist_head(&session->s_req_list); req != NULL; 104 req = smb_slist_next(&session->s_req_list, req)) { 105 106 /* never cancel self */ 107 if (req == sr) 108 continue; 109 110 if (sr->smb2_messageid >= req->smb2_first_msgid && 111 sr->smb2_messageid < (req->smb2_first_msgid + 112 req->smb2_total_credits)) { 113 smb_request_cancel(req); 114 cnt++; 115 } 116 } 117 smb_slist_exit(&session->s_req_list); 118 119 if (cnt != 1) { 120 DTRACE_PROBE2(smb2__cancel__error, 121 uint64_t, sr->smb2_messageid, int, cnt); 122 #ifdef DEBUG 123 /* 124 * It's somewhat common that we may see a cancel for a 125 * request that has already completed, so report that 126 * only in debug builds. 127 */ 128 cmn_err(CE_WARN, "SMB2 cancel failed, " 129 "client=%s, MID=0x%llx", 130 sr->session->ip_addr_str, 131 (u_longlong_t)sr->smb2_messageid); 132 #endif 133 } 134 } 135 136 /* 137 * Note that cancelling an async request doesn't have a race 138 * because the client doesn't learn about the async ID until we 139 * send it to them in an interim reply, and by that point the 140 * request has progressed to the point where smb_cancel can find 141 * the request and cancel it. 142 */ 143 static void 144 smb2_cancel_async(smb_request_t *sr) 145 { 146 struct smb_request *req; 147 struct smb_session *session = sr->session; 148 int cnt = 0; 149 150 if (sr->smb2_async_id == 0) 151 return; 152 153 smb_slist_enter(&session->s_req_list); 154 req = smb_slist_head(&session->s_req_list); 155 while (req) { 156 ASSERT(req->sr_magic == SMB_REQ_MAGIC); 157 if ((req != sr) && 158 (req->smb2_async_id == sr->smb2_async_id)) { 159 smb_request_cancel(req); 160 cnt++; 161 } 162 req = smb_slist_next(&session->s_req_list, req); 163 } 164 if (cnt != 1) { 165 DTRACE_PROBE2(smb2__cancel__error, 166 uint64_t, sr->smb2_async_id, int, cnt); 167 /* 168 * Not logging here, as this is normal, i.e. 169 * when both a cancel and a handle close 170 * terminates an SMB2_notify request. 171 */ 172 } 173 smb_slist_exit(&session->s_req_list); 174 } 175