static sense_reason_t rd_execute_rw(struct se_cmd *cmd) { struct scatterlist *sgl = cmd->t_data_sg; u32 sgl_nents = cmd->t_data_nents; enum dma_data_direction data_direction = cmd->data_direction; struct se_device *se_dev = cmd->se_dev; struct rd_dev *dev = RD_DEV(se_dev); struct rd_dev_sg_table *table; struct scatterlist *rd_sg; struct sg_mapping_iter m; u32 rd_offset; u32 rd_size; u32 rd_page; u32 src_len; u64 tmp; if (dev->rd_flags & RDF_NULLIO) { target_complete_cmd(cmd, SAM_STAT_GOOD); return 0; } tmp = cmd->t_task_lba * se_dev->dev_attrib.block_size; rd_offset = do_div(tmp, PAGE_SIZE); rd_page = tmp; rd_size = cmd->data_length; table = rd_get_sg_table(dev, rd_page); if (!table) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; rd_sg = &table->sg_table[rd_page - table->page_start_offset]; pr_debug("RD[%u]: %s LBA: %llu, Size: %u Page: %u, Offset: %u\n", dev->rd_dev_id, data_direction == DMA_FROM_DEVICE ? "Read" : "Write", cmd->t_task_lba, rd_size, rd_page, rd_offset); src_len = PAGE_SIZE - rd_offset; sg_miter_start(&m, sgl, sgl_nents, data_direction == DMA_FROM_DEVICE ? SG_MITER_TO_SG : SG_MITER_FROM_SG); while (rd_size) { u32 len; void *rd_addr; sg_miter_next(&m); if (!(u32)m.length) { pr_debug("RD[%u]: invalid sgl %p len %zu\n", dev->rd_dev_id, m.addr, m.length); sg_miter_stop(&m); return TCM_INCORRECT_AMOUNT_OF_DATA; } len = min((u32)m.length, src_len); if (len > rd_size) { pr_debug("RD[%u]: size underrun page %d offset %d " "size %d\n", dev->rd_dev_id, rd_page, rd_offset, rd_size); len = rd_size; } m.consumed = len; rd_addr = sg_virt(rd_sg) + rd_offset; if (data_direction == DMA_FROM_DEVICE) memcpy(m.addr, rd_addr, len); else memcpy(rd_addr, m.addr, len); rd_size -= len; if (!rd_size) continue; src_len -= len; if (src_len) { rd_offset += len; continue; } /* rd page completed, next one please */ rd_page++; rd_offset = 0; src_len = PAGE_SIZE; if (rd_page <= table->page_end_offset) { rd_sg++; continue; } table = rd_get_sg_table(dev, rd_page); if (!table) { sg_miter_stop(&m); return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } /* since we increment, the first sg entry is correct */ rd_sg = table->sg_table; } sg_miter_stop(&m); target_complete_cmd(cmd, SAM_STAT_GOOD); return 0; }
/* rd_MEMCPY_write(): * * */ static int rd_MEMCPY_write(struct rd_request *req) { struct se_task *task = &req->rd_task; struct rd_dev *dev = req->rd_task.task_se_cmd->se_dev->dev_ptr; struct rd_dev_sg_table *table; struct scatterlist *sg_d, *sg_s; void *dst, *src; u32 i = 0, j = 0, dst_offset = 0, src_offset = 0; u32 length, page_end = 0, table_sg_end; u32 rd_offset = req->rd_offset; table = rd_get_sg_table(dev, req->rd_page); if (!table) return -EINVAL; table_sg_end = (table->page_end_offset - req->rd_page); sg_d = &table->sg_table[req->rd_page - table->page_start_offset]; sg_s = task->task_sg; pr_debug("RD[%d] Write LBA: %llu, Size: %u, Page: %u," " Offset: %u\n", dev->rd_dev_id, task->task_lba, req->rd_size, req->rd_page, req->rd_offset); dst_offset = rd_offset; while (req->rd_size) { if ((sg_s[i].length - src_offset) < (sg_d[j].length - dst_offset)) { length = (sg_s[i].length - src_offset); pr_debug("Step 1 - sg_s[%d]: %p length: %d" " offset: %d sg_d[%d].length: %u\n", i, &sg_s[i], sg_s[i].length, sg_s[i].offset, j, sg_d[j].length); pr_debug("Step 1 - length: %u src_offset: %u" " dst_offset: %u\n", length, src_offset, dst_offset); if (length > req->rd_size) length = req->rd_size; src = sg_virt(&sg_s[i++]) + src_offset; BUG_ON(!src); dst = sg_virt(&sg_d[j]) + dst_offset; BUG_ON(!dst); src_offset = 0; dst_offset = length; page_end = 0; } else { length = (sg_d[j].length - dst_offset); pr_debug("Step 2 - sg_s[%d]: %p length: %d" " offset: %d sg_d[%d].length: %u\n", i, &sg_s[i], sg_s[i].length, sg_s[i].offset, j, sg_d[j].length); pr_debug("Step 2 - length: %u src_offset: %u" " dst_offset: %u\n", length, src_offset, dst_offset); if (length > req->rd_size) length = req->rd_size; src = sg_virt(&sg_s[i]) + src_offset; BUG_ON(!src); if (sg_s[i].length == length) { i++; src_offset = 0; } else src_offset = length; dst = sg_virt(&sg_d[j++]) + dst_offset; BUG_ON(!dst); dst_offset = 0; page_end = 1; } memcpy(dst, src, length); pr_debug("page: %u, remaining size: %u, length: %u," " i: %u, j: %u\n", req->rd_page, (req->rd_size - length), length, i, j); req->rd_size -= length; if (!req->rd_size) return 0; if (!page_end) continue; if (++req->rd_page <= table->page_end_offset) { pr_debug("page: %u in same page table\n", req->rd_page); continue; } pr_debug("getting new page table for page: %u\n", req->rd_page); table = rd_get_sg_table(dev, req->rd_page); if (!table) return -EINVAL; sg_d = &table->sg_table[j = 0]; } return 0; }
static int rd_MEMCPY(struct rd_request *req, u32 read_rd) { struct se_task *task = &req->rd_task; struct rd_dev *dev = req->rd_task.task_se_cmd->se_dev->dev_ptr; struct rd_dev_sg_table *table; struct scatterlist *rd_sg; struct sg_mapping_iter m; u32 rd_offset = req->rd_offset; u32 src_len; table = rd_get_sg_table(dev, req->rd_page); if (!table) return -EINVAL; rd_sg = &table->sg_table[req->rd_page - table->page_start_offset]; pr_debug("RD[%u]: %s LBA: %llu, Size: %u Page: %u, Offset: %u\n", dev->rd_dev_id, read_rd ? "Read" : "Write", task->task_lba, req->rd_size, req->rd_page, rd_offset); src_len = PAGE_SIZE - rd_offset; sg_miter_start(&m, task->task_sg, task->task_sg_nents, read_rd ? SG_MITER_TO_SG : SG_MITER_FROM_SG); while (req->rd_size) { u32 len; void *rd_addr; sg_miter_next(&m); len = min((u32)m.length, src_len); m.consumed = len; rd_addr = sg_virt(rd_sg) + rd_offset; if (read_rd) memcpy(m.addr, rd_addr, len); else memcpy(rd_addr, m.addr, len); req->rd_size -= len; if (!req->rd_size) continue; src_len -= len; if (src_len) { rd_offset += len; continue; } /* rd page completed, next one please */ req->rd_page++; rd_offset = 0; src_len = PAGE_SIZE; if (req->rd_page <= table->page_end_offset) { rd_sg++; continue; } table = rd_get_sg_table(dev, req->rd_page); if (!table) { sg_miter_stop(&m); return -EINVAL; } /* since we increment, the first sg entry is correct */ rd_sg = table->sg_table; } sg_miter_stop(&m); return 0; }