SMB2/3 Performance

David Disseldorp ddiss at suse.de
Mon Nov 25 08:53:22 MST 2013


Hi Metze,

I've a few minor comments on the following change...

On Fri, 22 Nov 2013 15:18:46 +0100
"Stefan (metze) Metzmacher" <metze at samba.org> wrote:

> From d8f784dbb55fa56f83dcea605a9fa5e38828063b Mon Sep 17 00:00:00 2001
> From: Stefan Metzmacher <metze at samba.org>
> Date: Mon, 14 Oct 2013 10:33:57 +0200
> Subject: [PATCH 2/6] s3:smb2_server: for performance reasons we use tevent_fd
>  and readv/writev directly

...

> +
> +static NTSTATUS smbd_smb2_io_handler(struct smbd_server_connection *sconn,
> +				     uint16_t fde_flags)
> +{

This function's a doozy. Please split it into separate TEVENT_FD_WRITE
/ TEVENT_FD_READ subfunctions.


> +	struct smbd_smb2_request_read_state *state = &sconn->smb2.request_read_state;
>  	struct smbd_smb2_request *req = NULL;
> +	size_t min_recvfile_size = UINT32_MAX;
> +	int ret;
> +	int err;
> +	bool retry;
> +	NTSTATUS status;
> +	NTTIME now;
> +
> +	if (fde_flags & TEVENT_FD_WRITE) {
...
> +	}
> +
> +may_read:
> +	if (!(fde_flags & TEVENT_FD_READ)) {
> +		return NT_STATUS_OK;
> +	}
> +
> +	if (state->req == NULL) {
> +		TEVENT_FD_NOT_READABLE(sconn->smb2.fde);
> +		return NT_STATUS_OK;
> +	}
> +
> +again:
> +	if (!state->hdr.done) {
> +		state->hdr.done = true;
> +
> +		state->vector.iov_base = (void *)state->hdr.nbt;
> +		state->vector.iov_len = NBT_HDR_SIZE;
> +	}
> +
> +	ret = readv(sconn->sock, &state->vector, 1);
> +	if (ret == 0) {
> +		/* propagate end of file */
> +		return NT_STATUS_END_OF_FILE;
> +	}
> +	err = socket_error_from_errno(ret, errno, &retry);
> +	if (retry) {
> +		/* retry later */
> +		TEVENT_FD_READABLE(sconn->smb2.fde);
> +		return NT_STATUS_OK;
> +	}
> +	if (err != 0) {
> +		return map_nt_error_from_unix_common(err);
> +	}
> +
> +	if (ret < state->vector.iov_len) {
> +		uint8_t *base;
> +		base = (uint8_t *)state->vector.iov_base;
> +		base += ret;
> +		state->vector.iov_base = (void *)base;
> +		state->vector.iov_len -= ret;
> +		/* we have more to read */
> +		TEVENT_FD_READABLE(sconn->smb2.fde);
> +		return NT_STATUS_OK;
> +	}
> +
> +	if (state->pktlen > 0) {
> +		if (state->doing_receivefile && !is_smb2_recvfile_write(state)) {
> +			/*
> +			 * Not a possible receivefile write.
> +			 * Read the rest of the data.
> +			 */
> +			state->doing_receivefile = false;
> +			state->vector.iov_base = (void *)(state->pktbuf +
> +				SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN);
> +			state->vector.iov_len = (state->pktlen -
> +				SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN);
> +			goto again;
> +		}
>  
> -	status = smbd_smb2_request_read_recv(subreq, sconn, &req);
> -	TALLOC_FREE(subreq);
> +		/*
> +		 * Either this is a receivefile write so we've
> +		 * done a short read, or if not we have all the data.
> +		 */
> +		goto got_full;
> +	}
> +
> +	/*
> +	 * Now we analyze the NBT header
> +	 */
> +	state->pktlen = smb2_len(state->hdr.nbt);
> +	if (state->pktlen == 0) {
> +		goto got_full;
> +	}
> +
> +	state->pktbuf = talloc_array(state->req, uint8_t, state->pktlen);
> +	if (state->pktbuf == NULL) {
> +		return NT_STATUS_NO_MEMORY;
> +	}
> +
> +	state->vector.iov_base = (void *)state->pktbuf;
> +
> +	if (state->min_recv_size != 0) {
> +		min_recvfile_size = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
> +		min_recvfile_size += state->min_recv_size;
> +	}
> +
> +	if (state->pktlen > min_recvfile_size) {
> +		/*
> +		 * Might be a receivefile write. Read the SMB2 HEADER +
> +		 * SMB2_WRITE header first. Set 'doing_receivefile'
> +		 * as we're *attempting* receivefile write. If this
> +		 * turns out not to be a SMB2_WRITE request or otherwise
> +		 * not suitable then we'll just read the rest of the data
> +		 * the next time this function is called.
> +		 */
> +		state->vector.iov_len = SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
> +		state->doing_receivefile = true;
> +	} else {
> +		state->vector.iov_len = state->pktlen;
> +	}
> +
> +	goto again;
> +
> +got_full:
> +
> +	if (state->hdr.nbt[0] != 0x00) {
> +		DEBUG(1,("smbd_smb2_connection_handler: ignore NBT[0x%02X] msg\n",
> +			 state->hdr.nbt[1]));

Function name prefix is wrong here, I'd prefer to drop it completely.

> +
> +		req = state->req;
> +		ZERO_STRUCTP(state);
> +		state->req = req;
> +		state->min_recv_size = get_min_receive_file_size(state->req);
> +		req = NULL;
> +		goto again;
> +	}
> +
> +	req = state->req;
> +	state->req = NULL;
> +
> +	req->request_time = timeval_current();
> +	now = timeval_to_nttime(&req->request_time);
> +
> +	status = smbd_smb2_inbuf_parse_compound(req->sconn->conn,
> +						now,
> +						state->pktbuf,
> +						state->pktlen,
> +						req,
> +						&req->in.vector,
> +						&req->in.vector_count);
>  	if (!NT_STATUS_IS_OK(status)) {
> -		DEBUG(2,("smbd_smb2_request_incoming: client read error %s\n",
> -			nt_errstr(status)));
> -		smbd_server_connection_terminate(sconn, nt_errstr(status));
> -		return;
> +		return status;
> +	}
> +
> +	if (state->doing_receivefile) {
> +		req->smb1req = talloc_zero(req, struct smb_request);
> +		if (req->smb1req == NULL) {
> +			return NT_STATUS_NO_MEMORY;
> +		}
> +		req->smb1req->unread_bytes =
> +			state->pktlen - SMBD_SMB2_SHORT_RECEIVEFILE_WRITE_LEN;
>  	}
>  
> +	ZERO_STRUCTP(state);
> +
> +	req->current_idx = 1;
> +
>  	DEBUG(10,("smbd_smb2_request_incoming: idx[%d] of %d vectors\n",

Incorrect prefix here too.

>  		 req->current_idx, req->in.vector_count));
>  
>  	status = smbd_smb2_request_validate(req);
>  	if (!NT_STATUS_IS_OK(status)) {
> -		smbd_server_connection_terminate(sconn, nt_errstr(status));
> -		return;
> +		return status;
>  	}
>  
>  	status = smbd_smb2_request_setup_out(req);
>  	if (!NT_STATUS_IS_OK(status)) {
> -		smbd_server_connection_terminate(sconn, nt_errstr(status));
> -		return;
> +		return status;
>  	}
>  
>  	status = smbd_smb2_request_dispatch(req);
>  	if (!NT_STATUS_IS_OK(status)) {
> -		smbd_server_connection_terminate(sconn, nt_errstr(status));
> -		return;
> -	}
> -
> -	status = smbd_smb2_request_next_incoming(sconn);
> -	if (!NT_STATUS_IS_OK(status)) {
> -		smbd_server_connection_terminate(sconn, nt_errstr(status));
> -		return;
> +		return status;
>  	}
>  
>  	sconn->num_requests++;
> @@ -3307,4 +3212,27 @@ static void smbd_smb2_request_incoming(struct tevent_req *subreq)
>  		change_to_root_user();
>  		check_log_size();
>  	}
> +
> +	status = smbd_smb2_request_next_incoming(sconn);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		return status;
> +	}
> +
> +	return NT_STATUS_OK;
> +}
> +
> +static void smbd_smb2_connection_handler(struct tevent_context *ev,
> +					 struct tevent_fd *fde,
> +					 uint16_t flags,
> +					 void *private_data)
> +{
> +	struct smbd_server_connection *sconn = talloc_get_type(private_data,
> +					      struct smbd_server_connection);

Should be talloc_get_type_abort().

> +	NTSTATUS status;
> +
> +	status = smbd_smb2_io_handler(sconn, flags);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		smbd_server_connection_terminate(sconn, nt_errstr(status));
> +		return;
> +	}
>  }


More information about the samba-technical mailing list