[SCSI] mvsas: add support for Task collector mode and fixed relative bugs

1. Add support for Task collector mode.
2. Fixed relative collector mode bug:
   - I/O failed when disks is on two ports
   - system hang when hotplug disk
   - system hang when unplug disk during run IO
3. Unlock ap->lock within .lldd_execute_task for direct mode to
   improve performance

Signed-off-by: Xiangliang Yu <yuxiangl@marvell.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Xiangliang Yu 2011-04-26 06:36:51 -07:00 committed by James Bottomley
parent 8214028344
commit 0b15fb1fdf
11 changed files with 296 additions and 154 deletions

View File

@ -3,6 +3,7 @@
#
# Copyright 2007 Red Hat, Inc.
# Copyright 2008 Marvell. <kewei@marvell.com>
# Copyright 2009-20011 Marvell. <yuxiangl@marvell.com>
#
# This file is licensed under GPLv2.
#

View File

@ -3,6 +3,7 @@
#
# Copyright 2007 Red Hat, Inc.
# Copyright 2008 Marvell. <kewei@marvell.com>
# Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
#
# This file is licensed under GPLv2.
#

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@ -25,7 +26,16 @@
#include "mv_sas.h"
static int lldd_max_execute_num = 1;
module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
MODULE_PARM_DESC(collector, "\n"
"\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
"\tMode. If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
"\tThe mvsas SAS LLDD supports both modes.\n"
"\tDefault: 1 (Direct Mode).\n");
static struct scsi_transport_template *mvs_stt;
struct kmem_cache *mvs_task_list_cache;
static const struct mvs_chip_info mvs_chips[] = {
[chip_6320] = { 1, 2, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
[chip_6440] = { 1, 4, 0x400, 17, 16, 9, &mvs_64xx_dispatch, },
@ -109,7 +119,6 @@ static void __devinit mvs_phy_init(struct mvs_info *mvi, int phy_id)
static void mvs_free(struct mvs_info *mvi)
{
int i;
struct mvs_wq *mwq;
int slot_nr;
@ -121,12 +130,8 @@ static void mvs_free(struct mvs_info *mvi)
else
slot_nr = MVS_SLOTS;
for (i = 0; i < mvi->tags_num; i++) {
struct mvs_slot_info *slot = &mvi->slot_info[i];
if (slot->buf)
dma_free_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
slot->buf, slot->buf_dma);
}
if (mvi->dma_pool)
pci_pool_destroy(mvi->dma_pool);
if (mvi->tx)
dma_free_coherent(mvi->dev,
@ -215,6 +220,7 @@ static irqreturn_t mvs_interrupt(int irq, void *opaque)
static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
{
int i = 0, slot_nr;
char pool_name[32];
if (mvi->flags & MVF_FLAG_SOC)
slot_nr = MVS_SOC_SLOTS;
@ -274,18 +280,14 @@ static int __devinit mvs_alloc(struct mvs_info *mvi, struct Scsi_Host *shost)
if (!mvi->bulk_buffer)
goto err_out;
#endif
for (i = 0; i < slot_nr; i++) {
struct mvs_slot_info *slot = &mvi->slot_info[i];
slot->buf = dma_alloc_coherent(mvi->dev, MVS_SLOT_BUF_SZ,
&slot->buf_dma, GFP_KERNEL);
if (!slot->buf) {
printk(KERN_DEBUG"failed to allocate slot->buf.\n");
sprintf(pool_name, "%s%d", "mvs_dma_pool", mvi->id);
mvi->dma_pool = pci_pool_create(pool_name, mvi->pdev, MVS_SLOT_BUF_SZ, 16, 0);
if (!mvi->dma_pool) {
printk(KERN_DEBUG "failed to create dma pool %s.\n", pool_name);
goto err_out;
}
memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
++mvi->tags_num;
}
mvi->tags_num = slot_nr;
/* Initialize tags */
mvs_tag_init(mvi);
return 0;
@ -486,7 +488,7 @@ static void __devinit mvs_post_sas_ha_init(struct Scsi_Host *shost,
sha->num_phys = nr_core * chip_info->n_phy;
sha->lldd_max_execute_num = 1;
sha->lldd_max_execute_num = lldd_max_execute_num;
if (mvi->flags & MVF_FLAG_SOC)
can_queue = MVS_SOC_CAN_QUEUE;
@ -710,6 +712,14 @@ static int __init mvs_init(void)
if (!mvs_stt)
return -ENOMEM;
mvs_task_list_cache = kmem_cache_create("mvs_task_list", sizeof(struct mvs_task_list),
0, SLAB_HWCACHE_ALIGN, NULL);
if (!mvs_task_list_cache) {
rc = -ENOMEM;
mv_printk("%s: mvs_task_list_cache alloc failed! \n", __func__);
goto err_out;
}
rc = pci_register_driver(&mvs_pci_driver);
if (rc)
@ -726,6 +736,7 @@ static void __exit mvs_exit(void)
{
pci_unregister_driver(&mvs_pci_driver);
sas_release_transport(mvs_stt);
kmem_cache_destroy(mvs_task_list_cache);
}
module_init(mvs_init);

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@ -862,178 +863,286 @@ static int mvs_task_prep_ssp(struct mvs_info *mvi,
}
#define DEV_IS_GONE(mvi_dev) ((!mvi_dev || (mvi_dev->dev_type == NO_DEVICE)))
static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
struct completion *completion,int is_tmf,
struct mvs_tmf_task *tmf)
static int mvs_task_prep(struct sas_task *task, struct mvs_info *mvi, int is_tmf,
struct mvs_tmf_task *tmf, int *pass)
{
struct domain_device *dev = task->dev;
struct mvs_device *mvi_dev = (struct mvs_device *)dev->lldd_dev;
struct mvs_info *mvi = mvi_dev->mvi_info;
struct mvs_device *mvi_dev = dev->lldd_dev;
struct mvs_task_exec_info tei;
struct sas_task *t = task;
struct mvs_slot_info *slot;
u32 tag = 0xdeadbeef, rc, n_elem = 0;
u32 n = num, pass = 0;
unsigned long flags = 0, flags_libsas = 0;
u32 tag = 0xdeadbeef, n_elem = 0;
int rc = 0;
if (!dev->port) {
struct task_status_struct *tsm = &t->task_status;
struct task_status_struct *tsm = &task->task_status;
tsm->resp = SAS_TASK_UNDELIVERED;
tsm->stat = SAS_PHY_DOWN;
/*
* libsas will use dev->port, should
* not call task_done for sata
*/
if (dev->dev_type != SATA_DEV)
t->task_done(t);
return 0;
task->task_done(task);
return rc;
}
spin_lock_irqsave(&mvi->lock, flags);
do {
dev = t->dev;
mvi_dev = dev->lldd_dev;
if (DEV_IS_GONE(mvi_dev)) {
if (mvi_dev)
mv_dprintk("device %d not ready.\n",
mvi_dev->device_id);
else
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
if (DEV_IS_GONE(mvi_dev)) {
if (mvi_dev)
mv_dprintk("device %d not ready.\n",
mvi_dev->device_id);
else
mv_dprintk("device %016llx not ready.\n",
SAS_ADDR(dev->sas_addr));
rc = SAS_PHY_DOWN;
goto out_done;
}
return rc;
}
tei.port = dev->port->lldd_port;
if (tei.port && !tei.port->port_attached && !tmf) {
if (sas_protocol_ata(task->task_proto)) {
struct task_status_struct *ts = &task->task_status;
mv_dprintk("SATA/STP port %d does not attach"
"device.\n", dev->port->id);
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_PHY_DOWN;
if (dev->port->id >= mvi->chip->n_phy)
tei.port = &mvi->port[dev->port->id - mvi->chip->n_phy];
else
tei.port = &mvi->port[dev->port->id];
task->task_done(task);
if (tei.port && !tei.port->port_attached) {
if (sas_protocol_ata(t->task_proto)) {
struct task_status_struct *ts = &t->task_status;
mv_dprintk("port %d does not"
"attached device.\n", dev->port->id);
ts->stat = SAS_PROTO_RESPONSE;
ts->stat = SAS_PHY_DOWN;
spin_unlock_irqrestore(dev->sata_dev.ap->lock,
flags_libsas);
spin_unlock_irqrestore(&mvi->lock, flags);
t->task_done(t);
spin_lock_irqsave(&mvi->lock, flags);
spin_lock_irqsave(dev->sata_dev.ap->lock,
flags_libsas);
if (n > 1)
t = list_entry(t->list.next,
struct sas_task, list);
continue;
} else {
struct task_status_struct *ts = &t->task_status;
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_PHY_DOWN;
t->task_done(t);
if (n > 1)
t = list_entry(t->list.next,
struct sas_task, list);
continue;
}
}
if (!sas_protocol_ata(t->task_proto)) {
if (t->num_scatter) {
n_elem = dma_map_sg(mvi->dev,
t->scatter,
t->num_scatter,
t->data_dir);
if (!n_elem) {
rc = -ENOMEM;
goto err_out;
}
}
} else {
n_elem = t->num_scatter;
struct task_status_struct *ts = &task->task_status;
mv_dprintk("SAS port %d does not attach"
"device.\n", dev->port->id);
ts->resp = SAS_TASK_UNDELIVERED;
ts->stat = SAS_PHY_DOWN;
task->task_done(task);
}
return rc;
}
rc = mvs_tag_alloc(mvi, &tag);
if (rc)
goto err_out;
slot = &mvi->slot_info[tag];
t->lldd_task = NULL;
slot->n_elem = n_elem;
slot->slot_tag = tag;
memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
tei.task = t;
tei.hdr = &mvi->slot[tag];
tei.tag = tag;
tei.n_elem = n_elem;
switch (t->task_proto) {
case SAS_PROTOCOL_SMP:
rc = mvs_task_prep_smp(mvi, &tei);
break;
case SAS_PROTOCOL_SSP:
rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
break;
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
rc = mvs_task_prep_ata(mvi, &tei);
break;
default:
dev_printk(KERN_ERR, mvi->dev,
"unknown sas_task proto: 0x%x\n",
t->task_proto);
rc = -EINVAL;
break;
if (!sas_protocol_ata(task->task_proto)) {
if (task->num_scatter) {
n_elem = dma_map_sg(mvi->dev,
task->scatter,
task->num_scatter,
task->data_dir);
if (!n_elem) {
rc = -ENOMEM;
goto prep_out;
}
}
} else {
n_elem = task->num_scatter;
}
if (rc) {
mv_dprintk("rc is %x\n", rc);
goto err_out_tag;
}
slot->task = t;
slot->port = tei.port;
t->lldd_task = slot;
list_add_tail(&slot->entry, &tei.port->list);
/* TODO: select normal or high priority */
spin_lock(&t->task_state_lock);
t->task_state_flags |= SAS_TASK_AT_INITIATOR;
spin_unlock(&t->task_state_lock);
rc = mvs_tag_alloc(mvi, &tag);
if (rc)
goto err_out;
mvs_hba_memory_dump(mvi, tag, t->task_proto);
mvi_dev->running_req++;
++pass;
mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
if (n > 1)
t = list_entry(t->list.next, struct sas_task, list);
if (likely(pass))
MVS_CHIP_DISP->start_delivery(mvi, (mvi->tx_prod - 1) &
(MVS_CHIP_SLOT_SZ - 1));
slot = &mvi->slot_info[tag];
} while (--n);
rc = 0;
goto out_done;
task->lldd_task = NULL;
slot->n_elem = n_elem;
slot->slot_tag = tag;
slot->buf = pci_pool_alloc(mvi->dma_pool, GFP_ATOMIC, &slot->buf_dma);
if (!slot->buf)
goto err_out_tag;
memset(slot->buf, 0, MVS_SLOT_BUF_SZ);
tei.task = task;
tei.hdr = &mvi->slot[tag];
tei.tag = tag;
tei.n_elem = n_elem;
switch (task->task_proto) {
case SAS_PROTOCOL_SMP:
rc = mvs_task_prep_smp(mvi, &tei);
break;
case SAS_PROTOCOL_SSP:
rc = mvs_task_prep_ssp(mvi, &tei, is_tmf, tmf);
break;
case SAS_PROTOCOL_SATA:
case SAS_PROTOCOL_STP:
case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP:
rc = mvs_task_prep_ata(mvi, &tei);
break;
default:
dev_printk(KERN_ERR, mvi->dev,
"unknown sas_task proto: 0x%x\n",
task->task_proto);
rc = -EINVAL;
break;
}
if (rc) {
mv_dprintk("rc is %x\n", rc);
goto err_out_slot_buf;
}
slot->task = task;
slot->port = tei.port;
task->lldd_task = slot;
list_add_tail(&slot->entry, &tei.port->list);
spin_lock(&task->task_state_lock);
task->task_state_flags |= SAS_TASK_AT_INITIATOR;
spin_unlock(&task->task_state_lock);
mvs_hba_memory_dump(mvi, tag, task->task_proto);
mvi_dev->running_req++;
++(*pass);
mvi->tx_prod = (mvi->tx_prod + 1) & (MVS_CHIP_SLOT_SZ - 1);
return rc;
err_out_slot_buf:
pci_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
err_out_tag:
mvs_tag_free(mvi, tag);
err_out:
dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
if (!sas_protocol_ata(t->task_proto))
dev_printk(KERN_ERR, mvi->dev, "mvsas prep failed[%d]!\n", rc);
if (!sas_protocol_ata(task->task_proto))
if (n_elem)
dma_unmap_sg(mvi->dev, t->scatter, n_elem,
t->data_dir);
out_done:
dma_unmap_sg(mvi->dev, task->scatter, n_elem,
task->data_dir);
prep_out:
return rc;
}
static struct mvs_task_list *mvs_task_alloc_list(int *num, gfp_t gfp_flags)
{
struct mvs_task_list *first = NULL;
for (; *num > 0; --*num) {
struct mvs_task_list *mvs_list = kmem_cache_zalloc(mvs_task_list_cache, gfp_flags);
if (!mvs_list)
break;
INIT_LIST_HEAD(&mvs_list->list);
if (!first)
first = mvs_list;
else
list_add_tail(&mvs_list->list, &first->list);
}
return first;
}
static inline void mvs_task_free_list(struct mvs_task_list *mvs_list)
{
LIST_HEAD(list);
struct list_head *pos, *a;
struct mvs_task_list *mlist = NULL;
__list_add(&list, mvs_list->list.prev, &mvs_list->list);
list_for_each_safe(pos, a, &list) {
list_del_init(pos);
mlist = list_entry(pos, struct mvs_task_list, list);
kmem_cache_free(mvs_task_list_cache, mlist);
}
}
static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
struct completion *completion, int is_tmf,
struct mvs_tmf_task *tmf)
{
struct domain_device *dev = task->dev;
struct mvs_info *mvi = NULL;
u32 rc = 0;
u32 pass = 0;
unsigned long flags = 0;
mvi = ((struct mvs_device *)task->dev->lldd_dev)->mvi_info;
if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
spin_unlock_irq(dev->sata_dev.ap->lock);
spin_lock_irqsave(&mvi->lock, flags);
rc = mvs_task_prep(task, mvi, is_tmf, tmf, &pass);
if (rc)
dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
if (likely(pass))
MVS_CHIP_DISP->start_delivery(mvi, (mvi->tx_prod - 1) &
(MVS_CHIP_SLOT_SZ - 1));
spin_unlock_irqrestore(&mvi->lock, flags);
if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
spin_lock_irq(dev->sata_dev.ap->lock);
return rc;
}
static int mvs_collector_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
struct completion *completion, int is_tmf,
struct mvs_tmf_task *tmf)
{
struct domain_device *dev = task->dev;
struct mvs_prv_info *mpi = dev->port->ha->lldd_ha;
struct mvs_info *mvi = NULL;
struct sas_task *t = task;
struct mvs_task_list *mvs_list = NULL, *a;
LIST_HEAD(q);
int pass[2] = {0};
u32 rc = 0;
u32 n = num;
unsigned long flags = 0;
mvs_list = mvs_task_alloc_list(&n, gfp_flags);
if (n) {
printk(KERN_ERR "%s: mvs alloc list failed.\n", __func__);
rc = -ENOMEM;
goto free_list;
}
__list_add(&q, mvs_list->list.prev, &mvs_list->list);
list_for_each_entry(a, &q, list) {
a->task = t;
t = list_entry(t->list.next, struct sas_task, list);
}
list_for_each_entry(a, &q , list) {
t = a->task;
mvi = ((struct mvs_device *)t->dev->lldd_dev)->mvi_info;
spin_lock_irqsave(&mvi->lock, flags);
rc = mvs_task_prep(t, mvi, is_tmf, tmf, &pass[mvi->id]);
if (rc)
dev_printk(KERN_ERR, mvi->dev, "mvsas exec failed[%d]!\n", rc);
spin_unlock_irqrestore(&mvi->lock, flags);
}
if (likely(pass[0]))
MVS_CHIP_DISP->start_delivery(mpi->mvi[0],
(mpi->mvi[0]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
if (likely(pass[1]))
MVS_CHIP_DISP->start_delivery(mpi->mvi[1],
(mpi->mvi[1]->tx_prod - 1) & (MVS_CHIP_SLOT_SZ - 1));
list_del_init(&q);
free_list:
if (mvs_list)
mvs_task_free_list(mvs_list);
return rc;
}
int mvs_queue_command(struct sas_task *task, const int num,
gfp_t gfp_flags)
{
return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
struct mvs_device *mvi_dev = task->dev->lldd_dev;
struct sas_ha_struct *sas = mvi_dev->mvi_info->sas;
if (sas->lldd_max_execute_num < 2)
return mvs_task_exec(task, num, gfp_flags, NULL, 0, NULL);
else
return mvs_collector_task_exec(task, num, gfp_flags, NULL, 0, NULL);
}
static void mvs_slot_free(struct mvs_info *mvi, u32 rx_desc)
@ -1067,6 +1176,11 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task,
/* do nothing */
break;
}
if (slot->buf) {
pci_pool_free(mvi->dma_pool, slot->buf, slot->buf_dma);
slot->buf = NULL;
}
list_del_init(&slot->entry);
task->lldd_task = NULL;
slot->task = NULL;
@ -1255,6 +1369,7 @@ static void mvs_port_notify_formed(struct asd_sas_phy *sas_phy, int lock)
spin_lock_irqsave(&mvi->lock, flags);
port->port_attached = 1;
phy->port = port;
sas_port->lldd_port = port;
if (phy->phy_type & PORT_TYPE_SAS) {
port->wide_port_phymap = sas_port->phy_mask;
mv_printk("set wide port phy map %x\n", sas_port->phy_mask);

View File

@ -3,6 +3,7 @@
*
* Copyright 2007 Red Hat, Inc.
* Copyright 2008 Marvell. <kewei@marvell.com>
* Copyright 2009-2011 Marvell. <yuxiangl@marvell.com>
*
* This file is licensed under GPLv2.
*
@ -67,6 +68,7 @@ extern struct mvs_tgt_initiator mvs_tgt;
extern struct mvs_info *tgt_mvi;
extern const struct mvs_dispatch mvs_64xx_dispatch;
extern const struct mvs_dispatch mvs_94xx_dispatch;
extern struct kmem_cache *mvs_task_list_cache;
#define DEV_IS_EXPANDER(type) \
((type == EDGE_DEV) || (type == FANOUT_DEV))
@ -341,6 +343,7 @@ struct mvs_info {
dma_addr_t bulk_buffer_dma;
#define TRASH_BUCKET_SIZE 0x20000
#endif
void *dma_pool;
struct mvs_slot_info slot_info[0];
};
@ -367,6 +370,11 @@ struct mvs_task_exec_info {
int n_elem;
};
struct mvs_task_list {
struct sas_task *task;
struct list_head list;
};
/******************** function prototype *********************/
void mvs_get_sas_addr(void *buf, u32 buflen);