static int srp_direct_data(struct scst_cmd *sc, struct srp_direct_buf *md, enum dma_data_direction dir, srp_rdma_t rdma_io, int dma_map) { struct iu_entry *iue = NULL; struct scatterlist *sg = NULL; int err, nsg = 0, len, sg_cnt; u32 tsize; enum dma_data_direction dma_dir; iue = scst_cmd_get_tgt_priv(sc); if (dir == DMA_TO_DEVICE) { scst_cmd_get_write_fields(sc, &sg, &sg_cnt); tsize = scst_cmd_get_bufflen(sc); dma_dir = DMA_FROM_DEVICE; } else { sg = scst_cmd_get_sg(sc); sg_cnt = scst_cmd_get_sg_cnt(sc); tsize = scst_cmd_get_adjusted_resp_data_len(sc); dma_dir = DMA_TO_DEVICE; } dprintk("%p %u %u %d\n", iue, tsize, be32_to_cpu(md->len), sg_cnt); len = min(tsize, be32_to_cpu(md->len)); if (dma_map) { nsg = dma_map_sg(iue->target->dev, sg, sg_cnt, dma_dir); if (!nsg) { eprintk(KERN_ERR "fail to map %p %d\n", iue, sg_cnt); return -ENOMEM; } } err = rdma_io(sc, sg, nsg, md, 1, dir, len); if (dma_map) dma_unmap_sg(iue->target->dev, sg, nsg, dma_dir); return err; }
static int srp_indirect_data(struct scst_cmd *sc, struct srp_cmd *cmd, struct srp_indirect_buf *id, enum dma_data_direction dir, srp_rdma_t rdma_io, int dma_map, int ext_desc) { struct iu_entry *iue = NULL; struct srp_direct_buf *md = NULL; struct scatterlist dummy, *sg = NULL; dma_addr_t token = 0; int err = 0; int nmd, nsg = 0, len, sg_cnt = 0; u32 tsize = 0; enum dma_data_direction dma_dir; iue = scst_cmd_get_tgt_priv(sc); if (dir == DMA_TO_DEVICE) { scst_cmd_get_write_fields(sc, &sg, &sg_cnt); tsize = scst_cmd_get_bufflen(sc); dma_dir = DMA_FROM_DEVICE; } else { sg = scst_cmd_get_sg(sc); sg_cnt = scst_cmd_get_sg_cnt(sc); tsize = scst_cmd_get_adjusted_resp_data_len(sc); dma_dir = DMA_TO_DEVICE; } dprintk("%p %u %u %d %d\n", iue, tsize, be32_to_cpu(id->len), be32_to_cpu(cmd->data_in_desc_cnt), be32_to_cpu(cmd->data_out_desc_cnt)); len = min(tsize, be32_to_cpu(id->len)); nmd = be32_to_cpu(id->table_desc.len) / sizeof(struct srp_direct_buf); if ((dir == DMA_FROM_DEVICE && nmd == cmd->data_in_desc_cnt) || (dir == DMA_TO_DEVICE && nmd == cmd->data_out_desc_cnt)) { md = &id->desc_list[0]; goto rdma; } if (ext_desc && dma_map) { md = dma_alloc_coherent(iue->target->dev, be32_to_cpu(id->table_desc.len), &token, GFP_KERNEL); if (!md) { eprintk("Can't get dma memory %u\n", id->table_desc.len); return -ENOMEM; } sg_init_one(&dummy, md, be32_to_cpu(id->table_desc.len)); sg_dma_address(&dummy) = token; sg_dma_len(&dummy) = be32_to_cpu(id->table_desc.len); err = rdma_io(sc, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE, be32_to_cpu(id->table_desc.len)); if (err) { eprintk("Error copying indirect table %d\n", err); goto free_mem; } } else { eprintk("This command uses external indirect buffer\n"); return -EINVAL; } rdma: if (dma_map) { nsg = dma_map_sg(iue->target->dev, sg, sg_cnt, dma_dir); if (!nsg) { eprintk("fail to map %p %d\n", iue, sg_cnt); err = -ENOMEM; goto free_mem; } } err = rdma_io(sc, sg, nsg, md, nmd, dir, len); if (dma_map) dma_unmap_sg(iue->target->dev, sg, nsg, dma_dir); free_mem: if (token && dma_map) dma_free_coherent(iue->target->dev, be32_to_cpu(id->table_desc.len), md, token); return err; }
/* * Receive write data frame. */ void ft_recv_write_data(struct scst_cmd *cmd, struct fc_frame *fp) { struct ft_cmd *fcmd; struct fc_frame_header *fh; unsigned int bufflen; u32 rel_off; size_t frame_len; size_t mem_len; size_t tlen; void *from; void *to; int dir; u8 *buf; dir = scst_cmd_get_data_direction(cmd); if (dir == SCST_DATA_BIDI) { mem_len = scst_get_out_buf_first(cmd, &buf); bufflen = scst_cmd_get_out_bufflen(cmd); } else { mem_len = scst_get_buf_first(cmd, &buf); bufflen = scst_cmd_get_bufflen(cmd); } to = buf; fcmd = scst_cmd_get_tgt_priv(cmd); fh = fc_frame_header_get(fp); frame_len = fr_len(fp); rel_off = ntohl(fh->fh_parm_offset); FT_IO_DBG("sid %x oxid %x payload_len %zd rel_off %x\n", ntoh24(fh->fh_s_id), ntohs(fh->fh_ox_id), frame_len - sizeof(*fh), rel_off); if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF)) goto drop; if (frame_len <= sizeof(*fh)) goto drop; frame_len -= sizeof(*fh); from = fc_frame_payload_get(fp, 0); if (rel_off >= bufflen) goto drop; if (frame_len + rel_off > bufflen) frame_len = bufflen - rel_off; while (frame_len) { if (!mem_len) { if (dir == SCST_DATA_BIDI) { scst_put_out_buf(cmd, buf); mem_len = scst_get_out_buf_next(cmd, &buf); } else { scst_put_buf(cmd, buf); mem_len = scst_get_buf_next(cmd, &buf); } to = buf; if (!mem_len) break; } if (rel_off) { if (rel_off >= mem_len) { rel_off -= mem_len; mem_len = 0; continue; } mem_len -= rel_off; to += rel_off; rel_off = 0; } tlen = min(mem_len, frame_len); memcpy(to, from, tlen); from += tlen; frame_len -= tlen; mem_len -= tlen; to += tlen; fcmd->write_data_len += tlen; } if (mem_len) { if (dir == SCST_DATA_BIDI) scst_put_out_buf(cmd, buf); else scst_put_buf(cmd, buf); } if (fcmd->write_data_len == cmd->data_len) scst_rx_data(cmd, SCST_RX_STATUS_SUCCESS, SCST_CONTEXT_THREAD); drop: fc_frame_free(fp); }