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);
}