summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2020-06-05 22:11:26 +0200
committerStefan Metzmacher <metze@samba.org>2020-07-08 15:54:40 +0000
commit88b0da106276c95e5690aa91dc5586e46a47fc92 (patch)
treec4d10615ca4763d84477fdf7b83a079cf72a90a4 /source3/smbd
parent8b8c5c4154f581c5d79585017443dffc1602bdf6 (diff)
downloadsamba-88b0da106276c95e5690aa91dc5586e46a47fc92.tar.gz
s3:smbd: make use of the new ack infrastructure for oplock/lease breaks
This finally implements the retry of failed oplock/lease breaks. Before smbd_smb2_break_send/recv completed directly after sendmsg() passed the pdu to the kernel. Now the completion is (at least) deferred until the the next smbXsrv_connection_ack_checker() run happens and smbd_smb2_send_queue_ack_bytes() found that all bytes of the break notification left the kernel send queue (and were TCP acked). If the connection is disconnected all pending break notifications are completed with an error, which is then returned by smbd_smb2_break_recv(). smbXsrv_pending_break_submit() will then submit another break notification via the next available connection/channel. The smbXsrv_connection_ack_checker() runs each rto_usecs (between 0.2s and 1.0s). smbd_smb2_break_send() will set a timeout of 6*rto_usecs (between 1.2s and 6s). If smbXsrv_connection_ack_checker() detects via smbd_smb2_send_queue_ack_bytes() that a pending break notification is pending for more than its timeout we'll disconnect the connection with NT_STATUS_IO_TIMEOUT. This will be handled as any other disconnect and will in turn also trigger the retry on the next channel. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11897 Signed-off-by: Stefan Metzmacher <metze@samba.org> Reviewed-by: Günther Deschner <gd@samba.org>
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/smb2_server.c52
1 files changed, 35 insertions, 17 deletions
diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c
index a147f858c91..699f293799b 100644
--- a/source3/smbd/smb2_server.c
+++ b/source3/smbd/smb2_server.c
@@ -3852,8 +3852,6 @@ struct smbd_smb2_break_state {
struct iovec vector[1+SMBD_SMB2_NUM_IOV_PER_REQ];
};
-static int smbd_smb2_break_state_destructor(struct smbd_smb2_break_state *state);
-
static struct tevent_req *smbd_smb2_break_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct smbXsrv_connection *xconn,
@@ -3874,7 +3872,6 @@ static struct tevent_req *smbd_smb2_break_send(TALLOC_CTX *mem_ctx,
state->req = req;
tevent_req_defer_callback(req, ev);
- talloc_set_destructor(state, smbd_smb2_break_state_destructor);
SIVAL(state->hdr, 0, SMB2_MAGIC);
SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
@@ -3921,6 +3918,27 @@ static struct tevent_req *smbd_smb2_break_send(TALLOC_CTX *mem_ctx,
return tevent_req_post(req, ev);
}
+ /*
+ * We require TCP acks for this PDU to the client!
+ * We want 5 retransmissions and timeout when the
+ * retransmission timeout (rto) passed 6 times.
+ *
+ * required_acked_bytes gets a dummy value of
+ * UINT64_MAX, as long it's in xconn->smb2.send_queue,
+ * it'll get the real value when it's moved to
+ * xconn->ack.queue.
+ *
+ * state->queue_entry.ack.req gets completed with
+ * 1. tevent_req_done(), when all bytes are acked.
+ * 2a. tevent_req_nterror(NT_STATUS_IO_TIMEOUT), when
+ * the timeout expired before all bytes were acked.
+ * 2b. tevent_req_nterror(transport_error), when the
+ * connection got a disconnect from the kernel.
+ */
+ state->queue_entry.ack.timeout =
+ timeval_current_ofs_usec(xconn->ack.rto_usecs * 6);
+ state->queue_entry.ack.required_acked_bytes = UINT64_MAX;
+ state->queue_entry.ack.req = req;
state->queue_entry.mem_ctx = state;
state->queue_entry.vector = state->vector;
state->queue_entry.count = ARRAY_SIZE(state->vector);
@@ -3935,20 +3953,6 @@ static struct tevent_req *smbd_smb2_break_send(TALLOC_CTX *mem_ctx,
return req;
}
-static int smbd_smb2_break_state_destructor(struct smbd_smb2_break_state *state)
-{
- if (state->queue_entry.mem_ctx != NULL) {
- /*
- * We used tevent_req_defer_callback()
- */
- tevent_req_done(state->req);
- state->queue_entry.mem_ctx = NULL;
- return -1;
- }
-
- return 0;
-}
-
static NTSTATUS smbd_smb2_break_recv(struct tevent_req *req)
{
return tevent_req_simple_recv_ntstatus(req);
@@ -3991,8 +3995,15 @@ static NTSTATUS smbXsrv_pending_break_submit(struct smbXsrv_pending_break *pb);
static NTSTATUS smbXsrv_pending_break_schedule(struct smbXsrv_pending_break *pb)
{
+ struct smbXsrv_client *client = pb->client;
NTSTATUS status;
+ DLIST_ADD_END(client->pending_breaks, pb);
+ status = smbXsrv_client_pending_breaks_updated(client);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
status = smbXsrv_pending_break_submit(pb);
if (!NT_STATUS_IS_OK(status)) {
return status;
@@ -4149,7 +4160,14 @@ static void smbXsrv_pending_break_done(struct tevent_req *subreq)
}
remove:
+ DLIST_REMOVE(client->pending_breaks, pb);
TALLOC_FREE(pb);
+
+ status = smbXsrv_client_pending_breaks_updated(client);
+ if (!NT_STATUS_IS_OK(status)) {
+ smbd_server_disconnect_client(client, nt_errstr(status));
+ return;
+ }
}
NTSTATUS smbd_smb2_send_oplock_break(struct smbXsrv_client *client,