WIP smb_direct_connection_negotatiate_accept_send/recv
authorRalph Boehme <slow@samba.org>
Sat, 1 Oct 2016 03:13:14 +0000 (20:13 -0700)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 Jun 2018 12:35:14 +0000 (14:35 +0200)
libcli/smb/smb_direct.c

index c9974722fc08c22f377c7d9dffeac509b518e7ae..60b7021efc9a875eec1db0f71a6b4e081b58678e 100644 (file)
@@ -953,11 +953,6 @@ static int smb_direct_connection_negotiate_connect_destructor(
        TALLOC_FREE(c->ibv.fde_channel);
        TALLOC_FREE(c->rdma.fde_channel);
 
-//     if (state->req.mr != NULL) {
-//             ibv_dereg_mr(state->req.mr);
-//             state->req.mr = NULL;
-//     }
-
        return 0;
 }
 
@@ -1502,34 +1497,439 @@ DEBUG(0,("%s:%s: sock.fd[%d] sock.tmp_fd[%d]\n",
 }
 
 struct smb_direct_connection_negotiate_accept_state {
+       struct smb_direct_connection *c;
 };
 
+static int smb_direct_connection_negotiate_accept_destructor(
+              struct smb_direct_connection_negotiate_accept_state *state)
+{
+       struct smb_direct_connection *c = state->c;
+
+       TALLOC_FREE(c->ibv.fde_channel);
+       TALLOC_FREE(c->rdma.fde_channel);
+
+       return 0;
+}
+
+static void smb_direct_connection_negotiate_accept_rdma_handler(struct tevent_context *ev,
+                                             struct tevent_fd *fde,
+                                             uint16_t flags,
+                                             void *private_data);
+static void smb_direct_connection_negotiate_accept_ibv_handler(struct tevent_context *ev,
+                                            struct tevent_fd *fde,
+                                            uint16_t flags,
+                                            void *private_data);
+
 static struct tevent_req *smb_direct_connection_negotiate_accept_send(
        TALLOC_CTX *mem_ctx,
        struct tevent_context *ev,
-       struct smb_direct_connection *c)
+       struct smb_direct_connection **_c)
 {
-#if 0
-       ret = rdma_accept(c->rdma.cm_id,conn_param);
+       struct tevent_req *req = NULL;
+       struct smb_direct_connection_negotiate_accept_state *state = NULL;
+       struct smb_direct_connection *c = NULL;
+       struct smb_direct_io *neg_recv = NULL;
+       struct ibv_recv_wr *bad_recv_wr = NULL;
+       NTSTATUS status;
+       int ret;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct smb_direct_connection_negotiate_accept_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       c = talloc_move(state, _c);
+       state->c = c;
+       talloc_set_destructor(state, smb_direct_connection_negotiate_accept_destructor);
+
+       c->rdma.fde_channel = tevent_add_fd(ev, state,
+                                           c->rdma.cm_channel->fd,
+                                           TEVENT_FD_READ,
+                                           smb_direct_connection_negotiate_accept_rdma_handler,
+                                           req);
+       if (tevent_req_nomem(c->rdma.fde_channel, req)) {
+               return tevent_req_post(req, ev);
+       }
+       c->ibv.fde_channel = tevent_add_fd(ev, state,
+                                          c->ibv.comp_channel->fd,
+                                          TEVENT_FD_READ,
+                                          smb_direct_connection_negotiate_accept_ibv_handler,
+                                          req);
+       if (tevent_req_nomem(c->ibv.fde_channel, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       neg_recv = smb_direct_io_create(c);
+       if (tevent_req_nomem(neg_recv, req)) {
+               return tevent_req_post(req, ev);
+       }
+       neg_recv->sge[0].addr = (uint64_t) (uintptr_t) neg_recv->data;
+       neg_recv->sge[0].length = sizeof(neg_recv->data);
+       neg_recv->sge[0].lkey = neg_recv->data_mr->lkey;
+       neg_recv->recv_wr.sg_list = neg_recv->sge;
+       neg_recv->recv_wr.num_sge = 1;
+
+       ret = ibv_post_recv(c->ibv.qp, &neg_recv->recv_wr, &bad_recv_wr);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       ret = rdma_accept(c->rdma.cm_id, &c->rdma.conn_param);
        if (ret != 0) {
                DBG_ERR("rdma_accept failed [%s] result [%d]\n", strerror(errno), ret);
-               c->rdma.cm_id->context = NULL;
-               c->rdma.cm_id->channel = NULL;
-               c->rdma.cm_id = NULL;
-               TALLOC_FREE(c);
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       return NULL;
+}
+
+static void smb_direct_connection_negotiate_accept_rdma_handler(
+       struct tevent_context *ev,
+       struct tevent_fd *fde,
+       uint16_t flags,
+       void *private_data)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(private_data,
+               struct tevent_req);
+       struct smb_direct_connection_negotiate_accept_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_negotiate_accept_state);
+       struct smb_direct_connection *c = state->c;
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       int ret;
+
+DEBUG(0,("%s:%s: here...\n", __location__, __func__));
+
+       errno = 0;
+       ret = rdma_get_cm_event(c->rdma.cm_channel,
+                               &c->rdma.cm_event);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       if (c->rdma.cm_event->status != 0) {
+               errno = c->rdma.cm_event->status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       switch (c->rdma.cm_event->event) {
+       case RDMA_CM_EVENT_DISCONNECTED:
+               status = NT_STATUS_CONNECTION_DISCONNECTED;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       case RDMA_CM_EVENT_ADDR_RESOLVED:
+       case RDMA_CM_EVENT_ADDR_ERROR:
+       case RDMA_CM_EVENT_ROUTE_RESOLVED:
+       case RDMA_CM_EVENT_ESTABLISHED:
+       case RDMA_CM_EVENT_ROUTE_ERROR:
+       case RDMA_CM_EVENT_CONNECT_REQUEST:
+       case RDMA_CM_EVENT_CONNECT_RESPONSE:
+       case RDMA_CM_EVENT_CONNECT_ERROR:
+       case RDMA_CM_EVENT_UNREACHABLE:
+       case RDMA_CM_EVENT_REJECTED:
+       case RDMA_CM_EVENT_DEVICE_REMOVAL:
+       case RDMA_CM_EVENT_MULTICAST_JOIN:
+       case RDMA_CM_EVENT_MULTICAST_ERROR:
+       case RDMA_CM_EVENT_ADDR_CHANGE:
+       case RDMA_CM_EVENT_TIMEWAIT_EXIT:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       status = NT_STATUS_INTERNAL_ERROR;
+       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+       tevent_req_nterror(req, status);
+}
+
+static void smb_direct_connection_negotiate_accept_ibv_handler(
+       struct tevent_context *ev,
+       struct tevent_fd *fde,
+       uint16_t flags,
+       void *private_data)
+{
+       struct tevent_req *req =
+               talloc_get_type_abort(private_data,
+               struct tevent_req);
+       struct smb_direct_connection_negotiate_accept_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_negotiate_accept_state);
+       struct smb_direct_connection *c = state->c;
+       struct ibv_cq *cq = NULL;
+       void *cq_context = NULL;
+       NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
+       struct ibv_wc wc;
+       int ret;
+       uint16_t credits_requested;
+       uint16_t credits_granted;
+       uint32_t max_read_write_size;
+       uint32_t preferred_send_size;
+       uint32_t max_receive_size;
+       uint32_t max_fragmented_size;
+       uint32_t tmp;
+       struct smb_direct_io *io = NULL;
+
+DEBUG(0,("%s:%s: here...\n", __location__, __func__));
+
+       errno = 0;
+       ret = ibv_get_cq_event(c->ibv.comp_channel,
+                              &cq, &cq_context);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       ibv_ack_cq_events(cq, 1);
+
+       if (cq_context != c) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       errno = 0;
+       ret = ibv_req_notify_cq(cq, 0);
+       if (ret != 0) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       errno = 0;
+       ZERO_STRUCT(wc);
+       ret = ibv_poll_cq(cq, 1, &wc);
+       if (ret != 1) {
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+       ret = 0;
+
+       if (wc.status == IBV_WC_WR_FLUSH_ERR) {
+               //errno = wc.status;
+               status = map_nt_error_from_unix_common(wc.status);//errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+               smb_direct_connection_negotiate_connect_rdma_handler(ev, fde, flags, private_data);
+               return;
+       }
+       if (wc.status != IBV_WC_SUCCESS) {
+               errno = wc.status;
+               status = map_nt_error_from_unix_common(errno);
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s] ibv[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status),
+                       ibv_wc_status_str(wc.status)));
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       io = talloc_get_type_abort((void *)(uintptr_t)wc.wr_id,
+                                  struct smb_direct_io);
 
-               //??? rdma_reject(l->rdma.cm_event->id);
-               /* wait for more */
+       switch (wc.opcode) {
+       case IBV_WC_SEND:
+               DEBUG(0,("%s:%s: GOT SEND[%p] next[%p] ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, io, io->send_wr.next, ret, errno));
+               TALLOC_FREE(io);
                break;
+       case IBV_WC_RDMA_READ:
+               DEBUG(0,("%s:%s: GOT RDMA_READ[%p] next[%p] ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, io, io->send_wr.next, ret, errno));
+               TALLOC_FREE(io);
+               break;
+       case IBV_WC_RECV:
+               DEBUG(0,("%s:%s: GOT RECV[%p] next[%p] ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, io, io->recv_wr.next, ret, errno));
+               //dump_data(0, io->data, wc.byte_len);
+               if (wc.byte_len < 0x20) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               if (SVAL(io->data, 0x00) != 0x0100) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               if (SVAL(io->data, 0x02) != 0x0100) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               if (SVAL(io->data, 0x04) != 0x0100) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               credits_requested = SVAL(io->data, 0x08);
+               if (credits_requested == 0) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               credits_granted = SVAL(io->data, 0x0A);
+               if (credits_granted == 0) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               status = NT_STATUS(IVAL(io->data, 0x0C));
+               if (!NT_STATUS_IS_OK(status)) {
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               max_read_write_size = IVAL(io->data, 0x10);
+               preferred_send_size = IVAL(io->data, 0x14);
+               if (preferred_send_size > c->state.max_receive_size) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               max_receive_size = IVAL(io->data, 0x18);
+               if (max_receive_size < 0x80) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+               max_fragmented_size = IVAL(io->data, 0x1C);
+               if (max_fragmented_size < 0x20000) {
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               c->state.receive_credit_target = credits_requested;
+
+               tmp = c->state.max_receive_size;
+               tmp = MIN(tmp, preferred_send_size);
+               tmp = MAX(tmp, 128);
+               c->state.max_receive_size = tmp;
+
+               tmp = c->state.max_send_size;
+               tmp = MIN(tmp, max_receive_size);
+               c->state.max_send_size = tmp;
+
+               tmp = MIN(1048576, max_read_write_size);
+               c->state.max_read_write_size = tmp;
+
+               tmp = c->state.max_fragmented_size;
+               tmp = MIN(tmp, max_fragmented_size);
+               c->state.max_fragmented_size = tmp;
+
+               c->state.send_credits = credits_granted;
+
+               TALLOC_FREE(c->ibv.fde_channel);
+               TALLOC_FREE(c->rdma.fde_channel);
+
+               DEBUG(0,("%s:%s: ret[%d] errno[%d]\n",
+                       __location__, __FUNCTION__, ret, errno));
+
+               TALLOC_FREE(io);
+
+               errno = 0;
+               ret = smb_direct_connection_post_recv(c);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix_common(errno);
+                       DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                               __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+                       tevent_req_nterror(req, status);
+                       return;
+               }
+
+               tevent_req_done(req);
+               return;
+
+       case IBV_WC_RDMA_WRITE:
+       default:
+               status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+               DEBUG(0,("%s:%s: ret[%d] errno[%d] status[%s]\n",
+                       __location__, __FUNCTION__, ret, errno, nt_errstr(status)));
+               tevent_req_nterror(req, status);
+               return;
        }
-#endif
-       return NULL;
 }
 
-static NTSTATUS smb_direct_connection_negotiate_accept_recv(struct tevent_req *req)
+static NTSTATUS smb_direct_connection_negotiate_accept_recv(
+       struct tevent_req *req,
+       TALLOC_CTX *mem_ctx,
+       struct smb_direct_connection **_c)
 {
+       struct smb_direct_connection_negotiate_accept_state *state =
+               tevent_req_data(req,
+               struct smb_direct_connection_negotiate_accept_state);
+       NTSTATUS status;
+
        DEBUG(0,("%s:%s: here...\n", __location__, __func__));
-       return tevent_req_simple_recv_ntstatus(req);
+
+       *_c = NULL;
+
+       if (tevent_req_is_nterror(req, &status)) {
+DEBUG(0,("%s:%s: here...\n", __location__, __func__));
+               tevent_req_received(req);
+               return status;
+       }
+
+       TALLOC_FREE(state->c->ibv.fde_channel);
+       TALLOC_FREE(state->c->rdma.fde_channel);
+       talloc_set_destructor(state, NULL);
+
+       *_c = talloc_move(mem_ctx, &state->c);
+
+       tevent_req_received(req);
+       return NT_STATUS_OK;
 }
 
 static void smb_direct_connection_disconnect(struct smb_direct_connection *c,
@@ -2508,14 +2908,14 @@ static void smb_direct_listener_accept_rdma_handler(struct tevent_context *ev,
 
                subreq = smb_direct_connection_negotiate_accept_send(state,
                                                                     state->ev,
-                                                                    c);
+                                                                    &c);
                if (subreq == NULL) {
                        DBG_ERR("smb_direct_connection_accept_send ENOMEM\n");
                        TALLOC_FREE(c);
                        /* wait for more */
                        break;
                }
-               tevent_req_set_callback(subreq, smb_direct_listener_accept_done, c);
+               tevent_req_set_callback(subreq, smb_direct_listener_accept_done, req);
                break;
 
        case RDMA_CM_EVENT_DISCONNECTED:
@@ -2539,15 +2939,36 @@ static void smb_direct_listener_accept_rdma_handler(struct tevent_context *ev,
 
 static void smb_direct_listener_accept_done(struct tevent_req *subreq)
 {
-#if 0
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct smb_direct_listener_accept_state *state =
+               tevent_req_data(req,
+               struct smb_direct_listener_accept_state);
+       struct smb_direct_listener *l = state->l;
+       struct smb_direct_connection *c = NULL;
+       NTSTATUS status;
+
        DEBUG(0,("%s:%s: here...\n", __location__, __func__));
 
+       status = smb_direct_connection_negotiate_accept_recv(subreq, state, &c);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(c);
+               return;
+       }
+
+       if (l == NULL) {
+               TALLOC_FREE(c);
+               return;
+       }
+
        DLIST_REMOVE(l->pending, c);
        DLIST_ADD_END(l->ready, c);
 
+       talloc_reparent(state, l, c);
+
        tevent_req_defer_callback(req, state->ev);
        tevent_req_done(req);
-#endif
        return;
 }