diff options
Diffstat (limited to 'drivers/scsi/mvsas/mv_sas.c')
-rw-r--r-- | drivers/scsi/mvsas/mv_sas.c | 383 |
1 files changed, 249 insertions, 134 deletions
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index adedaa916ecb..0ef27425c447 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -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]; - - 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; - } - } + task->task_done(task); - 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; + 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; + } - slot = &mvi->slot_info[tag]; + 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); + 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; + } - 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 (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); - 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); - - 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)); + 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); - } while (--n); - rc = 0; - goto out_done; + 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); |