| /* |
| * isert_pdu.c |
| * |
| * This file is part of iser target kernel module. |
| * |
| * Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved. |
| * Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com) |
| * |
| * This software is available to you under a choice of one of two |
| * licenses. You may choose to be licensed under the terms of the GNU |
| * General Public License (GPL) Version 2, available from the file |
| * COPYING in the main directory of this source tree, or the |
| * OpenIB.org BSD license below: |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials |
| * provided with the distribution. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <linux/kernel.h> |
| |
| #include "isert_dbg.h" |
| #include "iser.h" |
| #include "../iscsi.h" |
| #include "iser_datamover.h" |
| |
| static inline int isert_pdu_rx_buf_init(struct isert_cmnd *isert_pdu, |
| struct isert_connection *isert_conn) |
| { |
| struct isert_buf *isert_buf = &isert_pdu->buf; |
| |
| return isert_wr_init(&isert_pdu->wr[0], ISER_WR_RECV, isert_buf, |
| isert_conn, isert_pdu, isert_pdu->sg_pool, |
| 0, isert_buf->sg_cnt, 0); |
| } |
| |
| static inline int isert_pdu_tx_buf_init(struct isert_cmnd *isert_pdu, |
| struct isert_connection *isert_conn) |
| { |
| struct isert_buf *isert_buf = &isert_pdu->buf; |
| |
| return isert_wr_init(&isert_pdu->wr[0], ISER_WR_SEND, isert_buf, |
| isert_conn, isert_pdu, isert_pdu->sg_pool, |
| 0, isert_buf->sg_cnt, 0); |
| } |
| |
| static inline void isert_pdu_set_hdr_plain(struct isert_cmnd *isert_pdu) |
| { |
| struct isert_hdr *isert_hdr = isert_pdu->isert_hdr; |
| |
| isert_hdr->flags = ISER_ISCSI_CTRL; |
| isert_hdr->write_stag = 0; |
| isert_hdr->write_va = 0; |
| isert_hdr->read_stag = 0; |
| isert_hdr->read_va = 0; |
| } |
| |
| /* rx pdu should be initialized to get the posted buffer and |
| * the associated pointers right; after a pdu is received |
| * it should be parsed to setup isert_cmnd + iscsi_cmnd in full |
| */ |
| static int isert_rx_pdu_init(struct isert_cmnd *isert_pdu, |
| struct isert_connection *isert_conn) |
| { |
| struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; |
| int err = isert_pdu_rx_buf_init(isert_pdu, isert_conn); |
| |
| if (unlikely(err < 0)) |
| return err; |
| iscsi_cmnd->conn = &isert_conn->iscsi; |
| return 0; |
| } |
| |
| void isert_tx_pdu_init_iscsi(struct isert_cmnd *isert_pdu) |
| { |
| struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; |
| struct isert_buf *isert_buf = &isert_pdu->buf; |
| |
| memset(iscsi_cmnd, 0, sizeof(*iscsi_cmnd)); |
| |
| iscsi_cmnd->sg_cnt = isert_buf->sg_cnt; |
| iscsi_cmnd->sg = isert_buf->sg; |
| iscsi_cmnd->bufflen = isert_buf->size; |
| } |
| |
| /* tx pdu should set most of the pointers to enable filling out |
| * of the iscsi pdu struct |
| */ |
| void isert_tx_pdu_init(struct isert_cmnd *isert_pdu, |
| struct isert_connection *isert_conn) |
| { |
| struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi; |
| struct isert_buf *isert_buf = &isert_pdu->buf; |
| void *addr = isert_buf->addr; |
| struct iscsi_hdr *bhs = (struct iscsi_hdr *)(addr + sizeof(struct isert_hdr)); |
| |
| isert_pdu->isert_hdr = (struct isert_hdr *)addr; |
| isert_pdu->bhs = bhs; |
| isert_pdu->ahs = NULL; |
| |
| isert_tx_pdu_init_iscsi(isert_pdu); |
| iscsi_cmnd->conn = &isert_conn->iscsi; |
| } |
| |
| void isert_tx_pdu_convert_from_iscsi(struct isert_cmnd *isert_cmnd, |
| struct iscsi_cmnd *iscsi_cmnd) |
| { |
| struct iscsi_pdu *iscsi_pdu = &iscsi_cmnd->pdu; |
| |
| TRACE_ENTRY(); |
| |
| memcpy(isert_cmnd->bhs, &iscsi_pdu->bhs, sizeof(*isert_cmnd->bhs)); |
| if (unlikely(iscsi_pdu->ahssize)) { |
| isert_cmnd->ahs = isert_cmnd->bhs + 1; |
| memcpy(isert_cmnd->ahs, iscsi_pdu->ahs, iscsi_pdu->ahssize); |
| } |
| |
| #ifdef CONFIG_SCST_EXTRACHECKS |
| if (iscsi_cmnd->bufflen) |
| EXTRACHECKS_BUG_ON(!iscsi_cmnd->sg); |
| #endif |
| |
| TRACE_EXIT(); |
| return; |
| } |
| |
| static inline int isert_pdu_prepare_send(struct isert_connection *isert_conn, |
| struct isert_cmnd *tx_pdu) |
| { |
| struct isert_device *isert_dev = isert_conn->isert_dev; |
| struct ib_sge *sge = tx_pdu->wr[0].sge_list; |
| size_t to_sync, size; |
| int sg_cnt = 0; |
| |
| size = ISER_HDRS_SZ + tx_pdu->iscsi.pdu.ahssize + |
| tx_pdu->iscsi.pdu.datasize; |
| while (size) { |
| to_sync = size > PAGE_SIZE ? PAGE_SIZE : size; |
| ib_dma_sync_single_for_device(isert_dev->ib_dev, sge->addr, |
| to_sync, |
| DMA_TO_DEVICE); |
| |
| sge->length = to_sync; |
| size -= to_sync; |
| ++sge; |
| ++sg_cnt; |
| } |
| |
| return sg_cnt; |
| } |
| |
| static int isert_alloc_for_rdma(struct isert_cmnd *pdu, int sge_cnt, |
| struct isert_connection *isert_conn) |
| { |
| struct isert_wr *wr; |
| struct ib_sge *sg_pool; |
| int i, ret = 0; |
| int wr_cnt; |
| |
| sg_pool = kmalloc_array(sge_cnt, sizeof(*sg_pool), GFP_KERNEL); |
| if (unlikely(sg_pool == NULL)) { |
| ret = -ENOMEM; |
| goto out; |
| } |
| |
| wr_cnt = DIV_ROUND_UP(sge_cnt, isert_conn->max_sge); |
| wr = kmalloc_array(wr_cnt, sizeof(*wr), GFP_KERNEL); |
| if (unlikely(wr == NULL)) { |
| ret = -ENOMEM; |
| goto out_free_sg_pool; |
| } |
| |
| kfree(pdu->wr); |
| pdu->wr = wr; |
| |
| kfree(pdu->sg_pool); |
| pdu->sg_pool = sg_pool; |
| |
| pdu->n_wr = wr_cnt; |
| pdu->n_sge = sge_cnt; |
| |
| for (i = 0; i < wr_cnt; ++i) |
| isert_wr_set_fields(&pdu->wr[i], isert_conn, pdu); |
| |
| for (i = 0; i < sge_cnt; ++i) |
| pdu->sg_pool[i].lkey = isert_conn->isert_dev->lkey; |
| |
| goto out; |
| |
| out_free_sg_pool: |
| kfree(sg_pool); |
| out: |
| return ret; |
| } |
| |
| static inline void isert_link_send_wrs(struct isert_wr *from_wr, |
| struct isert_wr *to_wr) |
| { |
| #ifdef USE_PRE_440_WR_STRUCTURE |
| from_wr->send_wr.next = &to_wr->send_wr; |
| #else |
| from_wr->send_wr.wr.next = &to_wr->send_wr.wr; |
| #endif |
| } |
| |
| static inline void isert_link_send_pdu_wrs(struct isert_cmnd *from_pdu, |
| struct isert_cmnd *to_pdu, |
| int wr_cnt) |
| { |
| isert_link_send_wrs(&from_pdu->wr[wr_cnt - 1], &to_pdu->wr[0]); |
| #ifdef USE_PRE_440_WR_STRUCTURE |
| to_pdu->wr[0].send_wr.next = NULL; |
| #else |
| to_pdu->wr[0].send_wr.wr.next = NULL; |
| #endif |
| } |
| |
| int isert_prepare_rdma(struct isert_cmnd *isert_pdu, |
| struct isert_connection *isert_conn, |
| enum isert_wr_op op) |
| { |
| struct isert_buf *isert_buf = &isert_pdu->rdma_buf; |
| struct isert_device *isert_dev = isert_conn->isert_dev; |
| struct ib_device *ib_dev = isert_dev->ib_dev; |
| int err; |
| int buff_offset; |
| int sg_offset, sg_cnt; |
| int wr_cnt, i; |
| |
| isert_buf_init_sg(isert_buf, isert_pdu->iscsi.sg, |
| isert_pdu->iscsi.sg_cnt, |
| isert_pdu->iscsi.bufflen); |
| |
| if (op == ISER_WR_RDMA_WRITE) |
| isert_buf->dma_dir = DMA_TO_DEVICE; |
| else |
| isert_buf->dma_dir = DMA_FROM_DEVICE; |
| |
| if (unlikely(isert_buf->sg_cnt > isert_pdu->n_sge)) { |
| wr_cnt = isert_alloc_for_rdma(isert_pdu, isert_buf->sg_cnt, |
| isert_conn); |
| if (unlikely(wr_cnt)) |
| goto out; |
| } |
| |
| err = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, |
| isert_buf->dma_dir); |
| if (unlikely(!err)) { |
| PRINT_ERROR("Failed to DMA map iser sg:%p len:%d", |
| isert_buf->sg, isert_buf->sg_cnt); |
| wr_cnt = -EFAULT; |
| goto out; |
| } |
| |
| buff_offset = 0; |
| sg_cnt = 0; |
| for (wr_cnt = 0, sg_offset = 0; sg_offset < isert_buf->sg_cnt; |
| ++wr_cnt) { |
| sg_cnt = min((int)isert_conn->max_sge, |
| isert_buf->sg_cnt - sg_offset); |
| err = isert_wr_init(&isert_pdu->wr[wr_cnt], op, isert_buf, |
| isert_conn, isert_pdu, isert_pdu->sg_pool, |
| sg_offset, sg_cnt, buff_offset); |
| if (unlikely(err < 0)) { |
| wr_cnt = err; |
| goto out; |
| } |
| buff_offset = err; |
| sg_offset += sg_cnt; |
| } |
| |
| for (i = 1; i < wr_cnt; ++i) |
| isert_link_send_wrs(&isert_pdu->wr[i - 1], &isert_pdu->wr[i]); |
| |
| if (op == ISER_WR_RDMA_READ) { |
| #ifdef USE_PRE_440_WR_STRUCTURE |
| isert_pdu->wr[wr_cnt - 1].send_wr.send_flags = IB_SEND_SIGNALED; |
| isert_pdu->wr[wr_cnt - 1].send_wr.next = NULL; |
| #else |
| isert_pdu->wr[wr_cnt - 1].send_wr.wr.send_flags = |
| IB_SEND_SIGNALED; |
| isert_pdu->wr[wr_cnt - 1].send_wr.wr.next = NULL; |
| #endif |
| } |
| |
| out: |
| TRACE_EXIT_RES(wr_cnt); |
| return wr_cnt; |
| } |
| |
| void isert_pdu_free(struct isert_cmnd *pdu) |
| { |
| int i; |
| |
| list_del(&pdu->pool_node); |
| for (i = 0; i < pdu->n_wr; ++i) |
| isert_wr_release(&pdu->wr[i]); |
| |
| kfree(pdu->wr); |
| pdu->wr = NULL; |
| |
| kfree(pdu->sg_pool); |
| pdu->sg_pool = NULL; |
| |
| isert_pdu_kfree(pdu); |
| } |
| |
| struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn, |
| size_t size) |
| { |
| struct isert_cmnd *pdu = NULL; |
| int err; |
| |
| TRACE_ENTRY(); |
| |
| pdu = isert_pdu_alloc(); |
| if (unlikely(!pdu)) { |
| PRINT_ERROR("Failed to alloc pdu"); |
| goto out; |
| } |
| |
| err = isert_alloc_for_rdma(pdu, 4, isert_conn); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to alloc sge and wr for rx pdu"); |
| goto out; |
| } |
| |
| err = isert_buf_alloc_data_buf(isert_conn->isert_dev->ib_dev, |
| &pdu->buf, size, DMA_FROM_DEVICE); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to alloc rx pdu buf sz:%zd", size); |
| goto buf_alloc_failed; |
| } |
| |
| err = isert_rx_pdu_init(pdu, isert_conn); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to init rx pdu wr:%p size:%zd err:%d", |
| &pdu->wr, size, err); |
| goto pdu_init_failed; |
| } |
| |
| list_add_tail(&pdu->pool_node, &isert_conn->rx_buf_list); |
| |
| goto out; |
| |
| pdu_init_failed: |
| isert_buf_release(&pdu->buf); |
| buf_alloc_failed: |
| isert_pdu_kfree(pdu); |
| pdu = NULL; |
| out: |
| TRACE_EXIT(); |
| return pdu; |
| } |
| |
| struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn, |
| size_t size) |
| { |
| struct isert_cmnd *pdu = NULL; |
| int err; |
| |
| TRACE_ENTRY(); |
| |
| pdu = isert_pdu_alloc(); |
| if (unlikely(!pdu)) { |
| PRINT_ERROR("Failed to alloc pdu"); |
| goto out; |
| } |
| |
| err = isert_alloc_for_rdma(pdu, 4, isert_conn); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to alloc sge and wr for tx pdu"); |
| goto out; |
| } |
| |
| err = isert_buf_alloc_data_buf(isert_conn->isert_dev->ib_dev, |
| &pdu->buf, size, DMA_TO_DEVICE); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to alloc tx pdu buf sz:%zd", size); |
| goto buf_alloc_failed; |
| } |
| |
| err = isert_pdu_tx_buf_init(pdu, isert_conn); |
| if (unlikely(err < 0)) { |
| PRINT_ERROR("Failed to init tx pdu wr:%p size:%zd err:%d", |
| &pdu->wr, size, err); |
| goto buf_init_failed; |
| } |
| |
| isert_tx_pdu_init(pdu, isert_conn); |
| |
| isert_pdu_set_hdr_plain(pdu); |
| |
| list_add_tail(&pdu->pool_node, &isert_conn->tx_free_list); |
| |
| goto out; |
| |
| buf_init_failed: |
| isert_buf_release(&pdu->buf); |
| buf_alloc_failed: |
| isert_pdu_kfree(pdu); |
| pdu = NULL; |
| out: |
| TRACE_EXIT(); |
| return pdu; |
| } |
| |
| static inline void isert_link_recv_wrs(struct isert_wr *from_wr, |
| struct isert_wr *to_wr) |
| { |
| from_wr->recv_wr.next = &to_wr->recv_wr; |
| |
| to_wr->recv_wr.next = NULL; |
| } |
| |
| static inline void isert_link_recv_pdu_wrs(struct isert_cmnd *from_pdu, |
| struct isert_cmnd *to_pdu) |
| { |
| isert_link_recv_wrs(&from_pdu->wr[0], &to_pdu->wr[0]); |
| } |
| |
| int isert_alloc_conn_resources(struct isert_connection *isert_conn) |
| { |
| struct isert_cmnd *pdu, *prev_pdu = NULL, *first_pdu = NULL; |
| /* RFC states that minimum receive data size is 512 */ |
| int t_datasz = 512; |
| int i_datasz = ISER_HDRS_SZ + SCST_SENSE_BUFFERSIZE; |
| int i, err = 0; |
| int to_alloc; |
| |
| TRACE_ENTRY(); |
| |
| isert_conn->repost_threshold = 32; |
| to_alloc = isert_conn->queue_depth * 2 + isert_conn->repost_threshold; |
| |
| if (unlikely(to_alloc > ISER_MAX_WCE)) { |
| PRINT_ERROR("QueuedCommands larger than %d not supported", |
| (ISER_MAX_WCE - isert_conn->repost_threshold) / 2); |
| err = -EINVAL; |
| goto out; |
| } |
| |
| for (i = 0; i < to_alloc; i++) { |
| pdu = isert_rx_pdu_alloc(isert_conn, t_datasz); |
| if (unlikely(!pdu)) { |
| err = -ENOMEM; |
| goto clean_pdus; |
| } |
| |
| if (unlikely(first_pdu == NULL)) |
| first_pdu = pdu; |
| else |
| isert_link_recv_pdu_wrs(prev_pdu, pdu); |
| |
| prev_pdu = pdu; |
| |
| pdu = isert_tx_pdu_alloc(isert_conn, i_datasz); |
| if (unlikely(!pdu)) { |
| err = -ENOMEM; |
| goto clean_pdus; |
| } |
| } |
| |
| err = isert_post_recv(isert_conn, &first_pdu->wr[0], to_alloc); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to post recv err:%d", err); |
| goto clean_pdus; |
| } |
| |
| out: |
| TRACE_EXIT_RES(err); |
| return err; |
| |
| clean_pdus: |
| isert_free_conn_resources(isert_conn); |
| goto out; |
| } |
| |
| static int isert_reinit_rx_pdu(struct isert_cmnd *pdu) |
| { |
| struct isert_connection *isert_conn = container_of(pdu->iscsi.conn, |
| struct isert_connection, iscsi); |
| |
| pdu->is_rstag_valid = 0; |
| pdu->is_wstag_valid = 0; |
| |
| memset(&pdu->iscsi, 0, sizeof(pdu->iscsi)); |
| |
| return isert_rx_pdu_init(pdu, isert_conn); |
| } |
| |
| int isert_rx_pdu_done(struct isert_cmnd *pdu) |
| { |
| int err; |
| struct isert_connection *isert_conn = container_of(pdu->iscsi.conn, |
| struct isert_connection, iscsi); |
| |
| TRACE_ENTRY(); |
| |
| err = isert_reinit_rx_pdu(pdu); |
| if (unlikely(err)) |
| goto out; |
| |
| spin_lock(&isert_conn->post_recv_lock); |
| if (unlikely(isert_conn->to_post_recv == 0)) |
| isert_conn->post_recv_first = &pdu->wr[0]; |
| else |
| isert_link_recv_wrs(isert_conn->post_recv_curr, &pdu->wr[0]); |
| |
| isert_conn->post_recv_curr = &pdu->wr[0]; |
| |
| if (++isert_conn->to_post_recv > isert_conn->repost_threshold) { |
| err = isert_post_recv(isert_conn, isert_conn->post_recv_first, |
| isert_conn->to_post_recv); |
| isert_conn->to_post_recv = 0; |
| } |
| spin_unlock(&isert_conn->post_recv_lock); |
| |
| out: |
| TRACE_EXIT_RES(err); |
| return err; |
| } |
| |
| void isert_free_conn_resources(struct isert_connection *isert_conn) |
| { |
| struct isert_cmnd *pdu; |
| |
| TRACE_ENTRY(); |
| |
| if (isert_conn->login_rsp_pdu) { |
| isert_pdu_free(isert_conn->login_rsp_pdu); |
| isert_conn->login_rsp_pdu = NULL; |
| } |
| if (isert_conn->login_req_pdu) { |
| isert_pdu_free(isert_conn->login_req_pdu); |
| isert_conn->login_req_pdu = NULL; |
| } |
| |
| while (!list_empty(&isert_conn->rx_buf_list)) { |
| pdu = list_first_entry(&isert_conn->rx_buf_list, |
| struct isert_cmnd, pool_node); |
| isert_pdu_free(pdu); /* releases buffer as well */ |
| } |
| |
| spin_lock(&isert_conn->tx_lock); |
| while (!list_empty(&isert_conn->tx_free_list)) { |
| pdu = list_first_entry(&isert_conn->tx_free_list, |
| struct isert_cmnd, pool_node); |
| isert_pdu_free(pdu); /* releases buffer as well */ |
| } |
| |
| while (!list_empty(&isert_conn->tx_busy_list)) { |
| pdu = list_first_entry(&isert_conn->tx_busy_list, |
| struct isert_cmnd, pool_node); |
| isert_pdu_free(pdu); /* releases buffer as well */ |
| } |
| spin_unlock(&isert_conn->tx_lock); |
| |
| TRACE_EXIT(); |
| } |
| |
| int isert_pdu_send(struct isert_connection *isert_conn, |
| struct isert_cmnd *tx_pdu) |
| { |
| int err; |
| struct isert_wr *wr; |
| |
| TRACE_ENTRY(); |
| |
| #ifdef CONFIG_SCST_EXTRACHECKS |
| EXTRACHECKS_BUG_ON(!isert_conn); |
| EXTRACHECKS_BUG_ON(!tx_pdu); |
| #endif |
| |
| wr = &tx_pdu->wr[0]; |
| #ifdef USE_PRE_440_WR_STRUCTURE |
| wr->send_wr.num_sge = isert_pdu_prepare_send(isert_conn, tx_pdu); |
| #else |
| wr->send_wr.wr.num_sge = isert_pdu_prepare_send(isert_conn, tx_pdu); |
| #endif |
| |
| err = isert_post_send(isert_conn, wr, 1); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to send pdu conn:%p pdu:%p err:%d", |
| isert_conn, tx_pdu, err); |
| } |
| |
| TRACE_EXIT_RES(err); |
| return err; |
| } |
| |
| int isert_pdu_post_rdma_write(struct isert_connection *isert_conn, |
| struct isert_cmnd *isert_cmd, |
| struct isert_cmnd *isert_rsp, |
| int wr_cnt) |
| { |
| int err; |
| |
| TRACE_ENTRY(); |
| |
| #ifdef USE_PRE_440_WR_STRUCTURE |
| isert_rsp->wr[0].send_wr.num_sge = isert_pdu_prepare_send(isert_conn, |
| isert_rsp); |
| #else |
| isert_rsp->wr[0].send_wr.wr.num_sge = isert_pdu_prepare_send(isert_conn, |
| isert_rsp); |
| #endif |
| isert_link_send_pdu_wrs(isert_cmd, isert_rsp, wr_cnt); |
| err = isert_post_send(isert_conn, &isert_cmd->wr[0], wr_cnt + 1); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to send pdu conn:%p pdu:%p err:%d", |
| isert_conn, isert_cmd, err); |
| } |
| |
| TRACE_EXIT_RES(err); |
| return err; |
| } |
| |
| int isert_pdu_post_rdma_read(struct isert_connection *isert_conn, |
| struct isert_cmnd *isert_cmd, int wr_cnt) |
| { |
| int err; |
| |
| TRACE_ENTRY(); |
| |
| err = isert_post_send(isert_conn, &isert_cmd->wr[0], wr_cnt); |
| if (unlikely(err)) { |
| PRINT_ERROR("Failed to send pdu conn:%p pdu:%p err:%d", |
| isert_conn, isert_cmd, err); |
| } |
| |
| TRACE_EXIT_RES(err); |
| return err; |
| } |
| |