2011-05-11 18:55:33

by Nicholas A. Bellinger

[permalink] [raw]
Subject: [PATCH-v2 0/4] Bugfixes for .39-rc8

From: Nicholas Bellinger <[email protected]>

Hi James and Co,

This series is resend of four patches that should be considered critical
target core bugfixes for .39 that have been under stress testing in a
couple of different labs recently using lio-core-2.6.git/lio-4.1 (.39-rcX)
and /lio-4.0 (.38.3) branch code.

These are all important bugfixes that are required to run with in-flight
.40 HW target drivers in the upstream LIO tree. This includes fixes for
the main transport_do_task_sg_chain() Data I/O mapping logic, and
task->task_sg[] shutdown path from MSI-X interrupt context. These bugfixes
apply for all of the following HW fabric drivers:

tcm_fc(openfcoe) w/ ddp offload, tcm_qla2xxx, ib_srpt and ibmvscsis.

These patches address issues that are able to easily produce OOPsen with
small backstore max_sectors values. Please review+apply for .39 and
also queue into [email protected] for .38.

The series is also available here for a direct pull:

git://git.kernel.org/pub/scm/linux/kernel/git/nab/scsi-post-merge-2.6.git for-39-rc-fixes

and has been made against the following scsi-rc-fixes-2.6.git HEAD:

commit c055f5b2614b4f758ae6cc86733f31fa4c2c5844
Author: James Bottomley <[email protected]>
Date: Sun May 1 09:42:07 2011 -0500

[SCSI] fix oops in scsi_run_queue()

Thanks!

--nab

Nicholas Bellinger (4):
target: Fix multi task->task_sg[] chaining logic bug
target: Fix interrupt context bug with stats_lock and
core_tmr_alloc_req
target: Fix bug with task_sg chained transport_free_dev_tasks release
target: Fix task->task_execute_queue=1 clear bug + LUN_RESET OOPs

drivers/target/target_core_device.c | 4 +-
drivers/target/target_core_tmr.c | 7 +++--
drivers/target/target_core_transport.c | 46 +++++++++++++++++++++++--------
include/target/target_core_base.h | 1 +
include/target/target_core_transport.h | 1 +
5 files changed, 42 insertions(+), 17 deletions(-)

--
1.7.5.1


2011-05-11 18:55:20

by Nicholas A. Bellinger

[permalink] [raw]
Subject: [PATCH-v2 1/4] target: Fix multi task->task_sg[] chaining logic bug

From: Nicholas Bellinger <[email protected]>

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.

Reported-by: Kiran Patil <[email protected]>
Signed-off-by: Nicholas Bellinger <[email protected]>
---
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 9583b23..fefe10a 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)
sg_end_cur->page_link &= ~0x02;

sg_chain(sg_head, task_sg_num, sg_head_cur);
- sg_count += (task->task_sg_num + 1);
- } else
sg_count += task->task_sg_num;
+ task_sg_num = (task->task_sg_num + 1);
+ } else {
+ sg_chain(sg_head, task_sg_num, sg_head_cur);
+ sg_count += task->task_sg_num;
+ task_sg_num = task->task_sg_num;
+ }

