diff options
Diffstat (limited to 'drivers/scsi/libsas')
| -rw-r--r-- | drivers/scsi/libsas/Kconfig | 19 | ||||
| -rw-r--r-- | drivers/scsi/libsas/Makefile | 20 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_ata.c | 539 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_discover.c | 187 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_dump.c | 73 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_dump.h | 30 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_event.c | 187 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 919 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_host_smp.c | 117 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_init.c | 270 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 215 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_phy.c | 99 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_port.c | 183 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_scsi_host.c | 667 | ||||
| -rw-r--r-- | drivers/scsi/libsas/sas_task.c | 29 |
15 files changed, 2040 insertions, 1514 deletions
diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig index 9dafe64e7c7a..c640535d1ac0 100644 --- a/drivers/scsi/libsas/Kconfig +++ b/drivers/scsi/libsas/Kconfig @@ -1,26 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Kernel configuration file for the SAS Class # # Copyright (C) 2005 Adaptec, Inc. All rights reserved. # Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> # -# This file is licensed under GPLv2. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 of the -# License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA -# config SCSI_SAS_LIBSAS tristate "SAS Domain Transport Attributes" @@ -34,6 +18,7 @@ config SCSI_SAS_ATA bool "ATA support for libsas (requires libata)" depends on SCSI_SAS_LIBSAS depends on ATA = y || ATA = SCSI_SAS_LIBSAS + select SATA_HOST help Builds in ATA support into libsas. Will necessitate the loading of libata along with libsas. diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 2e70140f70c3..9dc32736cf21 100644 --- a/drivers/scsi/libsas/Makefile +++ b/drivers/scsi/libsas/Makefile @@ -1,35 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Kernel Makefile for the libsas helpers # # Copyright (C) 2005 Adaptec, Inc. All rights reserved. # Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> # -# This file is licensed under GPLv2. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; version 2 of the -# License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o libsas-y += sas_init.o \ sas_phy.o \ sas_port.o \ sas_event.o \ - sas_dump.o \ sas_discover.o \ sas_expander.o \ sas_scsi_host.o \ sas_task.o libsas-$(CONFIG_SCSI_SAS_ATA) += sas_ata.o libsas-$(CONFIG_SCSI_SAS_HOST_SMP) += sas_host_smp.o + +ccflags-y := -DDEBUG -I$(srctree)/drivers/scsi diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 87f5e694dbed..bcecb4911da9 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -1,24 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Support for SATA devices on Serial Attached SCSI (SAS) controllers * * Copyright (C) 2006 IBM Corporation * * Written by: Darrick J. Wong <djwong@us.ibm.com>, IBM Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA */ #include <linux/scatterlist.h> @@ -34,8 +20,8 @@ #include <scsi/scsi.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> -#include "../scsi_sas_internal.h" -#include "../scsi_transport_api.h" +#include "scsi_sas_internal.h" +#include "scsi_transport_api.h" #include <scsi/scsi_eh.h> static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) @@ -49,46 +35,38 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) /* ts->resp == SAS_TASK_COMPLETE */ /* task delivered, what happened afterwards? */ switch (ts->stat) { - case SAS_DEV_NO_RESPONSE: - return AC_ERR_TIMEOUT; - - case SAS_INTERRUPTED: - case SAS_PHY_DOWN: - case SAS_NAK_R_ERR: - return AC_ERR_ATA_BUS; - - - case SAS_DATA_UNDERRUN: - /* - * Some programs that use the taskfile interface - * (smartctl in particular) can cause underrun - * problems. Ignore these errors, perhaps at our - * peril. - */ - return 0; - - case SAS_DATA_OVERRUN: - case SAS_QUEUE_FULL: - case SAS_DEVICE_UNKNOWN: - case SAS_SG_ERR: - return AC_ERR_INVALID; - - case SAS_OPEN_TO: - case SAS_OPEN_REJECT: - SAS_DPRINTK("%s: Saw error %d. What to do?\n", - __func__, ts->stat); - return AC_ERR_OTHER; - - case SAM_STAT_CHECK_CONDITION: - case SAS_ABORTED_TASK: - return AC_ERR_DEV; - - case SAS_PROTO_RESPONSE: - /* This means the ending_fis has the error - * value; return 0 here to collect it */ - return 0; - default: - return 0; + case SAS_DEV_NO_RESPONSE: + return AC_ERR_TIMEOUT; + case SAS_INTERRUPTED: + case SAS_PHY_DOWN: + case SAS_NAK_R_ERR: + return AC_ERR_ATA_BUS; + case SAS_DATA_UNDERRUN: + /* + * Some programs that use the taskfile interface + * (smartctl in particular) can cause underrun + * problems. Ignore these errors, perhaps at our + * peril. + */ + return 0; + case SAS_DATA_OVERRUN: + case SAS_QUEUE_FULL: + case SAS_DEVICE_UNKNOWN: + case SAS_OPEN_TO: + case SAS_OPEN_REJECT: + pr_warn("%s: Saw error %d. What to do?\n", + __func__, ts->stat); + return AC_ERR_OTHER; + case SAM_STAT_CHECK_CONDITION: + case SAS_ABORTED_TASK: + return AC_ERR_DEV; + case SAS_PROTO_RESPONSE: + /* This means the ending_fis has the error + * value; return 0 here to collect it + */ + return 0; + default: + return 0; } } @@ -123,7 +101,7 @@ static void sas_ata_task_done(struct sas_task *task) spin_lock_irqsave(ap->lock, flags); /* check if we lost the race with libata/sas_ata_post_internal() */ - if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) { + if (unlikely(ata_port_is_frozen(ap))) { spin_unlock_irqrestore(ap->lock, flags); if (qc->scsicmd) goto qc_already_gone; @@ -136,9 +114,10 @@ static void sas_ata_task_done(struct sas_task *task) } } - if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD || - ((stat->stat == SAM_STAT_CHECK_CONDITION && - dev->sata_dev.class == ATA_DEV_ATAPI))) { + if (stat->stat == SAS_PROTO_RESPONSE || + stat->stat == SAS_SAM_STAT_GOOD || + (stat->stat == SAS_SAM_STAT_CHECK_CONDITION && + dev->sata_dev.class == ATA_DEV_ATAPI)) { memcpy(dev->sata_dev.fis, resp->ending_fis, ATA_RESP_FIS_SIZE); if (!link->sactive) { @@ -146,23 +125,22 @@ static void sas_ata_task_done(struct sas_task *task) } else { link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]); if (unlikely(link->eh_info.err_mask)) - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; } } else { ac = sas_to_ata_err(stat); if (ac) { - SAS_DPRINTK("%s: SAS error %x\n", __func__, - stat->stat); + pr_warn("%s: SAS error 0x%x\n", __func__, stat->stat); /* We saw a SAS error. Send a vague error. */ if (!link->sactive) { qc->err_mask = ac; } else { link->eh_info.err_mask |= AC_ERR_DEV; - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; } - dev->sata_dev.fis[3] = 0x04; /* status err */ - dev->sata_dev.fis[2] = ATA_ERR; + dev->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */ + dev->sata_dev.fis[3] = ATA_ABORTED; /* tf error */ } } @@ -175,8 +153,8 @@ qc_already_gone: } static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) + __must_hold(ap->lock) { - unsigned long flags; struct sas_task *task; struct scatterlist *sg; int ret = AC_ERR_SYSTEM; @@ -184,13 +162,10 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct domain_device *dev = ap->private_data; struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *host = sas_ha->core.shost; + struct Scsi_Host *host = sas_ha->shost; struct sas_internal *i = to_sas_internal(host->transportt); - /* TODO: audit callers to ensure they are ready for qc_issue to - * unconditionally re-enable interrupts - */ - local_irq_save(flags); + /* TODO: we should try to remove that unlock */ spin_unlock(ap->lock); /* If the device fell off, no sense in issuing commands */ @@ -204,14 +179,9 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->task_proto = SAS_PROTOCOL_STP; task->task_done = sas_ata_task_done; - if (qc->tf.command == ATA_CMD_FPDMA_WRITE || - qc->tf.command == ATA_CMD_FPDMA_READ || - qc->tf.command == ATA_CMD_FPDMA_RECV || - qc->tf.command == ATA_CMD_FPDMA_SEND || - qc->tf.command == ATA_CMD_NCQ_NON_DATA) { - /* Need to zero out the tag libata assigned us */ + /* For NCQ commands, zero out the tag libata assigned us */ + if (ata_is_ncq(qc->tf.protocol)) qc->tf.nsect = 0; - } ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, (u8 *)&task->ata_task.fis); task->uldd_task = qc; @@ -219,29 +189,32 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); task->total_xfer_len = qc->nbytes; task->num_scatter = qc->n_elem; + task->data_dir = qc->dma_dir; + } else if (!ata_is_data(qc->tf.protocol)) { + task->data_dir = DMA_NONE; } else { for_each_sg(qc->sg, sg, qc->n_elem, si) xfer += sg_dma_len(sg); task->total_xfer_len = xfer; task->num_scatter = si; + task->data_dir = qc->dma_dir; } - - task->data_dir = qc->dma_dir; task->scatter = qc->sg; - task->ata_task.retry_count = 1; - task->task_state_flags = SAS_TASK_STATE_PENDING; qc->lldd_task = task; task->ata_task.use_ncq = ata_is_ncq(qc->tf.protocol); task->ata_task.dma_xfer = ata_is_dma(qc->tf.protocol); + if (qc->flags & ATA_QCFLAG_RESULT_TF) + task->ata_task.return_fis_on_success = 1; + if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, task); ret = i->dft->lldd_execute_task(task, GFP_ATOMIC); if (ret) { - SAS_DPRINTK("lldd_execute_task returned: %d\n", ret); + pr_debug("lldd_execute_task returned: %d\n", ret); if (qc->scsicmd) ASSIGN_SAS_TASK(qc->scsicmd, NULL); @@ -252,26 +225,34 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) out: spin_lock(ap->lock); - local_irq_restore(flags); return ret; } -static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) +static void sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc) { struct domain_device *dev = qc->ap->private_data; ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf); - return true; } static struct sas_internal *dev_to_sas_internal(struct domain_device *dev) { - return to_sas_internal(dev->port->ha->core.shost->transportt); + return to_sas_internal(dev->port->ha->shost->transportt); } -static int sas_get_ata_command_set(struct domain_device *dev); +static int sas_get_ata_command_set(struct domain_device *dev) +{ + struct ata_taskfile tf; + + if (dev->dev_type == SAS_SATA_PENDING) + return ATA_DEV_UNKNOWN; + + ata_tf_from_fis(dev->frame_rcvd, &tf); -int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) + return ata_dev_classify(&tf); +} + +static int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) { if (phy->attached_tproto & SAS_PROTOCOL_STP) dev->tproto = phy->attached_tproto; @@ -287,9 +268,9 @@ int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy) res = sas_get_report_phy_sata(dev->parent, phy->phy_id, &dev->sata_dev.rps_resp); if (res) { - SAS_DPRINTK("report phy sata to %016llx:0x%x returned " - "0x%x\n", SAS_ADDR(dev->parent->sas_addr), - phy->phy_id, res); + pr_debug("report phy sata to %016llx:%02d returned 0x%x\n", + SAS_ADDR(dev->parent->sas_addr), + phy->phy_id, res); return res; } memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis, @@ -317,6 +298,31 @@ static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy) return 1; } +int smp_ata_check_ready_type(struct ata_link *link) +{ + struct domain_device *dev = link->ap->private_data; + struct sas_phy *phy = sas_get_local_phy(dev); + struct domain_device *ex_dev = dev->parent; + enum sas_device_type type = SAS_PHY_UNUSED; + u8 sas_addr[SAS_ADDR_SIZE]; + int res; + + res = sas_get_phy_attached_dev(ex_dev, phy->number, sas_addr, &type); + sas_put_local_phy(phy); + if (res) + return res; + + switch (type) { + case SAS_SATA_PENDING: + return 0; + case SAS_END_DEVICE: + return 1; + default: + return -ENODEV; + } +} +EXPORT_SYMBOL_GPL(smp_ata_check_ready_type); + static int smp_ata_check_ready(struct ata_link *link) { int res; @@ -343,6 +349,7 @@ static int smp_ata_check_ready(struct ata_link *link) case SAS_END_DEVICE: if (ex_phy->attached_sata_dev) return sas_ata_clear_pending(dev, ex_phy); + fallthrough; default: return -ENODEV; } @@ -379,7 +386,7 @@ static int sas_ata_printk(const char *level, const struct domain_device *ddev, vaf.fmt = fmt; vaf.va = &args; - r = printk("%ssas: ata%u: %s: %pV", + r = printk("%s" SAS_FMT "ata%u: %s: %pV", level, ap->print_id, dev_name(dev), &vaf); va_end(args); @@ -387,22 +394,14 @@ static int sas_ata_printk(const char *level, const struct domain_device *ddev, return r; } -static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static int sas_ata_wait_after_reset(struct domain_device *dev, unsigned long deadline) { - int ret = 0, res; - struct sas_phy *phy; - struct ata_port *ap = link->ap; + struct sata_device *sata_dev = &dev->sata_dev; int (*check_ready)(struct ata_link *link); - struct domain_device *dev = ap->private_data; - struct sas_internal *i = dev_to_sas_internal(dev); - - res = i->dft->lldd_I_T_nexus_reset(dev); - if (res == -ENODEV) - return res; - - if (res != TMF_RESP_FUNC_COMPLETE) - sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n"); + struct ata_port *ap = sata_dev->ap; + struct ata_link *link = &ap->link; + struct sas_phy *phy; + int ret; phy = sas_get_local_phy(dev); if (scsi_is_sas_phy_local(phy)) @@ -415,6 +414,26 @@ static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, if (ret && ret != -EAGAIN) sas_ata_printk(KERN_ERR, dev, "reset failed (errno=%d)\n", ret); + return ret; +} + +static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct domain_device *dev = ap->private_data; + struct sas_internal *i = dev_to_sas_internal(dev); + int ret; + + ret = i->dft->lldd_I_T_nexus_reset(dev); + if (ret == -ENODEV) + return ret; + + if (ret != TMF_RESP_FUNC_COMPLETE) + sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n"); + + ret = sas_ata_wait_after_reset(dev, deadline); + *class = dev->sata_dev.class; ap->cbl = ATA_CBL_SATA; @@ -435,8 +454,7 @@ static void sas_ata_internal_abort(struct sas_task *task) if (task->task_state_flags & SAS_TASK_STATE_ABORTED || task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: Task %p already finished.\n", __func__, - task); + pr_debug("%s: Task %p already finished.\n", __func__, task); goto out; } task->task_state_flags |= SAS_TASK_STATE_ABORTED; @@ -456,7 +474,7 @@ static void sas_ata_internal_abort(struct sas_task *task) * aborted ata tasks, otherwise we (likely) leak the sas task * here */ - SAS_DPRINTK("%s: Task %p leaked.\n", __func__, task); + pr_warn("%s: Task %p leaked.\n", __func__, task); if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; @@ -469,7 +487,7 @@ static void sas_ata_internal_abort(struct sas_task *task) static void sas_ata_post_internal(struct ata_queued_cmd *qc) { - if (qc->flags & ATA_QCFLAG_FAILED) + if (qc->flags & ATA_QCFLAG_EH) qc->err_mask |= AC_ERR_OTHER; if (qc->err_mask) { @@ -526,59 +544,81 @@ void sas_ata_end_eh(struct ata_port *ap) spin_unlock_irqrestore(&ha->lock, flags); } +static int sas_ata_prereset(struct ata_link *link, unsigned long deadline) +{ + struct ata_port *ap = link->ap; + struct domain_device *dev = ap->private_data; + struct sas_phy *local_phy = sas_get_local_phy(dev); + int res = 0; + + if (!local_phy->enabled || test_bit(SAS_DEV_GONE, &dev->state)) + res = -ENOENT; + sas_put_local_phy(local_phy); + + return res; +} + static struct ata_port_operations sas_sata_ops = { - .prereset = ata_std_prereset, - .hardreset = sas_ata_hard_reset, - .postreset = ata_std_postreset, + .reset.prereset = sas_ata_prereset, + .reset.hardreset = sas_ata_hard_reset, .error_handler = ata_std_error_handler, .post_internal_cmd = sas_ata_post_internal, .qc_defer = ata_std_qc_defer, - .qc_prep = ata_noop_qc_prep, .qc_issue = sas_ata_qc_issue, .qc_fill_rtf = sas_ata_qc_fill_rtf, - .port_start = ata_sas_port_start, - .port_stop = ata_sas_port_stop, .set_dmamode = sas_ata_set_dmamode, .sched_eh = sas_ata_sched_eh, .end_eh = sas_ata_end_eh, }; -static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | - ATA_FLAG_SAS_HOST | ATA_FLAG_FPDMA_AUX, - .pio_mask = ATA_PIO4, - .mwdma_mask = ATA_MWDMA2, - .udma_mask = ATA_UDMA6, - .port_ops = &sas_sata_ops -}; - int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; - struct Scsi_Host *shost = ha->core.shost; + struct Scsi_Host *shost = ha->shost; + struct ata_host *ata_host; struct ata_port *ap; int rc; - ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops); - ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, - &sata_port_info, - shost); + ata_host = kzalloc(sizeof(*ata_host), GFP_KERNEL); + if (!ata_host) { + pr_err("ata host alloc failed.\n"); + return -ENOMEM; + } + + ata_host_init(ata_host, ha->dev, &sas_sata_ops); + + ap = ata_port_alloc(ata_host); if (!ap) { - SAS_DPRINTK("ata_sas_port_alloc failed.\n"); - return -ENODEV; + pr_err("ata_port_alloc failed.\n"); + rc = -ENODEV; + goto free_host; } + ap->port_no = 0; + ap->pio_mask = ATA_PIO4; + ap->mwdma_mask = ATA_MWDMA2; + ap->udma_mask = ATA_UDMA6; + ap->flags |= ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | + ATA_FLAG_SAS_HOST | ATA_FLAG_FPDMA_AUX; + ap->ops = &sas_sata_ops; ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; - rc = ata_sas_port_init(ap); - if (rc) { - ata_sas_port_destroy(ap); - return rc; - } + + rc = ata_tport_add(ata_host->dev, ap); + if (rc) + goto free_port; + + found_dev->sata_dev.ata_host = ata_host; found_dev->sata_dev.ap = ap; return 0; + +free_port: + ata_port_free(ap); +free_host: + ata_host_put(ata_host); + return rc; } void sas_ata_task_abort(struct sas_task *task) @@ -588,37 +628,18 @@ void sas_ata_task_abort(struct sas_task *task) /* Bounce SCSI-initiated commands to the SCSI EH */ if (qc->scsicmd) { - struct request_queue *q = qc->scsicmd->device->request_queue; - unsigned long flags; - - spin_lock_irqsave(q->queue_lock, flags); - blk_abort_request(qc->scsicmd->request); - spin_unlock_irqrestore(q->queue_lock, flags); + blk_abort_request(scsi_cmd_to_rq(qc->scsicmd)); return; } /* Internal command, fake a timeout and complete. */ qc->flags &= ~ATA_QCFLAG_ACTIVE; - qc->flags |= ATA_QCFLAG_FAILED; + qc->flags |= ATA_QCFLAG_EH; qc->err_mask |= AC_ERR_TIMEOUT; waiting = qc->private_data; complete(waiting); } -static int sas_get_ata_command_set(struct domain_device *dev) -{ - struct dev_to_host_fis *fis = - (struct dev_to_host_fis *) dev->frame_rcvd; - struct ata_taskfile tf; - - if (dev->dev_type == SAS_SATA_PENDING) - return ATA_DEV_UNKNOWN; - - ata_tf_from_fis((const u8 *)fis, &tf); - - return ata_dev_classify(&tf); -} - void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; @@ -628,7 +649,7 @@ void sas_probe_sata(struct asd_sas_port *port) if (!dev_is_sata(dev)) continue; - ata_sas_async_probe(dev->sata_dev.ap); + ata_port_probe(dev->sata_dev.ap); } mutex_unlock(&port->ha->disco_mutex); @@ -641,12 +662,74 @@ void sas_probe_sata(struct asd_sas_port *port) /* if libata could not bring the link up, don't surface * the device */ - if (ata_dev_disabled(sas_to_ata_dev(dev))) + if (!ata_dev_enabled(sas_to_ata_dev(dev))) sas_fail_probe(dev, __func__, -ENODEV); } } +int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id) +{ + struct sas_rphy *rphy; + int ret; + + if (child->linkrate > parent->min_linkrate) { + struct sas_phy *cphy = child->phy; + enum sas_linkrate min_prate = cphy->minimum_linkrate, + parent_min_lrate = parent->min_linkrate, + min_linkrate = (min_prate > parent_min_lrate) ? + parent_min_lrate : 0; + struct sas_phy_linkrates rates = { + .maximum_linkrate = parent->min_linkrate, + .minimum_linkrate = min_linkrate, + }; + + pr_notice("ex %016llx phy%02d SATA device linkrate > min pathway connection rate, attempting to lower device linkrate\n", + SAS_ADDR(child->sas_addr), phy_id); + ret = sas_smp_phy_control(parent, phy_id, + PHY_FUNC_LINK_RESET, &rates); + if (ret) { + pr_err("ex %016llx phy%02d SATA device could not set linkrate (%d)\n", + SAS_ADDR(child->sas_addr), phy_id, ret); + return ret; + } + pr_notice("ex %016llx phy%02d SATA device set linkrate successfully\n", + SAS_ADDR(child->sas_addr), phy_id); + child->linkrate = child->min_linkrate; + } + ret = sas_get_ata_info(child, phy); + if (ret) + return ret; + + sas_init_dev(child); + ret = sas_ata_init(child); + if (ret) + return ret; + + rphy = sas_end_device_alloc(phy->port); + if (!rphy) + return -ENOMEM; + + rphy->identify.phy_identifier = phy_id; + child->rphy = rphy; + get_device(&rphy->dev); + + list_add_tail(&child->disco_list_node, &parent->port->disco_list); + + ret = sas_discover_sata(child); + if (ret) { + pr_notice("sas_discover_sata() for device %16llx at %016llx:%02d returned 0x%x\n", + SAS_ADDR(child->sas_addr), + SAS_ADDR(parent->sas_addr), phy_id, ret); + sas_rphy_free(child->rphy); + list_del(&child->disco_list_node); + return ret; + } + + return 0; +} + static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func) { struct domain_device *dev, *n; @@ -708,7 +791,7 @@ void sas_resume_sata(struct asd_sas_port *port) } /** - * sas_discover_sata -- discover an STP/SATA domain device + * sas_discover_sata - discover an STP/SATA domain device * @dev: pointer to struct domain_device of interest * * Devices directly attached to a HA port, have no parents. All other @@ -717,20 +800,13 @@ void sas_resume_sata(struct asd_sas_port *port) */ int sas_discover_sata(struct domain_device *dev) { - int res; - if (dev->dev_type == SAS_SATA_PM) return -ENODEV; dev->sata_dev.class = sas_get_ata_command_set(dev); sas_fill_in_rphy(dev, dev->rphy); - res = sas_notify_lldd_dev_found(dev); - if (res) - return res; - - sas_discover_event(dev->port, DISCE_PROBE); - return 0; + return sas_notify_lldd_dev_found(dev); } static void async_sas_ata_eh(void *data, async_cookie_t cookie) @@ -740,7 +816,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie) struct sas_ha_struct *ha = dev->port->ha; sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n"); - ata_scsi_port_error_handler(ha->core.shost, ap); + ata_scsi_port_error_handler(ha->shost, ap); sas_put_device(dev); } @@ -787,8 +863,7 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost) sas_enable_revalidation(sas_ha); } -void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, - struct list_head *done_q) +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q) { struct scsi_cmnd *cmd, *n; struct domain_device *eh_dev; @@ -852,11 +927,115 @@ EXPORT_SYMBOL_GPL(sas_ata_schedule_reset); void sas_ata_wait_eh(struct domain_device *dev) { - struct ata_port *ap; + ata_port_wait_eh(dev->sata_dev.ap); +} - if (!dev_is_sata(dev)) - return; +void sas_ata_device_link_abort(struct domain_device *device, bool force_reset) +{ + struct ata_port *ap = device->sata_dev.ap; + struct ata_link *link = &ap->link; + unsigned long flags; - ap = dev->sata_dev.ap; - ata_port_wait_eh(ap); + spin_lock_irqsave(ap->lock, flags); + device->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */ + device->sata_dev.fis[3] = ATA_ABORTED; /* tf error */ + + link->eh_info.err_mask |= AC_ERR_DEV; + if (force_reset) + link->eh_info.action |= ATA_EH_RESET; + ata_link_abort(link); + spin_unlock_irqrestore(ap->lock, flags); +} +EXPORT_SYMBOL_GPL(sas_ata_device_link_abort); + +int sas_execute_ata_cmd(struct domain_device *device, u8 *fis, int force_phy_id) +{ + struct sas_tmf_task tmf_task = {}; + return sas_execute_tmf(device, fis, sizeof(struct host_to_dev_fis), + force_phy_id, &tmf_task); +} +EXPORT_SYMBOL_GPL(sas_execute_ata_cmd); + +static ssize_t sas_ncq_prio_supported_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + bool supported; + int rc; + + rc = ata_ncq_prio_supported(ddev->sata_dev.ap, sdev, &supported); + if (rc) + return rc; + + return sysfs_emit(buf, "%d\n", supported); } + +static struct device_attribute dev_attr_sas_ncq_prio_supported = + __ATTR(ncq_prio_supported, S_IRUGO, sas_ncq_prio_supported_show, NULL); + +static ssize_t sas_ncq_prio_enable_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + bool enabled; + int rc; + + rc = ata_ncq_prio_enabled(ddev->sata_dev.ap, sdev, &enabled); + if (rc) + return rc; + + return sysfs_emit(buf, "%d\n", enabled); +} + +static ssize_t sas_ncq_prio_enable_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct scsi_device *sdev = to_scsi_device(device); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + bool enable; + int rc; + + rc = kstrtobool(buf, &enable); + if (rc) + return rc; + + rc = ata_ncq_prio_enable(ddev->sata_dev.ap, sdev, enable); + if (rc) + return rc; + + return len; +} + +static struct device_attribute dev_attr_sas_ncq_prio_enable = + __ATTR(ncq_prio_enable, S_IRUGO | S_IWUSR, + sas_ncq_prio_enable_show, sas_ncq_prio_enable_store); + +static struct attribute *sas_ata_sdev_attrs[] = { + &dev_attr_sas_ncq_prio_supported.attr, + &dev_attr_sas_ncq_prio_enable.attr, + NULL +}; + +static umode_t sas_ata_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + struct device *dev = kobj_to_dev(kobj); + struct scsi_device *sdev = to_scsi_device(dev); + struct domain_device *ddev = sdev_to_domain_dev(sdev); + + if (!dev_is_sata(ddev)) + return 0; + + return attr->mode; +} + +const struct attribute_group sas_ata_sdev_attr_group = { + .attrs = sas_ata_sdev_attrs, + .is_visible = sas_ata_attr_is_visible, +}; +EXPORT_SYMBOL_GPL(sas_ata_sdev_attr_group); diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 60de66252fa2..b07062db50b2 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -1,30 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Serial Attached SCSI (SAS) Discover process * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/scatterlist.h> #include <linux/slab.h> -#include <linux/async.h> #include <scsi/scsi_host.h> #include <scsi/scsi_eh.h> #include "sas_internal.h" @@ -32,7 +15,7 @@ #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> #include <scsi/sas_ata.h> -#include "../scsi_sas_internal.h" +#include "scsi_sas_internal.h" /* ---------- Basic task processing for discovery purposes ---------- */ @@ -55,7 +38,7 @@ void sas_init_dev(struct domain_device *dev) /* ---------- Domain device discovery ---------- */ /** - * sas_get_port_device -- Discover devices which caused port creation + * sas_get_port_device - Discover devices which caused port creation * @port: pointer to struct sas_port of interest * * Devices directly attached to a HA port, have no parent. This is @@ -91,18 +74,27 @@ static int sas_get_port_device(struct asd_sas_port *port) struct dev_to_host_fis *fis = (struct dev_to_host_fis *) dev->frame_rcvd; if (fis->interrupt_reason == 1 && fis->lbal == 1 && - fis->byte_count_low==0x69 && fis->byte_count_high == 0x96 + fis->byte_count_low == 0x69 && fis->byte_count_high == 0x96 && (fis->device & ~0x10) == 0) dev->dev_type = SAS_SATA_PM; else dev->dev_type = SAS_SATA_DEV; dev->tproto = SAS_PROTOCOL_SATA; - } else { + } else if (port->oob_mode == SAS_OOB_MODE) { struct sas_identify_frame *id = (struct sas_identify_frame *) dev->frame_rcvd; dev->dev_type = id->dev_type; dev->iproto = id->initiator_bits; dev->tproto = id->target_bits; + } else { + /* If the oob mode is OOB_NOT_CONNECTED, the port is + * disconnected due to race with PHY down. We cannot + * continue to discover this port + */ + sas_put_device(dev); + pr_warn("Port %016llx is disconnected when discovering\n", + SAS_ADDR(port->attached_sas_addr)); + return -ENODEV; } sas_init_dev(dev); @@ -115,7 +107,7 @@ static int sas_get_port_device(struct asd_sas_port *port) rphy = NULL; break; } - /* fall through */ + fallthrough; case SAS_END_DEVICE: rphy = sas_end_device_alloc(port->port); break; @@ -128,7 +120,7 @@ static int sas_get_port_device(struct asd_sas_port *port) SAS_FANOUT_EXPANDER_DEVICE); break; default: - printk("ERROR: Unidentified device type %d\n", dev->dev_type); + pr_warn("ERROR: Unidentified device type %d\n", dev->dev_type); rphy = NULL; break; } @@ -178,7 +170,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) { int res = 0; struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; struct sas_internal *i = to_sas_internal(shost->transportt); if (!i->dft->lldd_dev_found) @@ -186,21 +178,21 @@ int sas_notify_lldd_dev_found(struct domain_device *dev) res = i->dft->lldd_dev_found(dev); if (res) { - printk("sas: driver on pcidev %s cannot handle " - "device %llx, error:%d\n", - dev_name(sas_ha->dev), - SAS_ADDR(dev->sas_addr), res); + pr_warn("driver on host %s cannot handle device %016llx, error:%d\n", + dev_name(sas_ha->dev), + SAS_ADDR(dev->sas_addr), res); + return res; } set_bit(SAS_DEV_FOUND, &dev->state); kref_get(&dev->kref); - return res; + return 0; } void sas_notify_lldd_dev_gone(struct domain_device *dev) { struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *shost = sas_ha->core.shost; + struct Scsi_Host *shost = sas_ha->shost; struct sas_internal *i = to_sas_internal(shost->transportt); if (!i->dft->lldd_dev_gone) @@ -212,13 +204,9 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) } } -static void sas_probe_devices(struct work_struct *work) +static void sas_probe_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = to_sas_discovery_event(work); - struct asd_sas_port *port = ev->port; - - clear_bit(DISCE_PROBE, &port->disc.pending); /* devices must be domain members before link recovery and probe */ list_for_each_entry(dev, &port->disco_list, disco_list_node) { @@ -246,7 +234,7 @@ static void sas_suspend_devices(struct work_struct *work) struct domain_device *dev; struct sas_discovery_event *ev = to_sas_discovery_event(work); struct asd_sas_port *port = ev->port; - struct Scsi_Host *shost = port->ha->core.shost; + struct Scsi_Host *shost = port->ha->shost; struct sas_internal *si = to_sas_internal(shost->transportt); clear_bit(DISCE_SUSPEND, &port->disc.pending); @@ -264,7 +252,7 @@ static void sas_suspend_devices(struct work_struct *work) * phy_list is not being mutated */ list_for_each_entry(phy, &port->phy_list, port_phy_el) { - if (si->dft->lldd_port_formed) + if (si->dft->lldd_port_deformed) si->dft->lldd_port_deformed(phy); phy->suspended = 1; port->suspended = 1; @@ -282,21 +270,14 @@ static void sas_resume_devices(struct work_struct *work) } /** - * sas_discover_end_dev -- discover an end device (SSP, etc) - * @end: pointer to domain device of interest + * sas_discover_end_dev - discover an end device (SSP, etc) + * @dev: pointer to domain device of interest * * See comment in sas_discover_sata(). */ -int sas_discover_end_dev(struct domain_device *dev) +static int sas_discover_end_dev(struct domain_device *dev) { - int res; - - res = sas_notify_lldd_dev_found(dev); - if (res) - return res; - sas_discover_event(dev->port, DISCE_PROBE); - - return 0; + return sas_notify_lldd_dev_found(dev); } /* ---------- Device registration and unregistration ---------- */ @@ -315,11 +296,14 @@ void sas_free_device(struct kref *kref) dev->phy = NULL; /* remove the phys and ports, everything else should be gone */ - if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) + if (dev_is_expander(dev->dev_type)) kfree(dev->ex_dev.ex_phy); if (dev_is_sata(dev) && dev->sata_dev.ap) { - ata_sas_port_destroy(dev->sata_dev.ap); + ata_tport_delete(dev->sata_dev.ap); + ata_port_free(dev->sata_dev.ap); + ata_host_put(dev->sata_dev.ata_host); + dev->sata_dev.ata_host = NULL; dev->sata_dev.ap = NULL; } @@ -353,13 +337,9 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d sas_put_device(dev); } -static void sas_destruct_devices(struct work_struct *work) +void sas_destruct_devices(struct asd_sas_port *port) { struct domain_device *dev, *n; - struct sas_discovery_event *ev = to_sas_discovery_event(work); - struct asd_sas_port *port = ev->port; - - clear_bit(DISCE_DESTRUCT, &port->disc.pending); list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) { list_del_init(&dev->disco_list_node); @@ -370,6 +350,43 @@ static void sas_destruct_devices(struct work_struct *work) } } +static void sas_destruct_ports(struct asd_sas_port *port) +{ + struct sas_port *sas_port, *p; + + list_for_each_entry_safe(sas_port, p, &port->sas_port_del_list, del_list) { + list_del_init(&sas_port->del_list); + sas_port_delete(sas_port); + } +} + +static bool sas_abort_cmd(struct request *req, void *data) +{ + struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req); + struct domain_device *dev = data; + + if (dev == cmd_to_domain_dev(cmd)) + blk_abort_request(req); + return true; +} + +static void sas_abort_device_scsi_cmds(struct domain_device *dev) +{ + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *shost = sas_ha->shost; + + if (dev_is_expander(dev->dev_type)) + return; + + /* + * For removed device with active IOs, the user space applications have + * to spend very long time waiting for the timeout. This is not + * necessary because a removed device will not return the IOs. + * Abort the inflight IOs here so that EH can be quickly kicked in. + */ + blk_mq_tagset_busy_iter(&shost->tag_set, sas_abort_cmd, dev); +} + void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) { if (!test_bit(SAS_DEV_DESTROY, &dev->state) && @@ -382,13 +399,14 @@ void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev) } if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) { + if (test_bit(SAS_DEV_GONE, &dev->state)) + sas_abort_device_scsi_cmds(dev); sas_rphy_unlink(dev->rphy); list_move_tail(&dev->disco_list_node, &port->destroy_list); - sas_discover_event(dev->port, DISCE_DESTRUCT); } } -void sas_unregister_domain_devices(struct asd_sas_port *port, int gone) +void sas_unregister_domain_devices(struct asd_sas_port *port, bool gone) { struct domain_device *dev, *n; @@ -428,8 +446,8 @@ void sas_device_set_phy(struct domain_device *dev, struct sas_port *port) /* ---------- Discovery and Revalidation ---------- */ /** - * sas_discover_domain -- discover the domain - * @port: port to the domain of interest + * sas_discover_domain - discover the domain + * @work: work structure embedded in port domain device. * * NOTE: this process _must_ quit (return) as soon as any connection * errors are encountered. Connection recovery is done elsewhere. @@ -453,8 +471,8 @@ static void sas_discover_domain(struct work_struct *work) return; dev = port->port_dev; - SAS_DPRINTK("DOING DISCOVERY on port %d, pid:%d\n", port->id, - task_pid_nr(current)); + pr_debug("DOING DISCOVERY on port %d, pid:%d\n", port->id, + task_pid_nr(current)); switch (dev->dev_type) { case SAS_END_DEVICE: @@ -466,16 +484,11 @@ static void sas_discover_domain(struct work_struct *work) break; case SAS_SATA_DEV: case SAS_SATA_PM: -#ifdef CONFIG_SCSI_SAS_ATA error = sas_discover_sata(dev); break; -#else - SAS_DPRINTK("ATA device seen but CONFIG_SCSI_SAS_ATA=N so cannot attach\n"); - /* Fall through */ -#endif default: error = -ENXIO; - SAS_DPRINTK("unhandled device %d\n", dev->dev_type); + pr_err("unhandled device %d\n", dev->dev_type); break; } @@ -490,8 +503,10 @@ static void sas_discover_domain(struct work_struct *work) port->port_dev = NULL; } - SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id, - task_pid_nr(current), error); + sas_probe_devices(port); + + pr_debug("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id, + task_pid_nr(current), error); } static void sas_revalidate_domain(struct work_struct *work) @@ -505,24 +520,27 @@ static void sas_revalidate_domain(struct work_struct *work) /* prevent revalidation from finding sata links in recovery */ mutex_lock(&ha->disco_mutex); if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) { - SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n", - port->id, task_pid_nr(current)); + pr_debug("REVALIDATION DEFERRED on port %d, pid:%d\n", + port->id, task_pid_nr(current)); goto out; } clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending); - SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, - task_pid_nr(current)); + pr_debug("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id, + task_pid_nr(current)); - if (ddev && (ddev->dev_type == SAS_FANOUT_EXPANDER_DEVICE || - ddev->dev_type == SAS_EDGE_EXPANDER_DEVICE)) + if (ddev && dev_is_expander(ddev->dev_type)) res = sas_ex_revalidate_domain(ddev); - SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", - port->id, task_pid_nr(current), res); + pr_debug("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n", + port->id, task_pid_nr(current), res); out: mutex_unlock(&ha->disco_mutex); + + sas_destruct_devices(port); + sas_destruct_ports(port); + sas_probe_devices(port); } /* ---------- Events ---------- */ @@ -534,7 +552,7 @@ static void sas_chain_work(struct sas_ha_struct *ha, struct sas_work *sw) * workqueue, or known to be submitted from a context that is * not racing against draining */ - scsi_queue_work(ha->core.shost, &sw->work); + queue_work(ha->disco_q, &sw->work); } static void sas_chain_event(int event, unsigned long *pending, @@ -550,23 +568,22 @@ static void sas_chain_event(int event, unsigned long *pending, } } -int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) +void sas_discover_event(struct asd_sas_port *port, enum discover_event ev) { struct sas_discovery *disc; if (!port) - return 0; + return; disc = &port->disc; BUG_ON(ev >= DISC_NUM_EVENTS); sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha); - - return 0; } /** - * sas_init_disc -- initialize the discovery struct in the port + * sas_init_disc - initialize the discovery struct in the port + * @disc: port discovery structure * @port: pointer to struct port * * Called when the ports are being initialized. @@ -578,10 +595,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port) static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = { [DISCE_DISCOVER_DOMAIN] = sas_discover_domain, [DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain, - [DISCE_PROBE] = sas_probe_devices, [DISCE_SUSPEND] = sas_suspend_devices, [DISCE_RESUME] = sas_resume_devices, - [DISCE_DESTRUCT] = sas_destruct_devices, }; disc->pending = 0; diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c deleted file mode 100644 index cd6f99c1ae7e..000000000000 --- a/drivers/scsi/libsas/sas_dump.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Serial Attached SCSI (SAS) Dump/Debugging routines - * - * Copyright (C) 2005 Adaptec, Inc. All rights reserved. - * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "sas_dump.h" - -static const char *sas_hae_str[] = { - [0] = "HAE_RESET", -}; - -static const char *sas_porte_str[] = { - [0] = "PORTE_BYTES_DMAED", - [1] = "PORTE_BROADCAST_RCVD", - [2] = "PORTE_LINK_RESET_ERR", - [3] = "PORTE_TIMER_EVENT", - [4] = "PORTE_HARD_RESET", -}; - -static const char *sas_phye_str[] = { - [0] = "PHYE_LOSS_OF_SIGNAL", - [1] = "PHYE_OOB_DONE", - [2] = "PHYE_OOB_ERROR", - [3] = "PHYE_SPINUP_HOLD", - [4] = "PHYE_RESUME_TIMEOUT", -}; - -void sas_dprint_porte(int phyid, enum port_event pe) -{ - SAS_DPRINTK("phy%d: port event: %s\n", phyid, sas_porte_str[pe]); -} -void sas_dprint_phye(int phyid, enum phy_event pe) -{ - SAS_DPRINTK("phy%d: phy event: %s\n", phyid, sas_phye_str[pe]); -} - -void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he) -{ - SAS_DPRINTK("ha %s: %s event\n", dev_name(sas_ha->dev), - sas_hae_str[he]); -} - -void sas_dump_port(struct asd_sas_port *port) -{ - SAS_DPRINTK("port%d: class:0x%x\n", port->id, port->class); - SAS_DPRINTK("port%d: sas_addr:%llx\n", port->id, - SAS_ADDR(port->sas_addr)); - SAS_DPRINTK("port%d: attached_sas_addr:%llx\n", port->id, - SAS_ADDR(port->attached_sas_addr)); - SAS_DPRINTK("port%d: iproto:0x%x\n", port->id, port->iproto); - SAS_DPRINTK("port%d: tproto:0x%x\n", port->id, port->tproto); - SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode); - SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys); -} diff --git a/drivers/scsi/libsas/sas_dump.h b/drivers/scsi/libsas/sas_dump.h deleted file mode 100644 index 800e4c69093f..000000000000 --- a/drivers/scsi/libsas/sas_dump.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Serial Attached SCSI (SAS) Dump/Debugging routines header file - * - * Copyright (C) 2005 Adaptec, Inc. All rights reserved. - * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "sas_internal.h" - -void sas_dprint_porte(int phyid, enum port_event pe); -void sas_dprint_phye(int phyid, enum phy_event pe); -void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he); -void sas_dump_port(struct asd_sas_port *port); diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index c0d0d979b76d..f3a17191a4fe 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -1,86 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Serial Attached SCSI (SAS) Event processing * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/export.h> #include <scsi/scsi_host.h> #include "sas_internal.h" -#include "sas_dump.h" -int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) +bool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) { - int rc = 0; - if (!test_bit(SAS_HA_REGISTERED, &ha->state)) - return 0; + return false; if (test_bit(SAS_HA_DRAINING, &ha->state)) { /* add it to the defer list, if not already pending */ if (list_empty(&sw->drain_node)) - list_add(&sw->drain_node, &ha->defer_q); - } else - rc = scsi_queue_work(ha->core.shost, &sw->work); + list_add_tail(&sw->drain_node, &ha->defer_q); + return true; + } - return rc; + return queue_work(ha->event_q, &sw->work); } -static int sas_queue_event(int event, unsigned long *pending, - struct sas_work *work, +static bool sas_queue_event(int event, struct sas_work *work, struct sas_ha_struct *ha) { - int rc = 0; - - if (!test_and_set_bit(event, pending)) { - unsigned long flags; + unsigned long flags; + bool rc; - spin_lock_irqsave(&ha->lock, flags); - rc = sas_queue_work(ha, work); - spin_unlock_irqrestore(&ha->lock, flags); - } + spin_lock_irqsave(&ha->lock, flags); + rc = sas_queue_work(ha, work); + spin_unlock_irqrestore(&ha->lock, flags); return rc; } - -void __sas_drain_work(struct sas_ha_struct *ha) +void sas_queue_deferred_work(struct sas_ha_struct *ha) { - struct workqueue_struct *wq = ha->core.shost->work_q; struct sas_work *sw, *_sw; + spin_lock_irq(&ha->lock); + list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { + list_del_init(&sw->drain_node); + + if (!sas_queue_work(ha, sw)) { + pm_runtime_put(ha->dev); + sas_free_event(to_asd_sas_event(&sw->work)); + } + } + spin_unlock_irq(&ha->lock); +} + +void __sas_drain_work(struct sas_ha_struct *ha) +{ set_bit(SAS_HA_DRAINING, &ha->state); /* flush submitters */ spin_lock_irq(&ha->lock); spin_unlock_irq(&ha->lock); - drain_workqueue(wq); + drain_workqueue(ha->event_q); + drain_workqueue(ha->disco_q); - spin_lock_irq(&ha->lock); clear_bit(SAS_HA_DRAINING, &ha->state); - list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { - list_del_init(&sw->drain_node); - sas_queue_work(ha, sw); - } - spin_unlock_irq(&ha->lock); + sas_queue_deferred_work(ha); } int sas_drain_work(struct sas_ha_struct *ha) @@ -115,59 +100,117 @@ void sas_enable_revalidation(struct sas_ha_struct *ha) struct asd_sas_port *port = ha->sas_port[i]; const int ev = DISCE_REVALIDATE_DOMAIN; struct sas_discovery *d = &port->disc; + struct asd_sas_phy *sas_phy; if (!test_and_clear_bit(ev, &d->pending)) continue; - sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); + spin_lock(&port->phy_list_lock); + if (list_empty(&port->phy_list)) { + spin_unlock(&port->phy_list_lock); + continue; + } + + sas_phy = container_of(port->phy_list.next, struct asd_sas_phy, + port_phy_el); + spin_unlock(&port->phy_list_lock); + sas_notify_port_event(sas_phy, + PORTE_BROADCAST_RCVD, GFP_KERNEL); } mutex_unlock(&ha->disco_mutex); } -static int notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) + +static void sas_port_event_worker(struct work_struct *work) { - BUG_ON(event >= HA_NUM_EVENTS); + struct asd_sas_event *ev = to_asd_sas_event(work); + struct asd_sas_phy *phy = ev->phy; + struct sas_ha_struct *ha = phy->ha; - return sas_queue_event(event, &sas_ha->pending, - &sas_ha->ha_events[event].work, sas_ha); + sas_port_event_fns[ev->event](work); + pm_runtime_put(ha->dev); + sas_free_event(ev); } -static int notify_port_event(struct asd_sas_phy *phy, enum port_event event) +static void sas_phy_event_worker(struct work_struct *work) { + struct asd_sas_event *ev = to_asd_sas_event(work); + struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *ha = phy->ha; - BUG_ON(event >= PORT_NUM_EVENTS); - - return sas_queue_event(event, &phy->port_events_pending, - &phy->port_events[event].work, ha); + sas_phy_event_fns[ev->event](work); + pm_runtime_put(ha->dev); + sas_free_event(ev); } -int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) +/* defer works of new phys during suspend */ +static bool sas_defer_event(struct asd_sas_phy *phy, struct asd_sas_event *ev) { struct sas_ha_struct *ha = phy->ha; + unsigned long flags; + bool deferred = false; - BUG_ON(event >= PHY_NUM_EVENTS); + spin_lock_irqsave(&ha->lock, flags); + if (test_bit(SAS_HA_RESUMING, &ha->state) && !phy->suspended) { + struct sas_work *sw = &ev->work; - return sas_queue_event(event, &phy->phy_events_pending, - &phy->phy_events[event].work, ha); + list_add_tail(&sw->drain_node, &ha->defer_q); + deferred = true; + } + spin_unlock_irqrestore(&ha->lock, flags); + return deferred; } -int sas_init_events(struct sas_ha_struct *sas_ha) +void sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event, + gfp_t gfp_flags) { - static const work_func_t sas_ha_event_fns[HA_NUM_EVENTS] = { - [HAE_RESET] = sas_hae_reset, - }; + struct sas_ha_struct *ha = phy->ha; + struct asd_sas_event *ev; - int i; + BUG_ON(event >= PORT_NUM_EVENTS); + + ev = sas_alloc_event(phy, gfp_flags); + if (!ev) + return; + + /* Call pm_runtime_put() with pairs in sas_port_event_worker() */ + pm_runtime_get_noresume(ha->dev); - for (i = 0; i < HA_NUM_EVENTS; i++) { - INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); - sas_ha->ha_events[i].ha = sas_ha; + INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event); + + if (sas_defer_event(phy, ev)) + return; + + if (!sas_queue_event(event, &ev->work, ha)) { + pm_runtime_put(ha->dev); + sas_free_event(ev); } +} +EXPORT_SYMBOL_GPL(sas_notify_port_event); - sas_ha->notify_ha_event = notify_ha_event; - sas_ha->notify_port_event = notify_port_event; - sas_ha->notify_phy_event = sas_notify_phy_event; +void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event, + gfp_t gfp_flags) +{ + struct sas_ha_struct *ha = phy->ha; + struct asd_sas_event *ev; - return 0; + BUG_ON(event >= PHY_NUM_EVENTS); + + ev = sas_alloc_event(phy, gfp_flags); + if (!ev) + return; + + /* Call pm_runtime_put() with pairs in sas_phy_event_worker() */ + pm_runtime_get_noresume(ha->dev); + + INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event); + + if (sas_defer_event(phy, ev)) + return; + + if (!sas_queue_event(event, &ev->work, ha)) { + pm_runtime_put(ha->dev); + sas_free_event(ev); + } } +EXPORT_SYMBOL_GPL(sas_notify_phy_event); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 570b2cb2da43..d953225f6cc2 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Serial Attached SCSI (SAS) Expander discovery and configuration * @@ -5,33 +6,19 @@ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> * * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/scatterlist.h> #include <linux/blkdev.h> #include <linux/slab.h> +#include <linux/unaligned.h> #include "sas_internal.h" #include <scsi/sas_ata.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> -#include "../scsi_sas_internal.h" +#include "scsi_sas_internal.h" static int sas_discover_expander(struct domain_device *dev); static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr); @@ -39,39 +26,43 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id, u8 *sas_addr, int include); static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); -/* ---------- SMP task management ---------- */ - -static void smp_task_timedout(unsigned long _task) +static void sas_port_add_ex_phy(struct sas_port *port, struct ex_phy *ex_phy) { - struct sas_task *task = (void *) _task; - unsigned long flags; - - spin_lock_irqsave(&task->task_state_lock, flags); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; - spin_unlock_irqrestore(&task->task_state_lock, flags); - - complete(&task->slow_task->completion); + sas_port_add_phy(port, ex_phy->phy); + ex_phy->port = port; + ex_phy->phy_state = PHY_DEVICE_DISCOVERED; } -static void smp_task_done(struct sas_task *task) +static void sas_ex_add_parent_port(struct domain_device *dev, int phy_id) { - if (!del_timer(&task->slow_task->timer)) - return; - complete(&task->slow_task->completion); + struct expander_device *ex = &dev->ex_dev; + struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; + + if (!ex->parent_port) { + ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id); + /* FIXME: error handling */ + BUG_ON(!ex->parent_port); + BUG_ON(sas_port_add(ex->parent_port)); + sas_port_mark_backlink(ex->parent_port); + } + sas_port_add_ex_phy(ex->parent_port, ex_phy); } +/* ---------- SMP task management ---------- */ + /* Give it some long enough timeout. In seconds. */ #define SMP_TIMEOUT 10 -static int smp_execute_task(struct domain_device *dev, void *req, int req_size, - void *resp, int resp_size) +static int smp_execute_task_sg(struct domain_device *dev, + struct scatterlist *req, struct scatterlist *resp) { int res, retry; struct sas_task *task = NULL; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); + struct sas_ha_struct *ha = dev->port->ha; + pm_runtime_get_sync(ha->dev); mutex_lock(&dev->ex_dev.cmd_mutex); for (retry = 0; retry < 3; retry++) { if (test_bit(SAS_DEV_GONE, &dev->state)) { @@ -86,36 +77,35 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, } task->dev = dev; task->task_proto = dev->tproto; - sg_init_one(&task->smp_task.smp_req, req, req_size); - sg_init_one(&task->smp_task.smp_resp, resp, resp_size); + task->smp_task.smp_req = *req; + task->smp_task.smp_resp = *resp; - task->task_done = smp_task_done; + task->task_done = sas_task_internal_done; - task->slow_task->timer.data = (unsigned long) task; - task->slow_task->timer.function = smp_task_timedout; + task->slow_task->timer.function = sas_task_internal_timedout; task->slow_task->timer.expires = jiffies + SMP_TIMEOUT*HZ; add_timer(&task->slow_task->timer); res = i->dft->lldd_execute_task(task, GFP_KERNEL); if (res) { - del_timer(&task->slow_task->timer); - SAS_DPRINTK("executing SMP task failed:%d\n", res); + timer_delete_sync(&task->slow_task->timer); + pr_notice("executing SMP task failed:%d\n", res); break; } wait_for_completion(&task->slow_task->completion); res = -ECOMM; if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { - SAS_DPRINTK("smp task timed out or aborted\n"); + pr_notice("smp task timed out or aborted\n"); i->dft->lldd_abort_task(task); if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - SAS_DPRINTK("SMP task aborted and not done\n"); + pr_notice("SMP task aborted and not done\n"); break; } } if (task->task_status.resp == SAS_TASK_COMPLETE && - task->task_status.stat == SAM_STAT_GOOD) { + task->task_status.stat == SAS_SAM_STAT_GOOD) { res = 0; break; } @@ -135,27 +125,39 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size, task->task_status.stat == SAS_DEVICE_UNKNOWN) break; else { - SAS_DPRINTK("%s: task to dev %016llx response: 0x%x " - "status 0x%x\n", __func__, - SAS_ADDR(dev->sas_addr), - task->task_status.resp, - task->task_status.stat); + pr_notice("%s: task to dev %016llx response: 0x%x status 0x%x\n", + __func__, + SAS_ADDR(dev->sas_addr), + task->task_status.resp, + task->task_status.stat); sas_free_task(task); task = NULL; } } mutex_unlock(&dev->ex_dev.cmd_mutex); + pm_runtime_put_sync(ha->dev); BUG_ON(retry == 3 && task != NULL); sas_free_task(task); return res; } +static int smp_execute_task(struct domain_device *dev, void *req, int req_size, + void *resp, int resp_size) +{ + struct scatterlist req_sg; + struct scatterlist resp_sg; + + sg_init_one(&req_sg, req, req_size); + sg_init_one(&resp_sg, resp, resp_size); + return smp_execute_task_sg(dev, &req_sg, &resp_sg); +} + /* ---------- Allocations ---------- */ static inline void *alloc_smp_req(int size) { - u8 *p = kzalloc(size, GFP_KERNEL); + u8 *p = kzalloc(ALIGN(size, ARCH_DMA_MINALIGN), GFP_KERNEL); if (p) p[0] = SMP_REQUEST; return p; @@ -195,13 +197,13 @@ static enum sas_device_type to_dev_type(struct discover_resp *dr) return dr->attached_dev_type; } -static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) +static void sas_set_ex_phy(struct domain_device *dev, int phy_id, + struct smp_disc_resp *disc_resp) { enum sas_device_type dev_type; enum sas_linkrate linkrate; u8 sas_addr[SAS_ADDR_SIZE]; - struct smp_resp *resp = rsp; - struct discover_resp *dr = &resp->disc; + struct discover_resp *dr = &disc_resp->disc; struct sas_ha_struct *ha = dev->port->ha; struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; @@ -218,7 +220,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) BUG_ON(!phy->phy); } - switch (resp->result) { + switch (disc_resp->result) { case SMP_RESP_PHY_VACANT: phy->phy_state = PHY_VACANT; break; @@ -259,8 +261,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) /* help some expanders that fail to zero sas_address in the 'no * device' case */ - if (phy->attached_dev_type == SAS_PHY_UNUSED || - phy->linkrate < SAS_LINK_RATE_1_5_GBPS) + if (phy->attached_dev_type == SAS_PHY_UNUSED) memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); else memcpy(phy->attached_sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE); @@ -282,6 +283,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) phy->phy->minimum_linkrate = dr->pmin_linkrate; phy->phy->maximum_linkrate = dr->pmax_linkrate; phy->phy->negotiated_linkrate = phy->linkrate; + phy->phy->enabled = (phy->linkrate != SAS_PHY_DISABLED); skip: if (new_phy) @@ -336,11 +338,11 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) set_bit(DISCE_REVALIDATE_DOMAIN, &dev->port->disc.pending); - SAS_DPRINTK("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", - test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", - SAS_ADDR(dev->sas_addr), phy->phy_id, - sas_route_char(dev, phy), phy->linkrate, - SAS_ADDR(phy->attached_sas_addr), type); + pr_debug("%sex %016llx phy%02d:%c:%X attached: %016llx (%s)\n", + test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state) ? "ata: " : "", + SAS_ADDR(dev->sas_addr), phy->phy_id, + sas_route_char(dev, phy), phy->linkrate, + SAS_ADDR(phy->attached_sas_addr), type); } /* check if we have an existing attached ata device on this expander phy */ @@ -366,12 +368,13 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) } #define DISCOVER_REQ_SIZE 16 -#define DISCOVER_RESP_SIZE 56 +#define DISCOVER_RESP_SIZE sizeof(struct smp_disc_resp) static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, - u8 *disc_resp, int single) + struct smp_disc_resp *disc_resp, + int single) { - struct discover_resp *dr; + struct discover_resp *dr = &disc_resp->disc; int res; disc_req[9] = single; @@ -380,9 +383,8 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, disc_resp, DISCOVER_RESP_SIZE); if (res) return res; - dr = &((struct smp_resp *)disc_resp)->disc; if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { - sas_printk("Found loopback topology, just ignore it!\n"); + pr_notice("Found loopback topology, just ignore it!\n"); return 0; } sas_set_ex_phy(dev, single, disc_resp); @@ -394,7 +396,7 @@ int sas_ex_phy_discover(struct domain_device *dev, int single) struct expander_device *ex = &dev->ex_dev; int res = 0; u8 *disc_req; - u8 *disc_resp; + struct smp_disc_resp *disc_resp; disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); if (!disc_req) @@ -429,9 +431,9 @@ out_err: static int sas_expander_discover(struct domain_device *dev) { struct expander_device *ex = &dev->ex_dev; - int res = -ENOMEM; + int res; - ex->ex_phy = kzalloc(sizeof(*ex->ex_phy)*ex->num_phys, GFP_KERNEL); + ex->ex_phy = kcalloc(ex->num_phys, sizeof(*ex->ex_phy), GFP_KERNEL); if (!ex->ex_phy) return -ENOMEM; @@ -448,27 +450,14 @@ static int sas_expander_discover(struct domain_device *dev) #define MAX_EXPANDER_PHYS 128 -static void ex_assign_report_general(struct domain_device *dev, - struct smp_resp *resp) -{ - struct report_general_resp *rg = &resp->rg; - - dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); - dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); - dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); - dev->ex_dev.t2t_supp = rg->t2t_supp; - dev->ex_dev.conf_route_table = rg->conf_route_table; - dev->ex_dev.configuring = rg->configuring; - memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); -} - #define RG_REQ_SIZE 8 -#define RG_RESP_SIZE 32 +#define RG_RESP_SIZE sizeof(struct smp_rg_resp) static int sas_ex_general(struct domain_device *dev) { u8 *rg_req; - struct smp_resp *rg_resp; + struct smp_rg_resp *rg_resp; + struct report_general_resp *rg; int res; int i; @@ -489,21 +478,29 @@ static int sas_ex_general(struct domain_device *dev) RG_RESP_SIZE); if (res) { - SAS_DPRINTK("RG to ex %016llx failed:0x%x\n", - SAS_ADDR(dev->sas_addr), res); + pr_notice("RG to ex %016llx failed:0x%x\n", + SAS_ADDR(dev->sas_addr), res); goto out; } else if (rg_resp->result != SMP_RESP_FUNC_ACC) { - SAS_DPRINTK("RG:ex %016llx returned SMP result:0x%x\n", - SAS_ADDR(dev->sas_addr), rg_resp->result); + pr_debug("RG:ex %016llx returned SMP result:0x%x\n", + SAS_ADDR(dev->sas_addr), rg_resp->result); res = rg_resp->result; goto out; } - ex_assign_report_general(dev, rg_resp); + rg = &rg_resp->rg; + dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); + dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); + dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); + dev->ex_dev.t2t_supp = rg->t2t_supp; + dev->ex_dev.conf_route_table = rg->conf_route_table; + dev->ex_dev.configuring = rg->configuring; + memcpy(dev->ex_dev.enclosure_logical_id, + rg->enclosure_logical_id, 8); if (dev->ex_dev.configuring) { - SAS_DPRINTK("RG: ex %llx self-configuring...\n", - SAS_ADDR(dev->sas_addr)); + pr_debug("RG: ex %016llx self-configuring...\n", + SAS_ADDR(dev->sas_addr)); schedule_timeout_interruptible(5*HZ); } else break; @@ -555,14 +552,14 @@ static int sas_ex_manuf_info(struct domain_device *dev) mi_req[1] = SMP_REPORT_MANUF_INFO; - res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp,MI_RESP_SIZE); + res = smp_execute_task(dev, mi_req, MI_REQ_SIZE, mi_resp, MI_RESP_SIZE); if (res) { - SAS_DPRINTK("MI: ex %016llx failed:0x%x\n", - SAS_ADDR(dev->sas_addr), res); + pr_notice("MI: ex %016llx failed:0x%x\n", + SAS_ADDR(dev->sas_addr), res); goto out; } else if (mi_resp[2] != SMP_RESP_FUNC_ACC) { - SAS_DPRINTK("MI ex %016llx returned SMP result:0x%x\n", - SAS_ADDR(dev->sas_addr), mi_resp[2]); + pr_debug("MI ex %016llx returned SMP result:0x%x\n", + SAS_ADDR(dev->sas_addr), mi_resp[2]); goto out; } @@ -596,14 +593,21 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id, pc_req[1] = SMP_PHY_CONTROL; pc_req[9] = phy_id; - pc_req[10]= phy_func; + pc_req[10] = phy_func; if (rates) { pc_req[32] = rates->minimum_linkrate << 4; pc_req[33] = rates->maximum_linkrate << 4; } - res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp,PC_RESP_SIZE); - + res = smp_execute_task(dev, pc_req, PC_REQ_SIZE, pc_resp, PC_RESP_SIZE); + if (res) { + pr_err("ex %016llx phy%02d PHY control failed: %d\n", + SAS_ADDR(dev->sas_addr), phy_id, res); + } else if (pc_resp[2] != SMP_RESP_FUNC_ACC) { + pr_err("ex %016llx phy%02d PHY control failed: function result 0x%x\n", + SAS_ADDR(dev->sas_addr), phy_id, pc_resp[2]); + res = pc_resp[2]; + } kfree(pc_resp); kfree(pc_req); return res; @@ -673,17 +677,18 @@ int sas_smp_get_phy_events(struct sas_phy *phy) req[9] = phy->number; res = smp_execute_task(dev, req, RPEL_REQ_SIZE, - resp, RPEL_RESP_SIZE); + resp, RPEL_RESP_SIZE); - if (!res) + if (res) goto out; - phy->invalid_dword_count = scsi_to_u32(&resp[12]); - phy->running_disparity_error_count = scsi_to_u32(&resp[16]); - phy->loss_of_dword_sync_count = scsi_to_u32(&resp[20]); - phy->phy_reset_problem_count = scsi_to_u32(&resp[24]); + phy->invalid_dword_count = get_unaligned_be32(&resp[12]); + phy->running_disparity_error_count = get_unaligned_be32(&resp[16]); + phy->loss_of_dword_sync_count = get_unaligned_be32(&resp[20]); + phy->phy_reset_problem_count = get_unaligned_be32(&resp[24]); out: + kfree(req); kfree(resp); return res; @@ -692,10 +697,10 @@ int sas_smp_get_phy_events(struct sas_phy *phy) #ifdef CONFIG_SCSI_SAS_ATA #define RPS_REQ_SIZE 16 -#define RPS_RESP_SIZE 60 +#define RPS_RESP_SIZE sizeof(struct smp_rps_resp) int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, - struct smp_resp *rps_resp) + struct smp_rps_resp *rps_resp) { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); @@ -708,7 +713,7 @@ int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, rps_req[9] = phy_id; res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, - rps_resp, RPS_RESP_SIZE); + rps_resp, RPS_RESP_SIZE); /* 0x34 is the FIS type for the D2H fis. There's a potential * standards cockup here. sas-2 explicitly specifies the FIS @@ -754,9 +759,7 @@ static void sas_ex_get_linkrate(struct domain_device *parent, phy->phy_state == PHY_NOT_PRESENT) continue; - if (SAS_ADDR(phy->attached_sas_addr) == - SAS_ADDR(child->sas_addr)) { - + if (sas_phy_match_dev_addr(child, phy)) { child->min_linkrate = min(parent->min_linkrate, phy->linkrate); child->max_linkrate = max(parent->max_linkrate, @@ -769,13 +772,46 @@ static void sas_ex_get_linkrate(struct domain_device *parent, child->pathways = min(child->pathways, parent->pathways); } +static int sas_ex_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id) +{ + struct sas_rphy *rphy; + int res; + + child->dev_type = SAS_END_DEVICE; + rphy = sas_end_device_alloc(phy->port); + if (!rphy) + return -ENOMEM; + + child->tproto = phy->attached_tproto; + sas_init_dev(child); + + child->rphy = rphy; + get_device(&rphy->dev); + rphy->identify.phy_identifier = phy_id; + sas_fill_in_rphy(child, rphy); + + list_add_tail(&child->disco_list_node, &parent->port->disco_list); + + res = sas_notify_lldd_dev_found(child); + if (res) { + pr_notice("notify lldd for device %016llx at %016llx:%02d returned 0x%x\n", + SAS_ADDR(child->sas_addr), + SAS_ADDR(parent->sas_addr), phy_id, res); + sas_rphy_free(child->rphy); + list_del(&child->disco_list_node); + return res; + } + + return 0; +} + static struct domain_device *sas_ex_discover_end_dev( struct domain_device *parent, int phy_id) { struct expander_device *parent_ex = &parent->ex_dev; struct ex_phy *phy = &parent_ex->ex_phy[phy_id]; struct domain_device *child = NULL; - struct sas_rphy *rphy; int res; if (phy->attached_sata_host || phy->attached_sata_ps) @@ -803,74 +839,23 @@ static struct domain_device *sas_ex_discover_end_dev( sas_ex_get_linkrate(parent, child, phy); sas_device_set_phy(child, phy->port); -#ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) { - res = sas_get_ata_info(child, phy); - if (res) - goto out_free; - - sas_init_dev(child); - res = sas_ata_init(child); - if (res) - goto out_free; - rphy = sas_end_device_alloc(phy->port); - if (!rphy) - goto out_free; - - child->rphy = rphy; - get_device(&rphy->dev); - - list_add_tail(&child->disco_list_node, &parent->port->disco_list); - - res = sas_discover_sata(child); - if (res) { - SAS_DPRINTK("sas_discover_sata() for device %16llx at " - "%016llx:0x%x returned 0x%x\n", - SAS_ADDR(child->sas_addr), - SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_list_del; - } - } else -#endif - if (phy->attached_tproto & SAS_PROTOCOL_SSP) { - child->dev_type = SAS_END_DEVICE; - rphy = sas_end_device_alloc(phy->port); - /* FIXME: error handling */ - if (unlikely(!rphy)) - goto out_free; - child->tproto = phy->attached_tproto; - sas_init_dev(child); - - child->rphy = rphy; - get_device(&rphy->dev); - sas_fill_in_rphy(child, rphy); - - list_add_tail(&child->disco_list_node, &parent->port->disco_list); - - res = sas_discover_end_dev(child); - if (res) { - SAS_DPRINTK("sas_discover_end_dev() for device %16llx " - "at %016llx:0x%x returned 0x%x\n", - SAS_ADDR(child->sas_addr), - SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_list_del; - } + res = sas_ata_add_dev(parent, phy, child, phy_id); + } else if (phy->attached_tproto & SAS_PROTOCOL_SSP) { + res = sas_ex_add_dev(parent, phy, child, phy_id); } else { - SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", - phy->attached_tproto, SAS_ADDR(parent->sas_addr), - phy_id); - goto out_free; + pr_notice("target proto 0x%x at %016llx:0x%x not handled\n", + phy->attached_tproto, SAS_ADDR(parent->sas_addr), + phy_id); + res = -ENODEV; } + if (res) + goto out_free; + list_add_tail(&child->siblings, &parent_ex->children); return child; - out_list_del: - sas_rphy_free(child->rphy); - list_del(&child->disco_list_node); - spin_lock_irq(&parent->port->dev_list_lock); - list_del(&child->dev_list_node); - spin_unlock_irq(&parent->port->dev_list_lock); out_free: sas_port_delete(phy->port); out_err: @@ -893,9 +878,7 @@ static bool sas_ex_join_wide_port(struct domain_device *parent, int phy_id) if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr, SAS_ADDR_SIZE) && ephy->port) { - sas_port_add_phy(ephy->port, phy->phy); - phy->port = ephy->port; - phy->phy_state = PHY_DEVICE_DISCOVERED; + sas_port_add_ex_phy(ephy->port, phy); return true; } } @@ -915,11 +898,10 @@ static struct domain_device *sas_ex_discover_expander( int res; if (phy->routing_attr == DIRECT_ROUTING) { - SAS_DPRINTK("ex %016llx:0x%x:D <--> ex %016llx:0x%x is not " - "allowed\n", - SAS_ADDR(parent->sas_addr), phy_id, - SAS_ADDR(phy->attached_sas_addr), - phy->attached_phy_id); + pr_warn("ex %016llx:%02d:D <--> ex %016llx:0x%x is not allowed\n", + SAS_ADDR(parent->sas_addr), phy_id, + SAS_ADDR(phy->attached_sas_addr), + phy->attached_phy_id); return NULL; } child = sas_alloc_device(); @@ -975,6 +957,8 @@ static struct domain_device *sas_ex_discover_expander( list_del(&child->dev_list_node); spin_unlock_irq(&parent->port->dev_list_lock); sas_put_device(child); + sas_port_delete(phy->port); + phy->port = NULL; return NULL; } list_add_tail(&child->siblings, &parent->ex_dev.children); @@ -997,14 +981,12 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) } /* Parent and domain coherency */ - if (!dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) == - SAS_ADDR(dev->port->sas_addr))) { - sas_add_parent_port(dev, phy_id); + if (!dev->parent && sas_phy_match_port_addr(dev->port, ex_phy)) { + sas_ex_add_parent_port(dev, phy_id); return 0; } - if (dev->parent && (SAS_ADDR(ex_phy->attached_sas_addr) == - SAS_ADDR(dev->parent->sas_addr))) { - sas_add_parent_port(dev, phy_id); + if (dev->parent && sas_phy_match_dev_addr(dev->parent, ex_phy)) { + sas_ex_add_parent_port(dev, phy_id); if (ex_phy->routing_attr == TABLE_ROUTING) sas_configure_phy(dev, phy_id, dev->port->sas_addr, 1); return 0; @@ -1026,25 +1008,24 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) ex_phy->attached_dev_type != SAS_FANOUT_EXPANDER_DEVICE && ex_phy->attached_dev_type != SAS_EDGE_EXPANDER_DEVICE && ex_phy->attached_dev_type != SAS_SATA_PENDING) { - SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx " - "phy 0x%x\n", ex_phy->attached_dev_type, - SAS_ADDR(dev->sas_addr), - phy_id); + pr_warn("unknown device type(0x%x) attached to ex %016llx phy%02d\n", + ex_phy->attached_dev_type, + SAS_ADDR(dev->sas_addr), + phy_id); return 0; } res = sas_configure_routing(dev, ex_phy->attached_sas_addr); if (res) { - SAS_DPRINTK("configure routing for dev %016llx " - "reported 0x%x. Forgotten\n", - SAS_ADDR(ex_phy->attached_sas_addr), res); + pr_notice("configure routing for dev %016llx reported 0x%x. Forgotten\n", + SAS_ADDR(ex_phy->attached_sas_addr), res); sas_disable_routing(dev, ex_phy->attached_sas_addr); return res; } if (sas_ex_join_wide_port(dev, phy_id)) { - SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n", - phy_id, SAS_ADDR(ex_phy->attached_sas_addr)); + pr_debug("Attaching ex phy%02d to wide port %016llx\n", + phy_id, SAS_ADDR(ex_phy->attached_sas_addr)); return res; } @@ -1055,18 +1036,17 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) break; case SAS_FANOUT_EXPANDER_DEVICE: if (SAS_ADDR(dev->port->disc.fanout_sas_addr)) { - SAS_DPRINTK("second fanout expander %016llx phy 0x%x " - "attached to ex %016llx phy 0x%x\n", - SAS_ADDR(ex_phy->attached_sas_addr), - ex_phy->attached_phy_id, - SAS_ADDR(dev->sas_addr), - phy_id); + pr_debug("second fanout expander %016llx phy%02d attached to ex %016llx phy%02d\n", + SAS_ADDR(ex_phy->attached_sas_addr), + ex_phy->attached_phy_id, + SAS_ADDR(dev->sas_addr), + phy_id); sas_ex_disable_phy(dev, phy_id); - break; + return res; } else memcpy(dev->port->disc.fanout_sas_addr, ex_phy->attached_sas_addr, SAS_ADDR_SIZE); - /* fallthrough */ + fallthrough; case SAS_EDGE_EXPANDER_DEVICE: child = sas_ex_discover_expander(dev, phy_id); break; @@ -1074,28 +1054,9 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id) break; } - if (child) { - int i; - - for (i = 0; i < ex->num_phys; i++) { - if (ex->ex_phy[i].phy_state == PHY_VACANT || - ex->ex_phy[i].phy_state == PHY_NOT_PRESENT) - continue; - /* - * Due to races, the phy might not get added to the - * wide port, so we add the phy to the wide port here. - */ - if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) == - SAS_ADDR(child->sas_addr)) { - ex->ex_phy[i].phy_state= PHY_DEVICE_DISCOVERED; - if (sas_ex_join_wide_port(dev, i)) - SAS_DPRINTK("Attaching ex phy%d to wide port %016llx\n", - i, SAS_ADDR(ex->ex_phy[i].attached_sas_addr)); - - } - } - } - + if (!child) + pr_notice("ex %016llx phy%02d failed to discover\n", + SAS_ADDR(dev->sas_addr), phy_id); return res; } @@ -1111,11 +1072,10 @@ static int sas_find_sub_addr(struct domain_device *dev, u8 *sub_addr) phy->phy_state == PHY_NOT_PRESENT) continue; - if ((phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE || - phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE) && + if (dev_is_expander(phy->attached_dev_type) && phy->routing_attr == SUBTRACTIVE_ROUTING) { - memcpy(sub_addr, phy->attached_sas_addr,SAS_ADDR_SIZE); + memcpy(sub_addr, phy->attached_sas_addr, SAS_ADDR_SIZE); return 1; } @@ -1127,28 +1087,25 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev) { struct expander_device *ex = &dev->ex_dev; struct domain_device *child; - u8 sub_addr[8] = {0, }; + u8 sub_addr[SAS_ADDR_SIZE] = {0, }; list_for_each_entry(child, &ex->children, siblings) { - if (child->dev_type != SAS_EDGE_EXPANDER_DEVICE && - child->dev_type != SAS_FANOUT_EXPANDER_DEVICE) + if (!dev_is_expander(child->dev_type)) continue; if (sub_addr[0] == 0) { sas_find_sub_addr(child, sub_addr); continue; } else { - u8 s2[8]; + u8 s2[SAS_ADDR_SIZE]; if (sas_find_sub_addr(child, s2) && (SAS_ADDR(sub_addr) != SAS_ADDR(s2))) { - SAS_DPRINTK("ex %016llx->%016llx-?->%016llx " - "diverges from subtractive " - "boundary %016llx\n", - SAS_ADDR(dev->sas_addr), - SAS_ADDR(child->sas_addr), - SAS_ADDR(s2), - SAS_ADDR(sub_addr)); + pr_notice("ex %016llx->%016llx-?->%016llx diverges from subtractive boundary %016llx\n", + SAS_ADDR(dev->sas_addr), + SAS_ADDR(child->sas_addr), + SAS_ADDR(s2), + SAS_ADDR(sub_addr)); sas_ex_disable_port(child, s2); } @@ -1157,9 +1114,9 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev) return 0; } /** - * sas_ex_discover_devices -- discover devices attached to this expander - * dev: pointer to the expander domain device - * single: if you want to do a single phy, else set to -1; + * sas_ex_discover_devices - discover devices attached to this expander + * @dev: pointer to the expander domain device + * @single: if you want to do a single phy, else set to -1; * * Configure this expander for use with its devices and register the * devices of this expander. @@ -1218,8 +1175,7 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev) phy->phy_state == PHY_NOT_PRESENT) continue; - if ((phy->attached_dev_type == SAS_FANOUT_EXPANDER_DEVICE || - phy->attached_dev_type == SAS_EDGE_EXPANDER_DEVICE) && + if (dev_is_expander(phy->attached_dev_type) && phy->routing_attr == SUBTRACTIVE_ROUTING) { if (!sub_sas_addr) @@ -1227,12 +1183,10 @@ static int sas_check_ex_subtractive_boundary(struct domain_device *dev) else if (SAS_ADDR(sub_sas_addr) != SAS_ADDR(phy->attached_sas_addr)) { - SAS_DPRINTK("ex %016llx phy 0x%x " - "diverges(%016llx) on subtractive " - "boundary(%016llx). Disabled\n", - SAS_ADDR(dev->sas_addr), i, - SAS_ADDR(phy->attached_sas_addr), - SAS_ADDR(sub_sas_addr)); + pr_notice("ex %016llx phy%02d diverges(%016llx) on subtractive boundary(%016llx). Disabled\n", + SAS_ADDR(dev->sas_addr), i, + SAS_ADDR(phy->attached_sas_addr), + SAS_ADDR(sub_sas_addr)); sas_ex_disable_phy(dev, i); } } @@ -1250,128 +1204,138 @@ static void sas_print_parent_topology_bug(struct domain_device *child, }; struct domain_device *parent = child->parent; - sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx " - "phy 0x%x has %c:%c routing link!\n", + pr_notice("%s ex %016llx phy%02d <--> %s ex %016llx phy%02d has %c:%c routing link!\n", + ex_type[parent->dev_type], + SAS_ADDR(parent->sas_addr), + parent_phy->phy_id, - ex_type[parent->dev_type], - SAS_ADDR(parent->sas_addr), - parent_phy->phy_id, + ex_type[child->dev_type], + SAS_ADDR(child->sas_addr), + child_phy->phy_id, - ex_type[child->dev_type], - SAS_ADDR(child->sas_addr), - child_phy->phy_id, + sas_route_char(parent, parent_phy), + sas_route_char(child, child_phy)); +} - sas_route_char(parent, parent_phy), - sas_route_char(child, child_phy)); +static bool sas_eeds_valid(struct domain_device *parent, + struct domain_device *child) +{ + struct sas_discovery *disc = &parent->port->disc; + + return (SAS_ADDR(disc->eeds_a) == SAS_ADDR(parent->sas_addr) || + SAS_ADDR(disc->eeds_a) == SAS_ADDR(child->sas_addr)) && + (SAS_ADDR(disc->eeds_b) == SAS_ADDR(parent->sas_addr) || + SAS_ADDR(disc->eeds_b) == SAS_ADDR(child->sas_addr)); } static int sas_check_eeds(struct domain_device *child, - struct ex_phy *parent_phy, - struct ex_phy *child_phy) + struct ex_phy *parent_phy, + struct ex_phy *child_phy) { int res = 0; struct domain_device *parent = child->parent; + struct sas_discovery *disc = &parent->port->disc; - if (SAS_ADDR(parent->port->disc.fanout_sas_addr) != 0) { + if (SAS_ADDR(disc->fanout_sas_addr) != 0) { res = -ENODEV; - SAS_DPRINTK("edge ex %016llx phy S:0x%x <--> edge ex %016llx " - "phy S:0x%x, while there is a fanout ex %016llx\n", - SAS_ADDR(parent->sas_addr), - parent_phy->phy_id, - SAS_ADDR(child->sas_addr), - child_phy->phy_id, - SAS_ADDR(parent->port->disc.fanout_sas_addr)); - } else if (SAS_ADDR(parent->port->disc.eeds_a) == 0) { - memcpy(parent->port->disc.eeds_a, parent->sas_addr, - SAS_ADDR_SIZE); - memcpy(parent->port->disc.eeds_b, child->sas_addr, - SAS_ADDR_SIZE); - } else if (((SAS_ADDR(parent->port->disc.eeds_a) == - SAS_ADDR(parent->sas_addr)) || - (SAS_ADDR(parent->port->disc.eeds_a) == - SAS_ADDR(child->sas_addr))) - && - ((SAS_ADDR(parent->port->disc.eeds_b) == - SAS_ADDR(parent->sas_addr)) || - (SAS_ADDR(parent->port->disc.eeds_b) == - SAS_ADDR(child->sas_addr)))) - ; - else { + pr_warn("edge ex %016llx phy S:%02d <--> edge ex %016llx phy S:%02d, while there is a fanout ex %016llx\n", + SAS_ADDR(parent->sas_addr), + parent_phy->phy_id, + SAS_ADDR(child->sas_addr), + child_phy->phy_id, + SAS_ADDR(disc->fanout_sas_addr)); + } else if (SAS_ADDR(disc->eeds_a) == 0) { + memcpy(disc->eeds_a, parent->sas_addr, SAS_ADDR_SIZE); + memcpy(disc->eeds_b, child->sas_addr, SAS_ADDR_SIZE); + } else if (!sas_eeds_valid(parent, child)) { res = -ENODEV; - SAS_DPRINTK("edge ex %016llx phy 0x%x <--> edge ex %016llx " - "phy 0x%x link forms a third EEDS!\n", - SAS_ADDR(parent->sas_addr), - parent_phy->phy_id, - SAS_ADDR(child->sas_addr), - child_phy->phy_id); + pr_warn("edge ex %016llx phy%02d <--> edge ex %016llx phy%02d link forms a third EEDS!\n", + SAS_ADDR(parent->sas_addr), + parent_phy->phy_id, + SAS_ADDR(child->sas_addr), + child_phy->phy_id); } return res; } -/* Here we spill over 80 columns. It is intentional. - */ -static int sas_check_parent_topology(struct domain_device *child) +static int sas_check_edge_expander_topo(struct domain_device *child, + struct ex_phy *parent_phy) +{ + struct expander_device *child_ex = &child->ex_dev; + struct expander_device *parent_ex = &child->parent->ex_dev; + struct ex_phy *child_phy; + + child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; + + if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || + child_phy->routing_attr != TABLE_ROUTING) + goto error; + } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { + if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) + return sas_check_eeds(child, parent_phy, child_phy); + else if (child_phy->routing_attr != TABLE_ROUTING) + goto error; + } else if (parent_phy->routing_attr == TABLE_ROUTING) { + if (child_phy->routing_attr != SUBTRACTIVE_ROUTING && + (child_phy->routing_attr != TABLE_ROUTING || + !child_ex->t2t_supp || !parent_ex->t2t_supp)) + goto error; + } + + return 0; +error: + sas_print_parent_topology_bug(child, parent_phy, child_phy); + return -ENODEV; +} + +static int sas_check_fanout_expander_topo(struct domain_device *child, + struct ex_phy *parent_phy) { struct expander_device *child_ex = &child->ex_dev; + struct ex_phy *child_phy; + + child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; + + if (parent_phy->routing_attr == TABLE_ROUTING && + child_phy->routing_attr == SUBTRACTIVE_ROUTING) + return 0; + + sas_print_parent_topology_bug(child, parent_phy, child_phy); + + return -ENODEV; +} + +static int sas_check_parent_topology(struct domain_device *child) +{ struct expander_device *parent_ex; int i; int res = 0; - if (!child->parent) - return 0; - - if (child->parent->dev_type != SAS_EDGE_EXPANDER_DEVICE && - child->parent->dev_type != SAS_FANOUT_EXPANDER_DEVICE) + if (!dev_parent_is_expander(child)) return 0; parent_ex = &child->parent->ex_dev; for (i = 0; i < parent_ex->num_phys; i++) { struct ex_phy *parent_phy = &parent_ex->ex_phy[i]; - struct ex_phy *child_phy; if (parent_phy->phy_state == PHY_VACANT || parent_phy->phy_state == PHY_NOT_PRESENT) continue; - if (SAS_ADDR(parent_phy->attached_sas_addr) != SAS_ADDR(child->sas_addr)) + if (!sas_phy_match_dev_addr(child, parent_phy)) continue; - child_phy = &child_ex->ex_phy[parent_phy->attached_phy_id]; - switch (child->parent->dev_type) { case SAS_EDGE_EXPANDER_DEVICE: - if (child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { - if (parent_phy->routing_attr != SUBTRACTIVE_ROUTING || - child_phy->routing_attr != TABLE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; - } - } else if (parent_phy->routing_attr == SUBTRACTIVE_ROUTING) { - if (child_phy->routing_attr == SUBTRACTIVE_ROUTING) { - res = sas_check_eeds(child, parent_phy, child_phy); - } else if (child_phy->routing_attr != TABLE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; - } - } else if (parent_phy->routing_attr == TABLE_ROUTING) { - if (child_phy->routing_attr == SUBTRACTIVE_ROUTING || - (child_phy->routing_attr == TABLE_ROUTING && - child_ex->t2t_supp && parent_ex->t2t_supp)) { - /* All good */; - } else { - sas_print_parent_topology_bug(child, parent_phy, child_phy); - res = -ENODEV; - } - } + if (sas_check_edge_expander_topo(child, parent_phy)) + res = -ENODEV; break; case SAS_FANOUT_EXPANDER_DEVICE: - if (parent_phy->routing_attr != TABLE_ROUTING || - child_phy->routing_attr != SUBTRACTIVE_ROUTING) { - sas_print_parent_topology_bug(child, parent_phy, child_phy); + if (sas_check_fanout_expander_topo(child, parent_phy)) res = -ENODEV; - } break; default: break; @@ -1417,14 +1381,13 @@ static int sas_configure_present(struct domain_device *dev, int phy_id, goto out; res = rri_resp[2]; if (res == SMP_RESP_NO_INDEX) { - SAS_DPRINTK("overflow of indexes: dev %016llx " - "phy 0x%x index 0x%x\n", - SAS_ADDR(dev->sas_addr), phy_id, i); + pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n", + SAS_ADDR(dev->sas_addr), phy_id, i); goto out; } else if (res != SMP_RESP_FUNC_ACC) { - SAS_DPRINTK("%s: dev %016llx phy 0x%x index 0x%x " - "result 0x%x\n", __func__, - SAS_ADDR(dev->sas_addr), phy_id, i, res); + pr_notice("%s: dev %016llx phy%02d index 0x%x result 0x%x\n", + __func__, SAS_ADDR(dev->sas_addr), phy_id, + i, res); goto out; } if (SAS_ADDR(sas_addr) != 0) { @@ -1488,9 +1451,8 @@ static int sas_configure_set(struct domain_device *dev, int phy_id, goto out; res = cri_resp[2]; if (res == SMP_RESP_NO_INDEX) { - SAS_DPRINTK("overflow of indexes: dev %016llx phy 0x%x " - "index 0x%x\n", - SAS_ADDR(dev->sas_addr), phy_id, index); + pr_warn("overflow of indexes: dev %016llx phy%02d index 0x%x\n", + SAS_ADDR(dev->sas_addr), phy_id, index); } out: kfree(cri_req); @@ -1509,16 +1471,18 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id, if (res) return res; if (include ^ present) - return sas_configure_set(dev, phy_id, sas_addr, index,include); + return sas_configure_set(dev, phy_id, sas_addr, index, + include); return res; } /** - * sas_configure_parent -- configure routing table of parent - * parent: parent expander - * child: child expander - * sas_addr: SAS port identifier of device directly attached to child + * sas_configure_parent - configure routing table of parent + * @parent: parent expander + * @child: child expander + * @sas_addr: SAS port identifier of device directly attached to child + * @include: whether or not to include @child in the expander routing table */ static int sas_configure_parent(struct domain_device *parent, struct domain_device *child, @@ -1536,8 +1500,8 @@ static int sas_configure_parent(struct domain_device *parent, } if (ex_parent->conf_route_table == 0) { - SAS_DPRINTK("ex %016llx has self-configuring routing table\n", - SAS_ADDR(parent->sas_addr)); + pr_debug("ex %016llx has self-configuring routing table\n", + SAS_ADDR(parent->sas_addr)); return 0; } @@ -1545,8 +1509,7 @@ static int sas_configure_parent(struct domain_device *parent, struct ex_phy *phy = &ex_parent->ex_phy[i]; if ((phy->routing_attr == TABLE_ROUTING) && - (SAS_ADDR(phy->attached_sas_addr) == - SAS_ADDR(child->sas_addr))) { + sas_phy_match_dev_addr(child, phy)) { res = sas_configure_phy(parent, i, sas_addr, include); if (res) return res; @@ -1557,9 +1520,9 @@ static int sas_configure_parent(struct domain_device *parent, } /** - * sas_configure_routing -- configure routing - * dev: expander device - * sas_addr: port identifier of device directly attached to the expander device + * sas_configure_routing - configure routing + * @dev: expander device + * @sas_addr: port identifier of device directly attached to the expander device */ static int sas_configure_routing(struct domain_device *dev, u8 *sas_addr) { @@ -1576,8 +1539,8 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) } /** - * sas_discover_expander -- expander discovery - * @ex: pointer to expander domain device + * sas_discover_expander - expander discovery + * @dev: pointer to expander domain device * * See comment in sas_discover_sata(). */ @@ -1598,8 +1561,8 @@ static int sas_discover_expander(struct domain_device *dev) res = sas_expander_discover(dev); if (res) { - SAS_DPRINTK("expander %016llx discovery failed(0x%x)\n", - SAS_ADDR(dev->sas_addr), res); + pr_warn("expander %016llx discovery failed(0x%x)\n", + SAS_ADDR(dev->sas_addr), res); goto out_err; } @@ -1619,8 +1582,7 @@ static int sas_ex_level_discovery(struct asd_sas_port *port, const int level) struct domain_device *dev; list_for_each_entry(dev, &port->dev_list, dev_list_node) { - if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || - dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + if (dev_is_expander(dev->dev_type)) { struct sas_expander_device *ex = rphy_to_expander_device(dev->rphy); @@ -1675,8 +1637,18 @@ out_err: /* ---------- Domain revalidation ---------- */ +static void sas_get_sas_addr_and_dev_type(struct smp_disc_resp *disc_resp, + u8 *sas_addr, + enum sas_device_type *type) +{ + memcpy(sas_addr, disc_resp->disc.attached_sas_addr, SAS_ADDR_SIZE); + *type = to_dev_type(&disc_resp->disc); + if (*type == SAS_PHY_UNUSED) + memset(sas_addr, 0, SAS_ADDR_SIZE); +} + static int sas_get_phy_discover(struct domain_device *dev, - int phy_id, struct smp_resp *disc_resp) + int phy_id, struct smp_disc_resp *disc_resp) { int res; u8 *disc_req; @@ -1692,10 +1664,8 @@ static int sas_get_phy_discover(struct domain_device *dev, disc_resp, DISCOVER_RESP_SIZE); if (res) goto out; - else if (disc_resp->result != SMP_RESP_FUNC_ACC) { + if (disc_resp->result != SMP_RESP_FUNC_ACC) res = disc_resp->result; - goto out; - } out: kfree(disc_req); return res; @@ -1705,7 +1675,7 @@ static int sas_get_phy_change_count(struct domain_device *dev, int phy_id, int *pcc) { int res; - struct smp_resp *disc_resp; + struct smp_disc_resp *disc_resp; disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); if (!disc_resp) @@ -1719,25 +1689,19 @@ static int sas_get_phy_change_count(struct domain_device *dev, return res; } -static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, - u8 *sas_addr, enum sas_device_type *type) +int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, + u8 *sas_addr, enum sas_device_type *type) { int res; - struct smp_resp *disc_resp; - struct discover_resp *dr; + struct smp_disc_resp *disc_resp; disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); if (!disc_resp) return -ENOMEM; - dr = &disc_resp->disc; res = sas_get_phy_discover(dev, phy_id, disc_resp); - if (res == 0) { - memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8); - *type = to_dev_type(dr); - if (*type == 0) - memset(sas_addr, 0, 8); - } + if (res == 0) + sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, type); kfree(disc_resp); return res; } @@ -1778,7 +1742,7 @@ static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) { int res; u8 *rg_req; - struct smp_resp *rg_resp; + struct smp_rg_resp *rg_resp; rg_req = alloc_smp_req(RG_REQ_SIZE); if (!rg_req) @@ -1843,13 +1807,15 @@ static int sas_find_bcast_dev(struct domain_device *dev, if (phy_id != -1) { *src_dev = dev; ex->ex_change_count = ex_change_count; - SAS_DPRINTK("Expander phy change count has changed\n"); + pr_info("ex %016llx phy%02d change count has changed\n", + SAS_ADDR(dev->sas_addr), phy_id); return res; } else - SAS_DPRINTK("Expander phys DID NOT change\n"); + pr_info("ex %016llx phys DID NOT change\n", + SAS_ADDR(dev->sas_addr)); } list_for_each_entry(ch, &ex->children, siblings) { - if (ch->dev_type == SAS_EDGE_EXPANDER_DEVICE || ch->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + if (dev_is_expander(ch->dev_type)) { res = sas_find_bcast_dev(ch, src_dev); if (*src_dev) return res; @@ -1866,8 +1832,7 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi list_for_each_entry_safe(child, n, &ex->children, siblings) { set_bit(SAS_DEV_GONE, &child->state); - if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || - child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) + if (dev_is_expander(child->dev_type)) sas_unregister_ex_tree(port, child); else sas_unregister_dev(port, child); @@ -1884,11 +1849,9 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, if (last) { list_for_each_entry_safe(child, n, &ex_dev->children, siblings) { - if (SAS_ADDR(child->sas_addr) == - SAS_ADDR(phy->attached_sas_addr)) { + if (sas_phy_match_dev_addr(child, phy)) { set_bit(SAS_DEV_GONE, &child->state); - if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || - child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) + if (dev_is_expander(child->dev_type)) sas_unregister_ex_tree(parent->port, child); else sas_unregister_dev(parent->port, child); @@ -1902,8 +1865,12 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent, if (phy->port) { sas_port_delete_phy(phy->port, phy->phy); sas_device_set_phy(found, phy->port); - if (phy->port->num_phys == 0) - sas_port_delete(phy->port); + if (phy->port->num_phys == 0) { + list_add_tail(&phy->port->del_list, + &parent->port->sas_port_del_list); + if (ex_dev->parent_port == phy->port) + ex_dev->parent_port = NULL; + } phy->port = NULL; } } @@ -1916,8 +1883,7 @@ static int sas_discover_bfs_by_root_level(struct domain_device *root, int res = 0; list_for_each_entry(child, &ex_root->children, siblings) { - if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || - child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + if (dev_is_expander(child->dev_type)) { struct sas_expander_device *ex = rphy_to_expander_device(child->rphy); @@ -1955,8 +1921,8 @@ static int sas_discover_new(struct domain_device *dev, int phy_id) struct domain_device *child; int res; - SAS_DPRINTK("ex %016llx phy%d new device attached\n", - SAS_ADDR(dev->sas_addr), phy_id); + pr_debug("ex %016llx phy%02d new device attached\n", + SAS_ADDR(dev->sas_addr), phy_id); res = sas_ex_phy_discover(dev, phy_id); if (res) return res; @@ -1968,10 +1934,8 @@ static int sas_discover_new(struct domain_device *dev, int phy_id) if (res) return res; list_for_each_entry(child, &dev->ex_dev.children, siblings) { - if (SAS_ADDR(child->sas_addr) == - SAS_ADDR(ex_phy->attached_sas_addr)) { - if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE || - child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) + if (sas_phy_match_dev_addr(child, ex_phy)) { + if (dev_is_expander(child->dev_type)) res = sas_discover_bfs_by_root(child); break; } @@ -1994,37 +1958,59 @@ static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old) return false; } -static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) +static int sas_rediscover_dev(struct domain_device *dev, int phy_id, + bool last, int sibling) { struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; enum sas_device_type type = SAS_PHY_UNUSED; - u8 sas_addr[8]; + struct smp_disc_resp *disc_resp; + u8 sas_addr[SAS_ADDR_SIZE]; + char msg[80] = ""; int res; - memset(sas_addr, 0, 8); - res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type); + if (!last) + sprintf(msg, ", part of a wide port with phy%02d", sibling); + + pr_debug("ex %016llx rediscovering phy%02d%s\n", + SAS_ADDR(dev->sas_addr), phy_id, msg); + + memset(sas_addr, 0, SAS_ADDR_SIZE); + disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); + if (!disc_resp) + return -ENOMEM; + + res = sas_get_phy_discover(dev, phy_id, disc_resp); switch (res) { case SMP_RESP_NO_PHY: phy->phy_state = PHY_NOT_PRESENT; sas_unregister_devs_sas_addr(dev, phy_id, last); - return res; + goto out_free_resp; case SMP_RESP_PHY_VACANT: phy->phy_state = PHY_VACANT; sas_unregister_devs_sas_addr(dev, phy_id, last); - return res; + goto out_free_resp; case SMP_RESP_FUNC_ACC: break; case -ECOMM: break; default: - return res; + goto out_free_resp; } + if (res == 0) + sas_get_sas_addr_and_dev_type(disc_resp, sas_addr, &type); + if ((SAS_ADDR(sas_addr) == 0) || (res == -ECOMM)) { phy->phy_state = PHY_EMPTY; sas_unregister_devs_sas_addr(dev, phy_id, last); - return res; + /* + * Even though the PHY is empty, for convenience we update + * the PHY info, like negotiated linkrate. + */ + if (res == 0) + sas_set_ex_phy(dev, phy_id, disc_resp); + goto out_free_resp; } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) && dev_type_flutter(type, phy->attached_dev_type)) { struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id); @@ -2034,21 +2020,21 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING) action = ", needs recovery"; - SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", - SAS_ADDR(dev->sas_addr), phy_id, action); - return res; + pr_debug("ex %016llx phy%02d broadcast flutter%s\n", + SAS_ADDR(dev->sas_addr), phy_id, action); + goto out_free_resp; } - /* delete the old link */ - if (SAS_ADDR(phy->attached_sas_addr) && - SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr)) { - SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n", - SAS_ADDR(dev->sas_addr), phy_id, - SAS_ADDR(phy->attached_sas_addr)); - sas_unregister_devs_sas_addr(dev, phy_id, last); - } + /* we always have to delete the old device when we went here */ + pr_info("ex %016llx phy%02d replace %016llx\n", + SAS_ADDR(dev->sas_addr), phy_id, + SAS_ADDR(phy->attached_sas_addr)); + sas_unregister_devs_sas_addr(dev, phy_id, last); - return sas_discover_new(dev, phy_id); + res = sas_discover_new(dev, phy_id); +out_free_resp: + kfree(disc_resp); + return res; } /** @@ -2073,8 +2059,8 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id) int i; bool last = true; /* is this the last phy of the port */ - SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n", - SAS_ADDR(dev->sas_addr), phy_id); + pr_debug("ex %016llx phy%02d originated BROADCAST(CHANGE)\n", + SAS_ADDR(dev->sas_addr), phy_id); if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) { for (i = 0; i < ex->num_phys; i++) { @@ -2082,23 +2068,20 @@ static int sas_rediscover(struct domain_device *dev, const int phy_id) if (i == phy_id) continue; - if (SAS_ADDR(phy->attached_sas_addr) == - SAS_ADDR(changed_phy->attached_sas_addr)) { - SAS_DPRINTK("phy%d part of wide port with " - "phy%d\n", phy_id, i); + if (sas_phy_addr_match(phy, changed_phy)) { last = false; break; } } - res = sas_rediscover_dev(dev, phy_id, last); + res = sas_rediscover_dev(dev, phy_id, last, i); } else res = sas_discover_new(dev, phy_id); return res; } /** - * sas_revalidate_domain -- revalidate the domain - * @port: port to the domain of interest + * sas_ex_revalidate_domain - revalidate the domain + * @port_dev: port domain device. * * NOTE: this process _must_ quit (return) as soon as any connection * errors are encountered. Connection recovery is done elsewhere. @@ -2111,7 +2094,7 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) struct domain_device *dev = NULL; res = sas_find_bcast_dev(port_dev, &dev); - while (res == 0 && dev) { + if (res == 0 && dev) { struct expander_device *ex = &dev->ex_dev; int i = 0, phy_id; @@ -2123,64 +2106,70 @@ int sas_ex_revalidate_domain(struct domain_device *port_dev) res = sas_rediscover(dev, phy_id); i = phy_id + 1; } while (i < ex->num_phys); - - dev = NULL; - res = sas_find_bcast_dev(port_dev, &dev); } return res; } -int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, - struct request *req) +int sas_find_attached_phy_id(struct expander_device *ex_dev, + struct domain_device *dev) { - struct domain_device *dev; - int ret, type; - struct request *rsp = req->next_rq; + struct ex_phy *phy; + int phy_id; - if (!rsp) { - printk("%s: space for a smp response is missing\n", - __func__); - return -EINVAL; + for (phy_id = 0; phy_id < ex_dev->num_phys; phy_id++) { + phy = &ex_dev->ex_phy[phy_id]; + if (sas_phy_match_dev_addr(dev, phy)) + return phy_id; } + return -ENODEV; +} +EXPORT_SYMBOL_GPL(sas_find_attached_phy_id); + +void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, + struct sas_rphy *rphy) +{ + struct domain_device *dev; + unsigned int rcvlen = 0; + int ret = -EINVAL; + /* no rphy means no smp target support (ie aic94xx host) */ if (!rphy) - return sas_smp_host_handler(shost, req, rsp); - - type = rphy->identify.device_type; + return sas_smp_host_handler(job, shost); - if (type != SAS_EDGE_EXPANDER_DEVICE && - type != SAS_FANOUT_EXPANDER_DEVICE) { - printk("%s: can we send a smp request to a device?\n", + switch (rphy->identify.device_type) { + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: + break; + default: + pr_err("%s: can we send a smp request to a device?\n", __func__); - return -EINVAL; + goto out; } dev = sas_find_dev_by_rphy(rphy); if (!dev) { - printk("%s: fail to find a domain_device?\n", __func__); - return -EINVAL; + pr_err("%s: fail to find a domain_device?\n", __func__); + goto out; } /* do we need to support multiple segments? */ - if (bio_multiple_segments(req->bio) || - bio_multiple_segments(rsp->bio)) { - printk("%s: multiple segments req %u, rsp %u\n", - __func__, blk_rq_bytes(req), blk_rq_bytes(rsp)); - return -EINVAL; - } - - ret = smp_execute_task(dev, bio_data(req->bio), blk_rq_bytes(req), - bio_data(rsp->bio), blk_rq_bytes(rsp)); - if (ret > 0) { - /* positive number is the untransferred residual */ - scsi_req(rsp)->resid_len = ret; - scsi_req(req)->resid_len = 0; + if (job->request_payload.sg_cnt > 1 || + job->reply_payload.sg_cnt > 1) { + pr_info("%s: multiple segments req %u, rsp %u\n", + __func__, job->request_payload.payload_len, + job->reply_payload.payload_len); + goto out; + } + + ret = smp_execute_task_sg(dev, job->request_payload.sg_list, + job->reply_payload.sg_list); + if (ret >= 0) { + /* bsg_job_done() requires the length received */ + rcvlen = job->reply_payload.payload_len - ret; ret = 0; - } else if (ret == 0) { - scsi_req(rsp)->resid_len = 0; - scsi_req(req)->resid_len = 0; } - return ret; +out: + bsg_job_done(job, ret, rcvlen); } diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c index 45cbbc44f4d7..2ecb8535634c 100644 --- a/drivers/scsi/libsas/sas_host_smp.c +++ b/drivers/scsi/libsas/sas_host_smp.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Serial Attached SCSI (SAS) Expander discovery and configuration * * Copyright (C) 2007 James E.J. Bottomley * <James.Bottomley@HansenPartnership.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2 only. */ #include <linux/scatterlist.h> #include <linux/blkdev.h> @@ -17,7 +14,7 @@ #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> -#include "../scsi_sas_internal.h" +#include "scsi_sas_internal.h" static void sas_host_smp_discover(struct sas_ha_struct *sas_ha, u8 *resp_data, u8 phy_id) @@ -117,7 +114,7 @@ static int sas_host_smp_write_gpio(struct sas_ha_struct *sas_ha, u8 *resp_data, u8 reg_type, u8 reg_index, u8 reg_count, u8 *req_data) { - struct sas_internal *i = to_sas_internal(sas_ha->core.shost->transportt); + struct sas_internal *i = to_sas_internal(sas_ha->shost->transportt); int written; if (i->dft->lldd_write_gpio == NULL) { @@ -185,7 +182,7 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, enum sas_linkrate max, u8 *resp_data) { struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); struct sas_phy_linkrates rates; struct asd_sas_phy *asd_phy; @@ -225,47 +222,36 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id, resp_data[2] = SMP_RESP_FUNC_ACC; } -int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, - struct request *rsp) +void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost) { - u8 *req_data = NULL, *resp_data = NULL, *buf; struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + u8 *req_data, *resp_data; + unsigned int reslen = 0; int error = -EINVAL; /* eight is the minimum size for request and response frames */ - if (blk_rq_bytes(req) < 8 || blk_rq_bytes(rsp) < 8) + if (job->request_payload.payload_len < 8 || + job->reply_payload.payload_len < 8) goto out; - if (bio_offset(req->bio) + blk_rq_bytes(req) > PAGE_SIZE || - bio_offset(rsp->bio) + blk_rq_bytes(rsp) > PAGE_SIZE) { - shost_printk(KERN_ERR, shost, - "SMP request/response frame crosses page boundary"); + error = -ENOMEM; + req_data = kzalloc(job->request_payload.payload_len, GFP_KERNEL); + if (!req_data) goto out; - } - - req_data = kzalloc(blk_rq_bytes(req), GFP_KERNEL); + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, req_data, + job->request_payload.payload_len); /* make sure frame can always be built ... we copy * back only the requested length */ - resp_data = kzalloc(max(blk_rq_bytes(rsp), 128U), GFP_KERNEL); - - if (!req_data || !resp_data) { - error = -ENOMEM; - goto out; - } - - local_irq_disable(); - buf = kmap_atomic(bio_page(req->bio)); - memcpy(req_data, buf, blk_rq_bytes(req)); - kunmap_atomic(buf - bio_offset(req->bio)); - local_irq_enable(); + resp_data = kzalloc(max(job->reply_payload.payload_len, 128U), + GFP_KERNEL); + if (!resp_data) + goto out_free_req; + error = -EINVAL; if (req_data[0] != SMP_REQUEST) - goto out; - - /* always succeeds ... even if we can't process the request - * the result is in the response frame */ - error = 0; + goto out_free_resp; /* set up default don't know response */ resp_data[0] = SMP_RESPONSE; @@ -274,20 +260,18 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, switch (req_data[1]) { case SMP_REPORT_GENERAL: - scsi_req(req)->resid_len -= 8; - scsi_req(rsp)->resid_len -= 32; resp_data[2] = SMP_RESP_FUNC_ACC; resp_data[9] = sas_ha->num_phys; + reslen = 32; break; case SMP_REPORT_MANUF_INFO: - scsi_req(req)->resid_len -= 8; - scsi_req(rsp)->resid_len -= 64; resp_data[2] = SMP_RESP_FUNC_ACC; memcpy(resp_data + 12, shost->hostt->name, SAS_EXPANDER_VENDOR_ID_LEN); memcpy(resp_data + 20, "libsas virt phy", SAS_EXPANDER_PRODUCT_ID_LEN); + reslen = 64; break; case SMP_READ_GPIO_REG: @@ -295,14 +279,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; case SMP_DISCOVER: - scsi_req(req)->resid_len -= 16; - if ((int)scsi_req(req)->resid_len < 0) { - scsi_req(req)->resid_len = 0; - error = -EINVAL; - goto out; - } - scsi_req(rsp)->resid_len -= 56; + if (job->request_payload.payload_len < 16) + goto out_free_resp; sas_host_smp_discover(sas_ha, resp_data, req_data[9]); + reslen = 56; break; case SMP_REPORT_PHY_ERR_LOG: @@ -311,14 +291,10 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; case SMP_REPORT_PHY_SATA: - scsi_req(req)->resid_len -= 16; - if ((int)scsi_req(req)->resid_len < 0) { - scsi_req(req)->resid_len = 0; - error = -EINVAL; - goto out; - } - scsi_req(rsp)->resid_len -= 60; + if (job->request_payload.payload_len < 16) + goto out_free_resp; sas_report_phy_sata(sas_ha, resp_data, req_data[9]); + reslen = 60; break; case SMP_REPORT_ROUTE_INFO: @@ -330,16 +306,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, const int base_frame_size = 11; int to_write = req_data[4]; - if (blk_rq_bytes(req) < base_frame_size + to_write * 4 || - scsi_req(req)->resid_len < base_frame_size + to_write * 4) { + if (job->request_payload.payload_len < + base_frame_size + to_write * 4) { resp_data[2] = SMP_RESP_INV_FRM_LEN; break; } to_write = sas_host_smp_write_gpio(sas_ha, resp_data, req_data[2], req_data[3], to_write, &req_data[8]); - scsi_req(req)->resid_len -= base_frame_size + to_write * 4; - scsi_req(rsp)->resid_len -= 8; + reslen = 8; break; } @@ -348,16 +323,12 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; case SMP_PHY_CONTROL: - scsi_req(req)->resid_len -= 44; - if ((int)scsi_req(req)->resid_len < 0) { - scsi_req(req)->resid_len = 0; - error = -EINVAL; - goto out; - } - scsi_req(rsp)->resid_len -= 8; + if (job->request_payload.payload_len < 44) + goto out_free_resp; sas_phy_control(sas_ha, req_data[9], req_data[10], req_data[32] >> 4, req_data[33] >> 4, resp_data); + reslen = 8; break; case SMP_PHY_TEST_FUNCTION: @@ -369,15 +340,15 @@ int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, break; } - local_irq_disable(); - buf = kmap_atomic(bio_page(rsp->bio)); - memcpy(buf, resp_data, blk_rq_bytes(rsp)); - flush_kernel_dcache_page(bio_page(rsp->bio)); - kunmap_atomic(buf - bio_offset(rsp->bio)); - local_irq_enable(); + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, resp_data, + job->reply_payload.payload_len); - out: - kfree(req_data); + error = 0; +out_free_resp: kfree(resp_data); - return error; +out_free_req: + kfree(req_data); +out: + bsg_job_done(job, error, reslen); } diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 64e9cdda1c3c..8566bb1208a0 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -1,26 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Serial Attached SCSI (SAS) Transport Layer initialization * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * */ #include <linux/module.h> @@ -36,9 +19,10 @@ #include "sas_internal.h" -#include "../scsi_sas_internal.h" +#include "scsi_sas_internal.h" static struct kmem_cache *sas_task_cache; +static struct kmem_cache *sas_event_cache; struct sas_task *sas_alloc_task(gfp_t flags) { @@ -51,7 +35,6 @@ struct sas_task *sas_alloc_task(gfp_t flags) return task; } -EXPORT_SYMBOL_GPL(sas_alloc_task); struct sas_task *sas_alloc_slow_task(gfp_t flags) { @@ -66,12 +49,12 @@ struct sas_task *sas_alloc_slow_task(gfp_t flags) } task->slow_task = slow; - init_timer(&slow->timer); + slow->task = task; + timer_setup(&slow->timer, NULL, 0); init_completion(&slow->completion); return task; } -EXPORT_SYMBOL_GPL(sas_alloc_slow_task); void sas_free_task(struct sas_task *task) { @@ -80,45 +63,36 @@ void sas_free_task(struct sas_task *task) kmem_cache_free(sas_task_cache, task); } } -EXPORT_SYMBOL_GPL(sas_free_task); /*------------ SAS addr hash -----------*/ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) { - const u32 poly = 0x00DB2777; - u32 r = 0; - int i; - - for (i = 0; i < 8; i++) { - int b; - for (b = 7; b >= 0; b--) { - r <<= 1; - if ((1 << b) & sas_addr[i]) { - if (!(r & 0x01000000)) - r ^= poly; - } else if (r & 0x01000000) - r ^= poly; - } - } - - hashed[0] = (r >> 16) & 0xFF; - hashed[1] = (r >> 8) & 0xFF ; - hashed[2] = r & 0xFF; -} - - -/* ---------- HA events ---------- */ + const u32 poly = 0x00DB2777; + u32 r = 0; + int i; -void sas_hae_reset(struct work_struct *work) -{ - struct sas_ha_event *ev = to_sas_ha_event(work); - struct sas_ha_struct *ha = ev->ha; + for (i = 0; i < SAS_ADDR_SIZE; i++) { + int b; + + for (b = (SAS_ADDR_SIZE - 1); b >= 0; b--) { + r <<= 1; + if ((1 << b) & sas_addr[i]) { + if (!(r & 0x01000000)) + r ^= poly; + } else if (r & 0x01000000) { + r ^= poly; + } + } + } - clear_bit(HAE_RESET, &ha->pending); + hashed[0] = (r >> 16) & 0xFF; + hashed[1] = (r >> 8) & 0xFF; + hashed[2] = r & 0xFF; } int sas_register_ha(struct sas_ha_struct *sas_ha) { + char name[64]; int error = 0; mutex_init(&sas_ha->disco_mutex); @@ -132,35 +106,45 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) INIT_LIST_HEAD(&sas_ha->defer_q); INIT_LIST_HEAD(&sas_ha->eh_dev_q); + sas_ha->event_thres = SAS_PHY_SHUTDOWN_THRES; + error = sas_register_phys(sas_ha); if (error) { - printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); + pr_notice("couldn't register sas phys:%d\n", error); return error; } error = sas_register_ports(sas_ha); if (error) { - printk(KERN_NOTICE "couldn't register sas ports:%d\n", error); + pr_notice("couldn't register sas ports:%d\n", error); goto Undo_phys; } - error = sas_init_events(sas_ha); - if (error) { - printk(KERN_NOTICE "couldn't start event thread:%d\n", error); + error = -ENOMEM; + snprintf(name, sizeof(name), "%s_event_q", dev_name(sas_ha->dev)); + sas_ha->event_q = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name); + if (!sas_ha->event_q) goto Undo_ports; - } + + snprintf(name, sizeof(name), "%s_disco_q", dev_name(sas_ha->dev)); + sas_ha->disco_q = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM, name); + if (!sas_ha->disco_q) + goto Undo_event_q; INIT_LIST_HEAD(&sas_ha->eh_done_q); INIT_LIST_HEAD(&sas_ha->eh_ata_q); return 0; +Undo_event_q: + destroy_workqueue(sas_ha->event_q); Undo_ports: sas_unregister_ports(sas_ha); Undo_phys: return error; } +EXPORT_SYMBOL_GPL(sas_register_ha); static void sas_disable_events(struct sas_ha_struct *sas_ha) { @@ -185,8 +169,12 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha) __sas_drain_work(sas_ha); mutex_unlock(&sas_ha->drain_mutex); + destroy_workqueue(sas_ha->disco_q); + destroy_workqueue(sas_ha->event_q); + return 0; } +EXPORT_SYMBOL_GPL(sas_unregister_ha); static int sas_get_linkerrors(struct sas_phy *phy) { @@ -195,7 +183,7 @@ static int sas_get_linkerrors(struct sas_phy *phy) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); return i->dft->lldd_control_phy(asd_phy, PHY_FUNC_GET_EVENTS, NULL); } @@ -224,7 +212,7 @@ int sas_try_ata_reset(struct asd_sas_phy *asd_phy) return -ENODEV; } -/** +/* * transport_sas_phy_reset - reset a phy and permit libata to manage the link * * phy reset request via sysfs in host workqueue context so we know we @@ -244,7 +232,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); if (!hard_reset && sas_try_ata_reset(asd_phy) == 0) return 0; @@ -263,7 +251,7 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset) } } -static int sas_phy_enable(struct sas_phy *phy, int enable) +int sas_phy_enable(struct sas_phy *phy, int enable) { int ret; enum phy_func cmd; @@ -278,7 +266,7 @@ static int sas_phy_enable(struct sas_phy *phy, int enable) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); if (enable) ret = transport_sas_phy_reset(phy, 0); @@ -295,6 +283,7 @@ static int sas_phy_enable(struct sas_phy *phy, int enable) } return ret; } +EXPORT_SYMBOL_GPL(sas_phy_enable); int sas_phy_reset(struct sas_phy *phy, int hard_reset) { @@ -314,7 +303,7 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL); } else { @@ -324,9 +313,10 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) } return ret; } +EXPORT_SYMBOL_GPL(sas_phy_reset); -int sas_set_phy_speed(struct sas_phy *phy, - struct sas_phy_linkrates *rates) +static int sas_set_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates) { int ret; @@ -349,7 +339,7 @@ int sas_set_phy_speed(struct sas_phy *phy, struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE, rates); @@ -369,14 +359,13 @@ void sas_prep_resume_ha(struct sas_ha_struct *ha) int i; set_bit(SAS_HA_REGISTERED, &ha->state); + set_bit(SAS_HA_RESUMING, &ha->state); /* clear out any stale link events/data from the suspension path */ for (i = 0; i < ha->num_phys; i++) { struct asd_sas_phy *phy = ha->sas_phy[i]; memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE); - phy->port_events_pending = 0; - phy->phy_events_pending = 0; phy->frame_rcvd_size = 0; } } @@ -396,7 +385,31 @@ static int phys_suspended(struct sas_ha_struct *ha) return rc; } -void sas_resume_ha(struct sas_ha_struct *ha) +static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) +{ + int i; + + for (i = 0; i < ha->num_phys; i++) { + struct asd_sas_port *port = ha->sas_port[i]; + struct domain_device *dev = port->port_dev; + + if (dev && dev_is_expander(dev->dev_type)) { + struct asd_sas_phy *first_phy; + + spin_lock(&port->phy_list_lock); + first_phy = list_first_entry_or_null( + &port->phy_list, struct asd_sas_phy, + port_phy_el); + spin_unlock(&port->phy_list_lock); + + if (first_phy) + sas_notify_port_event(first_phy, + PORTE_BROADCAST_RCVD, GFP_KERNEL); + } + } +} + +static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) { const unsigned long tmo = msecs_to_jiffies(25000); int i; @@ -417,24 +430,45 @@ void sas_resume_ha(struct sas_ha_struct *ha) if (phy->suspended) { dev_warn(&phy->phy->dev, "resume timeout\n"); - sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT); + sas_notify_phy_event(phy, PHYE_RESUME_TIMEOUT, + GFP_KERNEL); } } /* all phys are back up or timed out, turn on i/o so we can * flush out disks that did not return */ - scsi_unblock_requests(ha->core.shost); - sas_drain_work(ha); + scsi_unblock_requests(ha->shost); + if (drain) + sas_drain_work(ha); + clear_bit(SAS_HA_RESUMING, &ha->state); + + sas_queue_deferred_work(ha); + /* send event PORTE_BROADCAST_RCVD to identify some new inserted + * disks for expander + */ + sas_resume_insert_broadcast_ha(ha); +} + +void sas_resume_ha(struct sas_ha_struct *ha) +{ + _sas_resume_ha(ha, true); } EXPORT_SYMBOL(sas_resume_ha); +/* A no-sync variant, which does not call sas_drain_ha(). */ +void sas_resume_ha_no_sync(struct sas_ha_struct *ha) +{ + _sas_resume_ha(ha, false); +} +EXPORT_SYMBOL(sas_resume_ha_no_sync); + void sas_suspend_ha(struct sas_ha_struct *ha) { int i; sas_disable_events(ha); - scsi_block_requests(ha->core.shost); + scsi_block_requests(ha->shost); for (i = 0; i < ha->num_phys; i++) { struct asd_sas_port *port = ha->sas_port[i]; @@ -494,6 +528,7 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) if (!d) return -ENOMEM; + pm_runtime_get_sync(ha->dev); /* libsas workqueue coordinates ata-eh reset with discovery */ mutex_lock(&d->event_lock); d->reset_result = 0; @@ -507,6 +542,7 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) if (rc == 0) rc = d->reset_result; mutex_unlock(&d->event_lock); + pm_runtime_put_sync(ha->dev); return rc; } @@ -521,6 +557,7 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) if (!d) return -ENOMEM; + pm_runtime_get_sync(ha->dev); /* libsas workqueue coordinates ata-eh reset with discovery */ mutex_lock(&d->event_lock); d->enable_result = 0; @@ -534,6 +571,7 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) if (rc == 0) rc = d->enable_result; mutex_unlock(&d->event_lock); + pm_runtime_put_sync(ha->dev); return rc; } @@ -548,6 +586,37 @@ static struct sas_function_template sft = { .smp_handler = sas_smp_handler, }; +static inline ssize_t phy_event_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + return scnprintf(buf, PAGE_SIZE, "%u\n", sha->event_thres); +} + +static inline ssize_t phy_event_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct Scsi_Host *shost = class_to_shost(dev); + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + + sha->event_thres = simple_strtol(buf, NULL, 10); + + /* threshold cannot be set too small */ + if (sha->event_thres < 32) + sha->event_thres = 32; + + return count; +} + +DEVICE_ATTR(phy_event_threshold, + S_IRUGO|S_IWUSR, + phy_event_threshold_show, + phy_event_threshold_store); +EXPORT_SYMBOL_GPL(dev_attr_phy_event_threshold); + struct scsi_transport_template * sas_domain_attach_transport(struct sas_domain_function_template *dft) { @@ -566,20 +635,71 @@ sas_domain_attach_transport(struct sas_domain_function_template *dft) } EXPORT_SYMBOL_GPL(sas_domain_attach_transport); +struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, + gfp_t gfp_flags) +{ + struct asd_sas_event *event; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = + to_sas_internal(sas_ha->shost->transportt); + + event = kmem_cache_zalloc(sas_event_cache, gfp_flags); + if (!event) + return NULL; + + atomic_inc(&phy->event_nr); + + if (atomic_read(&phy->event_nr) > phy->ha->event_thres) { + if (i->dft->lldd_control_phy) { + if (cmpxchg(&phy->in_shutdown, 0, 1) == 0) { + pr_notice("The phy%d bursting events, shut it down.\n", + phy->id); + sas_notify_phy_event(phy, PHYE_SHUTDOWN, + gfp_flags); + } + } else { + /* Do not support PHY control, stop allocating events */ + WARN_ONCE(1, "PHY control not supported.\n"); + kmem_cache_free(sas_event_cache, event); + atomic_dec(&phy->event_nr); + event = NULL; + } + } + + return event; +} + +void sas_free_event(struct asd_sas_event *event) +{ + struct asd_sas_phy *phy = event->phy; + + kmem_cache_free(sas_event_cache, event); + atomic_dec(&phy->event_nr); +} + /* ---------- SAS Class register/unregister ---------- */ static int __init sas_class_init(void) { sas_task_cache = KMEM_CACHE(sas_task, SLAB_HWCACHE_ALIGN); if (!sas_task_cache) - return -ENOMEM; + goto out; + + sas_event_cache = KMEM_CACHE(asd_sas_event, SLAB_HWCACHE_ALIGN); + if (!sas_event_cache) + goto free_task_kmem; return 0; +free_task_kmem: + kmem_cache_destroy(sas_task_cache); +out: + return -ENOMEM; } static void __exit sas_class_exit(void) { kmem_cache_destroy(sas_task_cache); + kmem_cache_destroy(sas_event_cache); } MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>"); @@ -589,5 +709,3 @@ MODULE_LICENSE("GPL v2"); module_init(sas_class_init); module_exit(sas_class_exit); -EXPORT_SYMBOL_GPL(sas_register_ha); -EXPORT_SYMBOL_GPL(sas_unregister_ha); diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a216c957b639..6706f2be8d27 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -1,26 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Serial Attached SCSI (SAS) class internal header file * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * */ #ifndef _SAS_INTERNAL_H_ @@ -31,10 +14,15 @@ #include <scsi/scsi_transport_sas.h> #include <scsi/libsas.h> #include <scsi/sas_ata.h> +#include <linux/pm_runtime.h> -#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__) +#ifdef pr_fmt +#undef pr_fmt +#endif + +#define SAS_FMT "sas: " -#define SAS_DPRINTK(fmt, ...) printk(KERN_DEBUG "sas: " fmt, ## __VA_ARGS__) +#define pr_fmt(fmt) SAS_FMT fmt #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) @@ -51,72 +39,126 @@ struct sas_phy_data { struct sas_work enable_work; }; -void sas_scsi_recover_host(struct Scsi_Host *shost); +void sas_hash_addr(u8 *hashed, const u8 *sas_addr); + +int sas_discover_root_expander(struct domain_device *dev); + +int sas_ex_revalidate_domain(struct domain_device *dev); +void sas_unregister_domain_devices(struct asd_sas_port *port, bool gone); +void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port); +void sas_discover_event(struct asd_sas_port *port, enum discover_event ev); -int sas_show_class(enum sas_class class, char *buf); -int sas_show_proto(enum sas_protocol proto, char *buf); -int sas_show_linkrate(enum sas_linkrate linkrate, char *buf); -int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf); +void sas_init_dev(struct domain_device *dev); +void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev); + +void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_register_phys(struct sas_ha_struct *sas_ha); -void sas_unregister_phys(struct sas_ha_struct *sas_ha); + +struct asd_sas_event *sas_alloc_event(struct asd_sas_phy *phy, gfp_t gfp_flags); +void sas_free_event(struct asd_sas_event *event); + +struct sas_task *sas_alloc_task(gfp_t flags); +struct sas_task *sas_alloc_slow_task(gfp_t flags); +void sas_free_task(struct sas_task *task); int sas_register_ports(struct sas_ha_struct *sas_ha); void sas_unregister_ports(struct sas_ha_struct *sas_ha); -int sas_init_events(struct sas_ha_struct *sas_ha); void sas_disable_revalidation(struct sas_ha_struct *ha); void sas_enable_revalidation(struct sas_ha_struct *ha); +void sas_queue_deferred_work(struct sas_ha_struct *ha); void __sas_drain_work(struct sas_ha_struct *ha); -void sas_deform_port(struct asd_sas_phy *phy, int gone); +void sas_deform_port(struct asd_sas_phy *phy, bool gone); void sas_porte_bytes_dmaed(struct work_struct *work); void sas_porte_broadcast_rcvd(struct work_struct *work); void sas_porte_link_reset_err(struct work_struct *work); void sas_porte_timer_event(struct work_struct *work); void sas_porte_hard_reset(struct work_struct *work); -int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw); +bool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw); int sas_notify_lldd_dev_found(struct domain_device *); void sas_notify_lldd_dev_gone(struct domain_device *); +void sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, + struct sas_rphy *rphy); int sas_smp_phy_control(struct domain_device *dev, int phy_id, enum phy_func phy_func, struct sas_phy_linkrates *); int sas_smp_get_phy_events(struct sas_phy *phy); -int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event); void sas_device_set_phy(struct domain_device *dev, struct sas_port *port); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); int sas_ex_phy_discover(struct domain_device *dev, int single); int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, - struct smp_resp *rps_resp); + struct smp_rps_resp *rps_resp); +int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, + u8 *sas_addr, enum sas_device_type *type); int sas_try_ata_reset(struct asd_sas_phy *phy); -void sas_hae_reset(struct work_struct *work); void sas_free_device(struct kref *kref); +void sas_destruct_devices(struct asd_sas_port *port); + +extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS]; +extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS]; + +void sas_task_internal_done(struct sas_task *task); +void sas_task_internal_timedout(struct timer_list *t); +int sas_execute_tmf(struct domain_device *device, void *parameter, + int para_len, int force_phy_id, + struct sas_tmf_task *tmf); #ifdef CONFIG_SCSI_SAS_HOST_SMP -extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req, - struct request *rsp); +extern void sas_smp_host_handler(struct bsg_job *job, struct Scsi_Host *shost); #else -static inline int sas_smp_host_handler(struct Scsi_Host *shost, - struct request *req, - struct request *rsp) +static inline void sas_smp_host_handler(struct bsg_job *job, + struct Scsi_Host *shost) { shost_printk(KERN_ERR, shost, "Cannot send SMP to a sas host (not enabled in CONFIG)\n"); - return -EINVAL; + bsg_job_done(job, -EINVAL, 0); } #endif +static inline bool sas_phy_match_dev_addr(struct domain_device *dev, + struct ex_phy *phy) +{ + return SAS_ADDR(dev->sas_addr) == SAS_ADDR(phy->attached_sas_addr); +} + +static inline bool sas_phy_match_port_addr(struct asd_sas_port *port, + struct ex_phy *phy) +{ + return SAS_ADDR(port->sas_addr) == SAS_ADDR(phy->attached_sas_addr); +} + +static inline bool sas_phy_addr_match(struct ex_phy *p1, struct ex_phy *p2) +{ + return SAS_ADDR(p1->attached_sas_addr) == SAS_ADDR(p2->attached_sas_addr); +} + static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err) { - SAS_DPRINTK("%s: for %s device %16llx returned %d\n", - func, dev->parent ? "exp-attached" : - "direct-attached", - SAS_ADDR(dev->sas_addr), err); + pr_warn("%s: for %s device %016llx returned %d\n", + func, dev->parent ? "exp-attached" : + "direct-attached", + SAS_ADDR(dev->sas_addr), err); + + /* + * If the device probe failed, the expander phy attached address + * needs to be reset so that the phy will not be treated as flutter + * in the next revalidation + */ + if (dev->parent && !dev_is_expander(dev->dev_type)) { + struct sas_phy *phy = dev->phy; + struct domain_device *parent = dev->parent; + struct ex_phy *ex_phy = &parent->ex_dev.ex_phy[phy->number]; + + memset(ex_phy->attached_sas_addr, 0, SAS_ADDR_SIZE); + } + sas_unregister_dev(dev->port, dev); } @@ -161,21 +203,6 @@ static inline void sas_phy_set_target(struct asd_sas_phy *p, struct domain_devic } } -static inline void sas_add_parent_port(struct domain_device *dev, int phy_id) -{ - struct expander_device *ex = &dev->ex_dev; - struct ex_phy *ex_phy = &ex->ex_phy[phy_id]; - - if (!ex->parent_port) { - ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id); - /* FIXME: error handling */ - BUG_ON(!ex->parent_port); - BUG_ON(sas_port_add(ex->parent_port)); - sas_port_mark_backlink(ex->parent_port); - } - sas_port_add_phy(ex->parent_port, ex_phy->phy); -} - static inline struct domain_device *sas_alloc_device(void) { struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); @@ -195,4 +222,78 @@ static inline void sas_put_device(struct domain_device *dev) kref_put(&dev->kref, sas_free_device); } +#ifdef CONFIG_SCSI_SAS_ATA + +int sas_ata_init(struct domain_device *dev); +void sas_ata_task_abort(struct sas_task *task); +int sas_discover_sata(struct domain_device *dev); +int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id); +void sas_ata_strategy_handler(struct Scsi_Host *shost); +void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q); +void sas_ata_end_eh(struct ata_port *ap); +void sas_ata_wait_eh(struct domain_device *dev); +void sas_probe_sata(struct asd_sas_port *port); +void sas_suspend_sata(struct asd_sas_port *port); +void sas_resume_sata(struct asd_sas_port *port); + +#else + +static inline int sas_ata_init(struct domain_device *dev) +{ + return 0; +} + +static inline void sas_ata_task_abort(struct sas_task *task) +{ +} + +static inline void sas_ata_strategy_handler(struct Scsi_Host *shost) +{ +} + +static inline void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q) +{ +} + +static inline void sas_ata_end_eh(struct ata_port *ap) +{ +} + +static inline void sas_ata_wait_eh(struct domain_device *dev) +{ +} + +static inline void sas_probe_sata(struct asd_sas_port *port) +{ +} + +static inline void sas_suspend_sata(struct asd_sas_port *port) +{ +} + +static inline void sas_resume_sata(struct asd_sas_port *port) +{ +} + +static inline void sas_ata_disabled_notice(void) +{ + pr_notice_once("ATA device seen but CONFIG_SCSI_SAS_ATA=N\n"); +} + +static inline int sas_discover_sata(struct domain_device *dev) +{ + sas_ata_disabled_notice(); + return -ENXIO; +} + +static inline int sas_ata_add_dev(struct domain_device *parent, struct ex_phy *phy, + struct domain_device *child, int phy_id) +{ + sas_ata_disabled_notice(); + return -ENODEV; +} + +#endif + #endif /* _SAS_INTERNAL_H_ */ diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c index cdee446c29e1..635835c28ecd 100644 --- a/drivers/scsi/libsas/sas_phy.c +++ b/drivers/scsi/libsas/sas_phy.c @@ -1,32 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Serial Attached SCSI (SAS) Phy class * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include "sas_internal.h" #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> -#include "../scsi_sas_internal.h" +#include "scsi_sas_internal.h" /* ---------- Phy events ---------- */ @@ -35,9 +19,8 @@ static void sas_phye_loss_of_signal(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); phy->error = 0; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } static void sas_phye_oob_done(struct work_struct *work) @@ -45,7 +28,6 @@ static void sas_phye_oob_done(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); phy->error = 0; } @@ -56,11 +38,9 @@ static void sas_phye_oob_error(struct work_struct *work) struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); - - clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending); + to_sas_internal(sas_ha->shost->transportt); - sas_deform_port(phy, 1); + sas_deform_port(phy, true); if (!port && phy->enabled && i->dft->lldd_control_phy) { phy->error++; @@ -86,9 +66,7 @@ static void sas_phye_spinup_hold(struct work_struct *work) struct asd_sas_phy *phy = ev->phy; struct sas_ha_struct *sas_ha = phy->ha; struct sas_internal *i = - to_sas_internal(sas_ha->core.shost->transportt); - - clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending); + to_sas_internal(sas_ha->shost->transportt); phy->error = 0; i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); @@ -99,8 +77,6 @@ static void sas_phye_resume_timeout(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending); - /* phew, lldd got the phy back in the nick of time */ if (!phy->suspended) { dev_info(&phy->phy->dev, "resume timeout cancelled\n"); @@ -109,49 +85,45 @@ static void sas_phye_resume_timeout(struct work_struct *work) phy->error = 0; phy->suspended = 0; - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } +static void sas_phye_shutdown(struct work_struct *work) +{ + struct asd_sas_event *ev = to_asd_sas_event(work); + struct asd_sas_phy *phy = ev->phy; + struct sas_ha_struct *sas_ha = phy->ha; + struct sas_internal *i = + to_sas_internal(sas_ha->shost->transportt); + + if (phy->enabled) { + int ret; + + phy->error = 0; + phy->enabled = 0; + ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); + if (ret) + pr_notice("lldd disable phy%d returned %d\n", phy->id, + ret); + } else + pr_notice("phy%d is not enabled, cannot shutdown\n", phy->id); + phy->in_shutdown = 0; +} + /* ---------- Phy class registration ---------- */ int sas_register_phys(struct sas_ha_struct *sas_ha) { int i; - static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { - [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, - [PHYE_OOB_DONE] = sas_phye_oob_done, - [PHYE_OOB_ERROR] = sas_phye_oob_error, - [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, - [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, - - }; - - static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { - [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed, - [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd, - [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err, - [PORTE_TIMER_EVENT] = sas_porte_timer_event, - [PORTE_HARD_RESET] = sas_porte_hard_reset, - }; - /* Now register the phys. */ for (i = 0; i < sas_ha->num_phys; i++) { - int k; struct asd_sas_phy *phy = sas_ha->sas_phy[i]; phy->error = 0; + atomic_set(&phy->event_nr, 0); INIT_LIST_HEAD(&phy->port_phy_el); - for (k = 0; k < PORT_NUM_EVENTS; k++) { - INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]); - phy->port_events[k].phy = phy; - } - - for (k = 0; k < PHY_NUM_EVENTS; k++) { - INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]); - phy->phy_events[k].phy = phy; - } phy->port = NULL; phy->ha = sas_ha; @@ -159,7 +131,7 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) spin_lock_init(&phy->sas_prim_lock); phy->frame_rcvd_size = 0; - phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); + phy->phy = sas_phy_alloc(&sas_ha->shost->shost_gendev, i); if (!phy->phy) return -ENOMEM; @@ -179,3 +151,12 @@ int sas_register_phys(struct sas_ha_struct *sas_ha) return 0; } + +const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { + [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, + [PHYE_OOB_DONE] = sas_phye_oob_done, + [PHYE_OOB_ERROR] = sas_phye_oob_error, + [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, + [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, + [PHYE_SHUTDOWN] = sas_phye_shutdown, +}; diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c index d3c5297c6c89..de7556070048 100644 --- a/drivers/scsi/libsas/sas_port.c +++ b/drivers/scsi/libsas/sas_port.c @@ -1,32 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Serial Attached SCSI (SAS) Port class * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include "sas_internal.h" #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> -#include "../scsi_sas_internal.h" +#include "scsi_sas_internal.h" static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy) { @@ -41,10 +25,10 @@ static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy static void sas_resume_port(struct asd_sas_phy *phy) { - struct domain_device *dev; + struct domain_device *dev, *n; struct asd_sas_port *port = phy->port; struct sas_ha_struct *sas_ha = phy->ha; - struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt); + struct sas_internal *si = to_sas_internal(sas_ha->shost->transportt); if (si->dft->lldd_port_formed) si->dft->lldd_port_formed(phy); @@ -60,16 +44,17 @@ static void sas_resume_port(struct asd_sas_phy *phy) * 1/ presume every device came back * 2/ force the next revalidation to check all expander phys */ - list_for_each_entry(dev, &port->dev_list, dev_list_node) { + list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) { int i, rc; rc = sas_notify_lldd_dev_found(dev); if (rc) { sas_unregister_dev(port, dev); + sas_destruct_devices(port); continue; } - if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE || dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) { + if (dev_is_expander(dev->dev_type)) { dev->ex_dev.ex_change_count = -1; for (i = 0; i < dev->ex_dev.num_phys; i++) { struct ex_phy *phy = &dev->ex_dev.ex_phy[i]; @@ -82,8 +67,35 @@ static void sas_resume_port(struct asd_sas_phy *phy) sas_discover_event(port, DISCE_RESUME); } +static void sas_form_port_add_phy(struct asd_sas_port *port, + struct asd_sas_phy *phy, bool wideport) +{ + list_add_tail(&phy->port_phy_el, &port->phy_list); + sas_phy_set_target(phy, port->port_dev); + phy->port = port; + port->num_phys++; + port->phy_mask |= (1U << phy->id); + + if (wideport) + pr_debug("phy%d matched wide port%d\n", phy->id, + port->id); + else + memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE); + + if (*(u64 *)port->attached_sas_addr == 0) { + memcpy(port->attached_sas_addr, phy->attached_sas_addr, + SAS_ADDR_SIZE); + port->iproto = phy->iproto; + port->tproto = phy->tproto; + port->oob_mode = phy->oob_mode; + port->linkrate = phy->linkrate; + } else { + port->linkrate = max(port->linkrate, phy->linkrate); + } +} + /** - * sas_form_port -- add this phy to a port + * sas_form_port - add this phy to a port * @phy: the phy of interest * * This function adds this phy to an existing port, thus creating a wide @@ -94,13 +106,14 @@ static void sas_form_port(struct asd_sas_phy *phy) int i; struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; + struct domain_device *port_dev = NULL; struct sas_internal *si = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); unsigned long flags; if (port) { if (!phy_is_wideport_member(port, phy)) - sas_deform_port(phy, 0); + sas_deform_port(phy, false); else if (phy->suspended) { phy->suspended = 0; sas_resume_port(phy); @@ -109,9 +122,9 @@ static void sas_form_port(struct asd_sas_phy *phy) wake_up(&sas_ha->eh_wait_q); return; } else { - SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n", - __func__, phy->id, phy->port->id, - phy->port->num_phys); + pr_info("%s: phy%d belongs to port%d already(%d)!\n", + __func__, phy->id, phy->port->id, + phy->port->num_phys); return; } } @@ -124,8 +137,9 @@ static void sas_form_port(struct asd_sas_phy *phy) if (*(u64 *) port->sas_addr && phy_is_wideport_member(port, phy) && port->num_phys > 0) { /* wide port */ - SAS_DPRINTK("phy%d matched wide port%d\n", phy->id, - port->id); + port_dev = port->port_dev; + sas_form_port_add_phy(port, phy, true); + spin_unlock(&port->phy_list_lock); break; } spin_unlock(&port->phy_list_lock); @@ -136,40 +150,22 @@ static void sas_form_port(struct asd_sas_phy *phy) port = sas_ha->sas_port[i]; spin_lock(&port->phy_list_lock); if (*(u64 *)port->sas_addr == 0 - && port->num_phys == 0) { - memcpy(port->sas_addr, phy->sas_addr, - SAS_ADDR_SIZE); + && port->num_phys == 0) { + port_dev = port->port_dev; + sas_form_port_add_phy(port, phy, false); + spin_unlock(&port->phy_list_lock); break; } spin_unlock(&port->phy_list_lock); } - } - if (i >= sas_ha->num_phys) { - printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n", - __func__); - spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); - return; + if (i >= sas_ha->num_phys) { + pr_err("%s: couldn't find a free port, bug?\n", + __func__); + spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); + return; + } } - - /* add the phy to the port */ - list_add_tail(&phy->port_phy_el, &port->phy_list); - sas_phy_set_target(phy, port->port_dev); - phy->port = port; - port->num_phys++; - port->phy_mask |= (1U << phy->id); - - if (*(u64 *)port->attached_sas_addr == 0) { - port->class = phy->class; - memcpy(port->attached_sas_addr, phy->attached_sas_addr, - SAS_ADDR_SIZE); - port->iproto = phy->iproto; - port->tproto = phy->tproto; - port->oob_mode = phy->oob_mode; - port->linkrate = phy->linkrate; - } else - port->linkrate = max(port->linkrate, phy->linkrate); - spin_unlock(&port->phy_list_lock); spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); if (!port->port) { @@ -179,34 +175,43 @@ static void sas_form_port(struct asd_sas_phy *phy) } sas_port_add_phy(port->port, phy->phy); - SAS_DPRINTK("%s added to %s, phy_mask:0x%x (%16llx)\n", - dev_name(&phy->phy->dev), dev_name(&port->port->dev), - port->phy_mask, - SAS_ADDR(port->attached_sas_addr)); + pr_debug("%s added to %s, phy_mask:0x%x (%016llx)\n", + dev_name(&phy->phy->dev), dev_name(&port->port->dev), + port->phy_mask, + SAS_ADDR(port->attached_sas_addr)); - if (port->port_dev) - port->port_dev->pathways = port->num_phys; + if (port_dev) + port_dev->pathways = port->num_phys; /* Tell the LLDD about this port formation. */ if (si->dft->lldd_port_formed) si->dft->lldd_port_formed(phy); sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN); + /* Only insert a revalidate event after initial discovery */ + if (port_dev && dev_is_expander(port_dev->dev_type)) { + struct expander_device *ex_dev = &port_dev->ex_dev; + + ex_dev->ex_change_count = -1; + sas_discover_event(port, DISCE_REVALIDATE_DOMAIN); + } + flush_workqueue(sas_ha->disco_q); } /** - * sas_deform_port -- remove this phy from the port it belongs to + * sas_deform_port - remove this phy from the port it belongs to * @phy: the phy of interest + * @gone: whether or not the PHY is gone * * This is called when the physical link to the other phy has been * lost (on this phy), in Event thread context. We cannot delay here. */ -void sas_deform_port(struct asd_sas_phy *phy, int gone) +void sas_deform_port(struct asd_sas_phy *phy, bool gone) { struct sas_ha_struct *sas_ha = phy->ha; struct asd_sas_port *port = phy->port; struct sas_internal *si = - to_sas_internal(sas_ha->core.shost->transportt); + to_sas_internal(sas_ha->shost->transportt); struct domain_device *dev; unsigned long flags; @@ -219,6 +224,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) if (port->num_phys == 1) { sas_unregister_domain_devices(port, gone); + sas_destruct_devices(port); sas_port_delete(port->port); port->port = NULL; } else { @@ -242,7 +248,6 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) INIT_LIST_HEAD(&port->phy_list); memset(port->sas_addr, 0, SAS_ADDR_SIZE); memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE); - port->class = 0; port->iproto = 0; port->tproto = 0; port->oob_mode = 0; @@ -251,6 +256,15 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone) spin_unlock(&port->phy_list_lock); spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags); + /* Only insert revalidate event if the port still has members */ + if (port->port && dev && dev_is_expander(dev->dev_type)) { + struct expander_device *ex_dev = &dev->ex_dev; + + ex_dev->ex_change_count = -1; + sas_discover_event(port, DISCE_REVALIDATE_DOMAIN); + } + flush_workqueue(sas_ha->disco_q); + return; } @@ -261,8 +275,6 @@ void sas_porte_bytes_dmaed(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending); - sas_form_port(phy); } @@ -273,14 +285,15 @@ void sas_porte_broadcast_rcvd(struct work_struct *work) unsigned long flags; u32 prim; - clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending); - spin_lock_irqsave(&phy->sas_prim_lock, flags); prim = phy->sas_prim; spin_unlock_irqrestore(&phy->sas_prim_lock, flags); - SAS_DPRINTK("broadcast received: %d\n", prim); + pr_debug("broadcast received: %d\n", prim); sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN); + + if (phy->port) + flush_workqueue(phy->port->ha->disco_q); } void sas_porte_link_reset_err(struct work_struct *work) @@ -288,9 +301,7 @@ void sas_porte_link_reset_err(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending); - - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } void sas_porte_timer_event(struct work_struct *work) @@ -298,9 +309,7 @@ void sas_porte_timer_event(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending); - - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } void sas_porte_hard_reset(struct work_struct *work) @@ -308,9 +317,7 @@ void sas_porte_hard_reset(struct work_struct *work) struct asd_sas_event *ev = to_asd_sas_event(work); struct asd_sas_phy *phy = ev->phy; - clear_bit(PORTE_HARD_RESET, &phy->port_events_pending); - - sas_deform_port(phy, 1); + sas_deform_port(phy, true); } /* ---------- SAS port registration ---------- */ @@ -323,6 +330,7 @@ static void sas_init_port(struct asd_sas_port *port, INIT_LIST_HEAD(&port->dev_list); INIT_LIST_HEAD(&port->disco_list); INIT_LIST_HEAD(&port->destroy_list); + INIT_LIST_HEAD(&port->sas_port_del_list); spin_lock_init(&port->phy_list_lock); INIT_LIST_HEAD(&port->phy_list); port->ha = sas_ha; @@ -350,6 +358,13 @@ void sas_unregister_ports(struct sas_ha_struct *sas_ha) for (i = 0; i < sas_ha->num_phys; i++) if (sas_ha->sas_phy[i]->port) - sas_deform_port(sas_ha->sas_phy[i], 0); - + sas_deform_port(sas_ha->sas_phy[i], false); } + +const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { + [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed, + [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd, + [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err, + [PORTE_TIMER_EVENT] = sas_porte_timer_event, + [PORTE_HARD_RESET] = sas_porte_hard_reset, +}; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 87e5079d816b..ffa5b49aaf08 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -1,32 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Serial Attached SCSI (SAS) class SCSI Host glue. * * Copyright (C) 2005 Adaptec, Inc. All rights reserved. * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> - * - * This file is licensed under GPLv2. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA - * */ #include <linux/kthread.h> #include <linux/firmware.h> #include <linux/export.h> #include <linux/ctype.h> +#include <linux/kernel.h> #include "sas_internal.h" @@ -38,9 +22,9 @@ #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_sas.h> #include <scsi/sas_ata.h> -#include "../scsi_sas_internal.h" -#include "../scsi_transport_api.h" -#include "../scsi_priv.h" +#include "scsi_sas_internal.h" +#include "scsi_transport_api.h" +#include "scsi_priv.h" #include <linux/err.h> #include <linux/blkdev.h> @@ -53,7 +37,8 @@ static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) { struct task_status_struct *ts = &task->task_status; - int hs = 0, stat = 0; + enum scsi_host_status hs = DID_OK; + enum exec_status stat = SAS_SAM_STAT_GOOD; if (ts->resp == SAS_TASK_UNDELIVERED) { /* transport error */ @@ -82,9 +67,6 @@ static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) case SAS_DEVICE_UNKNOWN: hs = DID_BAD_TARGET; break; - case SAS_SG_ERR: - hs = DID_PARITY; - break; case SAS_OPEN_REJECT: if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY) hs = DID_SOFT_ERROR; /* retry */ @@ -92,17 +74,16 @@ static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task) hs = DID_ERROR; break; case SAS_PROTO_RESPONSE: - SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP " - "task; please report this\n", - task->dev->port->ha->sas_ha_name); + pr_notice("LLDD:%s sent SAS_PROTO_RESP for an SSP task; please report this\n", + task->dev->port->ha->sas_ha_name); break; case SAS_ABORTED_TASK: hs = DID_ABORT; break; - case SAM_STAT_CHECK_CONDITION: + case SAS_SAM_STAT_CHECK_CONDITION: memcpy(sc->sense_buffer, ts->buf, min(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size)); - stat = SAM_STAT_CHECK_CONDITION; + stat = SAS_SAM_STAT_CHECK_CONDITION; break; default: stat = ts->stat; @@ -131,18 +112,18 @@ static void sas_scsi_task_done(struct sas_task *task) if (unlikely(!task)) { /* task will be completed by the error handler */ - SAS_DPRINTK("task done but aborted\n"); + pr_debug("task done but aborted\n"); return; } if (unlikely(!sc)) { - SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n"); + pr_debug("task_done called with non existing SCSI cmnd!\n"); sas_free_task(task); return; } sas_end_task(sc, task); - sc->scsi_done(sc); + scsi_done(sc); } static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, @@ -161,7 +142,6 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, task->dev = dev; task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */ - task->ssp_task.retry_count = 1; int_to_scsilun(cmd->device->lun, &lun); memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8); task->ssp_task.task_attr = TASK_ATTR_SIMPLE; @@ -207,7 +187,7 @@ int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) return 0; out_free_task: - SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + pr_debug("lldd_execute_task returned: %d\n", res); ASSIGN_SAS_TASK(cmd, NULL); sas_free_task(task); if (res == -SAS_QUEUE_FULL) @@ -215,13 +195,15 @@ out_free_task: else cmd->result = DID_ERROR << 16; out_done: - cmd->scsi_done(cmd); + scsi_done(cmd); return 0; } +EXPORT_SYMBOL_GPL(sas_queuecommand); static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) { struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host); + struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_task *task = TO_SAS_TASK(cmd); /* At this point, we only get called following an actual abort @@ -230,6 +212,14 @@ static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) */ sas_end_task(cmd, task); + if (dev_is_sata(dev)) { + /* defer commands to libata so that libata EH can + * handle ata qcs correctly + */ + list_move_tail(&cmd->eh_entry, &sas_ha->eh_ata_q); + return; + } + /* now finish the command and move it on to the error * handler done list, this also takes it off the * error handler pending list. @@ -237,22 +227,6 @@ static void sas_eh_finish_cmd(struct scsi_cmnd *cmd) scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q); } -static void sas_eh_defer_cmd(struct scsi_cmnd *cmd) -{ - struct domain_device *dev = cmd_to_domain_dev(cmd); - struct sas_ha_struct *ha = dev->port->ha; - struct sas_task *task = TO_SAS_TASK(cmd); - - if (!dev_is_sata(dev)) { - sas_eh_finish_cmd(cmd); - return; - } - - /* report the timeout to libata */ - sas_end_task(cmd, task); - list_move_tail(&cmd->eh_entry, &ha->eh_ata_q); -} - static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd) { struct scsi_cmnd *cmd, *n; @@ -260,7 +234,7 @@ static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd list_for_each_entry_safe(cmd, n, error_q, eh_entry) { if (cmd->device->sdev_target == my_cmd->device->sdev_target && cmd->device->lun == my_cmd->device->lun) - sas_eh_defer_cmd(cmd); + sas_eh_finish_cmd(cmd); } } @@ -304,47 +278,47 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) unsigned long flags; int i, res; struct sas_internal *si = - to_sas_internal(task->dev->port->ha->core.shost->transportt); + to_sas_internal(task->dev->port->ha->shost->transportt); for (i = 0; i < 5; i++) { - SAS_DPRINTK("%s: aborting task 0x%p\n", __func__, task); + pr_notice("%s: aborting task 0x%p\n", __func__, task); res = si->dft->lldd_abort_task(task); spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: task 0x%p is done\n", __func__, - task); + pr_debug("%s: task 0x%p is done\n", __func__, task); return TASK_IS_DONE; } spin_unlock_irqrestore(&task->task_state_lock, flags); if (res == TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("%s: task 0x%p is aborted\n", - __func__, task); + pr_notice("%s: task 0x%p is aborted\n", + __func__, task); return TASK_IS_ABORTED; } else if (si->dft->lldd_query_task) { - SAS_DPRINTK("%s: querying task 0x%p\n", - __func__, task); + pr_notice("%s: querying task 0x%p\n", __func__, task); res = si->dft->lldd_query_task(task); switch (res) { case TMF_RESP_FUNC_SUCC: - SAS_DPRINTK("%s: task 0x%p at LU\n", - __func__, task); + pr_notice("%s: task 0x%p at LU\n", __func__, + task); return TASK_IS_AT_LU; case TMF_RESP_FUNC_COMPLETE: - SAS_DPRINTK("%s: task 0x%p not at LU\n", - __func__, task); + pr_notice("%s: task 0x%p not at LU\n", + __func__, task); return TASK_IS_NOT_AT_LU; case TMF_RESP_FUNC_FAILED: - SAS_DPRINTK("%s: task 0x%p failed to abort\n", - __func__, task); - return TASK_ABORT_FAILED; - } - + pr_notice("%s: task 0x%p failed to abort\n", + __func__, task); + return TASK_ABORT_FAILED; + default: + pr_notice("%s: task 0x%p result code %d not handled\n", + __func__, task, res); + } } } - return res; + return TASK_ABORT_FAILED; } static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) @@ -352,13 +326,13 @@ static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd) int res = TMF_RESP_FUNC_FAILED; struct scsi_lun lun; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); int_to_scsilun(cmd->device->lun, &lun); - SAS_DPRINTK("eh: device %llx LUN %llx has the task\n", - SAS_ADDR(dev->sas_addr), - cmd->device->lun); + pr_notice("eh: device %016llx LUN 0x%llx has the task\n", + SAS_ADDR(dev->sas_addr), + cmd->device->lun); if (i->dft->lldd_abort_task_set) res = i->dft->lldd_abort_task_set(dev, lun.scsi_lun); @@ -380,10 +354,10 @@ static int sas_recover_I_T(struct domain_device *dev) { int res = TMF_RESP_FUNC_FAILED; struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); + to_sas_internal(dev->port->ha->shost->transportt); - SAS_DPRINTK("I_T nexus reset for dev %016llx\n", - SAS_ADDR(dev->sas_addr)); + pr_notice("I_T nexus reset for dev %016llx\n", + SAS_ADDR(dev->sas_addr)); if (i->dft->lldd_I_T_nexus_reset) res = i->dft->lldd_I_T_nexus_reset(dev); @@ -412,38 +386,7 @@ struct sas_phy *sas_get_local_phy(struct domain_device *dev) } EXPORT_SYMBOL_GPL(sas_get_local_phy); -static void sas_wait_eh(struct domain_device *dev) -{ - struct sas_ha_struct *ha = dev->port->ha; - DEFINE_WAIT(wait); - - if (dev_is_sata(dev)) { - ata_port_wait_eh(dev->sata_dev.ap); - return; - } - retry: - spin_lock_irq(&ha->lock); - - while (test_bit(SAS_DEV_EH_PENDING, &dev->state)) { - prepare_to_wait(&ha->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&ha->lock); - schedule(); - spin_lock_irq(&ha->lock); - } - finish_wait(&ha->eh_wait_q, &wait); - - spin_unlock_irq(&ha->lock); - - /* make sure SCSI EH is complete */ - if (scsi_host_in_recovery(ha->core.shost)) { - msleep(10); - goto retry; - } -} -EXPORT_SYMBOL(sas_wait_eh); - -static int sas_queue_reset(struct domain_device *dev, int reset_type, - u64 lun, int wait) +static int sas_queue_reset(struct domain_device *dev, int reset_type, u64 lun) { struct sas_ha_struct *ha = dev->port->ha; int scheduled = 0, tries = 100; @@ -451,8 +394,6 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, /* ata: promote lun reset to bus reset */ if (dev_is_sata(dev)) { sas_ata_schedule_reset(dev); - if (wait) - sas_ata_wait_eh(dev); return SUCCESS; } @@ -466,35 +407,45 @@ static int sas_queue_reset(struct domain_device *dev, int reset_type, set_bit(SAS_DEV_EH_PENDING, &dev->state); set_bit(reset_type, &dev->state); int_to_scsilun(lun, &dev->ssp_dev.reset_lun); - scsi_schedule_eh(ha->core.shost); + scsi_schedule_eh(ha->shost); } spin_unlock_irq(&ha->lock); - if (wait) - sas_wait_eh(dev); - if (scheduled) return SUCCESS; } - SAS_DPRINTK("%s reset of %s failed\n", - reset_type == SAS_DEV_LU_RESET ? "LUN" : "Bus", - dev_name(&dev->rphy->dev)); + pr_warn("%s reset of %s failed\n", + reset_type == SAS_DEV_LU_RESET ? "LUN" : "Bus", + dev_name(&dev->rphy->dev)); return FAILED; } int sas_eh_abort_handler(struct scsi_cmnd *cmd) { - int res; + int res = TMF_RESP_FUNC_FAILED; struct sas_task *task = TO_SAS_TASK(cmd); struct Scsi_Host *host = cmd->device->host; + struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_internal *i = to_sas_internal(host->transportt); + unsigned long flags; if (!i->dft->lldd_abort_task) return FAILED; - res = i->dft->lldd_abort_task(task); + spin_lock_irqsave(host->host_lock, flags); + /* We cannot do async aborts for SATA devices */ + if (dev_is_sata(dev) && !host->host_eh_scheduled) { + spin_unlock_irqrestore(host->host_lock, flags); + return FAILED; + } + spin_unlock_irqrestore(host->host_lock, flags); + + if (task) + res = i->dft->lldd_abort_task(task); + else + pr_notice("no task to abort\n"); if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) return SUCCESS; @@ -512,7 +463,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) struct sas_internal *i = to_sas_internal(host->transportt); if (current != host->ehandler) - return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun, 0); + return sas_queue_reset(dev, SAS_DEV_LU_RESET, cmd->device->lun); int_to_scsilun(cmd->device->lun, &lun); @@ -525,8 +476,9 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) return FAILED; } +EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); -int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) +int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) { int res; struct Scsi_Host *host = cmd->device->host; @@ -534,7 +486,7 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) struct sas_internal *i = to_sas_internal(host->transportt); if (current != host->ehandler) - return sas_queue_reset(dev, SAS_DEV_RESET, 0, 0); + return sas_queue_reset(dev, SAS_DEV_RESET, 0); if (!i->dft->lldd_I_T_nexus_reset) return FAILED; @@ -546,6 +498,7 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) return FAILED; } +EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler); /* Try to reset a device */ static int try_to_reset_cmd_device(struct scsi_cmnd *cmd) @@ -554,15 +507,15 @@ static int try_to_reset_cmd_device(struct scsi_cmnd *cmd) struct Scsi_Host *shost = cmd->device->host; if (!shost->hostt->eh_device_reset_handler) - goto try_bus_reset; + goto try_target_reset; res = shost->hostt->eh_device_reset_handler(cmd); if (res == SUCCESS) return res; -try_bus_reset: - if (shost->hostt->eh_bus_reset_handler) - return shost->hostt->eh_bus_reset_handler(cmd); +try_target_reset: + if (shost->hostt->eh_target_reset_handler) + return shost->hostt->eh_target_reset_handler(cmd); return FAILED; } @@ -605,49 +558,48 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * spin_unlock_irqrestore(&task->task_state_lock, flags); if (need_reset) { - SAS_DPRINTK("%s: task 0x%p requests reset\n", - __func__, task); + pr_notice("%s: task 0x%p requests reset\n", + __func__, task); goto reset; } - SAS_DPRINTK("trying to find task 0x%p\n", task); + pr_debug("trying to find task 0x%p\n", task); res = sas_scsi_find_task(task); switch (res) { case TASK_IS_DONE: - SAS_DPRINTK("%s: task 0x%p is done\n", __func__, + pr_notice("%s: task 0x%p is done\n", __func__, task); - sas_eh_defer_cmd(cmd); + sas_eh_finish_cmd(cmd); continue; case TASK_IS_ABORTED: - SAS_DPRINTK("%s: task 0x%p is aborted\n", - __func__, task); - sas_eh_defer_cmd(cmd); + pr_notice("%s: task 0x%p is aborted\n", + __func__, task); + sas_eh_finish_cmd(cmd); continue; case TASK_IS_AT_LU: - SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); + pr_info("task 0x%p is at LU: lu recover\n", task); reset: tmf_resp = sas_recover_lu(task->dev, cmd); if (tmf_resp == TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("dev %016llx LU %llx is " - "recovered\n", - SAS_ADDR(task->dev), - cmd->device->lun); - sas_eh_defer_cmd(cmd); + pr_notice("dev %016llx LU 0x%llx is recovered\n", + SAS_ADDR(task->dev), + cmd->device->lun); + sas_eh_finish_cmd(cmd); sas_scsi_clear_queue_lu(work_q, cmd); goto Again; } - /* fallthrough */ + fallthrough; case TASK_IS_NOT_AT_LU: case TASK_ABORT_FAILED: - SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n", - task); + pr_notice("task 0x%p is not at LU: I_T recover\n", + task); tmf_resp = sas_recover_I_T(task->dev); if (tmf_resp == TMF_RESP_FUNC_COMPLETE || tmf_resp == -ENODEV) { struct domain_device *dev = task->dev; - SAS_DPRINTK("I_T %016llx recovered\n", - SAS_ADDR(task->dev->sas_addr)); + pr_notice("I_T %016llx recovered\n", + SAS_ADDR(task->dev->sas_addr)); sas_eh_finish_cmd(cmd); sas_scsi_clear_queue_I_T(work_q, dev); goto Again; @@ -656,12 +608,12 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * try_to_reset_cmd_device(cmd); if (i->dft->lldd_clear_nexus_port) { struct asd_sas_port *port = task->dev->port; - SAS_DPRINTK("clearing nexus for port:%d\n", - port->id); + pr_debug("clearing nexus for port:%d\n", + port->id); res = i->dft->lldd_clear_nexus_port(port); if (res == TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("clear nexus port:%d " - "succeeded\n", port->id); + pr_notice("clear nexus port:%d succeeded\n", + port->id); sas_eh_finish_cmd(cmd); sas_scsi_clear_queue_port(work_q, port); @@ -669,11 +621,10 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * } } if (i->dft->lldd_clear_nexus_ha) { - SAS_DPRINTK("clear nexus ha\n"); + pr_debug("clear nexus ha\n"); res = i->dft->lldd_clear_nexus_ha(ha); if (res == TMF_RESP_FUNC_COMPLETE) { - SAS_DPRINTK("clear nexus ha " - "succeeded\n"); + pr_notice("clear nexus ha succeeded\n"); sas_eh_finish_cmd(cmd); goto clear_q; } @@ -682,10 +633,9 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * * of effort could recover from errors. Quite * possibly the HA just disappeared. */ - SAS_DPRINTK("error from device %llx, LUN %llx " - "couldn't be recovered in any way\n", - SAS_ADDR(task->dev->sas_addr), - cmd->device->lun); + pr_err("error from device %016llx, LUN 0x%llx couldn't be recovered in any way\n", + SAS_ADDR(task->dev->sas_addr), + cmd->device->lun); sas_eh_finish_cmd(cmd); goto clear_q; @@ -697,7 +647,7 @@ static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head * return; clear_q: - SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__); + pr_debug("--- Exit %s -- clear_q\n", __func__); list_for_each_entry_safe(cmd, n, work_q, eh_entry) sas_eh_finish_cmd(cmd); goto out; @@ -751,8 +701,8 @@ retry: list_splice_init(&shost->eh_cmd_q, &eh_work_q); spin_unlock_irq(shost->host_lock); - SAS_DPRINTK("Enter %s busy: %d failed: %d\n", - __func__, atomic_read(&shost->host_busy), shost->host_failed); + pr_notice("Enter %s busy: %d failed: %d\n", + __func__, scsi_host_busy(shost), shost->host_failed); /* * Deal with commands that still have SAS tasks (i.e. they didn't * complete via the normal sas_task completion mechanism), @@ -770,7 +720,7 @@ retry: * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any * command we see here has no sas_task and is thus unknown to the HA. */ - sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q); + sas_ata_eh(shost, &eh_work_q); if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); @@ -793,12 +743,12 @@ out: if (retry) goto retry; - SAS_DPRINTK("--- Exit %s: busy: %d failed: %d tries: %d\n", - __func__, atomic_read(&shost->host_busy), - shost->host_failed, tries); + pr_notice("--- Exit %s: busy: %d failed: %d tries: %d\n", + __func__, scsi_host_busy(shost), + shost->host_failed, tries); } -int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +int sas_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg) { struct domain_device *dev = sdev_to_domain_dev(sdev); @@ -807,6 +757,7 @@ int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) return -EINVAL; } +EXPORT_SYMBOL_GPL(sas_ioctl); struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) { @@ -849,31 +800,28 @@ int sas_target_alloc(struct scsi_target *starget) starget->hostdata = found_dev; return 0; } +EXPORT_SYMBOL_GPL(sas_target_alloc); #define SAS_DEF_QD 256 -int sas_slave_configure(struct scsi_device *scsi_dev) +int sas_sdev_configure(struct scsi_device *scsi_dev, struct queue_limits *lim) { struct domain_device *dev = sdev_to_domain_dev(scsi_dev); - struct sas_ha_struct *sas_ha; BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE); if (dev_is_sata(dev)) { - ata_sas_slave_configure(scsi_dev, dev->sata_dev.ap); + ata_sas_sdev_configure(scsi_dev, lim, dev->sata_dev.ap); return 0; } - sas_ha = dev->port->ha; - sas_read_port_mode_page(scsi_dev); if (scsi_dev->tagged_supported) { scsi_change_queue_depth(scsi_dev, SAS_DEF_QD); } else { - SAS_DPRINTK("device %llx, LUN %llx doesn't support " - "TCQ\n", SAS_ADDR(dev->sas_addr), - scsi_dev->lun); + pr_notice("device %016llx, LUN 0x%llx doesn't support TCQ\n", + SAS_ADDR(dev->sas_addr), scsi_dev->lun); scsi_change_queue_depth(scsi_dev, 1); } @@ -881,21 +829,23 @@ int sas_slave_configure(struct scsi_device *scsi_dev) return 0; } +EXPORT_SYMBOL_GPL(sas_sdev_configure); int sas_change_queue_depth(struct scsi_device *sdev, int depth) { struct domain_device *dev = sdev_to_domain_dev(sdev); if (dev_is_sata(dev)) - return __ata_change_queue_depth(dev->sata_dev.ap, sdev, depth); + return ata_change_queue_depth(dev->sata_dev.ap, sdev, depth); if (!sdev->tagged_supported) depth = 1; return scsi_change_queue_depth(sdev, depth); } +EXPORT_SYMBOL_GPL(sas_change_queue_depth); int sas_bios_param(struct scsi_device *scsi_dev, - struct block_device *bdev, + struct gendisk *unused, sector_t capacity, int *hsc) { hsc[0] = 255; @@ -905,6 +855,316 @@ int sas_bios_param(struct scsi_device *scsi_dev, return 0; } +EXPORT_SYMBOL_GPL(sas_bios_param); + +void sas_task_internal_done(struct sas_task *task) +{ + timer_delete(&task->slow_task->timer); + complete(&task->slow_task->completion); +} + +void sas_task_internal_timedout(struct timer_list *t) +{ + struct sas_task_slow *slow = timer_container_of(slow, t, timer); + struct sas_task *task = slow->task; + bool is_completed = true; + unsigned long flags; + + spin_lock_irqsave(&task->task_state_lock, flags); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + is_completed = false; + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + + if (!is_completed) + complete(&task->slow_task->completion); +} + +#define TASK_TIMEOUT (20 * HZ) +#define TASK_RETRY 3 + +static int sas_execute_internal_abort(struct domain_device *device, + enum sas_internal_abort type, u16 tag, + unsigned int qid, void *data) +{ + struct sas_ha_struct *ha = device->port->ha; + struct sas_internal *i = to_sas_internal(ha->shost->transportt); + struct sas_task *task = NULL; + int res, retry; + + for (retry = 0; retry < TASK_RETRY; retry++) { + task = sas_alloc_slow_task(GFP_KERNEL); + if (!task) + return -ENOMEM; + + task->dev = device; + task->task_proto = SAS_PROTOCOL_INTERNAL_ABORT; + task->task_done = sas_task_internal_done; + task->slow_task->timer.function = sas_task_internal_timedout; + task->slow_task->timer.expires = jiffies + TASK_TIMEOUT; + add_timer(&task->slow_task->timer); + + task->abort_task.tag = tag; + task->abort_task.type = type; + task->abort_task.qid = qid; + + res = i->dft->lldd_execute_task(task, GFP_KERNEL); + if (res) { + timer_delete_sync(&task->slow_task->timer); + pr_err("Executing internal abort failed %016llx (%d)\n", + SAS_ADDR(device->sas_addr), res); + break; + } + + wait_for_completion(&task->slow_task->completion); + res = TMF_RESP_FUNC_FAILED; + + /* Even if the internal abort timed out, return direct. */ + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + bool quit = true; + + if (i->dft->lldd_abort_timeout) + quit = i->dft->lldd_abort_timeout(task, data); + else + pr_err("Internal abort: timeout %016llx\n", + SAS_ADDR(device->sas_addr)); + res = -EIO; + if (quit) + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_SAM_STAT_GOOD) { + res = TMF_RESP_FUNC_COMPLETE; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_SUCC) { + res = TMF_RESP_FUNC_SUCC; + break; + } + + pr_err("Internal abort: task to dev %016llx response: 0x%x status 0x%x\n", + SAS_ADDR(device->sas_addr), task->task_status.resp, + task->task_status.stat); + sas_free_task(task); + task = NULL; + } + BUG_ON(retry == TASK_RETRY && task != NULL); + sas_free_task(task); + return res; +} + +int sas_execute_internal_abort_single(struct domain_device *device, u16 tag, + unsigned int qid, void *data) +{ + return sas_execute_internal_abort(device, SAS_INTERNAL_ABORT_SINGLE, + tag, qid, data); +} +EXPORT_SYMBOL_GPL(sas_execute_internal_abort_single); + +int sas_execute_internal_abort_dev(struct domain_device *device, + unsigned int qid, void *data) +{ + return sas_execute_internal_abort(device, SAS_INTERNAL_ABORT_DEV, + SCSI_NO_TAG, qid, data); +} +EXPORT_SYMBOL_GPL(sas_execute_internal_abort_dev); + +int sas_execute_tmf(struct domain_device *device, void *parameter, + int para_len, int force_phy_id, + struct sas_tmf_task *tmf) +{ + struct sas_task *task; + struct sas_internal *i = + to_sas_internal(device->port->ha->shost->transportt); + int res, retry; + + for (retry = 0; retry < TASK_RETRY; retry++) { + task = sas_alloc_slow_task(GFP_KERNEL); + if (!task) + return -ENOMEM; + + task->dev = device; + task->task_proto = device->tproto; + + if (dev_is_sata(device)) { + task->ata_task.device_control_reg_update = 1; + if (force_phy_id >= 0) { + task->ata_task.force_phy = true; + task->ata_task.force_phy_id = force_phy_id; + } + memcpy(&task->ata_task.fis, parameter, para_len); + } else { + memcpy(&task->ssp_task, parameter, para_len); + } + + task->task_done = sas_task_internal_done; + task->tmf = tmf; + + task->slow_task->timer.function = sas_task_internal_timedout; + task->slow_task->timer.expires = jiffies + TASK_TIMEOUT; + add_timer(&task->slow_task->timer); + + res = i->dft->lldd_execute_task(task, GFP_KERNEL); + if (res) { + timer_delete_sync(&task->slow_task->timer); + pr_err("executing TMF task failed %016llx (%d)\n", + SAS_ADDR(device->sas_addr), res); + break; + } + + wait_for_completion(&task->slow_task->completion); + + if (i->dft->lldd_tmf_exec_complete) + i->dft->lldd_tmf_exec_complete(device); + + res = TMF_RESP_FUNC_FAILED; + + if ((task->task_state_flags & SAS_TASK_STATE_ABORTED)) { + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + pr_err("TMF task timeout for %016llx and not done\n", + SAS_ADDR(device->sas_addr)); + if (i->dft->lldd_tmf_aborted) + i->dft->lldd_tmf_aborted(task); + break; + } + pr_warn("TMF task timeout for %016llx and done\n", + SAS_ADDR(device->sas_addr)); + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_COMPLETE) { + res = TMF_RESP_FUNC_COMPLETE; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == TMF_RESP_FUNC_SUCC) { + res = TMF_RESP_FUNC_SUCC; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_UNDERRUN) { + /* no error, but return the number of bytes of + * underrun + */ + pr_warn("TMF task to dev %016llx resp: 0x%x sts 0x%x underrun\n", + SAS_ADDR(device->sas_addr), + task->task_status.resp, + task->task_status.stat); + res = task->task_status.residual; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_DATA_OVERRUN) { + pr_warn("TMF task blocked task error %016llx\n", + SAS_ADDR(device->sas_addr)); + res = -EMSGSIZE; + break; + } + + if (task->task_status.resp == SAS_TASK_COMPLETE && + task->task_status.stat == SAS_OPEN_REJECT) { + pr_warn("TMF task open reject failed %016llx\n", + SAS_ADDR(device->sas_addr)); + res = -EIO; + } else { + pr_warn("TMF task to dev %016llx resp: 0x%x status 0x%x\n", + SAS_ADDR(device->sas_addr), + task->task_status.resp, + task->task_status.stat); + } + sas_free_task(task); + task = NULL; + } + + if (retry == TASK_RETRY) + pr_warn("executing TMF for %016llx failed after %d attempts!\n", + SAS_ADDR(device->sas_addr), TASK_RETRY); + sas_free_task(task); + + return res; +} + +static int sas_execute_ssp_tmf(struct domain_device *device, u8 *lun, + struct sas_tmf_task *tmf) +{ + struct sas_ssp_task ssp_task; + + if (!(device->tproto & SAS_PROTOCOL_SSP)) + return TMF_RESP_FUNC_ESUPP; + + memcpy(ssp_task.LUN, lun, 8); + + return sas_execute_tmf(device, &ssp_task, sizeof(ssp_task), -1, tmf); +} + +int sas_abort_task_set(struct domain_device *dev, u8 *lun) +{ + struct sas_tmf_task tmf_task = { + .tmf = TMF_ABORT_TASK_SET, + }; + + return sas_execute_ssp_tmf(dev, lun, &tmf_task); +} +EXPORT_SYMBOL_GPL(sas_abort_task_set); + +int sas_clear_task_set(struct domain_device *dev, u8 *lun) +{ + struct sas_tmf_task tmf_task = { + .tmf = TMF_CLEAR_TASK_SET, + }; + + return sas_execute_ssp_tmf(dev, lun, &tmf_task); +} +EXPORT_SYMBOL_GPL(sas_clear_task_set); + +int sas_lu_reset(struct domain_device *dev, u8 *lun) +{ + struct sas_tmf_task tmf_task = { + .tmf = TMF_LU_RESET, + }; + + return sas_execute_ssp_tmf(dev, lun, &tmf_task); +} +EXPORT_SYMBOL_GPL(sas_lu_reset); + +int sas_query_task(struct sas_task *task, u16 tag) +{ + struct sas_tmf_task tmf_task = { + .tmf = TMF_QUERY_TASK, + .tag_of_task_to_be_managed = tag, + }; + struct scsi_cmnd *cmnd = task->uldd_task; + struct domain_device *dev = task->dev; + struct scsi_lun lun; + + int_to_scsilun(cmnd->device->lun, &lun); + + return sas_execute_ssp_tmf(dev, lun.scsi_lun, &tmf_task); +} +EXPORT_SYMBOL_GPL(sas_query_task); + +int sas_abort_task(struct sas_task *task, u16 tag) +{ + struct sas_tmf_task tmf_task = { + .tmf = TMF_ABORT_TASK, + .tag_of_task_to_be_managed = tag, + }; + struct scsi_cmnd *cmnd = task->uldd_task; + struct domain_device *dev = task->dev; + struct scsi_lun lun; + + int_to_scsilun(cmnd->device->lun, &lun); + + return sas_execute_ssp_tmf(dev, lun.scsi_lun, &tmf_task); +} +EXPORT_SYMBOL_GPL(sas_abort_task); /* * Tell an upper layer that it needs to initiate an abort for a given task. @@ -920,23 +1180,27 @@ void sas_task_abort(struct sas_task *task) if (!slow) return; - if (!del_timer(&slow->timer)) + if (!timer_delete(&slow->timer)) return; - slow->timer.function(slow->timer.data); + slow->timer.function(&slow->timer); return; } - if (dev_is_sata(task->dev)) { + if (dev_is_sata(task->dev)) sas_ata_task_abort(task); - } else { - struct request_queue *q = sc->device->request_queue; - unsigned long flags; + else + blk_abort_request(scsi_cmd_to_rq(sc)); +} +EXPORT_SYMBOL_GPL(sas_task_abort); - spin_lock_irqsave(q->queue_lock, flags); - blk_abort_request(sc->request); - spin_unlock_irqrestore(q->queue_lock, flags); - } +int sas_sdev_init(struct scsi_device *sdev) +{ + if (dev_is_sata(sdev_to_domain_dev(sdev)) && sdev->lun) + return -ENXIO; + + return 0; } +EXPORT_SYMBOL_GPL(sas_sdev_init); void sas_target_destroy(struct scsi_target *starget) { @@ -948,21 +1212,7 @@ void sas_target_destroy(struct scsi_target *starget) starget->hostdata = NULL; sas_put_device(found_dev); } - -static void sas_parse_addr(u8 *sas_addr, const char *p) -{ - int i; - for (i = 0; i < SAS_ADDR_SIZE; i++) { - u8 h, l; - if (!*p) - break; - h = isdigit(*p) ? *p-'0' : toupper(*p)-'A'+10; - p++; - l = isdigit(*p) ? *p-'0' : toupper(*p)-'A'+10; - p++; - sas_addr[i] = (h<<4) | l; - } -} +EXPORT_SYMBOL_GPL(sas_target_destroy); #define SAS_STRING_ADDR_SIZE 16 @@ -980,7 +1230,9 @@ int sas_request_addr(struct Scsi_Host *shost, u8 *addr) goto out; } - sas_parse_addr(addr, fw->data); + res = hex2bin(addr, fw->data, strnlen(fw->data, SAS_ADDR_SIZE * 2) / 2); + if (res) + goto out; out: release_firmware(fw); @@ -988,14 +1240,3 @@ out: } EXPORT_SYMBOL_GPL(sas_request_addr); -EXPORT_SYMBOL_GPL(sas_queuecommand); -EXPORT_SYMBOL_GPL(sas_target_alloc); -EXPORT_SYMBOL_GPL(sas_slave_configure); -EXPORT_SYMBOL_GPL(sas_change_queue_depth); -EXPORT_SYMBOL_GPL(sas_bios_param); -EXPORT_SYMBOL_GPL(sas_task_abort); -EXPORT_SYMBOL_GPL(sas_phy_reset); -EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); -EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); -EXPORT_SYMBOL_GPL(sas_target_destroy); -EXPORT_SYMBOL_GPL(sas_ioctl); diff --git a/drivers/scsi/libsas/sas_task.c b/drivers/scsi/libsas/sas_task.c index a78e5bd3e514..e9d291007817 100644 --- a/drivers/scsi/libsas/sas_task.c +++ b/drivers/scsi/libsas/sas_task.c @@ -1,3 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "sas_internal.h" + #include <linux/kernel.h> #include <linux/export.h> #include <scsi/sas.h> @@ -11,27 +15,28 @@ void sas_ssp_task_response(struct device *dev, struct sas_task *task, tstat->resp = SAS_TASK_COMPLETE; - if (iu->datapres == 0) + switch (iu->datapres) { + case SAS_DATAPRES_NO_DATA: tstat->stat = iu->status; - else if (iu->datapres == 1) + break; + case SAS_DATAPRES_RESPONSE_DATA: tstat->stat = iu->resp_data[3]; - else if (iu->datapres == 2) { - tstat->stat = SAM_STAT_CHECK_CONDITION; + break; + case SAS_DATAPRES_SENSE_DATA: + tstat->stat = SAS_SAM_STAT_CHECK_CONDITION; tstat->buf_valid_size = min_t(int, SAS_STATUS_BUF_SIZE, be32_to_cpu(iu->sense_data_len)); memcpy(tstat->buf, iu->sense_data, tstat->buf_valid_size); if (iu->status != SAM_STAT_CHECK_CONDITION) - dev_printk(KERN_WARNING, dev, - "dev %llx sent sense data, but " - "stat(%x) is not CHECK CONDITION\n", - SAS_ADDR(task->dev->sas_addr), - iu->status); - } - else + dev_warn(dev, "dev %016llx sent sense data, but stat(0x%x) is not CHECK CONDITION\n", + SAS_ADDR(task->dev->sas_addr), iu->status); + break; + default: /* when datapres contains corrupt/unknown value... */ - tstat->stat = SAM_STAT_CHECK_CONDITION; + tstat->stat = SAS_SAM_STAT_CHECK_CONDITION; + } } EXPORT_SYMBOL_GPL(sas_ssp_task_response); |
