diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2011-05-19 23:19:09 -0400 |
---|---|---|
committer | James Bottomley <jbottomley@parallels.com> | 2011-05-24 12:56:58 -0400 |
commit | 97868c8905a1537153d406c4a3aa39a503a5c299 (patch) | |
tree | 3814892385b463a3ba14d81bf4101285855300f6 | |
parent | 3eef6257de48ff84a5d98ca533685df8a3beaeb8 (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.c | 26 |
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 | } |
4828 | EXPORT_SYMBOL(transport_do_task_sg_chain); | 4832 | EXPORT_SYMBOL(transport_do_task_sg_chain); |
4829 | 4833 | ||