sg_head = sg_head_cur;
sg_link = sg_link_cur;
- task_sg_num = task->task_sg_num;
continue;
}
sg_head = sg_first = &task->task_sg[0];
sg_link = &task->task_sg[task->task_sg_num];
- task_sg_num = task->task_sg_num;
/*
* Check for single task..
*/
@@ -4798,9 +4800,12 @@ void transport_do_task_sg_chain(struct se_cmd *cmd)
*/
sg_end = &task->task_sg[task->task_sg_num - 1];
sg_end->page_link &= ~0x02;
- sg_count += (task->task_sg_num + 1);
- } else
sg_count += task->task_sg_num;
+ task_sg_num = (task->task_sg_num + 1);
+ } else {
+ sg_count += task->task_sg_num;
+ task_sg_num = task->task_sg_num;
+ }
}
/*
* 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)
T_TASK(cmd)->t_tasks_sg_chained = sg_first;
T_TASK(cmd)->t_tasks_sg_chained_no = sg_count;

- DEBUG_CMD_M("Setup T_TASK(cmd)->t_tasks_sg_chained: %p and"
- " t_tasks_sg_chained_no: %u\n", T_TASK(cmd)->t_tasks_sg_chained,
+ DEBUG_CMD_M("Setup cmd: %p T_TASK(cmd)->t_tasks_sg_chained: %p and"
+ " t_tasks_sg_chained_no: %u\n", cmd, T_TASK(cmd)->t_tasks_sg_chained,
T_TASK(cmd)->t_tasks_sg_chained_no);

for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg,
T_TASK(cmd)->t_tasks_sg_chained_no, i) {

- DEBUG_CMD_M("SG: %p page: %p length: %d offset: %d\n",
- sg, sg_page(sg), sg->length, sg->offset);
+ DEBUG_CMD_M("SG[%d]: %p page: %p length: %d offset: %d, magic: 0x%08x\n",
+ i, sg, sg_page(sg), sg->length, sg->offset, sg->sg_magic);
if (sg_is_chain(sg))
DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg);
if (sg_is_last(sg))
DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg);
}
-
}
EXPORT_SYMBOL(transport_do_task_sg_chain);

--
1.7.5.1

2011-05-11 18:54:51

by Nicholas A. Bellinger

[permalink] [raw]
Subject: [PATCH-v2 2/4] target: Fix interrupt context bug with stats_lock and core_tmr_alloc_req

From: Nicholas Bellinger <[email protected]>

This patch fixes two bugs wrt to the interrupt context usage of target
core with HW target mode drivers. It first converts the usage of struct
se_device->stats_lock in transport_get_lun_for_cmd() and core_tmr_lun_reset()
to properly use spin_lock_irq() to address an BUG with CONFIG_LOCKDEP_SUPPORT=y
enabled.

This patch also adds a 'in_interrupt()' check to allow GFP_ATOMIC usage from
core_tmr_alloc_req() to fix a 'sleeping in interrupt context' BUG with HW
target fabrics that require this logic to function.

Signed-off-by: Nicholas Bellinger <[email protected]>
---
drivers/target/target_core_device.c | 4 ++--
drivers/target/target_core_tmr.c | 7 ++++---
2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index d25e208..fc10ed4 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -150,13 +150,13 @@ out:

{
struct se_device *dev = se_lun->lun_se_dev;
- spin_lock(&dev->stats_lock);
+ spin_lock_irq(&dev->stats_lock);
dev->num_cmds++;
if (se_cmd->data_direction == DMA_TO_DEVICE)
dev->write_bytes += se_cmd->data_length;
else if (se_cmd->data_direction == DMA_FROM_DEVICE)
dev->read_bytes += se_cmd->data_length;
- spin_unlock(&dev->stats_lock);
+ spin_unlock_irq(&dev->stats_lock);
}

/*
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 4a10983..59b8b9c 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -55,7 +55,8 @@ struct se_tmr_req *core_tmr_alloc_req(
{
struct se_tmr_req *tmr;

- tmr = kmem_cache_zalloc(se_tmr_req_cache, GFP_KERNEL);
+ tmr = kmem_cache_zalloc(se_tmr_req_cache, (in_interrupt()) ?
+ GFP_ATOMIC : GFP_KERNEL);
if (!(tmr)) {
printk(KERN_ERR "Unable to allocate struct se_tmr_req\n");
return ERR_PTR(-ENOMEM);
@@ -398,9 +399,9 @@ int core_tmr_lun_reset(
printk(KERN_INFO "LUN_RESET: SCSI-2 Released reservation\n");
}

- spin_lock(&dev->stats_lock);
+ spin_lock_irq(&dev->stats_lock);
dev->num_resets++;
- spin_unlock(&dev->stats_lock);
+ spin_unlock_irq(&dev->stats_lock);

DEBUG_LR("LUN_RESET: %s for [%s] Complete\n",
(preempt_and_abort_list) ? "Preempt" : "TMR",
--
1.7.5.1

2011-05-11 18:55:37

by Nicholas A. Bellinger

[permalink] [raw]
Subject: [PATCH-v2 3/4] target: Fix bug with task_sg chained transport_free_dev_tasks release

From: Nicholas Bellinger <[email protected]>

This patch addresses a bug in the target core release path for HW
operation where transport_free_dev_tasks() was incorrectly being called
from transport_lun_remove_cmd() while releasing a se_cmd reference and
calling struct target_core_fabric_ops->queue_data_in().

This would result in a OOPs with HW target mode when the release of
se_task->task_sg[] would happen before pci_unmap_sg() can be called in
HW target mode fabric module code. This patch addresses the issue by
moving transport_free_dev_tasks() from transport_lun_remove_cmd() into
transport_generic_free_cmd(), and adding TRANSPORT_FREE_CMD_INTR and
transport_generic_free_cmd_intr() to allow se_cmd descriptor release
to happen fromfrom within transport_processing_thread() process context
when release of se_cmd is not possible from HW interrupt context.

Signed-off-by: Nicholas Bellinger <[email protected]>
---
drivers/target/target_core_transport.c | 13 ++++++++++++-
include/target/target_core_base.h | 1 +
include/target/target_core_transport.h | 1 +
3 files changed, 14 insertions(+), 1 deletions(-)

diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index fefe10a..3eeb3e2 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -762,7 +762,6 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
transport_all_task_dev_remove_state(cmd);
spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);

- transport_free_dev_tasks(cmd);

check_lun:
spin_lock_irqsave(&lun->lun_cmd_lock, flags);
@@ -2058,6 +2057,13 @@ int transport_generic_handle_tmr(
}
EXPORT_SYMBOL(transport_generic_handle_tmr);

+void transport_generic_free_cmd_intr(
+ struct se_cmd *cmd)
+{
+ transport_add_cmd_to_queue(cmd, TRANSPORT_FREE_CMD_INTR);
+}
+EXPORT_SYMBOL(transport_generic_free_cmd_intr);
+
static int transport_stop_tasks_for_cmd(struct se_cmd *cmd)
{
struct se_task *task, *task_tmp;
@@ -5301,6 +5307,8 @@ void transport_generic_free_cmd(
if (wait_for_tasks && cmd->transport_wait_for_tasks)
cmd->transport_wait_for_tasks(cmd, 0, 0);

+ transport_free_dev_tasks(cmd);
+
transport_generic_remove(cmd, release_to_pool,
session_reinstatement);
}
@@ -6136,6 +6144,9 @@ get_cmd:
case TRANSPORT_REMOVE:
transport_generic_remove(cmd, 1, 0);
break;
+ case TRANSPORT_FREE_CMD_INTR:
+ transport_generic_free_cmd(cmd, 0, 1, 0);
+ break;
case TRANSPORT_PROCESS_TMR:
transport_generic_do_tmr(cmd);
break;
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 1d3b5b2..561ac99 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -98,6 +98,7 @@ enum transport_state_table {
TRANSPORT_REMOVE = 14,
TRANSPORT_FREE = 15,
TRANSPORT_NEW_CMD_MAP = 16,
+ TRANSPORT_FREE_CMD_INTR = 17,
};

/* Used for struct se_cmd->se_cmd_flags */
diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h
index 59aa464..24a1c6c 100644
--- a/include/target/target_core_transport.h
+++ b/include/target/target_core_transport.h
@@ -172,6 +172,7 @@ extern int transport_generic_handle_cdb_map(struct se_cmd *);
extern int transport_generic_handle_data(struct se_cmd *);
extern void transport_new_cmd_failure(struct se_cmd *);
extern int transport_generic_handle_tmr(struct se_cmd *);
+extern void transport_generic_free_cmd_intr(struct se_cmd *);
extern void __transport_stop_task_timer(struct se_task *, unsigned long *);
extern unsigned char transport_asciihex_to_binaryhex(unsigned char val[2]);
extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32,
--
1.7.5.1

