diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index f00f1d3fbd9c..0ee855a71ed4 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -793,6 +793,9 @@ static int cma_acquire_dev_by_src_ip(struct rdma_id_private *id_priv) mutex_lock(&lock); list_for_each_entry(cma_dev, &dev_list, list) { + if (id_priv->restricted_node_type != RDMA_NODE_UNSPECIFIED && + id_priv->restricted_node_type != cma_dev->device->node_type) + continue; rdma_for_each_port (cma_dev->device, port) { gidp = rdma_protocol_roce(cma_dev->device, port) ? &iboe_gid : &gid; @@ -1015,6 +1018,7 @@ __rdma_create_id(struct net *net, rdma_cm_event_handler event_handler, return ERR_PTR(-ENOMEM); id_priv->state = RDMA_CM_IDLE; + id_priv->restricted_node_type = RDMA_NODE_UNSPECIFIED; id_priv->id.context = context; id_priv->id.event_handler = event_handler; id_priv->id.ps = ps; @@ -4177,6 +4181,32 @@ err: } EXPORT_SYMBOL(rdma_resolve_addr); +int rdma_restrict_node_type(struct rdma_cm_id *id, u8 node_type) +{ + struct rdma_id_private *id_priv = + container_of(id, struct rdma_id_private, id); + int ret = 0; + + switch (node_type) { + case RDMA_NODE_UNSPECIFIED: + case RDMA_NODE_IB_CA: + case RDMA_NODE_RNIC: + break; + default: + return -EINVAL; + } + + mutex_lock(&lock); + if (id_priv->cma_dev) + ret = -EALREADY; + else + id_priv->restricted_node_type = node_type; + mutex_unlock(&lock); + + return ret; +} +EXPORT_SYMBOL(rdma_restrict_node_type); + int rdma_bind_addr(struct rdma_cm_id *id, struct sockaddr *addr) { struct rdma_id_private *id_priv = diff --git a/drivers/infiniband/core/cma_priv.h b/drivers/infiniband/core/cma_priv.h index c604b601f4d9..04332eb82668 100644 --- a/drivers/infiniband/core/cma_priv.h +++ b/drivers/infiniband/core/cma_priv.h @@ -72,6 +72,7 @@ struct rdma_id_private { int internal_id; enum rdma_cm_state state; + u8 restricted_node_type; spinlock_t lock; struct mutex qp_mutex; diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c index 788a0670c4a8..d44847c9d8fc 100644 --- a/fs/smb/client/smbdirect.c +++ b/fs/smb/client/smbdirect.c @@ -35,6 +35,10 @@ static void enqueue_reassembly( static struct smbdirect_recv_io *_get_first_reassembly( struct smbdirect_socket *sc); +static int smbd_post_send(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, + struct smbdirect_send_io *request); + static int smbd_post_recv( struct smbdirect_socket *sc, struct smbdirect_recv_io *response); @@ -97,8 +101,23 @@ int smbd_send_credit_target = 255; /* The maximum single message size can be sent to remote peer */ int smbd_max_send_size = 1364; -/* The maximum fragmented upper-layer payload receive size supported */ -int smbd_max_fragmented_recv_size = 1024 * 1024; +/* + * The maximum fragmented upper-layer payload receive size supported + * + * Assume max_payload_per_credit is + * smbd_max_receive_size - 24 = 1340 + * + * The maximum number would be + * smbd_receive_credit_max * max_payload_per_credit + * + * 1340 * 255 = 341700 (0x536C4) + * + * The minimum value from the spec is 131072 (0x20000) + * + * For now we use the logic we used in ksmbd before: + * (1364 * 255) / 2 = 173910 (0x2A756) + */ +int smbd_max_fragmented_recv_size = (1364 * 255) / 2; /* The maximum single-message size which can be received */ int smbd_max_receive_size = 1364; @@ -493,27 +512,103 @@ static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response return (void *)response->packet; } +static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket *sc) +{ + struct smbdirect_send_io *msg; + + msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL); + if (!msg) + return ERR_PTR(-ENOMEM); + msg->socket = sc; + INIT_LIST_HEAD(&msg->sibling_list); + msg->num_sge = 0; + + return msg; +} + +static void smbd_free_send_io(struct smbdirect_send_io *msg) +{ + struct smbdirect_socket *sc = msg->socket; + size_t i; + + /* + * The list needs to be empty! + * The caller should take care of it. + */ + WARN_ON_ONCE(!list_empty(&msg->sibling_list)); + + /* + * Note we call ib_dma_unmap_page(), even if some sges are mapped using + * ib_dma_map_single(). + * + * The difference between _single() and _page() only matters for the + * ib_dma_map_*() case. + * + * For the ib_dma_unmap_*() case it does not matter as both take the + * dma_addr_t and dma_unmap_single_attrs() is just an alias to + * dma_unmap_page_attrs(). + */ + for (i = 0; i < msg->num_sge; i++) + ib_dma_unmap_page(sc->ib.dev, + msg->sge[i].addr, + msg->sge[i].length, + DMA_TO_DEVICE); + + mempool_free(msg, sc->send_io.mem.pool); +} + /* Called when a RDMA send is done */ static void send_done(struct ib_cq *cq, struct ib_wc *wc) { - int i; struct smbdirect_send_io *request = container_of(wc->wr_cqe, struct smbdirect_send_io, cqe); struct smbdirect_socket *sc = request->socket; + struct smbdirect_send_io *sibling, *next; int lcredits = 0; log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n", request, ib_wc_status_msg(wc->status)); - for (i = 0; i < request->num_sge; i++) - ib_dma_unmap_single(sc->ib.dev, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - mempool_free(request, sc->send_io.mem.pool); + if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) { + /* + * This happens when smbdirect_send_io is a sibling + * before the final message, it is signaled on + * error anyway, so we need to skip + * smbdirect_connection_free_send_io here, + * otherwise is will destroy the memory + * of the siblings too, which will cause + * use after free problems for the others + * triggered from ib_drain_qp(). + */ + if (wc->status != IB_WC_SUCCESS) + goto skip_free; + + /* + * This should not happen! + * But we better just close the + * connection... + */ + log_rdma_send(ERR, + "unexpected send completion wc->status=%s (%d) wc->opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, wc->opcode); + smbd_disconnect_rdma_connection(sc); + return; + } + + /* + * Free possible siblings and then the main send_io + */ + list_for_each_entry_safe(sibling, next, &request->sibling_list, sibling_list) { + list_del_init(&sibling->sibling_list); + smbd_free_send_io(sibling); + lcredits += 1; + } + /* Note this frees wc->wr_cqe, but not wc */ + smbd_free_send_io(request); lcredits += 1; if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { +skip_free: if (wc->status != IB_WC_WR_FLUSH_ERR) log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n", ib_wc_status_msg(wc->status), wc->opcode); @@ -608,6 +703,7 @@ static bool process_negotiation_response( sp->max_frmr_depth * PAGE_SIZE); sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE; + atomic_set(&sc->send_io.bcredits.count, 1); sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER; return true; } @@ -618,6 +714,7 @@ static void smbd_post_send_credits(struct work_struct *work) struct smbdirect_recv_io *response; struct smbdirect_socket *sc = container_of(work, struct smbdirect_socket, recv_io.posted.refill_work); + int posted = 0; if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { return; @@ -640,9 +737,21 @@ static void smbd_post_send_credits(struct work_struct *work) } atomic_inc(&sc->recv_io.posted.count); + posted += 1; } } + atomic_add(posted, &sc->recv_io.credits.available); + + /* + * If the last send credit is waiting for credits + * it can grant we need to wake it up + */ + if (posted && + atomic_read(&sc->send_io.bcredits.count) == 0 && + atomic_read(&sc->send_io.credits.count) == 0) + wake_up(&sc->send_io.credits.wait_queue); + /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */ if (atomic_read(&sc->recv_io.credits.count) < sc->recv_io.credits.target - 1) { @@ -659,6 +768,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe); struct smbdirect_socket *sc = response->socket; struct smbdirect_socket_parameters *sp = &sc->parameters; + int current_recv_credits; u16 old_recv_credit_target; u32 data_offset = 0; u32 data_length = 0; @@ -743,7 +853,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } atomic_dec(&sc->recv_io.posted.count); - atomic_dec(&sc->recv_io.credits.count); + current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count); + old_recv_credit_target = sc->recv_io.credits.target; sc->recv_io.credits.target = le16_to_cpu(data_transfer->credits_requested); @@ -779,7 +890,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) * reassembly queue and wake up the reading thread */ if (data_length) { - if (sc->recv_io.credits.target > old_recv_credit_target) + if (current_recv_credits <= (sc->recv_io.credits.target / 4) || + sc->recv_io.credits.target > old_recv_credit_target) queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); enqueue_reassembly(sc, response, data_length); @@ -810,6 +922,7 @@ static struct rdma_cm_id *smbd_create_id( { struct smbdirect_socket_parameters *sp = &sc->parameters; struct rdma_cm_id *id; + u8 node_type = RDMA_NODE_UNSPECIFIED; int rc; __be16 *sport; @@ -821,6 +934,31 @@ static struct rdma_cm_id *smbd_create_id( return id; } + switch (port) { + case SMBD_PORT: + /* + * only allow iWarp devices + * for port 5445. + */ + node_type = RDMA_NODE_RNIC; + break; + case SMB_PORT: + /* + * only allow InfiniBand, RoCEv1 or RoCEv2 + * devices for port 445. + * + * (Basically don't allow iWarp devices) + */ + node_type = RDMA_NODE_IB_CA; + break; + } + rc = rdma_restrict_node_type(id, node_type); + if (rc) { + log_rdma_event(ERR, "rdma_restrict_node_type(%u) failed %i\n", + node_type, rc); + goto out; + } + if (dstaddr->sa_family == AF_INET6) sport = &((struct sockaddr_in6 *)dstaddr)->sin6_port; else @@ -955,16 +1093,13 @@ out1: static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc) { struct smbdirect_socket_parameters *sp = &sc->parameters; - struct ib_send_wr send_wr; - int rc = -ENOMEM; + int rc; struct smbdirect_send_io *request; struct smbdirect_negotiate_req *packet; - request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL); - if (!request) - return rc; - - request->socket = sc; + request = smbd_alloc_send_io(sc); + if (IS_ERR(request)) + return PTR_ERR(request); packet = smbdirect_send_io_payload(request); packet->min_version = cpu_to_le16(SMBDIRECT_V1); @@ -976,7 +1111,6 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc) packet->max_fragmented_size = cpu_to_le32(sp->max_fragmented_recv_size); - request->num_sge = 1; request->sge[0].addr = ib_dma_map_single( sc->ib.dev, (void *)packet, sizeof(*packet), DMA_TO_DEVICE); @@ -984,42 +1118,20 @@ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc) rc = -EIO; goto dma_mapping_failed; } + request->num_sge = 1; request->sge[0].length = sizeof(*packet); request->sge[0].lkey = sc->ib.pd->local_dma_lkey; - ib_dma_sync_single_for_device( - sc->ib.dev, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - request->cqe.done = send_done; - - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; - - log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n", - request->sge[0].addr, - request->sge[0].length, request->sge[0].lkey); - - atomic_inc(&sc->send_io.pending.count); - rc = ib_post_send(sc->ib.qp, &send_wr, NULL); + rc = smbd_post_send(sc, NULL, request); if (!rc) return 0; - /* if we reach here, post send failed */ - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - atomic_dec(&sc->send_io.pending.count); - ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr, - request->sge[0].length, DMA_TO_DEVICE); - - smbd_disconnect_rdma_connection(sc); + if (rc == -EAGAIN) + rc = -EIO; dma_mapping_failed: - mempool_free(request, sc->send_io.mem.pool); + smbd_free_send_io(request); return rc; } @@ -1033,19 +1145,38 @@ dma_mapping_failed: */ static int manage_credits_prior_sending(struct smbdirect_socket *sc) { + int missing; + int available; int new_credits; if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target) return 0; - new_credits = atomic_read(&sc->recv_io.posted.count); - if (new_credits == 0) + missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count); + available = atomic_xchg(&sc->recv_io.credits.available, 0); + new_credits = (u16)min3(U16_MAX, missing, available); + if (new_credits <= 0) { + /* + * If credits are available, but not granted + * we need to re-add them again. + */ + if (available) + atomic_add(available, &sc->recv_io.credits.available); return 0; + } - new_credits -= atomic_read(&sc->recv_io.credits.count); - if (new_credits <= 0) - return 0; + if (new_credits < available) { + /* + * Readd the remaining available again. + */ + available -= new_credits; + atomic_add(available, &sc->recv_io.credits.available); + } + /* + * Remember we granted the credits + */ + atomic_add(new_credits, &sc->recv_io.credits.count); return new_credits; } @@ -1075,12 +1206,27 @@ static int manage_keep_alive_before_sending(struct smbdirect_socket *sc) return 0; } +static int smbd_ib_post_send(struct smbdirect_socket *sc, + struct ib_send_wr *wr) +{ + int ret; + + atomic_inc(&sc->send_io.pending.count); + ret = ib_post_send(sc->ib.qp, wr, NULL); + if (ret) { + pr_err("failed to post send: %d\n", ret); + smbd_disconnect_rdma_connection(sc); + ret = -EAGAIN; + } + return ret; +} + /* Post the send request */ static int smbd_post_send(struct smbdirect_socket *sc, - struct smbdirect_send_io *request) + struct smbdirect_send_batch *batch, + struct smbdirect_send_io *request) { - struct ib_send_wr send_wr; - int rc, i; + int i; for (i = 0; i < request->num_sge; i++) { log_rdma_send(INFO, @@ -1094,79 +1240,245 @@ static int smbd_post_send(struct smbdirect_socket *sc, } request->cqe.done = send_done; + request->wr.next = NULL; + request->wr.sg_list = request->sge; + request->wr.num_sge = request->num_sge; + request->wr.opcode = IB_WR_SEND; - send_wr.next = NULL; - send_wr.wr_cqe = &request->cqe; - send_wr.sg_list = request->sge; - send_wr.num_sge = request->num_sge; - send_wr.opcode = IB_WR_SEND; - send_wr.send_flags = IB_SEND_SIGNALED; + if (batch) { + request->wr.wr_cqe = NULL; + request->wr.send_flags = 0; + if (!list_empty(&batch->msg_list)) { + struct smbdirect_send_io *last; - rc = ib_post_send(sc->ib.qp, &send_wr, NULL); - if (rc) { - log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc); - smbd_disconnect_rdma_connection(sc); - rc = -EAGAIN; + last = list_last_entry(&batch->msg_list, + struct smbdirect_send_io, + sibling_list); + last->wr.next = &request->wr; + } + list_add_tail(&request->sibling_list, &batch->msg_list); + batch->wr_cnt++; + return 0; } - return rc; + request->wr.wr_cqe = &request->cqe; + request->wr.send_flags = IB_SEND_SIGNALED; + return smbd_ib_post_send(sc, &request->wr); +} + +static void smbd_send_batch_init(struct smbdirect_send_batch *batch, + bool need_invalidate_rkey, + unsigned int remote_key) +{ + INIT_LIST_HEAD(&batch->msg_list); + batch->wr_cnt = 0; + batch->need_invalidate_rkey = need_invalidate_rkey; + batch->remote_key = remote_key; + batch->credit = 0; +} + +static int smbd_send_batch_flush(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, + bool is_last) +{ + struct smbdirect_send_io *first, *last; + int ret = 0; + + if (list_empty(&batch->msg_list)) + goto release_credit; + + first = list_first_entry(&batch->msg_list, + struct smbdirect_send_io, + sibling_list); + last = list_last_entry(&batch->msg_list, + struct smbdirect_send_io, + sibling_list); + + if (batch->need_invalidate_rkey) { + first->wr.opcode = IB_WR_SEND_WITH_INV; + first->wr.ex.invalidate_rkey = batch->remote_key; + batch->need_invalidate_rkey = false; + batch->remote_key = 0; + } + + last->wr.send_flags = IB_SEND_SIGNALED; + last->wr.wr_cqe = &last->cqe; + + /* + * Remove last from batch->msg_list + * and splice the rest of batch->msg_list + * to last->sibling_list. + * + * batch->msg_list is a valid empty list + * at the end. + */ + list_del_init(&last->sibling_list); + list_splice_tail_init(&batch->msg_list, &last->sibling_list); + batch->wr_cnt = 0; + + ret = smbd_ib_post_send(sc, &first->wr); + if (ret) { + struct smbdirect_send_io *sibling, *next; + + list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) { + list_del_init(&sibling->sibling_list); + smbd_free_send_io(sibling); + } + smbd_free_send_io(last); + } + +release_credit: + if (is_last && !ret && batch->credit) { + atomic_add(batch->credit, &sc->send_io.bcredits.count); + batch->credit = 0; + wake_up(&sc->send_io.bcredits.wait_queue); + } + + return ret; +} + +static int wait_for_credits(struct smbdirect_socket *sc, + wait_queue_head_t *waitq, atomic_t *total_credits, + int needed) +{ + int ret; + + do { + if (atomic_sub_return(needed, total_credits) >= 0) + return 0; + + atomic_add(needed, total_credits); + ret = wait_event_interruptible(*waitq, + atomic_read(total_credits) >= needed || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + return -ENOTCONN; + else if (ret < 0) + return ret; + } while (true); +} + +static int wait_for_send_bcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch) +{ + int ret; + + if (batch->credit) + return 0; + + ret = wait_for_credits(sc, + &sc->send_io.bcredits.wait_queue, + &sc->send_io.bcredits.count, + 1); + if (ret) + return ret; + + batch->credit = 1; + return 0; +} + +static int wait_for_send_lcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch) +{ + if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) { + int ret; + + ret = smbd_send_batch_flush(sc, batch, false); + if (ret) + return ret; + } + + return wait_for_credits(sc, + &sc->send_io.lcredits.wait_queue, + &sc->send_io.lcredits.count, + 1); +} + +static int wait_for_send_credits(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch) +{ + if (batch && + (batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) { + int ret; + + ret = smbd_send_batch_flush(sc, batch, false); + if (ret) + return ret; + } + + return wait_for_credits(sc, + &sc->send_io.credits.wait_queue, + &sc->send_io.credits.count, + 1); } static int smbd_post_send_iter(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, struct iov_iter *iter, int *_remaining_data_length) { struct smbdirect_socket_parameters *sp = &sc->parameters; - int i, rc; + int rc; int header_length; int data_length; struct smbdirect_send_io *request; struct smbdirect_data_transfer *packet; int new_credits = 0; + struct smbdirect_send_batch _batch; -wait_lcredit: - /* Wait for local send credits */ - rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue, - atomic_read(&sc->send_io.lcredits.count) > 0 || - sc->status != SMBDIRECT_SOCKET_CONNECTED); - if (rc) - goto err_wait_lcredit; + if (!batch) { + smbd_send_batch_init(&_batch, false, 0); + batch = &_batch; + } - if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { - log_outgoing(ERR, "disconnected not sending on wait_credit\n"); + rc = wait_for_send_bcredit(sc, batch); + if (rc) { + log_outgoing(ERR, "disconnected not sending on wait_bcredit\n"); + rc = -EAGAIN; + goto err_wait_bcredit; + } + + rc = wait_for_send_lcredit(sc, batch); + if (rc) { + log_outgoing(ERR, "disconnected not sending on wait_lcredit\n"); rc = -EAGAIN; goto err_wait_lcredit; } - if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) { - atomic_inc(&sc->send_io.lcredits.count); - goto wait_lcredit; - } -wait_credit: - /* Wait for send credits. A SMBD packet needs one credit */ - rc = wait_event_interruptible(sc->send_io.credits.wait_queue, - atomic_read(&sc->send_io.credits.count) > 0 || - sc->status != SMBDIRECT_SOCKET_CONNECTED); - if (rc) - goto err_wait_credit; - - if (sc->status != SMBDIRECT_SOCKET_CONNECTED) { + rc = wait_for_send_credits(sc, batch); + if (rc) { log_outgoing(ERR, "disconnected not sending on wait_credit\n"); rc = -EAGAIN; goto err_wait_credit; } - if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) { - atomic_inc(&sc->send_io.credits.count); - goto wait_credit; + + new_credits = manage_credits_prior_sending(sc); + if (new_credits == 0 && + atomic_read(&sc->send_io.credits.count) == 0 && + atomic_read(&sc->recv_io.credits.count) == 0) { + queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); + rc = wait_event_interruptible(sc->send_io.credits.wait_queue, + atomic_read(&sc->send_io.credits.count) >= 1 || + atomic_read(&sc->recv_io.credits.available) >= 1 || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + rc = -ENOTCONN; + if (rc < 0) { + log_outgoing(ERR, "disconnected not sending on last credit\n"); + rc = -EAGAIN; + goto err_wait_credit; + } + + new_credits = manage_credits_prior_sending(sc); } - request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL); - if (!request) { - rc = -ENOMEM; + request = smbd_alloc_send_io(sc); + if (IS_ERR(request)) { + rc = PTR_ERR(request); goto err_alloc; } - request->socket = sc; memset(request->sge, 0, sizeof(request->sge)); /* Map the packet to DMA */ @@ -1215,9 +1527,6 @@ wait_credit: /* Fill in the packet header */ packet->credits_requested = cpu_to_le16(sp->send_credit_target); - - new_credits = manage_credits_prior_sending(sc); - atomic_add(new_credits, &sc->recv_io.credits.count); packet->credits_granted = cpu_to_le16(new_credits); packet->flags = 0; @@ -1240,32 +1549,18 @@ wait_credit: le32_to_cpu(packet->data_length), le32_to_cpu(packet->remaining_data_length)); - /* - * Now that we got a local and a remote credit - * we add us as pending - */ - atomic_inc(&sc->send_io.pending.count); + rc = smbd_post_send(sc, batch, request); + if (!rc) { + if (batch != &_batch) + return 0; - rc = smbd_post_send(sc, request); - if (!rc) - return 0; - - if (atomic_dec_and_test(&sc->send_io.pending.count)) - wake_up(&sc->send_io.pending.zero_wait_queue); - - wake_up(&sc->send_io.pending.dec_wait_queue); + rc = smbd_send_batch_flush(sc, batch, true); + if (!rc) + return 0; + } err_dma: - for (i = 0; i < request->num_sge; i++) - if (request->sge[i].addr) - ib_dma_unmap_single(sc->ib.dev, - request->sge[i].addr, - request->sge[i].length, - DMA_TO_DEVICE); - mempool_free(request, sc->send_io.mem.pool); - - /* roll back the granted receive credits */ - atomic_sub(new_credits, &sc->recv_io.credits.count); + smbd_free_send_io(request); err_alloc: atomic_inc(&sc->send_io.credits.count); @@ -1276,6 +1571,11 @@ err_wait_credit: wake_up(&sc->send_io.lcredits.wait_queue); err_wait_lcredit: + atomic_add(batch->credit, &sc->send_io.bcredits.count); + batch->credit = 0; + wake_up(&sc->send_io.bcredits.wait_queue); + +err_wait_bcredit: return rc; } @@ -1289,10 +1589,11 @@ static int smbd_post_send_empty(struct smbdirect_socket *sc) int remaining_data_length = 0; sc->statistics.send_empty++; - return smbd_post_send_iter(sc, NULL, &remaining_data_length); + return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length); } static int smbd_post_send_full_iter(struct smbdirect_socket *sc, + struct smbdirect_send_batch *batch, struct iov_iter *iter, int *_remaining_data_length) { @@ -1305,7 +1606,7 @@ static int smbd_post_send_full_iter(struct smbdirect_socket *sc, */ while (iov_iter_count(iter) > 0) { - rc = smbd_post_send_iter(sc, iter, _remaining_data_length); + rc = smbd_post_send_iter(sc, batch, iter, _remaining_data_length); if (rc < 0) break; } @@ -2227,8 +2528,10 @@ int smbd_send(struct TCP_Server_Info *server, struct smbdirect_socket_parameters *sp = &sc->parameters; struct smb_rqst *rqst; struct iov_iter iter; + struct smbdirect_send_batch batch; unsigned int remaining_data_length, klen; int rc, i, rqst_idx; + int error = 0; if (sc->status != SMBDIRECT_SOCKET_CONNECTED) return -EAGAIN; @@ -2253,6 +2556,7 @@ int smbd_send(struct TCP_Server_Info *server, num_rqst, remaining_data_length); rqst_idx = 0; + smbd_send_batch_init(&batch, false, 0); do { rqst = &rqst_array[rqst_idx]; @@ -2271,20 +2575,28 @@ int smbd_send(struct TCP_Server_Info *server, klen += rqst->rq_iov[i].iov_len; iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen); - rc = smbd_post_send_full_iter(sc, &iter, &remaining_data_length); - if (rc < 0) + rc = smbd_post_send_full_iter(sc, &batch, &iter, &remaining_data_length); + if (rc < 0) { + error = rc; break; + } if (iov_iter_count(&rqst->rq_iter) > 0) { /* And then the data pages if there are any */ - rc = smbd_post_send_full_iter(sc, &rqst->rq_iter, + rc = smbd_post_send_full_iter(sc, &batch, &rqst->rq_iter, &remaining_data_length); - if (rc < 0) + if (rc < 0) { + error = rc; break; + } } } while (++rqst_idx < num_rqst); + rc = smbd_send_batch_flush(sc, &batch, true); + if (unlikely(!rc && error)) + rc = error; + /* * As an optimization, we don't wait for individual I/O to finish * before sending the next one. diff --git a/fs/smb/common/smbdirect/smbdirect_socket.h b/fs/smb/common/smbdirect/smbdirect_socket.h index ee4c2726771a..95265192bb01 100644 --- a/fs/smb/common/smbdirect/smbdirect_socket.h +++ b/fs/smb/common/smbdirect/smbdirect_socket.h @@ -162,6 +162,17 @@ struct smbdirect_socket { mempool_t *pool; } mem; + /* + * This is a coordination for smbdirect_send_batch. + * + * There's only one possible credit, which means + * only one instance is running at a time. + */ + struct { + atomic_t count; + wait_queue_head_t wait_queue; + } bcredits; + /* * The local credit state for ib_post_send() */ @@ -239,6 +250,7 @@ struct smbdirect_socket { */ struct { u16 target; + atomic_t available; atomic_t count; } credits; @@ -370,6 +382,9 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work); disable_delayed_work_sync(&sc->idle.timer_work); + atomic_set(&sc->send_io.bcredits.count, 0); + init_waitqueue_head(&sc->send_io.bcredits.wait_queue); + atomic_set(&sc->send_io.lcredits.count, 0); init_waitqueue_head(&sc->send_io.lcredits.wait_queue); @@ -387,6 +402,7 @@ static __always_inline void smbdirect_socket_init(struct smbdirect_socket *sc) INIT_WORK(&sc->recv_io.posted.refill_work, __smbdirect_socket_disabled_work); disable_work_sync(&sc->recv_io.posted.refill_work); + atomic_set(&sc->recv_io.credits.available, 0); atomic_set(&sc->recv_io.credits.count, 0); INIT_LIST_HEAD(&sc->recv_io.reassembly.list); @@ -483,6 +499,8 @@ struct smbdirect_send_batch { */ bool need_invalidate_rkey; u32 remote_key; + + int credit; }; struct smbdirect_recv_io { diff --git a/fs/smb/server/Makefile b/fs/smb/server/Makefile index 7d6337a7dee4..6407ba6b9340 100644 --- a/fs/smb/server/Makefile +++ b/fs/smb/server/Makefile @@ -18,3 +18,4 @@ $(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o +ksmbd-$(CONFIG_PROC_FS) += proc.o diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c index 6cac48c8fbe8..e7e3e77006b1 100644 --- a/fs/smb/server/connection.c +++ b/fs/smb/server/connection.c @@ -14,6 +14,7 @@ #include "connection.h" #include "transport_tcp.h" #include "transport_rdma.h" +#include "misc.h" static DEFINE_MUTEX(init_lock); @@ -22,6 +23,62 @@ static struct ksmbd_conn_ops default_conn_ops; DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS); DECLARE_RWSEM(conn_list_lock); +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_clients; + +static int proc_show_clients(struct seq_file *m, void *v) +{ + struct ksmbd_conn *conn; + struct timespec64 now, t; + int i; + + seq_printf(m, "#%-20s %-10s %-10s %-10s %-10s %-10s\n", + "", "", "", "", + "", ""); + + down_read(&conn_list_lock); + hash_for_each(conn_list, i, conn, hlist) { + jiffies_to_timespec64(jiffies - conn->last_active, &t); + ktime_get_real_ts64(&now); + t = timespec64_sub(now, t); +#if IS_ENABLED(CONFIG_IPV6) + if (!conn->inet_addr) + seq_printf(m, "%-20pI6c", &conn->inet6_addr); + else +#endif + seq_printf(m, "%-20pI4", &conn->inet_addr); + seq_printf(m, " 0x%-10x %-10u %-12d %-10d %ptT\n", + conn->dialect, + conn->total_credits, + atomic_read(&conn->stats.open_files_count), + atomic_read(&conn->req_running), + &t); + } + up_read(&conn_list_lock); + return 0; +} + +static int create_proc_clients(void) +{ + proc_clients = ksmbd_proc_create("clients", + proc_show_clients, NULL); + if (!proc_clients) + return -ENOMEM; + return 0; +} + +static void delete_proc_clients(void) +{ + if (proc_clients) { + proc_remove(proc_clients); + proc_clients = NULL; + } +} +#else +static int create_proc_clients(void) { return 0; } +static void delete_proc_clients(void) {} +#endif + /** * ksmbd_conn_free() - free resources of the connection instance * @@ -472,6 +529,7 @@ int ksmbd_conn_transport_init(void) } out: mutex_unlock(&init_lock); + create_proc_clients(); return ret; } @@ -502,6 +560,7 @@ again: void ksmbd_conn_transport_destroy(void) { + delete_proc_clients(); mutex_lock(&init_lock); ksmbd_tcp_destroy(); ksmbd_rdma_stop_listening(); diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index 7f9bcd9817b5..1e2587036bca 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -7,6 +7,7 @@ #define __KSMBD_CONNECTION_H__ #include +#include #include #include #include @@ -33,7 +34,7 @@ enum { KSMBD_SESS_RELEASING }; -struct ksmbd_stats { +struct ksmbd_conn_stats { atomic_t open_files_count; atomic64_t request_served; }; @@ -78,7 +79,7 @@ struct ksmbd_conn { struct list_head requests; struct list_head async_requests; int connection_type; - struct ksmbd_stats stats; + struct ksmbd_conn_stats stats; char ClientGUID[SMB2_CLIENT_GUID_SIZE]; struct ntlmssp_auth ntlmssp; diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c index d3483d9c757c..57dd47ef688c 100644 --- a/fs/smb/server/mgmt/tree_connect.c +++ b/fs/smb/server/mgmt/tree_connect.c @@ -9,6 +9,7 @@ #include "../transport_ipc.h" #include "../connection.h" +#include "../stats.h" #include "tree_connect.h" #include "user_config.h" @@ -79,12 +80,15 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name) status.tree_conn = tree_conn; atomic_set(&tree_conn->refcount, 1); + down_write(&sess->tree_conns_lock); ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn, KSMBD_DEFAULT_GFP)); + up_write(&sess->tree_conns_lock); if (ret) { status.ret = -ENOMEM; goto out_error; } + ksmbd_counter_inc(KSMBD_COUNTER_TREE_CONNS); kvfree(resp); return status; @@ -103,29 +107,36 @@ void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon) kfree(tcon); } -int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, - struct ksmbd_tree_connect *tree_conn) +static int __ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) { int ret; - write_lock(&sess->tree_conns_lock); - xa_erase(&sess->tree_conns, tree_conn->id); - write_unlock(&sess->tree_conns_lock); - ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id); ksmbd_release_tree_conn_id(sess, tree_conn->id); ksmbd_share_config_put(tree_conn->share_conf); + ksmbd_counter_dec(KSMBD_COUNTER_TREE_CONNS); if (atomic_dec_and_test(&tree_conn->refcount)) kfree(tree_conn); return ret; } +int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess, + struct ksmbd_tree_connect *tree_conn) +{ + down_write(&sess->tree_conns_lock); + xa_erase(&sess->tree_conns, tree_conn->id); + up_write(&sess->tree_conns_lock); + + return __ksmbd_tree_conn_disconnect(sess, tree_conn); +} + struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, unsigned int id) { struct ksmbd_tree_connect *tcon; - read_lock(&sess->tree_conns_lock); + down_read(&sess->tree_conns_lock); tcon = xa_load(&sess->tree_conns, id); if (tcon) { if (tcon->t_state != TREE_CONNECTED) @@ -133,7 +144,7 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess, else if (!atomic_inc_not_zero(&tcon->refcount)) tcon = NULL; } - read_unlock(&sess->tree_conns_lock); + up_read(&sess->tree_conns_lock); return tcon; } @@ -147,18 +158,19 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess) if (!sess) return -EINVAL; + down_write(&sess->tree_conns_lock); xa_for_each(&sess->tree_conns, id, tc) { - write_lock(&sess->tree_conns_lock); if (tc->t_state == TREE_DISCONNECTED) { - write_unlock(&sess->tree_conns_lock); ret = -ENOENT; continue; } tc->t_state = TREE_DISCONNECTED; - write_unlock(&sess->tree_conns_lock); - ret |= ksmbd_tree_conn_disconnect(sess, tc); + xa_erase(&sess->tree_conns, tc->id); + ret |= __ksmbd_tree_conn_disconnect(sess, tc); } xa_destroy(&sess->tree_conns); + up_write(&sess->tree_conns_lock); + return ret; } diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c index 56c9a38ca878..3267b86b8c16 100644 --- a/fs/smb/server/mgmt/user_config.c +++ b/fs/smb/server/mgmt/user_config.c @@ -90,11 +90,9 @@ void ksmbd_free_user(struct ksmbd_user *user) kfree(user); } -int ksmbd_anonymous_user(struct ksmbd_user *user) +bool ksmbd_anonymous_user(struct ksmbd_user *user) { - if (user->name[0] == '\0') - return 1; - return 0; + return user->name[0] == '\0'; } bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2) diff --git a/fs/smb/server/mgmt/user_config.h b/fs/smb/server/mgmt/user_config.h index 8c227b8d4954..cc460b4ff7d3 100644 --- a/fs/smb/server/mgmt/user_config.h +++ b/fs/smb/server/mgmt/user_config.h @@ -65,6 +65,6 @@ struct ksmbd_user *ksmbd_login_user(const char *account); struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp, struct ksmbd_login_response_ext *resp_ext); void ksmbd_free_user(struct ksmbd_user *user); -int ksmbd_anonymous_user(struct ksmbd_user *user); +bool ksmbd_anonymous_user(struct ksmbd_user *user); bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2); #endif /* __USER_CONFIG_MANAGEMENT_H__ */ diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c index 7d880ff34402..957a12de6a9d 100644 --- a/fs/smb/server/mgmt/user_session.c +++ b/fs/smb/server/mgmt/user_session.c @@ -12,9 +12,12 @@ #include "user_session.h" #include "user_config.h" #include "tree_connect.h" +#include "share_config.h" #include "../transport_ipc.h" #include "../connection.h" #include "../vfs_cache.h" +#include "../misc.h" +#include "../stats.h" static DEFINE_IDA(session_ida); @@ -27,17 +30,236 @@ struct ksmbd_session_rpc { unsigned int method; }; +#ifdef CONFIG_PROC_FS + +static const struct ksmbd_const_name ksmbd_sess_cap_const_names[] = { + {SMB2_GLOBAL_CAP_DFS, "dfs"}, + {SMB2_GLOBAL_CAP_LEASING, "lease"}, + {SMB2_GLOBAL_CAP_LARGE_MTU, "large-mtu"}, + {SMB2_GLOBAL_CAP_MULTI_CHANNEL, "multi-channel"}, + {SMB2_GLOBAL_CAP_PERSISTENT_HANDLES, "persistent-handles"}, + {SMB2_GLOBAL_CAP_DIRECTORY_LEASING, "dir-lease"}, + {SMB2_GLOBAL_CAP_ENCRYPTION, "encryption"} +}; + +static const struct ksmbd_const_name ksmbd_cipher_const_names[] = { + {le16_to_cpu(SMB2_ENCRYPTION_AES128_CCM), "aes128-ccm"}, + {le16_to_cpu(SMB2_ENCRYPTION_AES128_GCM), "aes128-gcm"}, + {le16_to_cpu(SMB2_ENCRYPTION_AES256_CCM), "aes256-ccm"}, + {le16_to_cpu(SMB2_ENCRYPTION_AES256_GCM), "aes256-gcm"}, +}; + +static const struct ksmbd_const_name ksmbd_signing_const_names[] = { + {SIGNING_ALG_HMAC_SHA256, "hmac-sha256"}, + {SIGNING_ALG_AES_CMAC, "aes-cmac"}, + {SIGNING_ALG_AES_GMAC, "aes-gmac"}, +}; + +static const char *session_state_string(struct ksmbd_session *session) +{ + switch (session->state) { + case SMB2_SESSION_VALID: + return "valid"; + case SMB2_SESSION_IN_PROGRESS: + return "progress"; + case SMB2_SESSION_EXPIRED: + return "expired"; + default: + return ""; + } +} + +static const char *session_user_name(struct ksmbd_session *session) +{ + if (user_guest(session->user)) + return "(Guest)"; + else if (ksmbd_anonymous_user(session->user)) + return "(Anonymous)"; + return session->user->name; +} + +static int show_proc_session(struct seq_file *m, void *v) +{ + struct ksmbd_session *sess; + struct ksmbd_tree_connect *tree_conn; + struct ksmbd_share_config *share_conf; + struct channel *chan; + unsigned long id; + int i = 0; + + sess = (struct ksmbd_session *)m->private; + ksmbd_user_session_get(sess); + + i = 0; + down_read(&sess->chann_lock); + xa_for_each(&sess->ksmbd_chann_list, id, chan) { +#if IS_ENABLED(CONFIG_IPV6) + if (chan->conn->inet_addr) + seq_printf(m, "%-20s\t%pI4\n", "client", + &chan->conn->inet_addr); + else + seq_printf(m, "%-20s\t%pI6c\n", "client", + &chan->conn->inet6_addr); +#else + seq_printf(m, "%-20s\t%pI4\n", "client", + &chan->conn->inet_addr); +#endif + seq_printf(m, "%-20s\t%s\n", "user", session_user_name(sess)); + seq_printf(m, "%-20s\t%llu\n", "id", sess->id); + seq_printf(m, "%-20s\t%s\n", "state", + session_state_string(sess)); + + seq_printf(m, "%-20s\t", "capabilities"); + ksmbd_proc_show_flag_names(m, + ksmbd_sess_cap_const_names, + ARRAY_SIZE(ksmbd_sess_cap_const_names), + chan->conn->vals->req_capabilities); + + if (sess->sign) { + seq_printf(m, "%-20s\t", "signing"); + ksmbd_proc_show_const_name(m, "%s\t", + ksmbd_signing_const_names, + ARRAY_SIZE(ksmbd_signing_const_names), + le16_to_cpu(chan->conn->signing_algorithm)); + } else if (sess->enc) { + seq_printf(m, "%-20s\t", "encryption"); + ksmbd_proc_show_const_name(m, "%s\t", + ksmbd_cipher_const_names, + ARRAY_SIZE(ksmbd_cipher_const_names), + le16_to_cpu(chan->conn->cipher_type)); + } + i++; + } + up_read(&sess->chann_lock); + + seq_printf(m, "%-20s\t%d\n", "channels", i); + + i = 0; + down_read(&sess->tree_conns_lock); + xa_for_each(&sess->tree_conns, id, tree_conn) { + share_conf = tree_conn->share_conf; + seq_printf(m, "%-20s\t%s\t%8d", "share", + share_conf->name, tree_conn->id); + if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_PIPE)) + seq_printf(m, " %s ", "pipe"); + else + seq_printf(m, " %s ", "disk"); + seq_putc(m, '\n'); + } + up_read(&sess->tree_conns_lock); + + ksmbd_user_session_put(sess); + return 0; +} + +void ksmbd_proc_show_flag_names(struct seq_file *m, + const struct ksmbd_const_name *table, + int count, + unsigned int flags) +{ + int i; + + for (i = 0; i < count; i++) { + if (table[i].const_value & flags) + seq_printf(m, "0x%08x\t", table[i].const_value); + } + seq_putc(m, '\n'); +} + +void ksmbd_proc_show_const_name(struct seq_file *m, + const char *format, + const struct ksmbd_const_name *table, + int count, + unsigned int const_value) +{ + int i; + + for (i = 0; i < count; i++) { + if (table[i].const_value & const_value) + seq_printf(m, format, table[i].name); + } + seq_putc(m, '\n'); +} + +static int create_proc_session(struct ksmbd_session *sess) +{ + char name[30]; + + snprintf(name, sizeof(name), "sessions/%llu", sess->id); + sess->proc_entry = ksmbd_proc_create(name, + show_proc_session, sess); + return 0; +} + +static void delete_proc_session(struct ksmbd_session *sess) +{ + if (sess->proc_entry) + proc_remove(sess->proc_entry); +} + +static int show_proc_sessions(struct seq_file *m, void *v) +{ + struct ksmbd_session *session; + struct channel *chan; + int i; + unsigned long id; + + seq_printf(m, "#%-40s %-15s %-10s %-10s\n", + "", "", "", ""); + + down_read(&sessions_table_lock); + hash_for_each(sessions_table, i, session, hlist) { + down_read(&session->chann_lock); + xa_for_each(&session->ksmbd_chann_list, id, chan) { + down_read(&chan->conn->session_lock); + ksmbd_user_session_get(session); + +#if IS_ENABLED(CONFIG_IPV6) + if (!chan->conn->inet_addr) + seq_printf(m, " %-40pI6c", &chan->conn->inet6_addr); + else +#endif + seq_printf(m, " %-40pI4", &chan->conn->inet_addr); + seq_printf(m, " %-15s %-10llu %-10s\n", + session_user_name(session), + session->id, + session_state_string(session)); + + ksmbd_user_session_put(session); + up_read(&chan->conn->session_lock); + } + up_read(&session->chann_lock); + } + up_read(&sessions_table_lock); + return 0; +} + +int create_proc_sessions(void) +{ + if (!ksmbd_proc_create("sessions/sessions", + show_proc_sessions, NULL)) + return -ENOMEM; + return 0; +} +#else +int create_proc_sessions(void) { return 0; } +static int create_proc_session(struct ksmbd_session *sess) { return 0; } +static void delete_proc_session(struct ksmbd_session *sess) {} +#endif + static void free_channel_list(struct ksmbd_session *sess) { struct channel *chann; unsigned long index; + down_write(&sess->chann_lock); xa_for_each(&sess->ksmbd_chann_list, index, chann) { xa_erase(&sess->ksmbd_chann_list, index); kfree(chann); } xa_destroy(&sess->ksmbd_chann_list); + up_write(&sess->chann_lock); } static void __session_rpc_close(struct ksmbd_session *sess, @@ -159,6 +381,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess) if (!sess) return; + delete_proc_session(sess); + if (sess->user) ksmbd_free_user(sess->user); @@ -220,7 +444,9 @@ static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess) { struct channel *chann; + down_write(&sess->chann_lock); chann = xa_erase(&sess->ksmbd_chann_list, (long)conn); + up_write(&sess->chann_lock); if (!chann) return -ENOENT; @@ -451,9 +677,10 @@ static struct ksmbd_session *__session_create(int protocol) xa_init(&sess->ksmbd_chann_list); xa_init(&sess->rpc_handle_list); sess->sequence_number = 1; - rwlock_init(&sess->tree_conns_lock); atomic_set(&sess->refcnt, 2); + init_rwsem(&sess->tree_conns_lock); init_rwsem(&sess->rpc_lock); + init_rwsem(&sess->chann_lock); ret = __init_smb2_session(sess); if (ret) @@ -465,6 +692,8 @@ static struct ksmbd_session *__session_create(int protocol) hash_add(sessions_table, &sess->hlist, sess->id); up_write(&sessions_table_lock); + create_proc_session(sess); + ksmbd_counter_inc(KSMBD_COUNTER_SESSIONS); return sess; error: diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h index c5749d6ec715..6aebd385be84 100644 --- a/fs/smb/server/mgmt/user_session.h +++ b/fs/smb/server/mgmt/user_session.h @@ -41,7 +41,6 @@ struct ksmbd_session { bool sign; bool enc; - bool is_anonymous; int state; __u8 *Preauth_HashValue; @@ -49,6 +48,7 @@ struct ksmbd_session { char sess_key[CIFS_KEY_SIZE]; struct hlist_node hlist; + struct rw_semaphore chann_lock; struct xarray ksmbd_chann_list; struct xarray tree_conns; struct ida tree_conn_ida; @@ -60,8 +60,11 @@ struct ksmbd_session { struct ksmbd_file_table file_table; unsigned long last_active; - rwlock_t tree_conns_lock; + struct rw_semaphore tree_conns_lock; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_entry; +#endif atomic_t refcnt; struct rw_semaphore rpc_lock; }; @@ -111,4 +114,5 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id); int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id); void ksmbd_user_session_get(struct ksmbd_session *sess); void ksmbd_user_session_put(struct ksmbd_session *sess); +int create_proc_sessions(void); #endif /* __USER_SESSION_MANAGEMENT_H__ */ diff --git a/fs/smb/server/misc.h b/fs/smb/server/misc.h index 1facfcd21200..13423696ae8c 100644 --- a/fs/smb/server/misc.h +++ b/fs/smb/server/misc.h @@ -6,6 +6,9 @@ #ifndef __KSMBD_MISC_H__ #define __KSMBD_MISC_H__ +#ifdef CONFIG_PROC_FS +#include +#endif struct ksmbd_share_config; struct nls_table; struct kstat; @@ -34,4 +37,31 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info, struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc); u64 ksmbd_UnixTimeToNT(struct timespec64 t); long long ksmbd_systime(void); + +#ifdef CONFIG_PROC_FS +struct ksmbd_const_name { + unsigned int const_value; + const char *name; +}; + +void ksmbd_proc_init(void); +void ksmbd_proc_cleanup(void); +void ksmbd_proc_reset(void); +struct proc_dir_entry *ksmbd_proc_create(const char *name, + int (*show)(struct seq_file *m, void *v), + void *v); +void ksmbd_proc_show_flag_names(struct seq_file *m, + const struct ksmbd_const_name *table, + int count, + unsigned int flags); +void ksmbd_proc_show_const_name(struct seq_file *m, + const char *format, + const struct ksmbd_const_name *table, + int count, + unsigned int const_value); +#else +static inline void ksmbd_proc_init(void) {} +static inline void ksmbd_proc_cleanup(void) {} +static inline void ksmbd_proc_reset(void) {} +#endif #endif /* __KSMBD_MISC_H__ */ diff --git a/fs/smb/server/proc.c b/fs/smb/server/proc.c new file mode 100644 index 000000000000..101a2cc45a44 --- /dev/null +++ b/fs/smb/server/proc.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2025, LG Electronics. + * Author(s): Hyunchul Lee + * Copyright (C) 2025, Samsung Electronics. + * Author(s): Vedansh Bhardwaj + */ + +#include +#include +#include + +#include "misc.h" +#include "server.h" +#include "stats.h" +#include "smb_common.h" +#include "smb2pdu.h" + +static struct proc_dir_entry *ksmbd_proc_fs; +struct ksmbd_counters ksmbd_counters; + +struct proc_dir_entry *ksmbd_proc_create(const char *name, + int (*show)(struct seq_file *m, void *v), + void *v) +{ + return proc_create_single_data(name, 0400, ksmbd_proc_fs, + show, v); +} + +struct ksmbd_const_smb2_process_req { + unsigned int const_value; + const char *name; +}; + +static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_MAX_REQS] = { + {le16_to_cpu(SMB2_NEGOTIATE), "SMB2_NEGOTIATE"}, + {le16_to_cpu(SMB2_SESSION_SETUP), "SMB2_SESSION_SETUP"}, + {le16_to_cpu(SMB2_LOGOFF), "SMB2_LOGOFF"}, + {le16_to_cpu(SMB2_TREE_CONNECT), "SMB2_TREE_CONNECT"}, + {le16_to_cpu(SMB2_TREE_DISCONNECT), "SMB2_TREE_DISCONNECT"}, + {le16_to_cpu(SMB2_CREATE), "SMB2_CREATE"}, + {le16_to_cpu(SMB2_CLOSE), "SMB2_CLOSE"}, + {le16_to_cpu(SMB2_FLUSH), "SMB2_FLUSH"}, + {le16_to_cpu(SMB2_READ), "SMB2_READ"}, + {le16_to_cpu(SMB2_WRITE), "SMB2_WRITE"}, + {le16_to_cpu(SMB2_LOCK), "SMB2_LOCK"}, + {le16_to_cpu(SMB2_IOCTL), "SMB2_IOCTL"}, + {le16_to_cpu(SMB2_CANCEL), "SMB2_CANCEL"}, + {le16_to_cpu(SMB2_ECHO), "SMB2_ECHO"}, + {le16_to_cpu(SMB2_QUERY_DIRECTORY), "SMB2_QUERY_DIRECTORY"}, + {le16_to_cpu(SMB2_CHANGE_NOTIFY), "SMB2_CHANGE_NOTIFY"}, + {le16_to_cpu(SMB2_QUERY_INFO), "SMB2_QUERY_INFO"}, + {le16_to_cpu(SMB2_SET_INFO), "SMB2_SET_INFO"}, + {le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"}, +}; + +static int proc_show_ksmbd_stats(struct seq_file *m, void *v) +{ + int i; + + seq_puts(m, "Server\n"); + seq_printf(m, "name: %s\n", ksmbd_server_string()); + seq_printf(m, "netbios: %s\n", ksmbd_netbios_name()); + seq_printf(m, "work group: %s\n", ksmbd_work_group()); + seq_printf(m, "min protocol: %s\n", ksmbd_get_protocol_string(server_conf.min_protocol)); + seq_printf(m, "max protocol: %s\n", ksmbd_get_protocol_string(server_conf.max_protocol)); + seq_printf(m, "flags: 0x%08x\n", server_conf.flags); + seq_printf(m, "share_fake_fscaps: 0x%08x\n", + server_conf.share_fake_fscaps); + seq_printf(m, "sessions: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS)); + seq_printf(m, "tree connects: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS)); + seq_printf(m, "read bytes: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES)); + seq_printf(m, "written bytes: %lld\n", + ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES)); + + seq_puts(m, "\nSMB2\n"); + for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++) + seq_printf(m, "%-20s:\t%lld\n", smb2_process_req[i].name, + ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i)); + return 0; +} + +void ksmbd_proc_cleanup(void) +{ + int i; + + if (!ksmbd_proc_fs) + return; + + proc_remove(ksmbd_proc_fs); + + for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) + percpu_counter_destroy(&ksmbd_counters.counters[i]); + + ksmbd_proc_fs = NULL; +} + +void ksmbd_proc_reset(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) + percpu_counter_set(&ksmbd_counters.counters[i], 0); +} + +void ksmbd_proc_init(void) +{ + int i; + int retval; + + ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL); + if (!ksmbd_proc_fs) + return; + + if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs)) + goto err_out; + + for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) { + retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL); + if (retval) + goto err_out; + } + + if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL)) + goto err_out; + + ksmbd_proc_reset(); + return; +err_out: + ksmbd_proc_cleanup(); +} diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c index 554ae90df906..c2c074346da1 100644 --- a/fs/smb/server/server.c +++ b/fs/smb/server/server.c @@ -21,6 +21,8 @@ #include "mgmt/user_session.h" #include "crypto_ctx.h" #include "auth.h" +#include "misc.h" +#include "stats.h" int ksmbd_debug_types; @@ -126,25 +128,27 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, andx_again: if (command >= conn->max_cmds) { conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); - return SERVER_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; } cmds = &conn->cmds[command]; if (!cmds->proc) { ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); - return SERVER_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; } if (work->sess && conn->ops->is_sign_req(work, command)) { ret = conn->ops->check_sign_req(work); if (!ret) { conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); - return SERVER_HANDLER_CONTINUE; + return SERVER_HANDLER_ABORT; } } ret = cmds->proc(work); + if (conn->ops->inc_reqs) + conn->ops->inc_reqs(command); if (ret < 0) ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); @@ -359,6 +363,7 @@ static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) { int ret; + ksmbd_proc_reset(); ret = ksmbd_conn_transport_init(); if (ret) { server_queue_ctrl_reset_work(); @@ -531,6 +536,7 @@ static int ksmbd_server_shutdown(void) { WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); + ksmbd_proc_cleanup(); class_unregister(&ksmbd_control_class); ksmbd_workqueue_destroy(); ksmbd_ipc_release(); @@ -554,6 +560,9 @@ static int __init ksmbd_server_init(void) return ret; } + ksmbd_proc_init(); + create_proc_sessions(); + ksmbd_server_tcp_callbacks_init(); ret = server_conf_init(); diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c index edd7eca0714a..c9a32ee096b5 100644 --- a/fs/smb/server/smb2ops.c +++ b/fs/smb/server/smb2ops.c @@ -11,6 +11,7 @@ #include "connection.h" #include "smb_common.h" #include "server.h" +#include "stats.h" static struct smb_version_values smb21_server_values = { .version_string = SMB21_VERSION_STRING, @@ -121,6 +122,7 @@ static struct smb_version_values smb311_server_values = { static struct smb_version_ops smb2_0_server_ops = { .get_cmd_val = get_smb2_cmd_val, + .inc_reqs = ksmbd_counter_inc_reqs, .init_rsp_hdr = init_smb2_rsp_hdr, .set_rsp_status = set_smb2_rsp_status, .allocate_rsp_buf = smb2_allocate_rsp_buf, @@ -134,6 +136,7 @@ static struct smb_version_ops smb2_0_server_ops = { static struct smb_version_ops smb3_0_server_ops = { .get_cmd_val = get_smb2_cmd_val, + .inc_reqs = ksmbd_counter_inc_reqs, .init_rsp_hdr = init_smb2_rsp_hdr, .set_rsp_status = set_smb2_rsp_status, .allocate_rsp_buf = smb2_allocate_rsp_buf, @@ -152,6 +155,7 @@ static struct smb_version_ops smb3_0_server_ops = { static struct smb_version_ops smb3_11_server_ops = { .get_cmd_val = get_smb2_cmd_val, + .inc_reqs = ksmbd_counter_inc_reqs, .init_rsp_hdr = init_smb2_rsp_hdr, .set_rsp_status = set_smb2_rsp_status, .allocate_rsp_buf = smb2_allocate_rsp_buf, diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 2fcd0d4d1fb0..cbb31efdbaa2 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -39,6 +39,7 @@ #include "mgmt/user_session.h" #include "mgmt/ksmbd_ida.h" #include "ndr.h" +#include "stats.h" #include "transport_tcp.h" static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) @@ -79,7 +80,13 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id) struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn) { - return xa_load(&sess->ksmbd_chann_list, (long)conn); + struct channel *chann; + + down_read(&sess->chann_lock); + chann = xa_load(&sess->ksmbd_chann_list, (long)conn); + up_read(&sess->chann_lock); + + return chann; } /** @@ -1558,8 +1565,10 @@ binding_session: return -ENOMEM; chann->conn = conn; + down_write(&sess->chann_lock); old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, KSMBD_DEFAULT_GFP); + up_write(&sess->chann_lock); if (xa_is_err(old)) { kfree(chann); return xa_err(old); @@ -1651,8 +1660,10 @@ binding_session: return -ENOMEM; chann->conn = conn; + down_write(&sess->chann_lock); old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann, KSMBD_DEFAULT_GFP); + up_write(&sess->chann_lock); if (xa_is_err(old)) { kfree(chann); return xa_err(old); @@ -2026,9 +2037,9 @@ int smb2_tree_connect(struct ksmbd_work *work) if (conn->posix_ext_supported) status.tree_conn->posix_extensions = true; - write_lock(&sess->tree_conns_lock); + down_write(&sess->tree_conns_lock); status.tree_conn->t_state = TREE_CONNECTED; - write_unlock(&sess->tree_conns_lock); + up_write(&sess->tree_conns_lock); rsp->StructureSize = cpu_to_le16(16); out_err1: if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && share && @@ -2182,16 +2193,16 @@ int smb2_tree_disconnect(struct ksmbd_work *work) ksmbd_close_tree_conn_fds(work); - write_lock(&sess->tree_conns_lock); + down_write(&sess->tree_conns_lock); if (tcon->t_state == TREE_DISCONNECTED) { - write_unlock(&sess->tree_conns_lock); + up_write(&sess->tree_conns_lock); rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED; err = -ENOENT; goto err_out; } tcon->t_state = TREE_DISCONNECTED; - write_unlock(&sess->tree_conns_lock); + up_write(&sess->tree_conns_lock); err = ksmbd_tree_conn_disconnect(sess, tcon); if (err) { diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index 1cd7e738434d..741aabdfcef5 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -98,6 +98,30 @@ inline int ksmbd_max_protocol(void) return SMB311_PROT; } +static const struct { + int version; + const char *string; +} version_strings[] = { +#ifdef CONFIG_SMB_INSECURE_SERVER + {SMB1_PROT, SMB1_VERSION_STRING}, +#endif + {SMB2_PROT, SMB20_VERSION_STRING}, + {SMB21_PROT, SMB21_VERSION_STRING}, + {SMB30_PROT, SMB30_VERSION_STRING}, + {SMB302_PROT, SMB302_VERSION_STRING}, + {SMB311_PROT, SMB311_VERSION_STRING}, +}; + +const char *ksmbd_get_protocol_string(int version) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(version_strings); i++) { + if (version_strings[i].version == version) + return version_strings[i].string; + } + return ""; +} int ksmbd_lookup_protocol_idx(char *str) { int offt = ARRAY_SIZE(smb1_protos) - 1; diff --git a/fs/smb/server/smb_common.h b/fs/smb/server/smb_common.h index ddd6867c50b2..ca7e3610d074 100644 --- a/fs/smb/server/smb_common.h +++ b/fs/smb/server/smb_common.h @@ -143,6 +143,7 @@ struct file_id_both_directory_info { struct smb_version_ops { u16 (*get_cmd_val)(struct ksmbd_work *swork); + void (*inc_reqs)(unsigned int cmd); int (*init_rsp_hdr)(struct ksmbd_work *swork); void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err); int (*allocate_rsp_buf)(struct ksmbd_work *work); @@ -165,6 +166,7 @@ struct smb_version_cmds { int ksmbd_min_protocol(void); int ksmbd_max_protocol(void); +const char *ksmbd_get_protocol_string(int version); int ksmbd_lookup_protocol_idx(char *str); diff --git a/fs/smb/server/stats.h b/fs/smb/server/stats.h new file mode 100644 index 000000000000..b60c30c69077 --- /dev/null +++ b/fs/smb/server/stats.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2025, LG Electronics. + * Author(s): Hyunchul Lee + * Copyright (C) 2025, Samsung Electronics. + * Author(s): Vedansh Bhardwaj + */ + +#ifndef __KSMBD_STATS_H__ +#define __KSMBD_STATS_H__ + +#define KSMBD_COUNTER_MAX_REQS 19 + +enum { + KSMBD_COUNTER_SESSIONS = 0, + KSMBD_COUNTER_TREE_CONNS, + KSMBD_COUNTER_REQUESTS, + KSMBD_COUNTER_READ_BYTES, + KSMBD_COUNTER_WRITE_BYTES, + KSMBD_COUNTER_FIRST_REQ, + KSMBD_COUNTER_LAST_REQ = KSMBD_COUNTER_FIRST_REQ + + KSMBD_COUNTER_MAX_REQS - 1, + KSMBD_COUNTER_MAX, +}; + +#ifdef CONFIG_PROC_FS +extern struct ksmbd_counters ksmbd_counters; + +struct ksmbd_counters { + struct percpu_counter counters[KSMBD_COUNTER_MAX]; +}; + +static inline void ksmbd_counter_inc(int type) +{ + percpu_counter_inc(&ksmbd_counters.counters[type]); +} + +static inline void ksmbd_counter_dec(int type) +{ + percpu_counter_dec(&ksmbd_counters.counters[type]); +} + +static inline void ksmbd_counter_add(int type, s64 value) +{ + percpu_counter_add(&ksmbd_counters.counters[type], value); +} + +static inline void ksmbd_counter_sub(int type, s64 value) +{ + percpu_counter_sub(&ksmbd_counters.counters[type], value); +} + +static inline void ksmbd_counter_inc_reqs(unsigned int cmd) +{ + if (cmd < KSMBD_COUNTER_MAX_REQS) + percpu_counter_inc(&ksmbd_counters.counters[KSMBD_COUNTER_FIRST_REQ + cmd]); +} + +static inline s64 ksmbd_counter_sum(int type) +{ + return percpu_counter_sum_positive(&ksmbd_counters.counters[type]); +} +#else + +static inline void ksmbd_counter_inc(int type) {} +static inline void ksmbd_counter_dec(int type) {} +static inline void ksmbd_counter_add(int type, s64 value) {} +static inline void ksmbd_counter_sub(int type, s64 value) {} +static inline void ksmbd_counter_inc_reqs(unsigned int cmd) {} +static inline s64 ksmbd_counter_sum(int type) { return 0; } +#endif + +#endif diff --git a/fs/smb/server/transport_rdma.c b/fs/smb/server/transport_rdma.c index e4273932e7e4..fb36fb9d5214 100644 --- a/fs/smb/server/transport_rdma.c +++ b/fs/smb/server/transport_rdma.c @@ -61,9 +61,6 @@ * Those may change after a SMB_DIRECT negotiation */ -/* Set 445 port to SMB Direct port by default */ -static int smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; - /* The local peer's maximum number of credits to grant to the peer */ static int smb_direct_receive_credit_max = 255; @@ -73,8 +70,23 @@ static int smb_direct_send_credit_target = 255; /* The maximum single message size can be sent to remote peer */ static int smb_direct_max_send_size = 1364; -/* The maximum fragmented upper-layer payload receive size supported */ -static int smb_direct_max_fragmented_recv_size = 1024 * 1024; +/* + * The maximum fragmented upper-layer payload receive size supported + * + * Assume max_payload_per_credit is + * smb_direct_receive_credit_max - 24 = 1340 + * + * The maximum number would be + * smb_direct_receive_credit_max * max_payload_per_credit + * + * 1340 * 255 = 341700 (0x536C4) + * + * The minimum value from the spec is 131072 (0x20000) + * + * For now we use the logic we used before: + * (1364 * 255) / 2 = 173910 (0x2A756) + */ +static int smb_direct_max_fragmented_recv_size = (1364 * 255) / 2; /* The maximum single-message size which can be received */ static int smb_direct_max_receive_size = 1364; @@ -90,8 +102,9 @@ struct smb_direct_device { }; static struct smb_direct_listener { + int port; struct rdma_cm_id *cm_id; -} smb_direct_listener; +} smb_direct_ib_listener, smb_direct_iw_listener; static struct workqueue_struct *smb_direct_wq; @@ -221,6 +234,7 @@ static void smb_direct_disconnect_wake_up_all(struct smbdirect_socket *sc) * in order to notice the broken connection. */ wake_up_all(&sc->status_wait); + wake_up_all(&sc->send_io.bcredits.wait_queue); wake_up_all(&sc->send_io.lcredits.wait_queue); wake_up_all(&sc->send_io.credits.wait_queue); wake_up_all(&sc->send_io.pending.zero_wait_queue); @@ -644,6 +658,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) struct smbdirect_data_transfer *data_transfer = (struct smbdirect_data_transfer *)recvmsg->packet; u32 remaining_data_length, data_offset, data_length; + int current_recv_credits; u16 old_recv_credit_target; if (wc->byte_len < @@ -682,7 +697,7 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) } atomic_dec(&sc->recv_io.posted.count); - atomic_dec(&sc->recv_io.credits.count); + current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count); old_recv_credit_target = sc->recv_io.credits.target; sc->recv_io.credits.target = @@ -702,7 +717,8 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc) wake_up(&sc->send_io.credits.wait_queue); if (data_length) { - if (sc->recv_io.credits.target > old_recv_credit_target) + if (current_recv_credits <= (sc->recv_io.credits.target / 4) || + sc->recv_io.credits.target > old_recv_credit_target) queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); enqueue_reassembly(sc, recvmsg, (int)data_length); @@ -1028,6 +1044,17 @@ static void smb_direct_post_recv_credits(struct work_struct *work) } } + atomic_add(credits, &sc->recv_io.credits.available); + + /* + * If the last send credit is waiting for credits + * it can grant we need to wake it up + */ + if (credits && + atomic_read(&sc->send_io.bcredits.count) == 0 && + atomic_read(&sc->send_io.credits.count) == 0) + wake_up(&sc->send_io.credits.wait_queue); + if (credits) queue_work(sc->workqueue, &sc->idle.immediate_work); } @@ -1045,6 +1072,31 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) ib_wc_status_msg(wc->status), wc->status, wc->opcode); + if (unlikely(!(sendmsg->wr.send_flags & IB_SEND_SIGNALED))) { + /* + * This happens when smbdirect_send_io is a sibling + * before the final message, it is signaled on + * error anyway, so we need to skip + * smbdirect_connection_free_send_io here, + * otherwise is will destroy the memory + * of the siblings too, which will cause + * use after free problems for the others + * triggered from ib_drain_qp(). + */ + if (wc->status != IB_WC_SUCCESS) + goto skip_free; + + /* + * This should not happen! + * But we better just close the + * connection... + */ + pr_err("unexpected send completion wc->status=%s (%d) wc->opcode=%d\n", + ib_wc_status_msg(wc->status), wc->status, wc->opcode); + smb_direct_disconnect_rdma_connection(sc); + return; + } + /* * Free possible siblings and then the main send_io */ @@ -1058,6 +1110,7 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) lcredits += 1; if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) { +skip_free: pr_err("Send error. status='%s (%d)', opcode=%d\n", ib_wc_status_msg(wc->status), wc->status, wc->opcode); @@ -1074,19 +1127,37 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) static int manage_credits_prior_sending(struct smbdirect_socket *sc) { + int missing; + int available; int new_credits; if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target) return 0; - new_credits = atomic_read(&sc->recv_io.posted.count); - if (new_credits == 0) + missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count); + available = atomic_xchg(&sc->recv_io.credits.available, 0); + new_credits = (u16)min3(U16_MAX, missing, available); + if (new_credits <= 0) { + /* + * If credits are available, but not granted + * we need to re-add them again. + */ + if (available) + atomic_add(available, &sc->recv_io.credits.available); return 0; + } - new_credits -= atomic_read(&sc->recv_io.credits.count); - if (new_credits <= 0) - return 0; + if (new_credits < available) { + /* + * Readd the remaining available again. + */ + available -= new_credits; + atomic_add(available, &sc->recv_io.credits.available); + } + /* + * Remember we granted the credits + */ atomic_add(new_credits, &sc->recv_io.credits.count); return new_credits; } @@ -1130,6 +1201,7 @@ static void smb_direct_send_ctx_init(struct smbdirect_send_batch *send_ctx, send_ctx->wr_cnt = 0; send_ctx->need_invalidate_rkey = need_invalidate_rkey; send_ctx->remote_key = remote_key; + send_ctx->credit = 0; } static int smb_direct_flush_send_list(struct smbdirect_socket *sc, @@ -1137,10 +1209,10 @@ static int smb_direct_flush_send_list(struct smbdirect_socket *sc, bool is_last) { struct smbdirect_send_io *first, *last; - int ret; + int ret = 0; if (list_empty(&send_ctx->msg_list)) - return 0; + goto release_credit; first = list_first_entry(&send_ctx->msg_list, struct smbdirect_send_io, @@ -1182,6 +1254,13 @@ static int smb_direct_flush_send_list(struct smbdirect_socket *sc, smb_direct_free_sendmsg(sc, last); } +release_credit: + if (is_last && !ret && send_ctx->credit) { + atomic_add(send_ctx->credit, &sc->send_io.bcredits.count); + send_ctx->credit = 0; + wake_up(&sc->send_io.bcredits.wait_queue); + } + return ret; } @@ -1207,6 +1286,25 @@ static int wait_for_credits(struct smbdirect_socket *sc, } while (true); } +static int wait_for_send_bcredit(struct smbdirect_socket *sc, + struct smbdirect_send_batch *send_ctx) +{ + int ret; + + if (send_ctx->credit) + return 0; + + ret = wait_for_credits(sc, + &sc->send_io.bcredits.wait_queue, + &sc->send_io.bcredits.count, + 1); + if (ret) + return ret; + + send_ctx->credit = 1; + return 0; +} + static int wait_for_send_lcredit(struct smbdirect_socket *sc, struct smbdirect_send_batch *send_ctx) { @@ -1256,6 +1354,7 @@ static int calc_rw_credits(struct smbdirect_socket *sc, static int smb_direct_create_header(struct smbdirect_socket *sc, int size, int remaining_data_length, + int new_credits, struct smbdirect_send_io **sendmsg_out) { struct smbdirect_socket_parameters *sp = &sc->parameters; @@ -1271,7 +1370,7 @@ static int smb_direct_create_header(struct smbdirect_socket *sc, /* Fill in the packet header */ packet = (struct smbdirect_data_transfer *)sendmsg->packet; packet->credits_requested = cpu_to_le16(sp->send_credit_target); - packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc)); + packet->credits_granted = cpu_to_le16(new_credits); packet->flags = 0; if (manage_keep_alive_before_sending(sc)) @@ -1408,6 +1507,17 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, struct smbdirect_send_io *msg; int data_length; struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1]; + struct smbdirect_send_batch _send_ctx; + int new_credits; + + if (!send_ctx) { + smb_direct_send_ctx_init(&_send_ctx, false, 0); + send_ctx = &_send_ctx; + } + + ret = wait_for_send_bcredit(sc, send_ctx); + if (ret) + goto bcredit_failed; ret = wait_for_send_lcredit(sc, send_ctx); if (ret) @@ -1417,12 +1527,29 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, if (ret) goto credit_failed; + new_credits = manage_credits_prior_sending(sc); + if (new_credits == 0 && + atomic_read(&sc->send_io.credits.count) == 0 && + atomic_read(&sc->recv_io.credits.count) == 0) { + queue_work(sc->workqueue, &sc->recv_io.posted.refill_work); + ret = wait_event_interruptible(sc->send_io.credits.wait_queue, + atomic_read(&sc->send_io.credits.count) >= 1 || + atomic_read(&sc->recv_io.credits.available) >= 1 || + sc->status != SMBDIRECT_SOCKET_CONNECTED); + if (sc->status != SMBDIRECT_SOCKET_CONNECTED) + ret = -ENOTCONN; + if (ret < 0) + goto credit_failed; + + new_credits = manage_credits_prior_sending(sc); + } + data_length = 0; for (i = 0; i < niov; i++) data_length += iov[i].iov_len; ret = smb_direct_create_header(sc, data_length, remaining_data_length, - &msg); + new_credits, &msg); if (ret) goto header_failed; @@ -1460,6 +1587,13 @@ static int smb_direct_post_send_data(struct smbdirect_socket *sc, ret = post_sendmsg(sc, send_ctx, msg); if (ret) goto err; + + if (send_ctx == &_send_ctx) { + ret = smb_direct_flush_send_list(sc, send_ctx, true); + if (ret) + goto err; + } + return 0; err: smb_direct_free_sendmsg(sc, msg); @@ -1468,6 +1602,9 @@ header_failed: credit_failed: atomic_inc(&sc->send_io.lcredits.count); lcredit_failed: + atomic_add(send_ctx->credit, &sc->send_io.bcredits.count); + send_ctx->credit = 0; +bcredit_failed: return ret; } @@ -1939,6 +2076,7 @@ static int smb_direct_send_negotiate_response(struct smbdirect_socket *sc, resp->max_fragmented_size = cpu_to_le32(sp->max_fragmented_recv_size); + atomic_set(&sc->send_io.bcredits.count, 1); sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER; sc->status = SMBDIRECT_SOCKET_CONNECTED; } @@ -2408,6 +2546,29 @@ static int smb_direct_prepare(struct ksmbd_transport *t) le32_to_cpu(req->max_receive_size)); sp->max_fragmented_send_size = le32_to_cpu(req->max_fragmented_size); + /* + * The maximum fragmented upper-layer payload receive size supported + * + * Assume max_payload_per_credit is + * smb_direct_receive_credit_max - 24 = 1340 + * + * The maximum number would be + * smb_direct_receive_credit_max * max_payload_per_credit + * + * 1340 * 255 = 341700 (0x536C4) + * + * The minimum value from the spec is 131072 (0x20000) + * + * For now we use the logic we used before: + * (1364 * 255) / 2 = 173910 (0x2A756) + * + * We need to adjust this here in case the peer + * lowered sp->max_recv_size. + * + * TODO: instead of adjusting max_fragmented_recv_size + * we should adjust the number of available buffers, + * but for now we keep the current logic. + */ sp->max_fragmented_recv_size = (sp->recv_credit_max * sp->max_recv_size) / 2; sc->recv_io.credits.target = le16_to_cpu(req->credits_requested); @@ -2495,6 +2656,7 @@ static bool rdma_frwr_is_supported(struct ib_device_attr *attrs) static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id, struct rdma_cm_event *event) { + struct smb_direct_listener *listener = new_cm_id->context; struct smb_direct_transport *t; struct smbdirect_socket *sc; struct smbdirect_socket_parameters *sp; @@ -2583,7 +2745,7 @@ static int smb_direct_handle_connect_request(struct rdma_cm_id *new_cm_id, handler = kthread_run(ksmbd_conn_handler_loop, KSMBD_TRANS(t)->conn, "ksmbd:r%u", - smb_direct_port); + listener->port); if (IS_ERR(handler)) { ret = PTR_ERR(handler); pr_err("Can't start thread\n"); @@ -2620,39 +2782,73 @@ static int smb_direct_listen_handler(struct rdma_cm_id *cm_id, return 0; } -static int smb_direct_listen(int port) +static int smb_direct_listen(struct smb_direct_listener *listener, + int port) { int ret; struct rdma_cm_id *cm_id; + u8 node_type = RDMA_NODE_UNSPECIFIED; struct sockaddr_in sin = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), .sin_port = htons(port), }; + switch (port) { + case SMB_DIRECT_PORT_IWARP: + /* + * only allow iWarp devices + * for port 5445. + */ + node_type = RDMA_NODE_RNIC; + break; + case SMB_DIRECT_PORT_INFINIBAND: + /* + * only allow InfiniBand, RoCEv1 or RoCEv2 + * devices for port 445. + * + * (Basically don't allow iWarp devices) + */ + node_type = RDMA_NODE_IB_CA; + break; + default: + pr_err("unsupported smbdirect port=%d!\n", port); + return -ENODEV; + } + cm_id = rdma_create_id(&init_net, smb_direct_listen_handler, - &smb_direct_listener, RDMA_PS_TCP, IB_QPT_RC); + listener, RDMA_PS_TCP, IB_QPT_RC); if (IS_ERR(cm_id)) { pr_err("Can't create cm id: %ld\n", PTR_ERR(cm_id)); return PTR_ERR(cm_id); } + ret = rdma_restrict_node_type(cm_id, node_type); + if (ret) { + pr_err("rdma_restrict_node_type(%u) failed %d\n", + node_type, ret); + goto err; + } + ret = rdma_bind_addr(cm_id, (struct sockaddr *)&sin); if (ret) { pr_err("Can't bind: %d\n", ret); goto err; } - smb_direct_listener.cm_id = cm_id; - ret = rdma_listen(cm_id, 10); if (ret) { pr_err("Can't listen: %d\n", ret); goto err; } + + listener->port = port; + listener->cm_id = cm_id; + return 0; err: - smb_direct_listener.cm_id = NULL; + listener->port = 0; + listener->cm_id = NULL; rdma_destroy_id(cm_id); return ret; } @@ -2661,10 +2857,6 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev) { struct smb_direct_device *smb_dev; - /* Set 5445 port if device type is iWARP(No IB) */ - if (ib_dev->node_type != RDMA_NODE_IB_CA) - smb_direct_port = SMB_DIRECT_PORT_IWARP; - if (!rdma_frwr_is_supported(&ib_dev->attrs)) return 0; @@ -2707,8 +2899,9 @@ int ksmbd_rdma_init(void) { int ret; - smb_direct_port = SMB_DIRECT_PORT_INFINIBAND; - smb_direct_listener.cm_id = NULL; + smb_direct_ib_listener = smb_direct_iw_listener = (struct smb_direct_listener) { + .cm_id = NULL, + }; ret = ib_register_client(&smb_direct_ib_client); if (ret) { @@ -2724,31 +2917,53 @@ int ksmbd_rdma_init(void) smb_direct_wq = alloc_workqueue("ksmbd-smb_direct-wq", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU, 0); - if (!smb_direct_wq) - return -ENOMEM; - - ret = smb_direct_listen(smb_direct_port); - if (ret) { - destroy_workqueue(smb_direct_wq); - smb_direct_wq = NULL; - pr_err("Can't listen: %d\n", ret); - return ret; + if (!smb_direct_wq) { + ret = -ENOMEM; + goto err; } - ksmbd_debug(RDMA, "init RDMA listener. cm_id=%p\n", - smb_direct_listener.cm_id); + ret = smb_direct_listen(&smb_direct_ib_listener, + SMB_DIRECT_PORT_INFINIBAND); + if (ret) { + pr_err("Can't listen on InfiniBand/RoCEv1/RoCEv2: %d\n", ret); + goto err; + } + + ksmbd_debug(RDMA, "InfiniBand/RoCEv1/RoCEv2 RDMA listener. cm_id=%p\n", + smb_direct_ib_listener.cm_id); + + ret = smb_direct_listen(&smb_direct_iw_listener, + SMB_DIRECT_PORT_IWARP); + if (ret) { + pr_err("Can't listen on iWarp: %d\n", ret); + goto err; + } + + ksmbd_debug(RDMA, "iWarp RDMA listener. cm_id=%p\n", + smb_direct_iw_listener.cm_id); + return 0; +err: + ksmbd_rdma_stop_listening(); + ksmbd_rdma_destroy(); + return ret; } void ksmbd_rdma_stop_listening(void) { - if (!smb_direct_listener.cm_id) + if (!smb_direct_ib_listener.cm_id && !smb_direct_iw_listener.cm_id) return; ib_unregister_client(&smb_direct_ib_client); - rdma_destroy_id(smb_direct_listener.cm_id); - smb_direct_listener.cm_id = NULL; + if (smb_direct_ib_listener.cm_id) + rdma_destroy_id(smb_direct_ib_listener.cm_id); + if (smb_direct_iw_listener.cm_id) + rdma_destroy_id(smb_direct_iw_listener.cm_id); + + smb_direct_ib_listener = smb_direct_iw_listener = (struct smb_direct_listener) { + .cm_id = NULL, + }; } void ksmbd_rdma_destroy(void) diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c index 4bb07937d7ef..2436dabada95 100644 --- a/fs/smb/server/transport_tcp.c +++ b/fs/smb/server/transport_tcp.c @@ -40,6 +40,7 @@ static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops; static void tcp_stop_kthread(struct task_struct *kthread); static struct interface *alloc_iface(char *ifname); +static void ksmbd_tcp_disconnect(struct ksmbd_transport *t); #define KSMBD_TRANS(t) (&(t)->transport) #define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \ @@ -202,7 +203,7 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk) if (IS_ERR(handler)) { pr_err("cannot start conn thread\n"); rc = PTR_ERR(handler); - free_transport(t); + ksmbd_tcp_disconnect(KSMBD_TRANS(t)); } return rc; } diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index fd0a5b2fb6c1..fbdc854dba0f 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -31,6 +31,7 @@ #include "ndr.h" #include "auth.h" #include "misc.h" +#include "stats.h" #include "smb_common.h" #include "mgmt/share_config.h" @@ -380,6 +381,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, } filp->f_pos = *pos; + ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, (s64)nbytes); return nbytes; } @@ -517,6 +519,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, pr_err("fsync failed for filename = %pD, err = %d\n", fp->filp, err); } + ksmbd_counter_add(KSMBD_COUNTER_WRITE_BYTES, (s64)*written); out: return err; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 6ef116585af6..e302e403e55a 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -16,10 +16,12 @@ #include "oplock.h" #include "vfs.h" #include "connection.h" +#include "misc.h" #include "mgmt/tree_connect.h" #include "mgmt/user_session.h" #include "smb_common.h" #include "server.h" +#include "smb2pdu.h" #define S_DEL_PENDING 1 #define S_DEL_ON_CLS 2 @@ -34,6 +36,97 @@ static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; static struct kmem_cache *filp_cache; +#define OPLOCK_NONE 0 +#define OPLOCK_EXCLUSIVE 1 +#define OPLOCK_BATCH 2 +#define OPLOCK_READ 3 /* level 2 oplock */ + +#ifdef CONFIG_PROC_FS + +static const struct ksmbd_const_name ksmbd_lease_const_names[] = { + {le32_to_cpu(SMB2_LEASE_NONE_LE), "LEASE_NONE"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE), "LEASE_R"}, + {le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_H"}, + {le32_to_cpu(SMB2_LEASE_WRITE_CACHING_LE), "LEASE_W"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_RH"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RW"}, + {le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE), "LEASE_WH"}, + {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RWH"}, +}; + +static const struct ksmbd_const_name ksmbd_oplock_const_names[] = { + {SMB2_OPLOCK_LEVEL_NONE, "OPLOCK_NONE"}, + {SMB2_OPLOCK_LEVEL_II, "OPLOCK_II"}, + {SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXECL"}, + {SMB2_OPLOCK_LEVEL_BATCH, "OPLOCK_BATCH"}, +}; + +static int proc_show_files(struct seq_file *m, void *v) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + struct oplock_info *opinfo; + + seq_printf(m, "#%-10s %-10s %-10s %-10s %-15s %-10s %-10s %s\n", + "", "", "", "", + "", "", "", + ""); + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + seq_printf(m, "%#-10x %#-10llx %#-10llx %#-10x", + fp->tcon->id, + fp->persistent_id, + fp->volatile_id, + atomic_read(&fp->refcount)); + + rcu_read_lock(); + opinfo = rcu_dereference(fp->f_opinfo); + rcu_read_unlock(); + + if (!opinfo) { + seq_printf(m, " %-15s", " "); + } else { + const struct ksmbd_const_name *const_names; + int count; + unsigned int level; + + if (opinfo->is_lease) { + const_names = ksmbd_lease_const_names; + count = ARRAY_SIZE(ksmbd_lease_const_names); + level = le32_to_cpu(opinfo->o_lease->state); + } else { + const_names = ksmbd_oplock_const_names; + count = ARRAY_SIZE(ksmbd_oplock_const_names); + level = opinfo->level; + } + ksmbd_proc_show_const_name(m, " %-15s", + const_names, count, level); + } + + seq_printf(m, " %#010x %#010x %s\n", + le32_to_cpu(fp->daccess), + le32_to_cpu(fp->saccess), + fp->filp->f_path.dentry->d_name.name); + } + read_unlock(&global_ft.lock); + return 0; +} + +static int create_proc_files(void) +{ + ksmbd_proc_create("files", proc_show_files, NULL); + return 0; +} +#else +static int create_proc_files(void) { return 0; } +#endif + static bool durable_scavenger_running; static DEFINE_MUTEX(durable_scavenger_lock); static wait_queue_head_t dh_wq; @@ -949,6 +1042,7 @@ void ksmbd_close_session_fds(struct ksmbd_work *work) int ksmbd_init_global_file_table(void) { + create_proc_files(); return ksmbd_init_file_table(&global_ft); } diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index 9bd930a83e6e..6de6fd8bd15e 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h @@ -168,6 +168,23 @@ struct rdma_cm_id *rdma_create_user_id(rdma_cm_event_handler event_handler, */ void rdma_destroy_id(struct rdma_cm_id *id); +/** + * rdma_restrict_node_type - Restrict an RDMA identifier to specific + * RDMA device node type. + * + * @id: RDMA identifier. + * @node_type: The device node type. Only RDMA_NODE_UNSPECIFIED (default), + * RDMA_NODE_RNIC and RDMA_NODE_IB_CA are allowed + * + * This allows the caller to restrict the possible devices + * used to iWarp (RDMA_NODE_RNIC) or InfiniBand/RoCEv1/RoCEv2 (RDMA_NODE_IB_CA). + * + * It needs to be called before the RDMA identifier is bound + * to an device, which mean it should be called before + * rdma_bind_addr(), rdma_bind_addr() and rdma_listen(). + */ +int rdma_restrict_node_type(struct rdma_cm_id *id, u8 node_type); + /** * rdma_bind_addr - Bind an RDMA identifier to a source address and * associated RDMA device, if needed.