/* * isert_buf.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 #include "isert_dbg.h" #include "iser.h" static int isert_buf_alloc_pg(struct ib_device *ib_dev, struct isert_buf *isert_buf, size_t size, enum dma_data_direction dma_dir) { int res = 0; int i; struct page *page; isert_buf->sg_cnt = DIV_ROUND_UP(size, PAGE_SIZE); isert_buf->sg = kmalloc_array(isert_buf->sg_cnt, sizeof(*isert_buf->sg), GFP_KERNEL); if (unlikely(!isert_buf->sg)) { PRINT_ERROR("Failed to allocate buffer SG"); res = -ENOMEM; goto out; } sg_init_table(isert_buf->sg, isert_buf->sg_cnt); for (i = 0; i < isert_buf->sg_cnt; ++i) { size_t page_len = min_t(size_t, size, PAGE_SIZE); page = alloc_page(GFP_KERNEL); if (unlikely(!page)) { PRINT_ERROR("Failed to allocate page"); res = -ENOMEM; goto out_map_failed; } sg_set_page(&isert_buf->sg[i], page, page_len, 0); size -= page_len; } res = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, dma_dir); if (unlikely(!res)) { --i; /* do not overrun isert_buf->sg */ PRINT_ERROR("Failed to DMA map iser sg:%p len:%d", isert_buf->sg, isert_buf->sg_cnt); res = -ENOMEM; goto out_map_failed; } isert_buf->addr = sg_virt(&isert_buf->sg[0]); res = 0; goto out; out_map_failed: for (; i >= 0; --i) __free_page(sg_page(&isert_buf->sg[i])); kfree(isert_buf->sg); isert_buf->sg = NULL; out: return res; } static void isert_buf_release_pg(struct isert_buf *isert_buf) { int i; for (i = 0; i < isert_buf->sg_cnt; ++i) __free_page(sg_page(&isert_buf->sg[i])); } static int isert_buf_malloc(struct ib_device *ib_dev, struct isert_buf *isert_buf, size_t size, enum dma_data_direction dma_dir) { int res = 0; isert_buf->sg_cnt = 1; isert_buf->sg = kmalloc(sizeof(isert_buf->sg[0]), GFP_KERNEL); if (unlikely(!isert_buf->sg)) { PRINT_ERROR("Failed to allocate buffer SG"); res = -ENOMEM; goto out; } isert_buf->addr = kmalloc(size, GFP_KERNEL); if (unlikely(!isert_buf->addr)) { PRINT_ERROR("Failed to allocate data buffer"); res = -ENOMEM; goto data_malloc_failed; } sg_init_one(&isert_buf->sg[0], isert_buf->addr, size); res = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, dma_dir); if (unlikely(!res)) { PRINT_ERROR("Failed to DMA map iser sg:%p len:%d", isert_buf->sg, isert_buf->sg_cnt); res = -ENOMEM; goto out_map_failed; } res = 0; goto out; out_map_failed: kfree(isert_buf->addr); isert_buf->addr = NULL; data_malloc_failed: kfree(isert_buf->addr); isert_buf->addr = NULL; out: return res; } static void isert_buf_release_kmalloc(struct isert_buf *isert_buf) { kfree(isert_buf->addr); isert_buf->addr = NULL; } int isert_buf_alloc_data_buf(struct ib_device *ib_dev, struct isert_buf *isert_buf, size_t size, enum dma_data_direction dma_dir) { int res = 0; isert_buf->is_alloced = 0; if (size >= PAGE_SIZE) { res = isert_buf_alloc_pg(ib_dev, isert_buf, size, dma_dir); if (unlikely(res)) goto out; isert_buf->is_pgalloced = 1; isert_buf->is_malloced = 0; isert_buf->is_alloced = 1; } else if (size) { res = isert_buf_malloc(ib_dev, isert_buf, size, dma_dir); if (unlikely(res)) goto out; isert_buf->is_pgalloced = 0; isert_buf->is_malloced = 1; isert_buf->is_alloced = 1; } isert_buf->size = size; isert_buf->dma_dir = dma_dir; out: return res; } void isert_buf_release(struct isert_buf *isert_buf) { if (isert_buf->is_alloced) { if (isert_buf->is_pgalloced) isert_buf_release_pg(isert_buf); if (isert_buf->is_malloced) isert_buf_release_kmalloc(isert_buf); isert_buf->is_alloced = 0; kfree(isert_buf->sg); isert_buf->sg = NULL; } } void isert_wr_set_fields(struct isert_wr *wr, struct isert_conn *isert_conn, struct isert_cmnd *pdu) { struct isert_device *isert_dev = isert_conn->isert_dev; wr->conn = isert_conn; wr->pdu = pdu; wr->isert_dev = isert_dev; } int isert_wr_init(struct isert_wr *wr, enum isert_wr_op wr_op, struct isert_buf *isert_buf, struct isert_conn *isert_conn, struct isert_cmnd *pdu, struct ib_sge *sge, int sg_offset, int sg_cnt, int buff_offset) { enum ib_wr_opcode send_wr_op = IB_WR_SEND; struct scatterlist *sg_tmp; int i; u32 send_flags = 0; TRACE_ENTRY(); switch (wr_op) { case ISER_WR_RECV: break; case ISER_WR_SEND: send_flags = IB_SEND_SIGNALED; break; case ISER_WR_RDMA_READ: send_wr_op = IB_WR_RDMA_READ; if (unlikely(!pdu->is_wstag_valid)) { PRINT_ERROR("No write tag/va specified for RDMA op"); isert_buf_release(isert_buf); buff_offset = -EFAULT; goto out; } #ifdef USE_PRE_440_WR_STRUCTURE wr->send_wr.wr.rdma.remote_addr = pdu->rem_write_va + buff_offset; wr->send_wr.wr.rdma.rkey = pdu->rem_write_stag; #else wr->send_wr.remote_addr = pdu->rem_write_va + buff_offset; wr->send_wr.rkey = pdu->rem_write_stag; #endif break; case ISER_WR_RDMA_WRITE: send_wr_op = IB_WR_RDMA_WRITE; if (unlikely(!pdu->is_rstag_valid)) { PRINT_ERROR("No read tag/va specified for RDMA op"); isert_buf_release(isert_buf); buff_offset = -EFAULT; goto out; } #ifdef USE_PRE_440_WR_STRUCTURE wr->send_wr.wr.rdma.remote_addr = pdu->rem_read_va + buff_offset; wr->send_wr.wr.rdma.rkey = pdu->rem_read_stag; #else wr->send_wr.remote_addr = pdu->rem_read_va + buff_offset; wr->send_wr.rkey = pdu->rem_read_stag; #endif break; default: BUG(); } EXTRACHECKS_BUG_ON(isert_buf->sg_cnt == 0); wr->wr_op = wr_op; wr->buf = isert_buf; wr->sge_list = sge + sg_offset; sg_tmp = &isert_buf->sg[sg_offset]; for (i = 0; i < sg_cnt; i++, sg_tmp++) { wr->sge_list[i].addr = sg_dma_address(sg_tmp); wr->sge_list[i].length = sg_dma_len(sg_tmp); buff_offset += wr->sge_list[i].length; } if (wr_op == ISER_WR_RECV) { wr->recv_wr.next = NULL; wr->recv_wr.wr_id = _ptr_to_u64(wr); wr->recv_wr.sg_list = wr->sge_list; wr->recv_wr.num_sge = sg_cnt; } else { #ifdef USE_PRE_440_WR_STRUCTURE wr->send_wr.next = NULL; wr->send_wr.wr_id = _ptr_to_u64(wr); wr->send_wr.sg_list = wr->sge_list; wr->send_wr.num_sge = sg_cnt; wr->send_wr.opcode = send_wr_op; wr->send_wr.send_flags = send_flags; #else wr->send_wr.wr.next = NULL; wr->send_wr.wr.wr_id = _ptr_to_u64(wr); wr->send_wr.wr.sg_list = wr->sge_list; wr->send_wr.wr.num_sge = sg_cnt; wr->send_wr.wr.opcode = send_wr_op; wr->send_wr.wr.send_flags = send_flags; #endif } out: TRACE_EXIT_RES(buff_offset); return buff_offset; } void isert_wr_release(struct isert_wr *wr) { struct isert_buf *isert_buf = wr->buf; if (isert_buf && isert_buf->is_alloced) { struct isert_device *isert_dev = wr->isert_dev; struct ib_device *ib_dev; if (isert_buf->sg_cnt) { ib_dev = isert_dev->ib_dev; ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, isert_buf->dma_dir); } isert_buf_release(isert_buf); } memset(wr, 0, sizeof(*wr)); }