s3:smbd: make use of the new ack infrastructure for oplock/lease breaks
authorStefan Metzmacher <metze@samba.org>
Fri, 5 Jun 2020 20:11:26 +0000 (22:11 +0200)
committerStefan Metzmacher <metze@samba.org>
Wed, 8 Jul 2020 15:54:40 +0000 (15:54 +0000)
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>
source3/smbd/smb2_server.c

index a147f858c91e456e15bcdade9e171c0ebf7bcfbd..699f293799bf2e2471a395de4ffa2a9a85df85d1 100644 (file)
@@ -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,