aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2011-05-19 23:19:09 -0400
committerJames Bottomley <jbottomley@parallels.com>2011-05-24 12:56:58 -0400
commit97868c8905a1537153d406c4a3aa39a503a5c299 (patch)
tree3814892385b463a3ba14d81bf4101285855300f6
parent3eef6257de48ff84a5d98ca533685df8a3beaeb8 (diff)
[SCSI] target: Fix multi task->task_sg[] chaining logic bug
This patch fixes a bug in transport_do_task_sg_chain() used by HW target mode modules with sg_chain() to provide a single sg_next() walkable memory layout for use with pci_map_sg() and friends. This patch addresses an issue with mapping multiple small block max_sector tasks across multiple struct se_task->task_sg[] mappings for HW target mode operation. This was causing OOPs with (cmd->t_task->t_tasks_no > 1) I/O traffic for HW target drivers using transport_do_task_sg_chain(), and has been tested so far with tcm_fc(openfcoe), tcm_qla2xxx, and ib_srpt fabrics with t_tasks_no > 1 IBLOCK backends using a smaller max_sectors to trigger the original issue. Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org> Acked-by: Kiran Patil <kiran.patil@intel.com> Cc: stable@kernel.org Signed-off-by: James Bottomley <jbottomley@parallels.com>
-rw-r--r--drivers/target/target_core_transport.c26
1 files changed, 15 insertions, 11 deletions
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index b9d3501bdd91..833060e0de5e 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -4776,18 +4776,20 @@ void transport_do_task_sg_chain(struct se_cmd *cmd)
4776 sg_end_cur->page_link &= ~0x02; 4776 sg_end_cur->page_link &= ~0x02;
4777 4777
4778 sg_chain(sg_head, task_sg_num, sg_head_cur); 4778 sg_chain(sg_head, task_sg_num, sg_head_cur);
4779 sg_count += (task->task_sg_num + 1);
4780 } else
4781 sg_count += task->task_sg_num; 4779 sg_count += task->task_sg_num;
4780 task_sg_num = (task->task_sg_num + 1);
4781 } else {
4782 sg_chain(sg_head, task_sg_num, sg_head_cur);
4783 sg_count += task->task_sg_num;
4784 task_sg_num = task->task_sg_num;
4785 }
4782 4786
4783 sg_head = sg_head_cur; 4787 sg_head = sg_head_cur;
4784 sg_link = sg_link_cur; 4788 sg_link = sg_link_cur;
4785 task_sg_num = task->task_sg_num;
4786 continue; 4789 continue;
4787 } 4790 }
4788 sg_head = sg_first = &task->task_sg[0]; 4791 sg_head = sg_first = &task->task_sg[0];
4789 sg_link = &task->task_sg[task->task_sg_num]; 4792 sg_link = &task->task_sg[task->task_sg_num];
4790 task_sg_num = task->task_sg_num;
4791 /* 4793 /*
4792 * Check for single task.. 4794 * Check for single task..
4793 */ 4795 */
@@ -4798,9 +4800,12 @@ void transport_do_task_sg_chain(struct se_cmd *cmd)
4798 */ 4800 */
4799 sg_end = &task->task_sg[task->task_sg_num - 1]; 4801 sg_end = &task->task_sg[task->task_sg_num - 1];
4800 sg_end->page_link &= ~0x02; 4802 sg_end->page_link &= ~0x02;
4801 sg_count += (task->task_sg_num + 1);
4802 } else
4803 sg_count += task->task_sg_num; 4803 sg_count += task->task_sg_num;
4804 task_sg_num = (task->task_sg_num + 1);
4805 } else {
4806 sg_count += task->task_sg_num;
4807 task_sg_num = task->task_sg_num;
4808 }
4804 } 4809 }
4805 /* 4810 /*
4806 * Setup the starting pointer and total t_tasks_sg_linked_no including 4811 * Setup the starting pointer and total t_tasks_sg_linked_no including
@@ -4809,21 +4814,20 @@ void transport_do_task_sg_chain(struct se_cmd *cmd)
4809 T_TASK(cmd)->t_tasks_sg_chained = sg_first; 4814 T_TASK(cmd)->t_tasks_sg_chained = sg_first;
4810 T_TASK(cmd)->t_tasks_sg_chained_no = sg_count; 4815 T_TASK(cmd)->t_tasks_sg_chained_no = sg_count;
4811 4816
4812 DEBUG_CMD_M("Setup T_TASK(cmd)->t_tasks_sg_chained: %p and" 4817 DEBUG_CMD_M("Setup cmd: %p T_TASK(cmd)->t_tasks_sg_chained: %p and"
4813 " t_tasks_sg_chained_no: %u\n", T_TASK(cmd)->t_tasks_sg_chained, 4818 " t_tasks_sg_chained_no: %u\n", cmd, T_TASK(cmd)->t_tasks_sg_chained,
4814 T_TASK(cmd)->t_tasks_sg_chained_no); 4819 T_TASK(cmd)->t_tasks_sg_chained_no);
4815 4820
4816 for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg, 4821 for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg,
4817 T_TASK(cmd)->t_tasks_sg_chained_no, i) { 4822 T_TASK(cmd)->t_tasks_sg_chained_no, i) {
4818 4823
4819 DEBUG_CMD_M("SG: %p page: %p length: %d offset: %d\n", 4824 DEBUG_CMD_M("SG[%d]: %p page: %p length: %d offset: %d, magic: 0x%08x\n",
4820 sg, sg_page(sg), sg->length, sg->offset); 4825 i, sg, sg_page(sg), sg->length, sg->offset, sg->sg_magic);
4821 if (sg_is_chain(sg)) 4826 if (sg_is_chain(sg))
4822 DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg); 4827 DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg);
4823 if (sg_is_last(sg)) 4828 if (sg_is_last(sg))
4824 DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg); 4829 DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg);
4825 } 4830 }
4826
4827} 4831}
4828EXPORT_SYMBOL(transport_do_task_sg_chain); 4832EXPORT_SYMBOL(transport_do_task_sg_chain);
4829 4833