Print this page
NEX-17589 Get "too high" smbd error when copy big file to cifs share (redo)
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-16943 network outages with thread stuck in smb2_scoreboard_cancel
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-1643 dtrace provider for smbsrv
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
NEX-9864 Some SMB cancel races remain after NEX-5845
Revert (part of) "NEX-5845 rework SMB immediate cancel"
reverts (part of) commit 7a5da69f6d42b17ebcc95ca3d02925d07a01343e.
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-5845 rework SMB immediate cancel
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
NEX-2975 SMB2 Cancel may fail (nits)
NEX-2975 SMB2 Cancel may fail
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Tony Nguyen <tony.nguyen@nexenta.com>
SMB-11 SMB2 message parse & dispatch
SMB-12 SMB2 Negotiate Protocol
SMB-13 SMB2 Session Setup
SMB-14 SMB2 Logoff
SMB-15 SMB2 Tree Connect
SMB-16 SMB2 Tree Disconnect
SMB-17 SMB2 Create
SMB-18 SMB2 Close
SMB-19 SMB2 Flush
SMB-20 SMB2 Read
SMB-21 SMB2 Write
SMB-22 SMB2 Lock/Unlock
SMB-23 SMB2 Ioctl
SMB-24 SMB2 Cancel
SMB-25 SMB2 Echo
SMB-26 SMB2 Query Dir
SMB-27 SMB2 Change Notify
SMB-28 SMB2 Query Info
SMB-29 SMB2 Set Info
SMB-30 SMB2 Oplocks
SMB-53 SMB2 Create Context options
(SMB2 code review cleanup 1, 2, 3)

*** 8,81 **** * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2014 Nexenta Systems, Inc. All rights reserved. */ /* * Dispatch function for SMB2_CANCEL */ #include <smbsrv/smb2_kproto.h> ! static void smb2sr_cancel_async(smb_request_t *); ! static void smb2sr_cancel_sync(smb_request_t *); /* * This handles an SMB2_CANCEL request when seen in the reader. * (See smb2sr_newrq) Handle this immediately, rather than * going through the normal taskq dispatch mechanism. * Note that Cancel does NOT get a response. */ int ! smb2sr_newrq_cancel(smb_request_t *sr) { - int rc; /* ! * Decode the header */ ! if ((rc = smb2_decode_header(sr)) != 0) ! return (rc); if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) ! smb2sr_cancel_async(sr); else ! smb2sr_cancel_sync(sr); return (0); } static void ! smb2sr_cancel_sync(smb_request_t *sr) { struct smb_request *req; struct smb_session *session = sr->session; int cnt = 0; smb_slist_enter(&session->s_req_list); ! req = smb_slist_head(&session->s_req_list); ! while (req) { ! ASSERT(req->sr_magic == SMB_REQ_MAGIC); ! if ((req != sr) && ! (req->smb2_messageid == sr->smb2_messageid)) { smb_request_cancel(req); cnt++; } - req = smb_slist_next(&session->s_req_list, req); } if (cnt != 1) { DTRACE_PROBE2(smb2__cancel__error, uint64_t, sr->smb2_messageid, int, cnt); } - smb_slist_exit(&session->s_req_list); } static void ! smb2sr_cancel_async(smb_request_t *sr) { struct smb_request *req; struct smb_session *session = sr->session; int cnt = 0; --- 8,150 ---- * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* ! * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ /* * Dispatch function for SMB2_CANCEL */ #include <smbsrv/smb2_kproto.h> ! static void smb2_cancel_async(smb_request_t *); ! static void smb2_cancel_sync(smb_request_t *); /* * This handles an SMB2_CANCEL request when seen in the reader. * (See smb2sr_newrq) Handle this immediately, rather than * going through the normal taskq dispatch mechanism. * Note that Cancel does NOT get a response. + * + * Any non-zero return causes disconnect. + * SMB2 header is already decoded. */ int ! smb2_newrq_cancel(smb_request_t *sr) { /* ! * If we get SMB2 cancel as part of a compound, ! * that's a protocol violation. Drop 'em! */ ! if (sr->smb2_next_command != 0) ! return (EINVAL); + DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr); + if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) ! smb2_cancel_async(sr); else ! smb2_cancel_sync(sr); + DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr); + return (0); } + /* + * Dispatch handler for SMB2_CANCEL. + * Note that Cancel does NOT get a response. + */ + smb_sdrc_t + smb2_cancel(smb_request_t *sr) + { + + /* + * If we get SMB2 cancel as part of a compound, + * that's a protocol violation. Drop 'em! + */ + if (sr->smb2_cmd_hdr != 0 || sr->smb2_next_command != 0) + return (SDRC_DROP_VC); + + DTRACE_SMB2_START(op__Cancel, smb_request_t *, sr); + + if (sr->smb2_hdr_flags & SMB2_FLAGS_ASYNC_COMMAND) { + smb2_cancel_async(sr); + } else { + smb2_cancel_sync(sr); + } + + DTRACE_SMB2_DONE(op__Cancel, smb_request_t *, sr); + + return (SDRC_NO_REPLY); + } + + /* + * SMB2 Cancel (sync) has an inherent race with the request being + * cancelled. The request may have been received but not yet + * executed by a worker thread, in which case we'll mark the + * request state as cancelled, and when a worker thread starts + * on this request we'll cancel everything in the compound. + */ static void ! smb2_cancel_sync(smb_request_t *sr) { struct smb_request *req; struct smb_session *session = sr->session; int cnt = 0; + if (sr->smb2_messageid == 0) + goto failure; + smb_slist_enter(&session->s_req_list); ! for (req = smb_slist_head(&session->s_req_list); req != NULL; ! req = smb_slist_next(&session->s_req_list, req)) { ! ! /* never cancel self */ ! if (req == sr) ! continue; ! ! if (sr->smb2_messageid >= req->smb2_first_msgid && ! sr->smb2_messageid < (req->smb2_first_msgid + ! req->smb2_total_credits)) { smb_request_cancel(req); cnt++; } } + smb_slist_exit(&session->s_req_list); + if (cnt != 1) { + failure: DTRACE_PROBE2(smb2__cancel__error, uint64_t, sr->smb2_messageid, int, cnt); + #ifdef DEBUG + /* + * It's somewhat common that we may see a cancel for a + * request that has already completed, so report that + * only in debug builds. + */ + cmn_err(CE_WARN, "SMB2 cancel failed, " + "client=%s, MID=0x%llx", + sr->session->ip_addr_str, + (u_longlong_t)sr->smb2_messageid); + #endif } } + /* + * Note that cancelling an async request doesn't have a race + * because the client doesn't learn about the async ID until we + * send it to them in an interim reply, and by that point the + * request has progressed to the point where smb_cancel can find + * the request and cancel it. + */ static void ! smb2_cancel_async(smb_request_t *sr) { struct smb_request *req; struct smb_session *session = sr->session; int cnt = 0;
*** 91,98 **** --- 160,172 ---- req = smb_slist_next(&session->s_req_list, req); } if (cnt != 1) { DTRACE_PROBE2(smb2__cancel__error, uint64_t, sr->smb2_async_id, int, cnt); + /* + * Not logging here, as this is normal, i.e. + * when both a cancel and a handle close + * terminates an SMB2_notify request. + */ } smb_slist_exit(&session->s_req_list); }