2011-05-12 02:01:21

by Nicholas A. Bellinger

[permalink] [raw]
Subject: [PATCH-v2 4/4] target: Fix task->task_execute_queue=1 clear bug + LUN_RESET OOPs

From: Nicholas Bellinger <[email protected]>

This patch fixes a bug where task->task_execute_queue=1 was not being
cleared once se_task had been removed from se_device->execute_task_list,
resulting in an OOPs in core_tmr_lun_reset() for the task->task_active=0
case where transport_remove_task_from_execute_queue() was incorrectly
being called.

This patch fixes two cases in transport_get_task_from_execute_queue()
and transport_remove_task_from_execute_queue() to properly clear
task->task_execute_queue=0 once list_del(&task->t_execute_list) has
been called.

It also adds an explict check in transport_remove_task_from_execute_queue()
to dump_stack + return if called with task->task_execute_queue=0.

Signed-off-by: Nicholas Bellinger <[email protected]>
---
drivers/target/target_core_transport.c | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 3eeb3e2..beaf8fa 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1194,6 +1194,7 @@ transport_get_task_from_execute_queue(struct se_device *dev)
break;

list_del(&task->t_execute_list);
+ atomic_set(&task->task_execute_queue, 0);
atomic_dec(&dev->execute_tasks);

return task;
@@ -1209,8 +1210,14 @@ void transport_remove_task_from_execute_queue(
{
unsigned long flags;

+ if (atomic_read(&task->task_execute_queue) == 0) {
+ dump_stack();
+ return;
+ }
+
spin_lock_irqsave(&dev->execute_task_lock, flags);
list_del(&task->t_execute_list);
+ atomic_set(&task->task_execute_queue, 0);
atomic_dec(&dev->execute_tasks);
spin_unlock_irqrestore(&dev->execute_task_lock, flags);
}
--
1.7.5.1

2011-05-14 00:13:37

by Nicholas A. Bellinger

[permalink] [raw]
Subject: Re: [PATCH-v2 0/4] Bugfixes for .39-rc8

On Tue, 2011-05-10 at 21:35 -0700, Nicholas A. Bellinger wrote:
> From: Nicholas Bellinger <[email protected]>
>
> Hi James and Co,
>
> This series is resend of four patches that should be considered critical
> target core bugfixes for .39 that have been under stress testing in a
> couple of different labs recently using lio-core-2.6.git/lio-4.1 (.39-rcX)
> and /lio-4.0 (.38.3) branch code.
>
> These are all important bugfixes that are required to run with in-flight
> .40 HW target drivers in the upstream LIO tree. This includes fixes for
> the main transport_do_task_sg_chain() Data I/O mapping logic, and
> task->task_sg[] shutdown path from MSI-X interrupt context. These bugfixes
> apply for all of the following HW fabric drivers:
>
> tcm_fc(openfcoe) w/ ddp offload, tcm_qla2xxx, ib_srpt and ibmvscsis.
>
> These patches address issues that are able to easily produce OOPsen with
> small backstore max_sectors values. Please review+apply for .39 and
> also queue into [email protected] for .38.
>
> The series is also available here for a direct pull:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/nab/scsi-post-merge-2.6.git for-39-rc-fixes
>

Hi James,

Ping on getting these bugfixes merged into .39-final and .38 stable..?

The tcm_fc(openfcoe) HW offload also in the queue for .40 from Kiran &
Co depends upon these fixes.

Thanks,

--nab

> and has been made against the following scsi-rc-fixes-2.6.git HEAD:
>
> commit c055f5b2614b4f758ae6cc86733f31fa4c2c5844
> Author: James Bottomley <[email protected]>
> Date: Sun May 1 09:42:07 2011 -0500
>
> [SCSI] fix oops in scsi_run_queue()
>
> Thanks!
>
> --nab
>
> Nicholas Bellinger (4):
> target: Fix multi task->task_sg[] chaining logic bug
> target: Fix interrupt context bug with stats_lock and
> core_tmr_alloc_req
> target: Fix bug with task_sg chained transport_free_dev_tasks release
> target: Fix task->task_execute_queue=1 clear bug + LUN_RESET OOPs
>
> drivers/target/target_core_device.c | 4 +-
> drivers/target/target_core_tmr.c | 7 +++--
> drivers/target/target_core_transport.c | 46 +++++++++++++++++++++++--------
> include/target/target_core_base.h | 1 +
> include/target/target_core_transport.h | 1 +
> 5 files changed, 42 insertions(+), 17 deletions(-)
>

2011-05-17 21:27:41

by Patil, Kiran

[permalink] [raw]
Subject: Re: [PATCH-v2 1/4] target: Fix multi task->task_sg[] chaining logic bug

Acked-by: Kiran Patil <[email protected]>

On 5/10/2011 9:35 PM, Nicholas A. Bellinger wrote:
> From: Nicholas Bellinger<[email protected]>
>
> 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.
>
> Reported-by: Kiran Patil<[email protected]>
> Signed-off-by: Nicholas Bellinger<[email protected]>
> ---
> 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 9583b23..fefe10a 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)
> sg_end_cur->page_link&= ~0x02;
>
> sg_chain(sg_head, task_sg_num, sg_head_cur);
> - sg_count += (task->task_sg_num + 1);
> - } else
> sg_count += task->task_sg_num;
> + task_sg_num = (task->task_sg_num + 1);
> + } else {
> + sg_chain(sg_head, task_sg_num, sg_head_cur);
> + sg_count += task->task_sg_num;
> + task_sg_num = task->task_sg_num;
> + }
>
> sg_head = sg_head_cur;
> sg_link = sg_link_cur;
> - task_sg_num = task->task_sg_num;
> continue;
> }
> sg_head = sg_first =&task->task_sg[0];
> sg_link =&task->task_sg[task->task_sg_num];
> - task_sg_num = task->task_sg_num;
> /*
> * Check for single task..
> */
> @@ -4798,9 +4800,12 @@ void transport_do_task_sg_chain(struct se_cmd *cmd)
> */
> sg_end =&task->task_sg[task->task_sg_num - 1];
> sg_end->page_link&= ~0x02;
> - sg_count += (task->task_sg_num + 1);
> - } else
> sg_count += task->task_sg_num;
> + task_sg_num = (task->task_sg_num + 1);
> + } else {
> + sg_count += task->task_sg_num;
> + task_sg_num = task->task_sg_num;
> + }
> }
> /*
> * 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)
> T_TASK(cmd)->t_tasks_sg_chained = sg_first;
> T_TASK(cmd)->t_tasks_sg_chained_no = sg_count;
>
> - DEBUG_CMD_M("Setup T_TASK(cmd)->t_tasks_sg_chained: %p and"
> - " t_tasks_sg_chained_no: %u\n", T_TASK(cmd)->t_tasks_sg_chained,
> + DEBUG_CMD_M("Setup cmd: %p T_TASK(cmd)->t_tasks_sg_chained: %p and"
> + " t_tasks_sg_chained_no: %u\n", cmd, T_TASK(cmd)->t_tasks_sg_chained,
> T_TASK(cmd)->t_tasks_sg_chained_no);
>
> for_each_sg(T_TASK(cmd)->t_tasks_sg_chained, sg,
> T_TASK(cmd)->t_tasks_sg_chained_no, i) {
>
> - DEBUG_CMD_M("SG: %p page: %p length: %d offset: %d\n",
> - sg, sg_page(sg), sg->length, sg->offset);
> + DEBUG_CMD_M("SG[%d]: %p page: %p length: %d offset: %d, magic: 0x%08x\n",
> + i, sg, sg_page(sg), sg->length, sg->offset, sg->sg_magic);
> if (sg_is_chain(sg))
> DEBUG_CMD_M("SG: %p sg_is_chain=1\n", sg);
> if (sg_is_last(sg))
> DEBUG_CMD_M("SG: %p sg_is_last=1\n", sg);
> }
> -
> }
> EXPORT_SYMBOL(transport_do_task_sg_chain);
>