From e11e0ff8705000f4ca67878d6209bec16927ee0d Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:29:56 -0500 Subject: scsi: cxlflash: Preserve number of interrupts for master contexts The number of interrupts requested for user contexts are stored in the context specific structures and utilized to manage the interrupts. For the master contexts, this number is only used once and therefore not saved. To prepare for future commits where the number of interrupts will be required in more than one place, preserve the value in the master context structure. [mkp: typo in comment] Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/common.h | 1 + drivers/scsi/cxlflash/main.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 102fd26ca886..715c63c08892 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -211,6 +211,7 @@ struct hwq { struct sisl_ctrl_map __iomem *ctrl_map; /* MC control map */ ctx_hndl_t ctx_hndl; /* master's context handle */ u32 index; /* Index of this hwq */ + int num_irqs; /* Number of interrupts requested for context */ struct list_head pending_cmds; /* Commands pending completion */ atomic_t hsq_credits; diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index d8fe7ab870b8..3d3e0032bc2a 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -1911,7 +1911,7 @@ static enum undo_level init_intr(struct cxlflash_cfg *cfg, int rc = 0; enum undo_level level = UNDO_NOOP; bool is_primary_hwq = (hwq->index == PRIMARY_HWQ); - int num_irqs = is_primary_hwq ? 3 : 2; + int num_irqs = hwq->num_irqs; rc = cfg->ops->allocate_afu_irqs(ctx, num_irqs); if (unlikely(rc)) { @@ -1965,16 +1965,20 @@ static int init_mc(struct cxlflash_cfg *cfg, u32 index) struct device *dev = &cfg->dev->dev; struct hwq *hwq = get_hwq(cfg->afu, index); int rc = 0; + int num_irqs; enum undo_level level; hwq->afu = cfg->afu; hwq->index = index; INIT_LIST_HEAD(&hwq->pending_cmds); - if (index == PRIMARY_HWQ) + if (index == PRIMARY_HWQ) { ctx = cfg->ops->get_context(cfg->dev, cfg->afu_cookie); - else + num_irqs = 3; + } else { ctx = cfg->ops->dev_context_init(cfg->dev, cfg->afu_cookie); + num_irqs = 2; + } if (IS_ERR_OR_NULL(ctx)) { rc = -ENOMEM; goto err1; @@ -1982,6 +1986,7 @@ static int init_mc(struct cxlflash_cfg *cfg, u32 index) WARN_ON(hwq->ctx_cookie); hwq->ctx_cookie = ctx; + hwq->num_irqs = num_irqs; /* Set it up as a master with the CXL */ cfg->ops->set_master(ctx); -- cgit v1.2.3 From 465891fe9237b02f8d0fd26448f733fae7236f4a Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Mon, 26 Mar 2018 11:30:22 -0500 Subject: scsi: cxlflash: Avoid clobbering context control register value The SISLite specification originally defined the context control register with a single field of bits to represent the LISN and also stipulated that the register reset value be 0. The cxlflash driver took advantage of this when programming the LISN for the master contexts via an unconditional write - no other bits were preserved. When unmap support was added, SISLite was updated to define bit 0 of the context control register as a way for the AFU to notify the context owner that unmap operations were supported. Thus the assumptions under which the register is setup changed and the existing unconditional write is clobbering the unmap state for master contexts. This is presently not an issue due to the order in which the context control register is programmed in relation to the unmap bit being queried but should be addressed to avoid a future regression in the event this code is moved elsewhere. To remedy this issue, preserve the bits when programming the LISN field in the context control register. Since the LISN will now be programmed using a read value, assert that the initial state of the LISN field is as described in SISLite (0). Signed-off-by: Matthew R. Ochs Signed-off-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 5 ++++- drivers/scsi/cxlflash/sislite.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 3d3e0032bc2a..b83a55a93ba6 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -1303,7 +1303,10 @@ static void afu_err_intr_init(struct afu *afu) for (i = 0; i < afu->num_hwqs; i++) { hwq = get_hwq(afu, i); - writeq_be(SISL_MSI_SYNC_ERROR, &hwq->host_map->ctx_ctrl); + reg = readq_be(&hwq->host_map->ctx_ctrl); + WARN_ON((reg & SISL_CTX_CTRL_LISN_MASK) != 0); + reg |= SISL_MSI_SYNC_ERROR; + writeq_be(reg, &hwq->host_map->ctx_ctrl); writeq_be(SISL_ISTATUS_MASK, &hwq->host_map->intr_mask); } } diff --git a/drivers/scsi/cxlflash/sislite.h b/drivers/scsi/cxlflash/sislite.h index bedf1ce2f33c..d8940f1ae219 100644 --- a/drivers/scsi/cxlflash/sislite.h +++ b/drivers/scsi/cxlflash/sislite.h @@ -284,6 +284,7 @@ struct sisl_host_map { __be64 cmd_room; __be64 ctx_ctrl; /* least significant byte or b56:63 is LISN# */ #define SISL_CTX_CTRL_UNMAP_SECTOR 0x8000000000000000ULL /* b0 */ +#define SISL_CTX_CTRL_LISN_MASK (0xFFULL) __be64 mbox_w; /* restricted use */ __be64 sq_start; /* Submission Queue (R/W): write sequence and */ __be64 sq_end; /* inclusion semantics are the same as RRQ */ -- cgit v1.2.3 From fb77e52804afc51a1988438215197d1395fd1a95 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:30:37 -0500 Subject: scsi: cxlflash: Add argument identifier names Checkpatch throws a warning when the argument identifier names are not included in the function definitions. To avoid these warnings, argument identifiers are added in the existing function definitions. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/backend.h | 47 ++++++++++++++++++++++------------------- drivers/scsi/cxlflash/common.h | 4 ++-- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/cxlflash/backend.h b/drivers/scsi/cxlflash/backend.h index 339e42b03c49..7b721497ae22 100644 --- a/drivers/scsi/cxlflash/backend.h +++ b/drivers/scsi/cxlflash/backend.h @@ -16,26 +16,29 @@ extern const struct cxlflash_backend_ops cxlflash_cxl_ops; struct cxlflash_backend_ops { struct module *module; - void __iomem * (*psa_map)(void *); - void (*psa_unmap)(void __iomem *); - int (*process_element)(void *); - int (*map_afu_irq)(void *, int, irq_handler_t, void *, char *); - void (*unmap_afu_irq)(void *, int, void *); - int (*start_context)(void *); - int (*stop_context)(void *); - int (*afu_reset)(void *); - void (*set_master)(void *); - void * (*get_context)(struct pci_dev *, void *); - void * (*dev_context_init)(struct pci_dev *, void *); - int (*release_context)(void *); - void (*perst_reloads_same_image)(void *, bool); - ssize_t (*read_adapter_vpd)(struct pci_dev *, void *, size_t); - int (*allocate_afu_irqs)(void *, int); - void (*free_afu_irqs)(void *); - void * (*create_afu)(struct pci_dev *); - struct file * (*get_fd)(void *, struct file_operations *, int *); - void * (*fops_get_context)(struct file *); - int (*start_work)(void *, u64); - int (*fd_mmap)(struct file *, struct vm_area_struct *); - int (*fd_release)(struct inode *, struct file *); + void __iomem * (*psa_map)(void *ctx_cookie); + void (*psa_unmap)(void __iomem *addr); + int (*process_element)(void *ctx_cookie); + int (*map_afu_irq)(void *ctx_cookie, int num, irq_handler_t handler, + void *cookie, char *name); + void (*unmap_afu_irq)(void *ctx_cookie, int num, void *cookie); + int (*start_context)(void *ctx_cookie); + int (*stop_context)(void *ctx_cookie); + int (*afu_reset)(void *ctx_cookie); + void (*set_master)(void *ctx_cookie); + void * (*get_context)(struct pci_dev *dev, void *afu_cookie); + void * (*dev_context_init)(struct pci_dev *dev, void *afu_cookie); + int (*release_context)(void *ctx_cookie); + void (*perst_reloads_same_image)(void *afu_cookie, bool image); + ssize_t (*read_adapter_vpd)(struct pci_dev *dev, void *buf, + size_t count); + int (*allocate_afu_irqs)(void *ctx_cookie, int num); + void (*free_afu_irqs)(void *ctx_cookie); + void * (*create_afu)(struct pci_dev *dev); + struct file * (*get_fd)(void *ctx_cookie, struct file_operations *fops, + int *fd); + void * (*fops_get_context)(struct file *file); + int (*start_work)(void *ctx_cookie, u64 irqs); + int (*fd_mmap)(struct file *file, struct vm_area_struct *vm); + int (*fd_release)(struct inode *inode, struct file *file); }; diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 715c63c08892..ffcb1c62fbb7 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -232,8 +232,8 @@ struct hwq { struct afu { struct hwq hwqs[CXLFLASH_MAX_HWQS]; - int (*send_cmd)(struct afu *, struct afu_cmd *); - int (*context_reset)(struct hwq *); + int (*send_cmd)(struct afu *afu, struct afu_cmd *cmd); + int (*context_reset)(struct hwq *hwq); /* AFU HW */ struct cxlflash_afu_map __iomem *afu_map; /* entire MMIO map */ -- cgit v1.2.3 From 76ebe01fce11d905e9816f6b6f4293d751823c5f Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:30:51 -0500 Subject: scsi: cxlflash: Introduce OCXL backend Add initial infrastructure to support a new cxlflash transport, OCXL. Claim a dependency on OCXL and add a new file, ocxl_hw.c, which will host the backend routines that are specific to OCXL. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/Kconfig | 2 +- drivers/scsi/cxlflash/Makefile | 2 +- drivers/scsi/cxlflash/backend.h | 1 + drivers/scsi/cxlflash/ocxl_hw.c | 22 ++++++++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 drivers/scsi/cxlflash/ocxl_hw.c diff --git a/drivers/scsi/cxlflash/Kconfig b/drivers/scsi/cxlflash/Kconfig index a011c5dbf214..e2a3a1ba26fa 100644 --- a/drivers/scsi/cxlflash/Kconfig +++ b/drivers/scsi/cxlflash/Kconfig @@ -4,7 +4,7 @@ config CXLFLASH tristate "Support for IBM CAPI Flash" - depends on PCI && SCSI && CXL && EEH + depends on PCI && SCSI && CXL && OCXL && EEH select IRQ_POLL default m help diff --git a/drivers/scsi/cxlflash/Makefile b/drivers/scsi/cxlflash/Makefile index 7ec3f6b55dde..5124c68f8d88 100644 --- a/drivers/scsi/cxlflash/Makefile +++ b/drivers/scsi/cxlflash/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_CXLFLASH) += cxlflash.o -cxlflash-y += main.o superpipe.o lunmgt.o vlun.o cxl_hw.o +cxlflash-y += main.o superpipe.o lunmgt.o vlun.o cxl_hw.o ocxl_hw.o diff --git a/drivers/scsi/cxlflash/backend.h b/drivers/scsi/cxlflash/backend.h index 7b721497ae22..a60f0516090b 100644 --- a/drivers/scsi/cxlflash/backend.h +++ b/drivers/scsi/cxlflash/backend.h @@ -13,6 +13,7 @@ */ extern const struct cxlflash_backend_ops cxlflash_cxl_ops; +extern const struct cxlflash_backend_ops cxlflash_ocxl_ops; struct cxlflash_backend_ops { struct module *module; diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c new file mode 100644 index 000000000000..58a31822d936 --- /dev/null +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -0,0 +1,22 @@ +/* + * CXL Flash Device Driver + * + * Written by: Matthew R. Ochs , IBM Corporation + * Uma Krishnan , IBM Corporation + * + * Copyright (C) 2018 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. + */ + +#include + +#include "backend.h" + +/* Backend ops to ocxlflash services */ +const struct cxlflash_backend_ops cxlflash_ocxl_ops = { + .module = THIS_MODULE, +}; -- cgit v1.2.3 From 48e077dbb41d8ddc2ba5af7a60af4c56302ea8c7 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:01 -0500 Subject: scsi: cxlflash: Hardware AFU for OCXL When an adapter is initialized, transport specific configuration and MMIO mapping details need to be saved. For CXL, this data is managed by the underlying kernel module. To maintain a separation between the cxlflash core and underlying transports, introduce a new structure to store data specific to the OCXL AFU. Initially only the pointers to underlying PCI and generic devices are added to this new structure - it will be expanded further in future commits. Services to create and destroy this hardware AFU are added and integrated in the probe and exit paths of the driver. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/backend.h | 1 + drivers/scsi/cxlflash/cxl_hw.c | 6 ++++++ drivers/scsi/cxlflash/main.c | 9 +++++++-- drivers/scsi/cxlflash/ocxl_hw.c | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 19 +++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 drivers/scsi/cxlflash/ocxl_hw.h diff --git a/drivers/scsi/cxlflash/backend.h b/drivers/scsi/cxlflash/backend.h index a60f0516090b..f675bcb4f153 100644 --- a/drivers/scsi/cxlflash/backend.h +++ b/drivers/scsi/cxlflash/backend.h @@ -36,6 +36,7 @@ struct cxlflash_backend_ops { int (*allocate_afu_irqs)(void *ctx_cookie, int num); void (*free_afu_irqs)(void *ctx_cookie); void * (*create_afu)(struct pci_dev *dev); + void (*destroy_afu)(void *afu_cookie); struct file * (*get_fd)(void *ctx_cookie, struct file_operations *fops, int *fd); void * (*fops_get_context)(struct file *file); diff --git a/drivers/scsi/cxlflash/cxl_hw.c b/drivers/scsi/cxlflash/cxl_hw.c index db1cadad5c5d..a1d6d12090d3 100644 --- a/drivers/scsi/cxlflash/cxl_hw.c +++ b/drivers/scsi/cxlflash/cxl_hw.c @@ -110,6 +110,11 @@ static void *cxlflash_create_afu(struct pci_dev *dev) return cxl_pci_to_afu(dev); } +static void cxlflash_destroy_afu(void *afu) +{ + /* Dummy fop for cxl */ +} + static struct file *cxlflash_get_fd(void *ctx_cookie, struct file_operations *fops, int *fd) { @@ -160,6 +165,7 @@ const struct cxlflash_backend_ops cxlflash_cxl_ops = { .allocate_afu_irqs = cxlflash_allocate_afu_irqs, .free_afu_irqs = cxlflash_free_afu_irqs, .create_afu = cxlflash_create_afu, + .destroy_afu = cxlflash_destroy_afu, .get_fd = cxlflash_get_fd, .fops_get_context = cxlflash_fops_get_context, .start_work = cxlflash_start_work, diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index b83a55a93ba6..5d754d1c22a0 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -971,6 +971,7 @@ static void cxlflash_remove(struct pci_dev *pdev) case INIT_STATE_AFU: term_afu(cfg); case INIT_STATE_PCI: + cfg->ops->destroy_afu(cfg->afu_cookie); pci_disable_device(pdev); case INIT_STATE_NONE: free_mem(cfg); @@ -3689,8 +3690,6 @@ static int cxlflash_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, cfg); - cfg->afu_cookie = cfg->ops->create_afu(pdev); - rc = init_pci(cfg); if (rc) { dev_err(dev, "%s: init_pci failed rc=%d\n", __func__, rc); @@ -3698,6 +3697,12 @@ static int cxlflash_probe(struct pci_dev *pdev, } cfg->init_state = INIT_STATE_PCI; + cfg->afu_cookie = cfg->ops->create_afu(pdev); + if (unlikely(!cfg->afu_cookie)) { + dev_err(dev, "%s: create_afu failed\n", __func__); + goto out_remove; + } + rc = init_afu(cfg); if (rc && !wq_has_sleeper(&cfg->reset_waitq)) { dev_err(dev, "%s: init_afu failed rc=%d\n", __func__, rc); diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 58a31822d936..e3a0a9b42e8f 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -15,8 +15,48 @@ #include #include "backend.h" +#include "ocxl_hw.h" + +/** + * ocxlflash_destroy_afu() - destroy the AFU structure + * @afu_cookie: AFU to be freed. + */ +static void ocxlflash_destroy_afu(void *afu_cookie) +{ + struct ocxl_hw_afu *afu = afu_cookie; + + if (!afu) + return; + + kfree(afu); +} + +/** + * ocxlflash_create_afu() - create the AFU for OCXL + * @pdev: PCI device associated with the host. + * + * Return: AFU on success, NULL on failure + */ +static void *ocxlflash_create_afu(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct ocxl_hw_afu *afu; + + afu = kzalloc(sizeof(*afu), GFP_KERNEL); + if (unlikely(!afu)) { + dev_err(dev, "%s: HW AFU allocation failed\n", __func__); + goto out; + } + + afu->pdev = pdev; + afu->dev = dev; +out: + return afu; +} /* Backend ops to ocxlflash services */ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, + .create_afu = ocxlflash_create_afu, + .destroy_afu = ocxlflash_destroy_afu, }; diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h new file mode 100644 index 000000000000..c7e5c4dba49c --- /dev/null +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -0,0 +1,19 @@ +/* + * CXL Flash Device Driver + * + * Written by: Matthew R. Ochs , IBM Corporation + * Uma Krishnan , IBM Corporation + * + * Copyright (C) 2018 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. + */ + +/* OCXL hardware AFU associated with the host */ +struct ocxl_hw_afu { + struct pci_dev *pdev; /* PCI device */ + struct device *dev; /* Generic device */ +}; -- cgit v1.2.3 From e9dfceda92ae6c59041e7256de137b6132f37cdf Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:09 -0500 Subject: scsi: cxlflash: Read host function configuration Per the OCXL specification, the underlying host can have multiple AFUs per function with each function supporting its own configuration. The host function configuration is read on the initialization path to evaluate the number of functions present and identify the features and configuration of the functions present. This data is cached for use in later configuration steps. Note that for the OCXL hardware supported by the cxlflash driver, only one AFU per function is expected. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 44 +++++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 4 ++++ 2 files changed, 48 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index e3a0a9b42e8f..85c0f84f2a68 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -31,6 +31,38 @@ static void ocxlflash_destroy_afu(void *afu_cookie) kfree(afu); } +/** + * ocxlflash_config_fn() - configure the host function + * @pdev: PCI device associated with the host. + * @afu: AFU associated with the host. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) +{ + struct ocxl_fn_config *fcfg = &afu->fcfg; + struct device *dev = &pdev->dev; + int rc = 0; + + /* Read DVSEC config of the function */ + rc = ocxl_config_read_function(pdev, fcfg); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_config_read_function failed rc=%d\n", + __func__, rc); + goto out; + } + + /* Check if function has AFUs defined, only 1 per function supported */ + if (fcfg->max_afu_index >= 0) { + afu->is_present = true; + if (fcfg->max_afu_index != 0) + dev_warn(dev, "%s: Unexpected AFU index value %d\n", + __func__, fcfg->max_afu_index); + } +out: + return rc; +} + /** * ocxlflash_create_afu() - create the AFU for OCXL * @pdev: PCI device associated with the host. @@ -41,6 +73,7 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) { struct device *dev = &pdev->dev; struct ocxl_hw_afu *afu; + int rc; afu = kzalloc(sizeof(*afu), GFP_KERNEL); if (unlikely(!afu)) { @@ -50,8 +83,19 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) afu->pdev = pdev; afu->dev = dev; + + rc = ocxlflash_config_fn(pdev, afu); + if (unlikely(rc)) { + dev_err(dev, "%s: Function configuration failed rc=%d\n", + __func__, rc); + goto err1; + } out: return afu; +err1: + kfree(afu); + afu = NULL; + goto out; } /* Backend ops to ocxlflash services */ diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index c7e5c4dba49c..356a6a36be5c 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -16,4 +16,8 @@ struct ocxl_hw_afu { struct pci_dev *pdev; /* PCI device */ struct device *dev; /* Generic device */ + + struct ocxl_fn_config fcfg; /* DVSEC config of the function */ + + bool is_present; /* Function has AFUs defined */ }; -- cgit v1.2.3 From 2e222779aea6ca93d371185b6cb134734cbf80de Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:21 -0500 Subject: scsi: cxlflash: Setup function acTag range The OCXL specification supports distributing acTags amongst different AFUs and functions on the link. The platform-specific acTag range for the link is obtained using the OCXL provider services and then assigned to the host function based on implementation. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 15 +++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 85c0f84f2a68..778384237b8c 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -42,6 +42,7 @@ static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) { struct ocxl_fn_config *fcfg = &afu->fcfg; struct device *dev = &pdev->dev; + u16 base, enabled, supported; int rc = 0; /* Read DVSEC config of the function */ @@ -59,6 +60,20 @@ static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) dev_warn(dev, "%s: Unexpected AFU index value %d\n", __func__, fcfg->max_afu_index); } + + rc = ocxl_config_get_actag_info(pdev, &base, &enabled, &supported); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_config_get_actag_info failed rc=%d\n", + __func__, rc); + goto out; + } + + afu->fn_actag_base = base; + afu->fn_actag_enabled = enabled; + + ocxl_config_set_actag(pdev, fcfg->dvsec_function_pos, base, enabled); + dev_dbg(dev, "%s: Function acTag range base=%u enabled=%u\n", + __func__, base, enabled); out: return rc; } diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 356a6a36be5c..55e833cb0890 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -19,5 +19,8 @@ struct ocxl_hw_afu { struct ocxl_fn_config fcfg; /* DVSEC config of the function */ + int fn_actag_base; /* Function acTag base */ + int fn_actag_enabled; /* Function acTag number enabled */ + bool is_present; /* Function has AFUs defined */ }; -- cgit v1.2.3 From 9cc84291be6ee6c1336d38baf0acb03fe087fd28 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:29 -0500 Subject: scsi: cxlflash: Read host AFU configuration The host AFU configuration is read on the initialization path to identify the features and configuration of the AFU. This data is cached for use in later configuration steps. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 1 + 2 files changed, 39 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 778384237b8c..11399cf12ebd 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -78,6 +78,37 @@ out: return rc; } +/** + * ocxlflash_config_afu() - configure the host AFU + * @pdev: PCI device associated with the host. + * @afu: AFU associated with the host. + * + * Must be called _after_ host function configuration. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) +{ + struct ocxl_afu_config *acfg = &afu->acfg; + struct ocxl_fn_config *fcfg = &afu->fcfg; + struct device *dev = &pdev->dev; + int rc = 0; + + /* This HW AFU function does not have any AFUs defined */ + if (!afu->is_present) + goto out; + + /* Read AFU config at index 0 */ + rc = ocxl_config_read_afu(pdev, fcfg, acfg, 0); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_config_read_afu failed rc=%d\n", + __func__, rc); + goto out; + } +out: + return rc; +} + /** * ocxlflash_create_afu() - create the AFU for OCXL * @pdev: PCI device associated with the host. @@ -105,6 +136,13 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) __func__, rc); goto err1; } + + rc = ocxlflash_config_afu(pdev, afu); + if (unlikely(rc)) { + dev_err(dev, "%s: AFU configuration failed rc=%d\n", + __func__, rc); + goto err1; + } out: return afu; err1: diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 55e833cb0890..c9b284377cb7 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -18,6 +18,7 @@ struct ocxl_hw_afu { struct device *dev; /* Generic device */ struct ocxl_fn_config fcfg; /* DVSEC config of the function */ + struct ocxl_afu_config acfg; /* AFU configuration data */ int fn_actag_base; /* Function acTag base */ int fn_actag_enabled; /* Function acTag number enabled */ -- cgit v1.2.3 From d926519e8f9b39d307d97cd4b904e006c309e37e Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:36 -0500 Subject: scsi: cxlflash: Setup AFU acTag range The OCXL specification supports distributing acTags amongst different AFUs and functions on the link. As cxlflash devices are expected to only support a single AFU per function, the entire range that was assigned to the function is also assigned to the AFU. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 13 +++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 11399cf12ebd..105712b29b2e 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -92,6 +92,9 @@ static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) struct ocxl_afu_config *acfg = &afu->acfg; struct ocxl_fn_config *fcfg = &afu->fcfg; struct device *dev = &pdev->dev; + int count; + int base; + int pos; int rc = 0; /* This HW AFU function does not have any AFUs defined */ @@ -105,6 +108,16 @@ static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) __func__, rc); goto out; } + + /* Only one AFU per function is supported, so actag_base is same */ + base = afu->fn_actag_base; + count = min_t(int, acfg->actag_supported, afu->fn_actag_enabled); + pos = acfg->dvsec_afu_control_pos; + + ocxl_config_set_afu_actag(pdev, pos, base, count); + dev_dbg(dev, "%s: acTag base=%d enabled=%d\n", __func__, base, count); + afu->afu_actag_base = base; + afu->afu_actag_enabled = count; out: return rc; } diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index c9b284377cb7..21803b52e3be 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -22,6 +22,8 @@ struct ocxl_hw_afu { int fn_actag_base; /* Function acTag base */ int fn_actag_enabled; /* Function acTag number enabled */ + int afu_actag_base; /* AFU acTag base */ + int afu_actag_enabled; /* AFU acTag number enabled */ bool is_present; /* Function has AFUs defined */ }; -- cgit v1.2.3 From 41df40d817781c9052c8755b612479445e5db638 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:44 -0500 Subject: scsi: cxlflash: Setup AFU PASID Per the OCXL specification, the maximum PASID supported by the AFU is indicated by a field within the configuration space. Similar to acTags, implementations can choose to use any sub-range of PASID within their assigned range. For cxlflash, the entire range is used. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Andrew Donnellan Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 3 +++ drivers/scsi/cxlflash/ocxl_hw.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 105712b29b2e..44688af7af3a 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -118,6 +118,9 @@ static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) dev_dbg(dev, "%s: acTag base=%d enabled=%d\n", __func__, base, count); afu->afu_actag_base = base; afu->afu_actag_enabled = count; + afu->max_pasid = 1 << acfg->pasid_supported_log; + + ocxl_config_set_afu_pasid(pdev, pos, 0, acfg->pasid_supported_log); out: return rc; } diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 21803b52e3be..46de75b59b1a 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -25,5 +25,6 @@ struct ocxl_hw_afu { int afu_actag_base; /* AFU acTag base */ int afu_actag_enabled; /* AFU acTag number enabled */ + int max_pasid; /* Maximum number of contexts */ bool is_present; /* Function has AFUs defined */ }; -- cgit v1.2.3 From f6b4557c98121e5571c484b037897130d898287c Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:31:53 -0500 Subject: scsi: cxlflash: Adapter context support for OCXL Add support to create and release the adapter contexts for OCXL and provide means to specify certain contexts as a master. The existing cxlflash core has a design requirement that each host will have a single host context available by default. To satisfy this requirement, one host adapter context is created when the hardware AFU is initialized. This is returned by the get_context() fop. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 90 +++++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 6 +++ 2 files changed, 96 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 44688af7af3a..cbe4d9341f5a 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -17,6 +17,80 @@ #include "backend.h" #include "ocxl_hw.h" +/** + * ocxlflash_set_master() - sets the context as master + * @ctx_cookie: Adapter context to set as master. + */ +static void ocxlflash_set_master(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + + ctx->master = true; +} + +/** + * ocxlflash_get_context() - obtains the context associated with the host + * @pdev: PCI device associated with the host. + * @afu_cookie: Hardware AFU associated with the host. + * + * Return: returns the pointer to host adapter context + */ +static void *ocxlflash_get_context(struct pci_dev *pdev, void *afu_cookie) +{ + struct ocxl_hw_afu *afu = afu_cookie; + + return afu->ocxl_ctx; +} + +/** + * ocxlflash_dev_context_init() - allocate and initialize an adapter context + * @pdev: PCI device associated with the host. + * @afu_cookie: Hardware AFU associated with the host. + * + * Return: returns the adapter context on success, ERR_PTR on failure + */ +static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) +{ + struct ocxl_hw_afu *afu = afu_cookie; + struct device *dev = afu->dev; + struct ocxlflash_context *ctx; + int rc; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(!ctx)) { + dev_err(dev, "%s: Context allocation failed\n", __func__); + rc = -ENOMEM; + goto err; + } + + ctx->master = false; + ctx->hw_afu = afu; +out: + return ctx; +err: + ctx = ERR_PTR(rc); + goto out; +} + +/** + * ocxlflash_release_context() - releases an adapter context + * @ctx_cookie: Adapter context to be released. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_release_context(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + int rc = 0; + + if (!ctx) + goto out; + + kfree(ctx); +out: + return rc; +} + /** * ocxlflash_destroy_afu() - destroy the AFU structure * @afu_cookie: AFU to be freed. @@ -28,6 +102,7 @@ static void ocxlflash_destroy_afu(void *afu_cookie) if (!afu) return; + ocxlflash_release_context(afu->ocxl_ctx); kfree(afu); } @@ -134,6 +209,7 @@ out: static void *ocxlflash_create_afu(struct pci_dev *pdev) { struct device *dev = &pdev->dev; + struct ocxlflash_context *ctx; struct ocxl_hw_afu *afu; int rc; @@ -159,6 +235,16 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) __func__, rc); goto err1; } + + ctx = ocxlflash_dev_context_init(pdev, afu); + if (IS_ERR(ctx)) { + rc = PTR_ERR(ctx); + dev_err(dev, "%s: ocxlflash_dev_context_init failed rc=%d\n", + __func__, rc); + goto err1; + } + + afu->ocxl_ctx = ctx; out: return afu; err1: @@ -170,6 +256,10 @@ err1: /* Backend ops to ocxlflash services */ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, + .set_master = ocxlflash_set_master, + .get_context = ocxlflash_get_context, + .dev_context_init = ocxlflash_dev_context_init, + .release_context = ocxlflash_release_context, .create_afu = ocxlflash_create_afu, .destroy_afu = ocxlflash_destroy_afu, }; diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 46de75b59b1a..f41ba0ba23c6 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -14,6 +14,7 @@ /* OCXL hardware AFU associated with the host */ struct ocxl_hw_afu { + struct ocxlflash_context *ocxl_ctx; /* Host context */ struct pci_dev *pdev; /* PCI device */ struct device *dev; /* Generic device */ @@ -28,3 +29,8 @@ struct ocxl_hw_afu { int max_pasid; /* Maximum number of contexts */ bool is_present; /* Function has AFUs defined */ }; + +struct ocxlflash_context { + struct ocxl_hw_afu *hw_afu; /* HW AFU back pointer */ + bool master; /* Whether this is a master context */ +}; -- cgit v1.2.3 From 429ebfa69b8a456194cd424772dace40b1c3f963 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:00 -0500 Subject: scsi: cxlflash: Use IDR to manage adapter contexts A range of PASIDs are used as identifiers for the adapter contexts. These contexts may be destroyed and created randomly. Use an IDR to keep track of contexts that are in use and assign a unique identifier to new ones. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 21 +++++++++++++++++++-- drivers/scsi/cxlflash/ocxl_hw.h | 2 ++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index cbe4d9341f5a..e8864a1f244d 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -12,6 +12,8 @@ * 2 of the License, or (at your option) any later version. */ +#include + #include #include "backend.h" @@ -60,14 +62,25 @@ static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) if (unlikely(!ctx)) { dev_err(dev, "%s: Context allocation failed\n", __func__); rc = -ENOMEM; - goto err; + goto err1; + } + + idr_preload(GFP_KERNEL); + rc = idr_alloc(&afu->idr, ctx, 0, afu->max_pasid, GFP_NOWAIT); + idr_preload_end(); + if (unlikely(rc < 0)) { + dev_err(dev, "%s: idr_alloc failed rc=%d\n", __func__, rc); + goto err2; } + ctx->pe = rc; ctx->master = false; ctx->hw_afu = afu; out: return ctx; -err: +err2: + kfree(ctx); +err1: ctx = ERR_PTR(rc); goto out; } @@ -86,6 +99,7 @@ static int ocxlflash_release_context(void *ctx_cookie) if (!ctx) goto out; + idr_remove(&ctx->hw_afu->idr, ctx->pe); kfree(ctx); out: return rc; @@ -103,6 +117,7 @@ static void ocxlflash_destroy_afu(void *afu_cookie) return; ocxlflash_release_context(afu->ocxl_ctx); + idr_destroy(&afu->idr); kfree(afu); } @@ -221,6 +236,7 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) afu->pdev = pdev; afu->dev = dev; + idr_init(&afu->idr); rc = ocxlflash_config_fn(pdev, afu); if (unlikely(rc)) { @@ -248,6 +264,7 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) out: return afu; err1: + idr_destroy(&afu->idr); kfree(afu); afu = NULL; goto out; diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index f41ba0ba23c6..a5337b62a557 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -26,6 +26,7 @@ struct ocxl_hw_afu { int afu_actag_base; /* AFU acTag base */ int afu_actag_enabled; /* AFU acTag number enabled */ + struct idr idr; /* IDR to manage contexts */ int max_pasid; /* Maximum number of contexts */ bool is_present; /* Function has AFUs defined */ }; @@ -33,4 +34,5 @@ struct ocxl_hw_afu { struct ocxlflash_context { struct ocxl_hw_afu *hw_afu; /* HW AFU back pointer */ bool master; /* Whether this is a master context */ + int pe; /* Process element */ }; -- cgit v1.2.3 From 926a62f9bd53f5ad411e831eeb259a9564fb1532 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:09 -0500 Subject: scsi: cxlflash: Support adapter file descriptors for OCXL Allocate a file descriptor for an adapter context when requested. In order to allocate inodes for the file descriptors, a pseudo filesystem is created and used. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 200 ++++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 1 + 2 files changed, 201 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index e8864a1f244d..49af0597ba84 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -12,13 +12,144 @@ * 2 of the License, or (at your option) any later version. */ +#include #include +#include +#include #include #include "backend.h" #include "ocxl_hw.h" +/* + * Pseudo-filesystem to allocate inodes. + */ + +#define OCXLFLASH_FS_MAGIC 0x1697698f + +static int ocxlflash_fs_cnt; +static struct vfsmount *ocxlflash_vfs_mount; + +static const struct dentry_operations ocxlflash_fs_dops = { + .d_dname = simple_dname, +}; + +/* + * ocxlflash_fs_mount() - mount the pseudo-filesystem + * @fs_type: File system type. + * @flags: Flags for the filesystem. + * @dev_name: Device name associated with the filesystem. + * @data: Data pointer. + * + * Return: pointer to the directory entry structure + */ +static struct dentry *ocxlflash_fs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return mount_pseudo(fs_type, "ocxlflash:", NULL, &ocxlflash_fs_dops, + OCXLFLASH_FS_MAGIC); +} + +static struct file_system_type ocxlflash_fs_type = { + .name = "ocxlflash", + .owner = THIS_MODULE, + .mount = ocxlflash_fs_mount, + .kill_sb = kill_anon_super, +}; + +/* + * ocxlflash_release_mapping() - release the memory mapping + * @ctx: Context whose mapping is to be released. + */ +static void ocxlflash_release_mapping(struct ocxlflash_context *ctx) +{ + if (ctx->mapping) + simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); + ctx->mapping = NULL; +} + +/* + * ocxlflash_getfile() - allocate pseudo filesystem, inode, and the file + * @dev: Generic device of the host. + * @name: Name of the pseudo filesystem. + * @fops: File operations. + * @priv: Private data. + * @flags: Flags for the file. + * + * Return: pointer to the file on success, ERR_PTR on failure + */ +static struct file *ocxlflash_getfile(struct device *dev, const char *name, + const struct file_operations *fops, + void *priv, int flags) +{ + struct qstr this; + struct path path; + struct file *file; + struct inode *inode = NULL; + int rc; + + if (fops->owner && !try_module_get(fops->owner)) { + dev_err(dev, "%s: Owner does not exist\n", __func__); + rc = -ENOENT; + goto err1; + } + + rc = simple_pin_fs(&ocxlflash_fs_type, &ocxlflash_vfs_mount, + &ocxlflash_fs_cnt); + if (unlikely(rc < 0)) { + dev_err(dev, "%s: Cannot mount ocxlflash pseudofs rc=%d\n", + __func__, rc); + goto err2; + } + + inode = alloc_anon_inode(ocxlflash_vfs_mount->mnt_sb); + if (IS_ERR(inode)) { + rc = PTR_ERR(inode); + dev_err(dev, "%s: alloc_anon_inode failed rc=%d\n", + __func__, rc); + goto err3; + } + + this.name = name; + this.len = strlen(name); + this.hash = 0; + path.dentry = d_alloc_pseudo(ocxlflash_vfs_mount->mnt_sb, &this); + if (!path.dentry) { + dev_err(dev, "%s: d_alloc_pseudo failed\n", __func__); + rc = -ENOMEM; + goto err4; + } + + path.mnt = mntget(ocxlflash_vfs_mount); + d_instantiate(path.dentry, inode); + + file = alloc_file(&path, OPEN_FMODE(flags), fops); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + dev_err(dev, "%s: alloc_file failed rc=%d\n", + __func__, rc); + goto err5; + } + + file->f_flags = flags & (O_ACCMODE | O_NONBLOCK); + file->private_data = priv; +out: + return file; +err5: + path_put(&path); +err4: + iput(inode); +err3: + simple_release_fs(&ocxlflash_vfs_mount, &ocxlflash_fs_cnt); +err2: + module_put(fops->owner); +err1: + file = ERR_PTR(rc); + goto out; +} + /** * ocxlflash_set_master() - sets the context as master * @ctx_cookie: Adapter context to set as master. @@ -75,6 +206,7 @@ static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) ctx->pe = rc; ctx->master = false; + ctx->mapping = NULL; ctx->hw_afu = afu; out: return ctx; @@ -100,6 +232,7 @@ static int ocxlflash_release_context(void *ctx_cookie) goto out; idr_remove(&ctx->hw_afu->idr, ctx->pe); + ocxlflash_release_mapping(ctx); kfree(ctx); out: return rc; @@ -270,6 +403,72 @@ err1: goto out; } +static const struct file_operations ocxl_afu_fops = { + .owner = THIS_MODULE, +}; + +/** + * ocxlflash_get_fd() - get file descriptor for an adapter context + * @ctx_cookie: Adapter context. + * @fops: File operations to be associated. + * @fd: File descriptor to be returned back. + * + * Return: pointer to the file on success, ERR_PTR on failure + */ +static struct file *ocxlflash_get_fd(void *ctx_cookie, + struct file_operations *fops, int *fd) +{ + struct ocxlflash_context *ctx = ctx_cookie; + struct device *dev = ctx->hw_afu->dev; + struct file *file; + int flags, fdtmp; + int rc = 0; + char *name = NULL; + + /* Only allow one fd per context */ + if (ctx->mapping) { + dev_err(dev, "%s: Context is already mapped to an fd\n", + __func__); + rc = -EEXIST; + goto err1; + } + + flags = O_RDWR | O_CLOEXEC; + + /* This code is similar to anon_inode_getfd() */ + rc = get_unused_fd_flags(flags); + if (unlikely(rc < 0)) { + dev_err(dev, "%s: get_unused_fd_flags failed rc=%d\n", + __func__, rc); + goto err1; + } + fdtmp = rc; + + /* Use default ops if there is no fops */ + if (!fops) + fops = (struct file_operations *)&ocxl_afu_fops; + + name = kasprintf(GFP_KERNEL, "ocxlflash:%d", ctx->pe); + file = ocxlflash_getfile(dev, name, fops, ctx, flags); + kfree(name); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + dev_err(dev, "%s: ocxlflash_getfile failed rc=%d\n", + __func__, rc); + goto err2; + } + + ctx->mapping = file->f_mapping; + *fd = fdtmp; +out: + return file; +err2: + put_unused_fd(fdtmp); +err1: + file = ERR_PTR(rc); + goto out; +} + /* Backend ops to ocxlflash services */ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, @@ -279,4 +478,5 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .release_context = ocxlflash_release_context, .create_afu = ocxlflash_create_afu, .destroy_afu = ocxlflash_destroy_afu, + .get_fd = ocxlflash_get_fd, }; diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index a5337b62a557..55db0052a53c 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -33,6 +33,7 @@ struct ocxl_hw_afu { struct ocxlflash_context { struct ocxl_hw_afu *hw_afu; /* HW AFU back pointer */ + struct address_space *mapping; /* Mapping for pseudo filesystem */ bool master; /* Whether this is a master context */ int pe; /* Process element */ }; -- cgit v1.2.3 From b18718c626919f475f82eec6a4940540e7ce44da Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:20 -0500 Subject: scsi: cxlflash: Support adapter context discovery Provide means to obtain the process element of an adapter context as well as locate an adapter context by file. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 49af0597ba84..21915b726106 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -150,6 +150,19 @@ err1: goto out; } +/** + * ocxlflash_process_element() - get process element of the adapter context + * @ctx_cookie: Adapter context associated with the process element. + * + * Return: process element of the adapter context + */ +static int ocxlflash_process_element(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + + return ctx->pe; +} + /** * ocxlflash_set_master() - sets the context as master * @ctx_cookie: Adapter context to set as master. @@ -469,9 +482,21 @@ err1: goto out; } +/** + * ocxlflash_fops_get_context() - get the context associated with the file + * @file: File associated with the adapter context. + * + * Return: pointer to the context + */ +static void *ocxlflash_fops_get_context(struct file *file) +{ + return file->private_data; +} + /* Backend ops to ocxlflash services */ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, + .process_element = ocxlflash_process_element, .set_master = ocxlflash_set_master, .get_context = ocxlflash_get_context, .dev_context_init = ocxlflash_dev_context_init, @@ -479,4 +504,5 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .create_afu = ocxlflash_create_afu, .destroy_afu = ocxlflash_destroy_afu, .get_fd = ocxlflash_get_fd, + .fops_get_context = ocxlflash_fops_get_context, }; -- cgit v1.2.3 From 8b7a55215000496a6284bc15a2cb47f46ae78a80 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:29 -0500 Subject: scsi: cxlflash: Support image reload policy modification On a PERST, the AFU image can be reloaded or left intact. Provide means to set this image reload policy. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 13 +++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 1 + 2 files changed, 14 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 21915b726106..f26bbb793f4e 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -251,6 +251,18 @@ out: return rc; } +/** + * ocxlflash_perst_reloads_same_image() - sets the image reload policy + * @afu_cookie: Hardware AFU associated with the host. + * @image: Whether to load the same image on PERST. + */ +static void ocxlflash_perst_reloads_same_image(void *afu_cookie, bool image) +{ + struct ocxl_hw_afu *afu = afu_cookie; + + afu->perst_same_image = image; +} + /** * ocxlflash_destroy_afu() - destroy the AFU structure * @afu_cookie: AFU to be freed. @@ -501,6 +513,7 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .get_context = ocxlflash_get_context, .dev_context_init = ocxlflash_dev_context_init, .release_context = ocxlflash_release_context, + .perst_reloads_same_image = ocxlflash_perst_reloads_same_image, .create_afu = ocxlflash_create_afu, .destroy_afu = ocxlflash_destroy_afu, .get_fd = ocxlflash_get_fd, diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 55db0052a53c..b96387114515 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -17,6 +17,7 @@ struct ocxl_hw_afu { struct ocxlflash_context *ocxl_ctx; /* Host context */ struct pci_dev *pdev; /* PCI device */ struct device *dev; /* Generic device */ + bool perst_same_image; /* Same image loaded on perst */ struct ocxl_fn_config fcfg; /* DVSEC config of the function */ struct ocxl_afu_config acfg; /* AFU configuration data */ -- cgit v1.2.3 From 54370503a7ec6460689e3cb46759c63162ab0c98 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:37 -0500 Subject: scsi: cxlflash: MMIO map the AFU When the AFU is configured, the global and per process MMIO regions are presented by the configuration space. Save these regions and map the global MMIO region that is used to access all of the control and provisioning data in the AFU. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 74 ++++++++++++++++++++++++++++++++++++++++- drivers/scsi/cxlflash/ocxl_hw.h | 4 +++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index f26bbb793f4e..235803407b89 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -263,6 +263,18 @@ static void ocxlflash_perst_reloads_same_image(void *afu_cookie, bool image) afu->perst_same_image = image; } +/** + * ocxlflash_unconfig_afu() - unconfigure the AFU + * @afu: AFU associated with the host. + */ +static void ocxlflash_unconfig_afu(struct ocxl_hw_afu *afu) +{ + if (afu->gmmio_virt) { + iounmap(afu->gmmio_virt); + afu->gmmio_virt = NULL; + } +} + /** * ocxlflash_destroy_afu() - destroy the AFU structure * @afu_cookie: AFU to be freed. @@ -276,6 +288,7 @@ static void ocxlflash_destroy_afu(void *afu_cookie) ocxlflash_release_context(afu->ocxl_ctx); idr_destroy(&afu->idr); + ocxlflash_unconfig_afu(afu); kfree(afu); } @@ -326,6 +339,56 @@ out: return rc; } +/** + * ocxlflash_map_mmio() - map the AFU MMIO space + * @afu: AFU associated with the host. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_map_mmio(struct ocxl_hw_afu *afu) +{ + struct ocxl_afu_config *acfg = &afu->acfg; + struct pci_dev *pdev = afu->pdev; + struct device *dev = afu->dev; + phys_addr_t gmmio, ppmmio; + int rc = 0; + + rc = pci_request_region(pdev, acfg->global_mmio_bar, "ocxlflash"); + if (unlikely(rc)) { + dev_err(dev, "%s: pci_request_region for global failed rc=%d\n", + __func__, rc); + goto out; + } + gmmio = pci_resource_start(pdev, acfg->global_mmio_bar); + gmmio += acfg->global_mmio_offset; + + rc = pci_request_region(pdev, acfg->pp_mmio_bar, "ocxlflash"); + if (unlikely(rc)) { + dev_err(dev, "%s: pci_request_region for pp bar failed rc=%d\n", + __func__, rc); + goto err1; + } + ppmmio = pci_resource_start(pdev, acfg->pp_mmio_bar); + ppmmio += acfg->pp_mmio_offset; + + afu->gmmio_virt = ioremap(gmmio, acfg->global_mmio_size); + if (unlikely(!afu->gmmio_virt)) { + dev_err(dev, "%s: MMIO mapping failed\n", __func__); + rc = -ENOMEM; + goto err2; + } + + afu->gmmio_phys = gmmio; + afu->ppmmio_phys = ppmmio; +out: + return rc; +err2: + pci_release_region(pdev, acfg->pp_mmio_bar); +err1: + pci_release_region(pdev, acfg->global_mmio_bar); + goto out; +} + /** * ocxlflash_config_afu() - configure the host AFU * @pdev: PCI device associated with the host. @@ -369,6 +432,13 @@ static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) afu->max_pasid = 1 << acfg->pasid_supported_log; ocxl_config_set_afu_pasid(pdev, pos, 0, acfg->pasid_supported_log); + + rc = ocxlflash_map_mmio(afu); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxlflash_map_mmio failed rc=%d\n", + __func__, rc); + goto out; + } out: return rc; } @@ -415,12 +485,14 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) rc = PTR_ERR(ctx); dev_err(dev, "%s: ocxlflash_dev_context_init failed rc=%d\n", __func__, rc); - goto err1; + goto err2; } afu->ocxl_ctx = ctx; out: return afu; +err2: + ocxlflash_unconfig_afu(afu); err1: idr_destroy(&afu->idr); kfree(afu); diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index b96387114515..18236e6dafd5 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -27,6 +27,10 @@ struct ocxl_hw_afu { int afu_actag_base; /* AFU acTag base */ int afu_actag_enabled; /* AFU acTag number enabled */ + phys_addr_t ppmmio_phys; /* Per process MMIO space */ + phys_addr_t gmmio_phys; /* Global AFU MMIO space */ + void __iomem *gmmio_virt; /* Global MMIO map */ + struct idr idr; /* IDR to manage contexts */ int max_pasid; /* Maximum number of contexts */ bool is_present; /* Function has AFUs defined */ -- cgit v1.2.3 From 6b938ac91017d9375bda42251f559ae135623fbd Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:48 -0500 Subject: scsi: cxlflash: Support starting an adapter context Once the adapter context is created, it needs to be started by assigning the MMIO space for the context and by enabling the process element in the link. This commit adds the skeleton for starting the context and assigns the context specific MMIO space. Master contexts have access to the global MMIO space while the rest have access to the context specific space. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 39 +++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 3 +++ 2 files changed, 42 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 235803407b89..b39a03d149d0 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -163,6 +163,44 @@ static int ocxlflash_process_element(void *ctx_cookie) return ctx->pe; } +/** + * start_context() - local routine to start a context + * @ctx: Adapter context to be started. + * + * Assign the context specific MMIO space. + * + * Return: 0 on success, -errno on failure + */ +static int start_context(struct ocxlflash_context *ctx) +{ + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct ocxl_afu_config *acfg = &afu->acfg; + bool master = ctx->master; + + if (master) { + ctx->psn_size = acfg->global_mmio_size; + ctx->psn_phys = afu->gmmio_phys; + } else { + ctx->psn_size = acfg->pp_mmio_stride; + ctx->psn_phys = afu->ppmmio_phys + (ctx->pe * ctx->psn_size); + } + + return 0; +} + +/** + * ocxlflash_start_context() - start a kernel context + * @ctx_cookie: Adapter context to be started. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_start_context(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + + return start_context(ctx); +} + /** * ocxlflash_set_master() - sets the context as master * @ctx_cookie: Adapter context to set as master. @@ -581,6 +619,7 @@ static void *ocxlflash_fops_get_context(struct file *file) const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, .process_element = ocxlflash_process_element, + .start_context = ocxlflash_start_context, .set_master = ocxlflash_set_master, .get_context = ocxlflash_get_context, .dev_context_init = ocxlflash_dev_context_init, diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 18236e6dafd5..ee19717ad682 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -41,4 +41,7 @@ struct ocxlflash_context { struct address_space *mapping; /* Mapping for pseudo filesystem */ bool master; /* Whether this is a master context */ int pe; /* Process element */ + + phys_addr_t psn_phys; /* Process mapping */ + u64 psn_size; /* Process mapping size */ }; -- cgit v1.2.3 From 012f394cb81c61dd1207caecaaeed20caf0a8e36 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:32:56 -0500 Subject: scsi: cxlflash: Support process specific mappings Once the context is started, the assigned MMIO space can be mapped and unmapped. Provide means to map and unmap the context MMIO space. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index b39a03d149d0..73b0b104439c 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -150,6 +150,28 @@ err1: goto out; } +/** + * ocxlflash_psa_map() - map the process specific MMIO space + * @ctx_cookie: Adapter context for which the mapping needs to be done. + * + * Return: MMIO pointer of the mapped region + */ +static void __iomem *ocxlflash_psa_map(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + + return ioremap(ctx->psn_phys, ctx->psn_size); +} + +/** + * ocxlflash_psa_unmap() - unmap the process specific MMIO space + * @addr: MMIO pointer to unmap. + */ +static void ocxlflash_psa_unmap(void __iomem *addr) +{ + iounmap(addr); +} + /** * ocxlflash_process_element() - get process element of the adapter context * @ctx_cookie: Adapter context associated with the process element. @@ -618,6 +640,8 @@ static void *ocxlflash_fops_get_context(struct file *file) /* Backend ops to ocxlflash services */ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, + .psa_map = ocxlflash_psa_map, + .psa_unmap = ocxlflash_psa_unmap, .process_element = ocxlflash_process_element, .start_context = ocxlflash_start_context, .set_master = ocxlflash_set_master, -- cgit v1.2.3 From 3351e4f025f772e76054b1b6dd7ad9afd49a9b99 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:05 -0500 Subject: scsi: cxlflash: Support AFU state toggling The AFU should be enabled following a successful configuration and disabled near the end of the cleanup path. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 73b0b104439c..3042057c2680 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -342,12 +342,18 @@ static void ocxlflash_unconfig_afu(struct ocxl_hw_afu *afu) static void ocxlflash_destroy_afu(void *afu_cookie) { struct ocxl_hw_afu *afu = afu_cookie; + int pos; if (!afu) return; ocxlflash_release_context(afu->ocxl_ctx); idr_destroy(&afu->idr); + + /* Disable the AFU */ + pos = afu->acfg.dvsec_afu_control_pos; + ocxl_config_set_afu_state(afu->pdev, pos, 0); + ocxlflash_unconfig_afu(afu); kfree(afu); } @@ -499,6 +505,9 @@ static int ocxlflash_config_afu(struct pci_dev *pdev, struct ocxl_hw_afu *afu) __func__, rc); goto out; } + + /* Enable the AFU */ + ocxl_config_set_afu_state(pdev, acfg->dvsec_afu_control_pos, 1); out: return rc; } -- cgit v1.2.3 From 119c920073f4b492c1c1a7bf43a7e472d9dba19a Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:14 -0500 Subject: scsi: cxlflash: Support reading adapter VPD data Use the PCI VPD services to support reading the VPD data of the underlying adapter. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 3042057c2680..a2f04d3f8aa8 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -323,6 +323,20 @@ static void ocxlflash_perst_reloads_same_image(void *afu_cookie, bool image) afu->perst_same_image = image; } +/** + * ocxlflash_read_adapter_vpd() - reads the adapter VPD + * @pdev: PCI device associated with the host. + * @buf: Buffer to get the VPD data. + * @count: Size of buffer (maximum bytes that can be read). + * + * Return: size of VPD on success, -errno on failure + */ +static ssize_t ocxlflash_read_adapter_vpd(struct pci_dev *pdev, void *buf, + size_t count) +{ + return pci_read_vpd(pdev, 0, count, buf); +} + /** * ocxlflash_unconfig_afu() - unconfigure the AFU * @afu: AFU associated with the host. @@ -658,6 +672,7 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .dev_context_init = ocxlflash_dev_context_init, .release_context = ocxlflash_release_context, .perst_reloads_same_image = ocxlflash_perst_reloads_same_image, + .read_adapter_vpd = ocxlflash_read_adapter_vpd, .create_afu = ocxlflash_create_afu, .destroy_afu = ocxlflash_destroy_afu, .get_fd = ocxlflash_get_fd, -- cgit v1.2.3 From 73904823764d67da345dbe0d848c1a478c4c92ad Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:21 -0500 Subject: scsi: cxlflash: Setup function OCXL link After reading and modifying the function configuration, setup the OCXL link using the OCXL provider services. The link is released when the adapter is unconfigured. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Reviewed-by: Frederic Barrat Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 25 ++++++++++++++++++++++--- drivers/scsi/cxlflash/ocxl_hw.h | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index a2f04d3f8aa8..67517f8322ec 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -415,10 +415,27 @@ static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) ocxl_config_set_actag(pdev, fcfg->dvsec_function_pos, base, enabled); dev_dbg(dev, "%s: Function acTag range base=%u enabled=%u\n", __func__, base, enabled); + + rc = ocxl_link_setup(pdev, 0, &afu->link_token); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_link_setup failed rc=%d\n", + __func__, rc); + goto out; + } out: return rc; } +/** + * ocxlflash_unconfig_fn() - unconfigure the host function + * @pdev: PCI device associated with the host. + * @afu: AFU associated with the host. + */ +static void ocxlflash_unconfig_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) +{ + ocxl_link_release(pdev, afu->link_token); +} + /** * ocxlflash_map_mmio() - map the AFU MMIO space * @afu: AFU associated with the host. @@ -560,7 +577,7 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) if (unlikely(rc)) { dev_err(dev, "%s: AFU configuration failed rc=%d\n", __func__, rc); - goto err1; + goto err2; } ctx = ocxlflash_dev_context_init(pdev, afu); @@ -568,14 +585,16 @@ static void *ocxlflash_create_afu(struct pci_dev *pdev) rc = PTR_ERR(ctx); dev_err(dev, "%s: ocxlflash_dev_context_init failed rc=%d\n", __func__, rc); - goto err2; + goto err3; } afu->ocxl_ctx = ctx; out: return afu; -err2: +err3: ocxlflash_unconfig_afu(afu); +err2: + ocxlflash_unconfig_fn(pdev, afu); err1: idr_destroy(&afu->idr); kfree(afu); diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index ee19717ad682..09fa94c73501 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -31,6 +31,7 @@ struct ocxl_hw_afu { phys_addr_t gmmio_phys; /* Global AFU MMIO space */ void __iomem *gmmio_virt; /* Global MMIO map */ + void *link_token; /* Link token for the SPA */ struct idr idr; /* IDR to manage contexts */ int max_pasid; /* Maximum number of contexts */ bool is_present; /* Function has AFUs defined */ -- cgit v1.2.3 From c52bf5b384532ebc49deea16daf65c5cd1afbab2 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:28 -0500 Subject: scsi: cxlflash: Setup OCXL transaction layer The first function of the link needs to configure the transaction layer between the host and device. This is accomplished by a call to the OCXL provider services. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 67517f8322ec..db612bd8a434 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -422,8 +422,18 @@ static int ocxlflash_config_fn(struct pci_dev *pdev, struct ocxl_hw_afu *afu) __func__, rc); goto out; } + + rc = ocxl_config_set_TL(pdev, fcfg->dvsec_tl_pos); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_config_set_TL failed rc=%d\n", + __func__, rc); + goto err; + } out: return rc; +err: + ocxl_link_release(pdev, afu->link_token); + goto out; } /** -- cgit v1.2.3 From c207b5714304528b9c1719800eb3a191370b6d80 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:35 -0500 Subject: scsi: cxlflash: Support process element lifecycle As part of the context lifecycle, the associated process element within the Shared Process Area (SPA) of the link must be updated. Each process is defined by various parameters (pid, tid, PASID mm) that are stored in the SPA upon starting a context and invalidated when a context is stopped. Use the OCXL provider services to configure the SPA with the appropriate data that is unique to the process when starting a context. Initially only kernel contexts are supported and therefore these process values are not applicable. Note that the OCXL service used has an optional callback for translation fault error notification. While not used here, it will be expanded in a future commit. Also add a service to stop a context by terminating the corresponding PASID and remove the process element from the SPA. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 52 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index db612bd8a434..8d21f945016d 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -189,7 +189,7 @@ static int ocxlflash_process_element(void *ctx_cookie) * start_context() - local routine to start a context * @ctx: Adapter context to be started. * - * Assign the context specific MMIO space. + * Assign the context specific MMIO space, add and enable the PE. * * Return: 0 on success, -errno on failure */ @@ -197,7 +197,10 @@ static int start_context(struct ocxlflash_context *ctx) { struct ocxl_hw_afu *afu = ctx->hw_afu; struct ocxl_afu_config *acfg = &afu->acfg; + void *link_token = afu->link_token; + struct device *dev = afu->dev; bool master = ctx->master; + int rc = 0; if (master) { ctx->psn_size = acfg->global_mmio_size; @@ -207,7 +210,16 @@ static int start_context(struct ocxlflash_context *ctx) ctx->psn_phys = afu->ppmmio_phys + (ctx->pe * ctx->psn_size); } - return 0; + + /* pid, tid, amr and mm are zeroes/NULL for a kernel context */ + rc = ocxl_link_add_pe(link_token, ctx->pe, 0, 0, 0, NULL, NULL, NULL); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_link_add_pe failed rc=%d\n", + __func__, rc); + goto out; + } +out: + return rc; } /** @@ -223,6 +235,41 @@ static int ocxlflash_start_context(void *ctx_cookie) return start_context(ctx); } +/** + * ocxlflash_stop_context() - stop a context + * @ctx_cookie: Adapter context to be stopped. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_stop_context(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct ocxl_afu_config *acfg = &afu->acfg; + struct pci_dev *pdev = afu->pdev; + struct device *dev = afu->dev; + int rc; + + rc = ocxl_config_terminate_pasid(pdev, acfg->dvsec_afu_control_pos, + ctx->pe); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_config_terminate_pasid failed rc=%d\n", + __func__, rc); + /* If EBUSY, PE could be referenced in future by the AFU */ + if (rc == -EBUSY) + goto out; + } + + rc = ocxl_link_remove_pe(afu->link_token, ctx->pe); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_link_remove_pe failed rc=%d\n", + __func__, rc); + goto out; + } +out: + return rc; +} + /** * ocxlflash_set_master() - sets the context as master * @ctx_cookie: Adapter context to set as master. @@ -696,6 +743,7 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .psa_unmap = ocxlflash_psa_unmap, .process_element = ocxlflash_process_element, .start_context = ocxlflash_start_context, + .stop_context = ocxlflash_stop_context, .set_master = ocxlflash_set_master, .get_context = ocxlflash_get_context, .dev_context_init = ocxlflash_dev_context_init, -- cgit v1.2.3 From bc65c1c7bfb34a6eeea99eb725b467ff64eda3d8 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:41 -0500 Subject: scsi: cxlflash: Support AFU interrupt management Add support to allocate and free AFU interrupts using the OCXL provider services. The trigger page returned upon successful allocation will be mapped and exposed to the cxlflash core in a future commit. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 104 ++++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 10 ++++ 2 files changed, 114 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 8d21f945016d..75351c3ca9bd 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -384,6 +384,108 @@ static ssize_t ocxlflash_read_adapter_vpd(struct pci_dev *pdev, void *buf, return pci_read_vpd(pdev, 0, count, buf); } +/** + * free_afu_irqs() - internal service to free interrupts + * @ctx: Adapter context. + */ +static void free_afu_irqs(struct ocxlflash_context *ctx) +{ + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct device *dev = afu->dev; + int i; + + if (!ctx->irqs) { + dev_err(dev, "%s: Interrupts not allocated\n", __func__); + return; + } + + for (i = ctx->num_irqs; i >= 0; i--) + ocxl_link_free_irq(afu->link_token, ctx->irqs[i].hwirq); + + kfree(ctx->irqs); + ctx->irqs = NULL; +} + +/** + * alloc_afu_irqs() - internal service to allocate interrupts + * @ctx: Context associated with the request. + * @num: Number of interrupts requested. + * + * Return: 0 on success, -errno on failure + */ +static int alloc_afu_irqs(struct ocxlflash_context *ctx, int num) +{ + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct device *dev = afu->dev; + struct ocxlflash_irqs *irqs; + u64 addr; + int rc = 0; + int hwirq; + int i; + + if (ctx->irqs) { + dev_err(dev, "%s: Interrupts already allocated\n", __func__); + rc = -EEXIST; + goto out; + } + + if (num > OCXL_MAX_IRQS) { + dev_err(dev, "%s: Too many interrupts num=%d\n", __func__, num); + rc = -EINVAL; + goto out; + } + + irqs = kcalloc(num, sizeof(*irqs), GFP_KERNEL); + if (unlikely(!irqs)) { + dev_err(dev, "%s: Context irqs allocation failed\n", __func__); + rc = -ENOMEM; + goto out; + } + + for (i = 0; i < num; i++) { + rc = ocxl_link_irq_alloc(afu->link_token, &hwirq, &addr); + if (unlikely(rc)) { + dev_err(dev, "%s: ocxl_link_irq_alloc failed rc=%d\n", + __func__, rc); + goto err; + } + + irqs[i].hwirq = hwirq; + irqs[i].ptrig = addr; + } + + ctx->irqs = irqs; + ctx->num_irqs = num; +out: + return rc; +err: + for (i = i-1; i >= 0; i--) + ocxl_link_free_irq(afu->link_token, irqs[i].hwirq); + kfree(irqs); + goto out; +} + +/** + * ocxlflash_allocate_afu_irqs() - allocates the requested number of interrupts + * @ctx_cookie: Context associated with the request. + * @num: Number of interrupts requested. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_allocate_afu_irqs(void *ctx_cookie, int num) +{ + return alloc_afu_irqs(ctx_cookie, num); +} + +/** + * ocxlflash_free_afu_irqs() - frees the interrupts of an adapter context + * @ctx_cookie: Adapter context. + */ +static void ocxlflash_free_afu_irqs(void *ctx_cookie) +{ + free_afu_irqs(ctx_cookie); +} + /** * ocxlflash_unconfig_afu() - unconfigure the AFU * @afu: AFU associated with the host. @@ -750,6 +852,8 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .release_context = ocxlflash_release_context, .perst_reloads_same_image = ocxlflash_perst_reloads_same_image, .read_adapter_vpd = ocxlflash_read_adapter_vpd, + .allocate_afu_irqs = ocxlflash_allocate_afu_irqs, + .free_afu_irqs = ocxlflash_free_afu_irqs, .create_afu = ocxlflash_create_afu, .destroy_afu = ocxlflash_destroy_afu, .get_fd = ocxlflash_get_fd, diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 09fa94c73501..85b3fad24263 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -12,6 +12,13 @@ * 2 of the License, or (at your option) any later version. */ +#define OCXL_MAX_IRQS 4 /* Max interrupts per process */ + +struct ocxlflash_irqs { + int hwirq; + u64 ptrig; +}; + /* OCXL hardware AFU associated with the host */ struct ocxl_hw_afu { struct ocxlflash_context *ocxl_ctx; /* Host context */ @@ -45,4 +52,7 @@ struct ocxlflash_context { phys_addr_t psn_phys; /* Process mapping */ u64 psn_size; /* Process mapping size */ + + struct ocxlflash_irqs *irqs; /* Pointer to array of structures */ + int num_irqs; /* Number of interrupts */ }; -- cgit v1.2.3 From a06b1cfc0400a699374e9c41d0ca46de2cb4d0d7 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:48 -0500 Subject: scsi: cxlflash: Support AFU interrupt mapping and registration Add support to map and unmap the irq space and manage irq registrations with the kernel for each allocated AFU interrupt. Also support mapping the physical trigger page to obtain an effective address that will be provided to the cxlflash core in a future commit. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 120 ++++++++++++++++++++++++++++++++++++++++ drivers/scsi/cxlflash/ocxl_hw.h | 2 + 2 files changed, 122 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 75351c3ca9bd..c53405e7190e 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -185,6 +185,124 @@ static int ocxlflash_process_element(void *ctx_cookie) return ctx->pe; } +/** + * afu_map_irq() - map the interrupt of the adapter context + * @flags: Flags. + * @ctx: Adapter context. + * @num: Per-context AFU interrupt number. + * @handler: Interrupt handler to register. + * @cookie: Interrupt handler private data. + * @name: Name of the interrupt. + * + * Return: 0 on success, -errno on failure + */ +static int afu_map_irq(u64 flags, struct ocxlflash_context *ctx, int num, + irq_handler_t handler, void *cookie, char *name) +{ + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct device *dev = afu->dev; + struct ocxlflash_irqs *irq; + void __iomem *vtrig; + u32 virq; + int rc = 0; + + if (num < 0 || num >= ctx->num_irqs) { + dev_err(dev, "%s: Interrupt %d not allocated\n", __func__, num); + rc = -ENOENT; + goto out; + } + + irq = &ctx->irqs[num]; + virq = irq_create_mapping(NULL, irq->hwirq); + if (unlikely(!virq)) { + dev_err(dev, "%s: irq_create_mapping failed\n", __func__); + rc = -ENOMEM; + goto out; + } + + rc = request_irq(virq, handler, 0, name, cookie); + if (unlikely(rc)) { + dev_err(dev, "%s: request_irq failed rc=%d\n", __func__, rc); + goto err1; + } + + vtrig = ioremap(irq->ptrig, PAGE_SIZE); + if (unlikely(!vtrig)) { + dev_err(dev, "%s: Trigger page mapping failed\n", __func__); + rc = -ENOMEM; + goto err2; + } + + irq->virq = virq; + irq->vtrig = vtrig; +out: + return rc; +err2: + free_irq(virq, cookie); +err1: + irq_dispose_mapping(virq); + goto out; +} + +/** + * ocxlflash_map_afu_irq() - map the interrupt of the adapter context + * @ctx_cookie: Adapter context. + * @num: Per-context AFU interrupt number. + * @handler: Interrupt handler to register. + * @cookie: Interrupt handler private data. + * @name: Name of the interrupt. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_map_afu_irq(void *ctx_cookie, int num, + irq_handler_t handler, void *cookie, + char *name) +{ + return afu_map_irq(0, ctx_cookie, num, handler, cookie, name); +} + +/** + * afu_unmap_irq() - unmap the interrupt + * @flags: Flags. + * @ctx: Adapter context. + * @num: Per-context AFU interrupt number. + * @cookie: Interrupt handler private data. + */ +static void afu_unmap_irq(u64 flags, struct ocxlflash_context *ctx, int num, + void *cookie) +{ + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct device *dev = afu->dev; + struct ocxlflash_irqs *irq; + + if (num < 0 || num >= ctx->num_irqs) { + dev_err(dev, "%s: Interrupt %d not allocated\n", __func__, num); + return; + } + + irq = &ctx->irqs[num]; + if (irq->vtrig) + iounmap(irq->vtrig); + + if (irq_find_mapping(NULL, irq->hwirq)) { + free_irq(irq->virq, cookie); + irq_dispose_mapping(irq->virq); + } + + memset(irq, 0, sizeof(*irq)); +} + +/** + * ocxlflash_unmap_afu_irq() - unmap the interrupt + * @ctx_cookie: Adapter context. + * @num: Per-context AFU interrupt number. + * @cookie: Interrupt handler private data. + */ +static void ocxlflash_unmap_afu_irq(void *ctx_cookie, int num, void *cookie) +{ + return afu_unmap_irq(0, ctx_cookie, num, cookie); +} + /** * start_context() - local routine to start a context * @ctx: Adapter context to be started. @@ -844,6 +962,8 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .psa_map = ocxlflash_psa_map, .psa_unmap = ocxlflash_psa_unmap, .process_element = ocxlflash_process_element, + .map_afu_irq = ocxlflash_map_afu_irq, + .unmap_afu_irq = ocxlflash_unmap_afu_irq, .start_context = ocxlflash_start_context, .stop_context = ocxlflash_stop_context, .set_master = ocxlflash_set_master, diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 85b3fad24263..9011af08a957 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -16,7 +16,9 @@ struct ocxlflash_irqs { int hwirq; + u32 virq; u64 ptrig; + void __iomem *vtrig; }; /* OCXL hardware AFU associated with the host */ -- cgit v1.2.3 From 762c7e9332584d2d7aa645c24dda190d3be8d5f9 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:33:55 -0500 Subject: scsi: cxlflash: Support starting user contexts User contexts request interrupts and are started using the "start work" interface. Populate the start_work() fop to allocate and map interrupts before starting the user context. As part of starting the context, update the user process identification logic to properly derive the data required by the SPA. Also, introduce a skeleton interrupt handler using a bitmap, flag, and spinlock to track interrupts. This handler will be expanded in future commits. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 99 ++++++++++++++++++++++++++++++++++++++++- drivers/scsi/cxlflash/ocxl_hw.h | 3 ++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index c53405e7190e..576a39e6b198 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -318,7 +318,9 @@ static int start_context(struct ocxlflash_context *ctx) void *link_token = afu->link_token; struct device *dev = afu->dev; bool master = ctx->master; + struct mm_struct *mm; int rc = 0; + u32 pid; if (master) { ctx->psn_size = acfg->global_mmio_size; @@ -328,9 +330,16 @@ static int start_context(struct ocxlflash_context *ctx) ctx->psn_phys = afu->ppmmio_phys + (ctx->pe * ctx->psn_size); } + /* pid and mm not set for master contexts */ + if (master) { + pid = 0; + mm = NULL; + } else { + pid = current->mm->context.id; + mm = current->mm; + } - /* pid, tid, amr and mm are zeroes/NULL for a kernel context */ - rc = ocxl_link_add_pe(link_token, ctx->pe, 0, 0, 0, NULL, NULL, NULL); + rc = ocxl_link_add_pe(link_token, ctx->pe, pid, 0, 0, mm, NULL, NULL); if (unlikely(rc)) { dev_err(dev, "%s: ocxl_link_add_pe failed rc=%d\n", __func__, rc); @@ -442,10 +451,14 @@ static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) goto err2; } + spin_lock_init(&ctx->slock); + ctx->pe = rc; ctx->master = false; ctx->mapping = NULL; ctx->hw_afu = afu; + ctx->irq_bitmap = 0; + ctx->pending_irq = false; out: return ctx; err2: @@ -956,6 +969,87 @@ static void *ocxlflash_fops_get_context(struct file *file) return file->private_data; } +/** + * ocxlflash_afu_irq() - interrupt handler for user contexts + * @irq: Interrupt number. + * @data: Private data provided at interrupt registration, the context. + * + * Return: Always return IRQ_HANDLED. + */ +static irqreturn_t ocxlflash_afu_irq(int irq, void *data) +{ + struct ocxlflash_context *ctx = data; + struct device *dev = ctx->hw_afu->dev; + int i; + + dev_dbg(dev, "%s: Interrupt raised for pe %i virq %i\n", + __func__, ctx->pe, irq); + + for (i = 0; i < ctx->num_irqs; i++) { + if (ctx->irqs[i].virq == irq) + break; + } + if (unlikely(i >= ctx->num_irqs)) { + dev_err(dev, "%s: Received AFU IRQ out of range\n", __func__); + goto out; + } + + spin_lock(&ctx->slock); + set_bit(i - 1, &ctx->irq_bitmap); + ctx->pending_irq = true; + spin_unlock(&ctx->slock); +out: + return IRQ_HANDLED; +} + +/** + * ocxlflash_start_work() - start a user context + * @ctx_cookie: Context to be started. + * @num_irqs: Number of interrupts requested. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_start_work(void *ctx_cookie, u64 num_irqs) +{ + struct ocxlflash_context *ctx = ctx_cookie; + struct ocxl_hw_afu *afu = ctx->hw_afu; + struct device *dev = afu->dev; + char *name; + int rc = 0; + int i; + + rc = alloc_afu_irqs(ctx, num_irqs); + if (unlikely(rc < 0)) { + dev_err(dev, "%s: alloc_afu_irqs failed rc=%d\n", __func__, rc); + goto out; + } + + for (i = 0; i < num_irqs; i++) { + name = kasprintf(GFP_KERNEL, "ocxlflash-%s-pe%i-%i", + dev_name(dev), ctx->pe, i); + rc = afu_map_irq(0, ctx, i, ocxlflash_afu_irq, ctx, name); + kfree(name); + if (unlikely(rc < 0)) { + dev_err(dev, "%s: afu_map_irq failed rc=%d\n", + __func__, rc); + goto err; + } + } + + rc = start_context(ctx); + if (unlikely(rc)) { + dev_err(dev, "%s: start_context failed rc=%d\n", __func__, rc); + goto err; + } +out: + return rc; +err: + for (i = i-1; i >= 0; i--) + afu_unmap_irq(0, ctx, i, ctx); + free_afu_irqs(ctx); + goto out; +} + /* Backend ops to ocxlflash services */ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .module = THIS_MODULE, @@ -978,4 +1072,5 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .destroy_afu = ocxlflash_destroy_afu, .get_fd = ocxlflash_get_fd, .fops_get_context = ocxlflash_fops_get_context, + .start_work = ocxlflash_start_work, }; diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 9011af08a957..2eb53901daf3 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -55,6 +55,9 @@ struct ocxlflash_context { phys_addr_t psn_phys; /* Process mapping */ u64 psn_size; /* Process mapping size */ + spinlock_t slock; /* Protects irq/fault/event updates */ struct ocxlflash_irqs *irqs; /* Pointer to array of structures */ int num_irqs; /* Number of interrupts */ + bool pending_irq; /* Pending interrupt on the context */ + ulong irq_bitmap; /* Bits indicating pending irq num */ }; -- cgit v1.2.3 From 56f1db1a2a0c1ba4da0b970f211154b7781c725a Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:03 -0500 Subject: scsi: cxlflash: Support adapter context polling The cxlflash userspace API requires that users be able to poll the adapter context for any pending events or interrupts from the AFU. Support polling on various events by implementing the AFU poll fop using a waitqueue. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 57 +++++++++++++++++++++++++++++++++++++++-- drivers/scsi/cxlflash/ocxl_hw.h | 2 ++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 576a39e6b198..655377d17a70 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -16,6 +16,7 @@ #include #include #include +#include #include @@ -452,6 +453,7 @@ static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) } spin_lock_init(&ctx->slock); + init_waitqueue_head(&ctx->wq); ctx->pe = rc; ctx->master = false; @@ -892,10 +894,57 @@ err1: goto out; } +/** + * ctx_event_pending() - check for any event pending on the context + * @ctx: Context to be checked. + * + * Return: true if there is an event pending, false if none pending + */ +static inline bool ctx_event_pending(struct ocxlflash_context *ctx) +{ + if (ctx->pending_irq) + return true; + + return false; +} + +/** + * afu_poll() - poll the AFU for events on the context + * @file: File associated with the adapter context. + * @poll: Poll structure from the user. + * + * Return: poll mask + */ +static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) +{ + struct ocxlflash_context *ctx = file->private_data; + struct device *dev = ctx->hw_afu->dev; + ulong lock_flags; + int mask = 0; + + poll_wait(file, &ctx->wq, poll); + + spin_lock_irqsave(&ctx->slock, lock_flags); + if (ctx_event_pending(ctx)) + mask |= POLLIN | POLLRDNORM; + else + mask |= POLLERR; + spin_unlock_irqrestore(&ctx->slock, lock_flags); + + dev_dbg(dev, "%s: Poll wait completed for pe %i mask %i\n", + __func__, ctx->pe, mask); + + return mask; +} + static const struct file_operations ocxl_afu_fops = { .owner = THIS_MODULE, + .poll = afu_poll, }; +#define PATCH_FOPS(NAME) \ + do { if (!fops->NAME) fops->NAME = ocxl_afu_fops.NAME; } while (0) + /** * ocxlflash_get_fd() - get file descriptor for an adapter context * @ctx_cookie: Adapter context. @@ -933,8 +982,10 @@ static struct file *ocxlflash_get_fd(void *ctx_cookie, } fdtmp = rc; - /* Use default ops if there is no fops */ - if (!fops) + /* Patch the file ops that are not defined */ + if (fops) { + PATCH_FOPS(poll); + } else /* Use default ops */ fops = (struct file_operations *)&ocxl_afu_fops; name = kasprintf(GFP_KERNEL, "ocxlflash:%d", ctx->pe); @@ -998,6 +1049,8 @@ static irqreturn_t ocxlflash_afu_irq(int irq, void *data) set_bit(i - 1, &ctx->irq_bitmap); ctx->pending_irq = true; spin_unlock(&ctx->slock); + + wake_up_all(&ctx->wq); out: return IRQ_HANDLED; } diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 2eb53901daf3..acd280135989 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -56,6 +56,8 @@ struct ocxlflash_context { u64 psn_size; /* Process mapping size */ spinlock_t slock; /* Protects irq/fault/event updates */ + wait_queue_head_t wq; /* Wait queue for poll and interrupts */ + struct ocxlflash_irqs *irqs; /* Pointer to array of structures */ int num_irqs; /* Number of interrupts */ bool pending_irq; /* Pending interrupt on the context */ -- cgit v1.2.3 From 03aa9c519c1699e027bfa6ce76b999025d05f2c3 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:12 -0500 Subject: scsi: cxlflash: Support adapter context reading The cxlflash userspace API requires that users be able to read the adapter context for any pending events or interrupts from the AFU. Support reading various events by implementing the AFU read fop to copy out event data. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 94 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 655377d17a70..f531403cdeb4 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -17,9 +17,12 @@ #include #include #include +#include #include +#include + #include "backend.h" #include "ocxl_hw.h" @@ -937,9 +940,99 @@ static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) return mask; } +/** + * afu_read() - perform a read on the context for any event + * @file: File associated with the adapter context. + * @buf: Buffer to receive the data. + * @count: Size of buffer (maximum bytes that can be read). + * @off: Offset. + * + * Return: size of the data read on success, -errno on failure + */ +static ssize_t afu_read(struct file *file, char __user *buf, size_t count, + loff_t *off) +{ + struct ocxlflash_context *ctx = file->private_data; + struct device *dev = ctx->hw_afu->dev; + struct cxl_event event; + ulong lock_flags; + ssize_t esize; + ssize_t rc; + int bit; + DEFINE_WAIT(event_wait); + + if (*off != 0) { + dev_err(dev, "%s: Non-zero offset not supported, off=%lld\n", + __func__, *off); + rc = -EINVAL; + goto out; + } + + spin_lock_irqsave(&ctx->slock, lock_flags); + + for (;;) { + prepare_to_wait(&ctx->wq, &event_wait, TASK_INTERRUPTIBLE); + + if (ctx_event_pending(ctx)) + break; + + if (file->f_flags & O_NONBLOCK) { + dev_err(dev, "%s: File cannot be blocked on I/O\n", + __func__); + rc = -EAGAIN; + goto err; + } + + if (signal_pending(current)) { + dev_err(dev, "%s: Signal pending on the process\n", + __func__); + rc = -ERESTARTSYS; + goto err; + } + + spin_unlock_irqrestore(&ctx->slock, lock_flags); + schedule(); + spin_lock_irqsave(&ctx->slock, lock_flags); + } + + finish_wait(&ctx->wq, &event_wait); + + memset(&event, 0, sizeof(event)); + event.header.process_element = ctx->pe; + event.header.size = sizeof(struct cxl_event_header); + if (ctx->pending_irq) { + esize = sizeof(struct cxl_event_afu_interrupt); + event.header.size += esize; + event.header.type = CXL_EVENT_AFU_INTERRUPT; + + bit = find_first_bit(&ctx->irq_bitmap, ctx->num_irqs); + clear_bit(bit, &ctx->irq_bitmap); + event.irq.irq = bit + 1; + if (bitmap_empty(&ctx->irq_bitmap, ctx->num_irqs)) + ctx->pending_irq = false; + } + + spin_unlock_irqrestore(&ctx->slock, lock_flags); + + if (copy_to_user(buf, &event, event.header.size)) { + dev_err(dev, "%s: copy_to_user failed\n", __func__); + rc = -EFAULT; + goto out; + } + + rc = event.header.size; +out: + return rc; +err: + finish_wait(&ctx->wq, &event_wait); + spin_unlock_irqrestore(&ctx->slock, lock_flags); + goto out; +} + static const struct file_operations ocxl_afu_fops = { .owner = THIS_MODULE, .poll = afu_poll, + .read = afu_read, }; #define PATCH_FOPS(NAME) \ @@ -985,6 +1078,7 @@ static struct file *ocxlflash_get_fd(void *ctx_cookie, /* Patch the file ops that are not defined */ if (fops) { PATCH_FOPS(poll); + PATCH_FOPS(read); } else /* Use default ops */ fops = (struct file_operations *)&ocxl_afu_fops; -- cgit v1.2.3 From 93b8f8df552c5a30bfddb75a632f1b2a436c2cae Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:20 -0500 Subject: scsi: cxlflash: Support adapter context mmap and release The cxlflash userspace API requires that users be able to mmap and release the adapter context. Support mapping by implementing the AFU mmap fop to map the context MMIO space and install the corresponding page table entry upon page fault. Similarly, implement the AFU release fop to terminate and clean up the context when invoked. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 72 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index f531403cdeb4..8a9f2f45a577 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -1029,10 +1029,80 @@ err: goto out; } +/** + * afu_release() - release and free the context + * @inode: File inode pointer. + * @file: File associated with the context. + * + * Return: 0 on success, -errno on failure + */ +static int afu_release(struct inode *inode, struct file *file) +{ + struct ocxlflash_context *ctx = file->private_data; + int i; + + /* Unmap and free the interrupts associated with the context */ + for (i = ctx->num_irqs; i >= 0; i--) + afu_unmap_irq(0, ctx, i, ctx); + free_afu_irqs(ctx); + + return ocxlflash_release_context(ctx); +} + +/** + * ocxlflash_mmap_fault() - mmap fault handler + * @vmf: VM fault associated with current fault. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_mmap_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct ocxlflash_context *ctx = vma->vm_file->private_data; + u64 mmio_area, offset; + + offset = vmf->pgoff << PAGE_SHIFT; + if (offset >= ctx->psn_size) + return VM_FAULT_SIGBUS; + + mmio_area = ctx->psn_phys; + mmio_area += offset; + + vm_insert_pfn(vma, vmf->address, mmio_area >> PAGE_SHIFT); + return VM_FAULT_NOPAGE; +} + +static const struct vm_operations_struct ocxlflash_vmops = { + .fault = ocxlflash_mmap_fault, +}; + +/** + * afu_mmap() - map the fault handler operations + * @file: File associated with the context. + * @vma: VM area associated with mapping. + * + * Return: 0 on success, -errno on failure + */ +static int afu_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ocxlflash_context *ctx = file->private_data; + + if ((vma_pages(vma) + vma->vm_pgoff) > + (ctx->psn_size >> PAGE_SHIFT)) + return -EINVAL; + + vma->vm_flags |= VM_IO | VM_PFNMAP; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_ops = &ocxlflash_vmops; + return 0; +} + static const struct file_operations ocxl_afu_fops = { .owner = THIS_MODULE, .poll = afu_poll, .read = afu_read, + .release = afu_release, + .mmap = afu_mmap, }; #define PATCH_FOPS(NAME) \ @@ -1079,6 +1149,8 @@ static struct file *ocxlflash_get_fd(void *ctx_cookie, if (fops) { PATCH_FOPS(poll); PATCH_FOPS(read); + PATCH_FOPS(release); + PATCH_FOPS(mmap); } else /* Use default ops */ fops = (struct file_operations *)&ocxl_afu_fops; -- cgit v1.2.3 From e117c3c731c26bae77b16707740876ede6f73a1b Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:27 -0500 Subject: scsi: cxlflash: Support file descriptor mapping The cxlflash core fop API requires a way to invoke the fault and release handlers of underlying transports using their native file-based APIs. This provides the core with the ability to insert selectively itself into the processing stream of these operations for cleanup. Implement these two fops to map and release when requested. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 8a9f2f45a577..4bbc1d197a62 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -1267,6 +1267,30 @@ err: afu_unmap_irq(0, ctx, i, ctx); free_afu_irqs(ctx); goto out; +}; + +/** + * ocxlflash_fd_mmap() - mmap handler for adapter file descriptor + * @file: File installed with adapter file descriptor. + * @vma: VM area associated with mapping. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_fd_mmap(struct file *file, struct vm_area_struct *vma) +{ + return afu_mmap(file, vma); +} + +/** + * ocxlflash_fd_release() - release the context associated with the file + * @inode: File inode pointer. + * @file: File associated with the adapter context. + * + * Return: 0 on success, -errno on failure + */ +static int ocxlflash_fd_release(struct inode *inode, struct file *file) +{ + return afu_release(inode, file); } /* Backend ops to ocxlflash services */ @@ -1292,4 +1316,6 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .get_fd = ocxlflash_get_fd, .fops_get_context = ocxlflash_fops_get_context, .start_work = ocxlflash_start_work, + .fd_mmap = ocxlflash_fd_mmap, + .fd_release = ocxlflash_fd_release, }; -- cgit v1.2.3 From 402a55ea473a37b06eeae9abda00886bfd3bfe6d Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:35 -0500 Subject: scsi: cxlflash: Introduce object handle fop OCXL requires that AFUs use an opaque object handle to represent an AFU interrupt. The specification does not provide a common means to communicate the object handle to the AFU - each AFU must define this within the AFU specification. To support this model, the object handle must be passed back to the core driver as it manages the AFU specification (SISLite) for cxlflash. Note that for Power systems, the object handle is the effective address of the trigger page. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/backend.h | 1 + drivers/scsi/cxlflash/cxl_hw.c | 7 +++++++ drivers/scsi/cxlflash/ocxl_hw.c | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/drivers/scsi/cxlflash/backend.h b/drivers/scsi/cxlflash/backend.h index f675bcb4f153..bcd8a6c588d3 100644 --- a/drivers/scsi/cxlflash/backend.h +++ b/drivers/scsi/cxlflash/backend.h @@ -23,6 +23,7 @@ struct cxlflash_backend_ops { int (*map_afu_irq)(void *ctx_cookie, int num, irq_handler_t handler, void *cookie, char *name); void (*unmap_afu_irq)(void *ctx_cookie, int num, void *cookie); + u64 (*get_irq_objhndl)(void *ctx_cookie, int irq); int (*start_context)(void *ctx_cookie); int (*stop_context)(void *ctx_cookie); int (*afu_reset)(void *ctx_cookie); diff --git a/drivers/scsi/cxlflash/cxl_hw.c b/drivers/scsi/cxlflash/cxl_hw.c index a1d6d12090d3..b42da88386bd 100644 --- a/drivers/scsi/cxlflash/cxl_hw.c +++ b/drivers/scsi/cxlflash/cxl_hw.c @@ -49,6 +49,12 @@ static void cxlflash_unmap_afu_irq(void *ctx_cookie, int num, void *cookie) cxl_unmap_afu_irq(ctx_cookie, num, cookie); } +static u64 cxlflash_get_irq_objhndl(void *ctx_cookie, int irq) +{ + /* Dummy fop for cxl */ + return 0; +} + static int cxlflash_start_context(void *ctx_cookie) { return cxl_start_context(ctx_cookie, 0, NULL); @@ -153,6 +159,7 @@ const struct cxlflash_backend_ops cxlflash_cxl_ops = { .process_element = cxlflash_process_element, .map_afu_irq = cxlflash_map_afu_irq, .unmap_afu_irq = cxlflash_unmap_afu_irq, + .get_irq_objhndl = cxlflash_get_irq_objhndl, .start_context = cxlflash_start_context, .stop_context = cxlflash_stop_context, .afu_reset = cxlflash_afu_reset, diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 4bbc1d197a62..f77f4d7f6a34 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -307,6 +307,23 @@ static void ocxlflash_unmap_afu_irq(void *ctx_cookie, int num, void *cookie) return afu_unmap_irq(0, ctx_cookie, num, cookie); } +/** + * ocxlflash_get_irq_objhndl() - get the object handle for an interrupt + * @ctx_cookie: Context associated with the interrupt. + * @irq: Interrupt number. + * + * Return: effective address of the mapped region + */ +static u64 ocxlflash_get_irq_objhndl(void *ctx_cookie, int irq) +{ + struct ocxlflash_context *ctx = ctx_cookie; + + if (irq < 0 || irq >= ctx->num_irqs) + return 0; + + return (__force u64)ctx->irqs[irq].vtrig; +} + /** * start_context() - local routine to start a context * @ctx: Adapter context to be started. @@ -1301,6 +1318,7 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .process_element = ocxlflash_process_element, .map_afu_irq = ocxlflash_map_afu_irq, .unmap_afu_irq = ocxlflash_unmap_afu_irq, + .get_irq_objhndl = ocxlflash_get_irq_objhndl, .start_context = ocxlflash_start_context, .stop_context = ocxlflash_stop_context, .set_master = ocxlflash_set_master, -- cgit v1.2.3 From 23239eeccbe647b1da60dd930931c9520a336749 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:42 -0500 Subject: scsi: cxlflash: Setup LISNs for user contexts The SISLite specification has been updated for OCXL to support communicating data to generate AFU interrupts to the AFU. This includes a new capability bit that is advertised for OCXL AFUs and new registers to hold the object handle and translation PASID of each interrupt. For Power, the object handle is the mapped trigger page. Note that because these mappings are kernel only, the PASID of a kernel context must be used to satisfy the translation. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/common.h | 5 +++++ drivers/scsi/cxlflash/sislite.h | 5 +++++ drivers/scsi/cxlflash/superpipe.c | 14 ++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index ffcb1c62fbb7..fffa2c1699ca 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -273,6 +273,11 @@ static inline bool afu_has_cap(struct afu *afu, u64 cap) return afu_cap & cap; } +static inline bool afu_is_ocxl_lisn(struct afu *afu) +{ + return afu_has_cap(afu, SISL_INTVER_CAP_OCXL_LISN); +} + static inline bool afu_is_afu_debug(struct afu *afu) { return afu_has_cap(afu, SISL_INTVER_CAP_AFU_DEBUG); diff --git a/drivers/scsi/cxlflash/sislite.h b/drivers/scsi/cxlflash/sislite.h index d8940f1ae219..c08b9d307662 100644 --- a/drivers/scsi/cxlflash/sislite.h +++ b/drivers/scsi/cxlflash/sislite.h @@ -310,6 +310,10 @@ struct sisl_ctrl_map { #define SISL_CTX_CAP_WRITE_CMD 0x0000000000000002ULL /* afu_rc 0x21 */ #define SISL_CTX_CAP_READ_CMD 0x0000000000000001ULL /* afu_rc 0x21 */ __be64 mbox_r; + __be64 lisn_pasid[2]; + /* pasid _a arg must be ULL */ +#define SISL_LISN_PASID(_a, _b) (((_a) << 32) | (_b)) + __be64 lisn_ea[3]; }; /* single copy global regs */ @@ -416,6 +420,7 @@ struct sisl_global_regs { #define SISL_INTVER_CAP_RESERVED_CMD_MODE_B 0x100000000000ULL #define SISL_INTVER_CAP_LUN_PROVISION 0x080000000000ULL #define SISL_INTVER_CAP_AFU_DEBUG 0x040000000000ULL +#define SISL_INTVER_CAP_OCXL_LISN 0x020000000000ULL }; #define CXLFLASH_NUM_FC_PORTS_PER_BANK 2 /* fixed # of ports per bank */ diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index 2fe79df5c73c..04a3bf9dc85f 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -269,6 +269,7 @@ static int afu_attach(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) int rc = 0; struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ); u64 val; + int i; /* Unlock cap and restrict user to read/write cmds in translated mode */ readq_be(&ctrl_map->mbox_r); @@ -282,6 +283,19 @@ static int afu_attach(struct cxlflash_cfg *cfg, struct ctx_info *ctxi) goto out; } + if (afu_is_ocxl_lisn(afu)) { + /* Set up the LISN effective address for each interrupt */ + for (i = 0; i < ctxi->irqs; i++) { + val = cfg->ops->get_irq_objhndl(ctxi->ctx, i); + writeq_be(val, &ctrl_map->lisn_ea[i]); + } + + /* Use primary HWQ PASID as identifier for all interrupts */ + val = hwq->ctx_hndl; + writeq_be(SISL_LISN_PASID(val, val), &ctrl_map->lisn_pasid[0]); + writeq_be(SISL_LISN_PASID(0UL, val), &ctrl_map->lisn_pasid[1]); + } + /* Set up MMIO registers pointing to the RHT */ writeq_be((u64)ctxi->rht_start, &ctrl_map->rht_start); val = SISL_RHT_CNT_ID((u64)MAX_RHT_PER_CONTEXT, (u64)(hwq->ctx_hndl)); -- cgit v1.2.3 From d44af4b09026609cf339de238b1051ec2701d6f0 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:48 -0500 Subject: scsi: cxlflash: Setup LISNs for master contexts Similar to user contexts, master contexts also require that the per-context LISN registers be programmed for certain AFUs. The mapped trigger page is obtained from underlying transport and registered with AFU for each master context. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 5d754d1c22a0..8c55fcdc4f4b 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -1756,6 +1756,8 @@ static int init_global(struct cxlflash_cfg *cfg) u64 wwpn[MAX_FC_PORTS]; /* wwpn of AFU ports */ int i = 0, num_ports = 0; int rc = 0; + int j; + void *ctx; u64 reg; rc = read_vpd(cfg, &wwpn[0]); @@ -1816,6 +1818,25 @@ static int init_global(struct cxlflash_cfg *cfg) msleep(100); } + if (afu_is_ocxl_lisn(afu)) { + /* Set up the LISN effective address for each master */ + for (i = 0; i < afu->num_hwqs; i++) { + hwq = get_hwq(afu, i); + ctx = hwq->ctx_cookie; + + for (j = 0; j < hwq->num_irqs; j++) { + reg = cfg->ops->get_irq_objhndl(ctx, j); + writeq_be(reg, &hwq->ctrl_map->lisn_ea[j]); + } + + reg = hwq->ctx_hndl; + writeq_be(SISL_LISN_PASID(reg, reg), + &hwq->ctrl_map->lisn_pasid[0]); + writeq_be(SISL_LISN_PASID(0UL, reg), + &hwq->ctrl_map->lisn_pasid[1]); + } + } + /* Set up master's own CTX_CAP to allow real mode, host translation */ /* tables, afu cmds and read/write GSCSI cmds. */ /* First, unlock ctx_cap write by reading mbox */ -- cgit v1.2.3 From d91dd3a7d11a17a2e309a15e4765283212e30290 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:34:55 -0500 Subject: scsi: cxlflash: Update synchronous interrupt status bits The SISLite specification has been updated to define new synchronous interrupt status bits. These bits are set by the AFU when a given PASID or EA is bad and a synchronous interrupt is triggered. The SISLite header file is updated to support these new bits. Note that there are also some formatting updates to some of the existing bits to allow all of the definitions to line up uniformly. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/sislite.h | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/cxlflash/sislite.h b/drivers/scsi/cxlflash/sislite.h index c08b9d307662..874abce35ab4 100644 --- a/drivers/scsi/cxlflash/sislite.h +++ b/drivers/scsi/cxlflash/sislite.h @@ -258,23 +258,30 @@ struct sisl_host_map { * exit since there is no way to tell which * command caused the error. */ -#define SISL_ISTATUS_PERM_ERR_CMDROOM 0x0010ULL /* b59, user error */ -#define SISL_ISTATUS_PERM_ERR_RCB_READ 0x0008ULL /* b60, user error */ -#define SISL_ISTATUS_PERM_ERR_SA_WRITE 0x0004ULL /* b61, user error */ -#define SISL_ISTATUS_PERM_ERR_RRQ_WRITE 0x0002ULL /* b62, user error */ +#define SISL_ISTATUS_PERM_ERR_LISN_3_EA 0x0400ULL /* b53, user error */ +#define SISL_ISTATUS_PERM_ERR_LISN_2_EA 0x0200ULL /* b54, user error */ +#define SISL_ISTATUS_PERM_ERR_LISN_1_EA 0x0100ULL /* b55, user error */ +#define SISL_ISTATUS_PERM_ERR_LISN_3_PASID 0x0080ULL /* b56, user error */ +#define SISL_ISTATUS_PERM_ERR_LISN_2_PASID 0x0040ULL /* b57, user error */ +#define SISL_ISTATUS_PERM_ERR_LISN_1_PASID 0x0020ULL /* b58, user error */ +#define SISL_ISTATUS_PERM_ERR_CMDROOM 0x0010ULL /* b59, user error */ +#define SISL_ISTATUS_PERM_ERR_RCB_READ 0x0008ULL /* b60, user error */ +#define SISL_ISTATUS_PERM_ERR_SA_WRITE 0x0004ULL /* b61, user error */ +#define SISL_ISTATUS_PERM_ERR_RRQ_WRITE 0x0002ULL /* b62, user error */ /* Page in wait accessing RCB/IOASA/RRQ is reported in b63. * Same error in data/LXT/RHT access is reported via IOASA. */ -#define SISL_ISTATUS_TEMP_ERR_PAGEIN 0x0001ULL /* b63, can be generated - * only when AFU auto - * retry is disabled. - * If user can determine - * the command that - * caused the error, it - * can be retried. - */ -#define SISL_ISTATUS_UNMASK (0x001FULL) /* 1 means unmasked */ -#define SISL_ISTATUS_MASK ~(SISL_ISTATUS_UNMASK) /* 1 means masked */ +#define SISL_ISTATUS_TEMP_ERR_PAGEIN 0x0001ULL /* b63, can only be + * generated when AFU + * auto retry is + * disabled. If user + * can determine the + * command that caused + * the error, it can + * be retried. + */ +#define SISL_ISTATUS_UNMASK (0x07FFULL) /* 1 means unmasked */ +#define SISL_ISTATUS_MASK ~(SISL_ISTATUS_UNMASK) /* 1 means masked */ __be64 intr_clear; __be64 intr_mask; -- cgit v1.2.3 From f81face7256339c584ee9baba3240ddac74a0293 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:00 -0500 Subject: scsi: cxlflash: Introduce OCXL context state machine In order to protect the OCXL hardware contexts from getting clobbered, a simple state machine is added to indicate when a context is in open, close or start state. The expected states are validated throughout the code to prevent illegal operations on a context. A mutex is added to protect writes to the context state field. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 59 ++++++++++++++++++++++++++++++++++++++--- drivers/scsi/cxlflash/ocxl_hw.h | 8 ++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index f77f4d7f6a34..89d3d89f22ed 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -163,6 +163,16 @@ err1: static void __iomem *ocxlflash_psa_map(void *ctx_cookie) { struct ocxlflash_context *ctx = ctx_cookie; + struct device *dev = ctx->hw_afu->dev; + + mutex_lock(&ctx->state_mutex); + if (ctx->state != STARTED) { + dev_err(dev, "%s: Context not started, state=%d\n", __func__, + ctx->state); + mutex_unlock(&ctx->state_mutex); + return NULL; + } + mutex_unlock(&ctx->state_mutex); return ioremap(ctx->psn_phys, ctx->psn_size); } @@ -343,6 +353,14 @@ static int start_context(struct ocxlflash_context *ctx) int rc = 0; u32 pid; + mutex_lock(&ctx->state_mutex); + if (ctx->state != OPENED) { + dev_err(dev, "%s: Context state invalid, state=%d\n", + __func__, ctx->state); + rc = -EINVAL; + goto out; + } + if (master) { ctx->psn_size = acfg->global_mmio_size; ctx->psn_phys = afu->gmmio_phys; @@ -366,7 +384,10 @@ static int start_context(struct ocxlflash_context *ctx) __func__, rc); goto out; } + + ctx->state = STARTED; out: + mutex_unlock(&ctx->state_mutex); return rc; } @@ -396,7 +417,15 @@ static int ocxlflash_stop_context(void *ctx_cookie) struct ocxl_afu_config *acfg = &afu->acfg; struct pci_dev *pdev = afu->pdev; struct device *dev = afu->dev; - int rc; + enum ocxlflash_ctx_state state; + int rc = 0; + + mutex_lock(&ctx->state_mutex); + state = ctx->state; + ctx->state = CLOSED; + mutex_unlock(&ctx->state_mutex); + if (state != STARTED) + goto out; rc = ocxl_config_terminate_pasid(pdev, acfg->dvsec_afu_control_pos, ctx->pe); @@ -474,7 +503,9 @@ static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) spin_lock_init(&ctx->slock); init_waitqueue_head(&ctx->wq); + mutex_init(&ctx->state_mutex); + ctx->state = OPENED; ctx->pe = rc; ctx->master = false; ctx->mapping = NULL; @@ -499,11 +530,23 @@ err1: static int ocxlflash_release_context(void *ctx_cookie) { struct ocxlflash_context *ctx = ctx_cookie; + struct device *dev; int rc = 0; if (!ctx) goto out; + dev = ctx->hw_afu->dev; + mutex_lock(&ctx->state_mutex); + if (ctx->state >= STARTED) { + dev_err(dev, "%s: Context in use, state=%d\n", __func__, + ctx->state); + mutex_unlock(&ctx->state_mutex); + rc = -EBUSY; + goto out; + } + mutex_unlock(&ctx->state_mutex); + idr_remove(&ctx->hw_afu->idr, ctx->pe); ocxlflash_release_mapping(ctx); kfree(ctx); @@ -947,7 +990,7 @@ static unsigned int afu_poll(struct file *file, struct poll_table_struct *poll) spin_lock_irqsave(&ctx->slock, lock_flags); if (ctx_event_pending(ctx)) mask |= POLLIN | POLLRDNORM; - else + else if (ctx->state == CLOSED) mask |= POLLERR; spin_unlock_irqrestore(&ctx->slock, lock_flags); @@ -990,7 +1033,7 @@ static ssize_t afu_read(struct file *file, char __user *buf, size_t count, for (;;) { prepare_to_wait(&ctx->wq, &event_wait, TASK_INTERRUPTIBLE); - if (ctx_event_pending(ctx)) + if (ctx_event_pending(ctx) || (ctx->state == CLOSED)) break; if (file->f_flags & O_NONBLOCK) { @@ -1076,12 +1119,22 @@ static int ocxlflash_mmap_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct ocxlflash_context *ctx = vma->vm_file->private_data; + struct device *dev = ctx->hw_afu->dev; u64 mmio_area, offset; offset = vmf->pgoff << PAGE_SHIFT; if (offset >= ctx->psn_size) return VM_FAULT_SIGBUS; + mutex_lock(&ctx->state_mutex); + if (ctx->state != STARTED) { + dev_err(dev, "%s: Context not started, state=%d\n", + __func__, ctx->state); + mutex_unlock(&ctx->state_mutex); + return VM_FAULT_SIGBUS; + } + mutex_unlock(&ctx->state_mutex); + mmio_area = ctx->psn_phys; mmio_area += offset; diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index acd280135989..1829e55c974a 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -46,6 +46,12 @@ struct ocxl_hw_afu { bool is_present; /* Function has AFUs defined */ }; +enum ocxlflash_ctx_state { + CLOSED, + OPENED, + STARTED +}; + struct ocxlflash_context { struct ocxl_hw_afu *hw_afu; /* HW AFU back pointer */ struct address_space *mapping; /* Mapping for pseudo filesystem */ @@ -57,6 +63,8 @@ struct ocxlflash_context { spinlock_t slock; /* Protects irq/fault/event updates */ wait_queue_head_t wq; /* Wait queue for poll and interrupts */ + struct mutex state_mutex; /* Mutex to update context state */ + enum ocxlflash_ctx_state state; /* Context state */ struct ocxlflash_irqs *irqs; /* Pointer to array of structures */ int num_irqs; /* Number of interrupts */ -- cgit v1.2.3 From 66ae644b922abcbf6d3303a4e69f658b02165b31 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:07 -0500 Subject: scsi: cxlflash: Register for translation errors While enabling a context on the link, a predefined callback can be registered with the OCXL provider services to be notified on translation errors. These errors can in turn be passed back to the user on a read operation. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 31 +++++++++++++++++++++++++++++-- drivers/scsi/cxlflash/ocxl_hw.h | 4 ++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 89d3d89f22ed..5b5565d6572e 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -334,6 +334,25 @@ static u64 ocxlflash_get_irq_objhndl(void *ctx_cookie, int irq) return (__force u64)ctx->irqs[irq].vtrig; } +/** + * ocxlflash_xsl_fault() - callback when translation error is triggered + * @data: Private data provided at callback registration, the context. + * @addr: Address that triggered the error. + * @dsisr: Value of dsisr register. + */ +static void ocxlflash_xsl_fault(void *data, u64 addr, u64 dsisr) +{ + struct ocxlflash_context *ctx = data; + + spin_lock(&ctx->slock); + ctx->fault_addr = addr; + ctx->fault_dsisr = dsisr; + ctx->pending_fault = true; + spin_unlock(&ctx->slock); + + wake_up_all(&ctx->wq); +} + /** * start_context() - local routine to start a context * @ctx: Adapter context to be started. @@ -378,7 +397,8 @@ static int start_context(struct ocxlflash_context *ctx) mm = current->mm; } - rc = ocxl_link_add_pe(link_token, ctx->pe, pid, 0, 0, mm, NULL, NULL); + rc = ocxl_link_add_pe(link_token, ctx->pe, pid, 0, 0, mm, + ocxlflash_xsl_fault, ctx); if (unlikely(rc)) { dev_err(dev, "%s: ocxl_link_add_pe failed rc=%d\n", __func__, rc); @@ -512,6 +532,7 @@ static void *ocxlflash_dev_context_init(struct pci_dev *pdev, void *afu_cookie) ctx->hw_afu = afu; ctx->irq_bitmap = 0; ctx->pending_irq = false; + ctx->pending_fault = false; out: return ctx; err2: @@ -965,7 +986,7 @@ err1: */ static inline bool ctx_event_pending(struct ocxlflash_context *ctx) { - if (ctx->pending_irq) + if (ctx->pending_irq || ctx->pending_fault) return true; return false; @@ -1070,6 +1091,12 @@ static ssize_t afu_read(struct file *file, char __user *buf, size_t count, event.irq.irq = bit + 1; if (bitmap_empty(&ctx->irq_bitmap, ctx->num_irqs)) ctx->pending_irq = false; + } else if (ctx->pending_fault) { + event.header.size += sizeof(struct cxl_event_data_storage); + event.header.type = CXL_EVENT_DATA_STORAGE; + event.fault.addr = ctx->fault_addr; + event.fault.dsisr = ctx->fault_dsisr; + ctx->pending_fault = false; } spin_unlock_irqrestore(&ctx->slock, lock_flags); diff --git a/drivers/scsi/cxlflash/ocxl_hw.h b/drivers/scsi/cxlflash/ocxl_hw.h index 1829e55c974a..9270d35c4620 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.h +++ b/drivers/scsi/cxlflash/ocxl_hw.h @@ -70,4 +70,8 @@ struct ocxlflash_context { int num_irqs; /* Number of interrupts */ bool pending_irq; /* Pending interrupt on the context */ ulong irq_bitmap; /* Bits indicating pending irq num */ + + u64 fault_addr; /* Address that triggered the fault */ + u64 fault_dsisr; /* Value of dsisr register at fault */ + bool pending_fault; /* Pending translation fault */ }; -- cgit v1.2.3 From 9433fb32b7c57c0e1cee49025a720b38b449e68c Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:15 -0500 Subject: scsi: cxlflash: Support AFU reset The cxlflash core driver resets the AFU when the master contexts are created in the initialization or recovery paths. Today, the OCXL provider service to perform this operation is pending implementation. To avoid a crash due to a missing fop, log an error once and return success to continue with execution. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/ocxl_hw.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/scsi/cxlflash/ocxl_hw.c b/drivers/scsi/cxlflash/ocxl_hw.c index 5b5565d6572e..0a95b5f25380 100644 --- a/drivers/scsi/cxlflash/ocxl_hw.c +++ b/drivers/scsi/cxlflash/ocxl_hw.c @@ -467,6 +467,22 @@ out: return rc; } +/** + * ocxlflash_afu_reset() - reset the AFU + * @ctx_cookie: Adapter context. + */ +static int ocxlflash_afu_reset(void *ctx_cookie) +{ + struct ocxlflash_context *ctx = ctx_cookie; + struct device *dev = ctx->hw_afu->dev; + + /* Pending implementation from OCXL transport services */ + dev_err_once(dev, "%s: afu_reset() fop not supported\n", __func__); + + /* Silently return success until it is implemented */ + return 0; +} + /** * ocxlflash_set_master() - sets the context as master * @ctx_cookie: Adapter context to set as master. @@ -1401,6 +1417,7 @@ const struct cxlflash_backend_ops cxlflash_ocxl_ops = { .get_irq_objhndl = ocxlflash_get_irq_objhndl, .start_context = ocxlflash_start_context, .stop_context = ocxlflash_stop_context, + .afu_reset = ocxlflash_afu_reset, .set_master = ocxlflash_set_master, .get_context = ocxlflash_get_context, .dev_context_init = ocxlflash_dev_context_init, -- cgit v1.2.3 From 07d0c52f875de92af52913969d656a5fabe78504 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:21 -0500 Subject: scsi: cxlflash: Enable OCXL operations This commit enables the OCXL operations for the OCXL devices. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 9 +++++++-- drivers/scsi/cxlflash/main.h | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 8c55fcdc4f4b..42a95b762bc0 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -3168,7 +3168,8 @@ static struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS, static struct dev_dependent_vals dev_flash_gt_vals = { CXLFLASH_MAX_SECTORS, CXLFLASH_NOTIFY_SHUTDOWN }; static struct dev_dependent_vals dev_briard_vals = { CXLFLASH_MAX_SECTORS, - CXLFLASH_NOTIFY_SHUTDOWN }; + (CXLFLASH_NOTIFY_SHUTDOWN | + CXLFLASH_OCXL_DEV) }; /* * PCI device binding table @@ -3679,9 +3680,13 @@ static int cxlflash_probe(struct pci_dev *pdev, cfg->init_state = INIT_STATE_NONE; cfg->dev = pdev; - cfg->ops = &cxlflash_cxl_ops; cfg->cxl_fops = cxlflash_cxl_fops; + if (ddv->flags & CXLFLASH_OCXL_DEV) + cfg->ops = &cxlflash_ocxl_ops; + else + cfg->ops = &cxlflash_cxl_ops; + /* * Promoted LUNs move to the top of the LUN table. The rest stay on * the bottom half. The bottom half grows from the end (index = 255), diff --git a/drivers/scsi/cxlflash/main.h b/drivers/scsi/cxlflash/main.h index ba0108a7a9c2..6f1be621e473 100644 --- a/drivers/scsi/cxlflash/main.h +++ b/drivers/scsi/cxlflash/main.h @@ -97,6 +97,7 @@ struct dev_dependent_vals { u64 flags; #define CXLFLASH_NOTIFY_SHUTDOWN 0x0000000000000001ULL #define CXLFLASH_WWPN_VPD_REQUIRED 0x0000000000000002ULL +#define CXLFLASH_OCXL_DEV 0x0000000000000004ULL }; struct asyc_intr_info { -- cgit v1.2.3 From a3feb6ef50def7c91244d7bd15a3625b7b49b81f Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:27 -0500 Subject: scsi: cxlflash: Synchronize reset and remove ops The following Oops can be encountered if a device removal or system shutdown is initiated while an EEH recovery is in process: [c000000ff2f479c0] c008000015256f18 cxlflash_pci_slot_reset+0xa0/0x100 [cxlflash] [c000000ff2f47a30] c00800000dae22e0 cxl_pci_slot_reset+0x168/0x290 [cxl] [c000000ff2f47ae0] c00000000003ef1c eeh_report_reset+0xec/0x170 [c000000ff2f47b20] c00000000003d0b8 eeh_pe_dev_traverse+0x98/0x170 [c000000ff2f47bb0] c00000000003f80c eeh_handle_normal_event+0x56c/0x580 [c000000ff2f47c60] c00000000003fba4 eeh_handle_event+0x2a4/0x338 [c000000ff2f47d10] c0000000000400b8 eeh_event_handler+0x1f8/0x200 [c000000ff2f47dc0] c00000000013da48 kthread+0x1a8/0x1b0 [c000000ff2f47e30] c00000000000b528 ret_from_kernel_thread+0x5c/0xb4 The remove handler frees AFU memory while the EEH recovery is in progress, leading to a race condition. This can result in a crash if the recovery thread tries to access this memory. To resolve this issue, the cxlflash remove handler will evaluate the device state and yield to any active reset or probing threads. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 42a95b762bc0..dfe76485aab6 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -946,9 +946,9 @@ static void cxlflash_remove(struct pci_dev *pdev) return; } - /* If a Task Management Function is active, wait for it to complete - * before continuing with remove. - */ + /* Yield to running recovery threads before continuing with remove */ + wait_event(cfg->reset_waitq, cfg->state != STATE_RESET && + cfg->state != STATE_PROBING); spin_lock_irqsave(&cfg->tmf_slock, lock_flags); if (cfg->tmf_active) wait_event_interruptible_lock_irq(cfg->tmf_waitq, -- cgit v1.2.3 From 9a597cd4c0cebd61657f7449cb8bcb681f464500 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:34 -0500 Subject: scsi: cxlflash: Remove commmands from pending list on timeout The following Oops can occur if an internal command sent to the AFU does not complete within the timeout: [c000000ff101b810] c008000016020d94 term_mc+0xfc/0x1b0 [cxlflash] [c000000ff101b8a0] c008000016020fb0 term_afu+0x168/0x280 [cxlflash] [c000000ff101b930] c0080000160232ec cxlflash_pci_error_detected+0x184/0x230 [cxlflash] [c000000ff101b9e0] c00800000d95d468 cxl_vphb_error_detected+0x90/0x150[cxl] [c000000ff101ba20] c00800000d95f27c cxl_pci_error_detected+0xa4/0x240 [cxl] [c000000ff101bac0] c00000000003eaf8 eeh_report_error+0xd8/0x1b0 [c000000ff101bb20] c00000000003d0b8 eeh_pe_dev_traverse+0x98/0x170 [c000000ff101bbb0] c00000000003f438 eeh_handle_normal_event+0x198/0x580 [c000000ff101bc60] c00000000003fba4 eeh_handle_event+0x2a4/0x338 [c000000ff101bd10] c0000000000400b8 eeh_event_handler+0x1f8/0x200 [c000000ff101bdc0] c00000000013da48 kthread+0x1a8/0x1b0 [c000000ff101be30] c00000000000b528 ret_from_kernel_thread+0x5c/0xb4 When an internal command times out, the command buffer is freed while it is still in the pending commands list of the context. This corrupts the list and when the context is cleaned up, a crash is encountered. To resolve this issue, when an AFU command or TMF command times out, the command should be deleted from the hardware queue pending command list before freeing the buffer. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index dfe76485aab6..c9203282d943 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -473,6 +473,7 @@ static int send_tmf(struct cxlflash_cfg *cfg, struct scsi_device *sdev, struct afu_cmd *cmd = NULL; struct device *dev = &cfg->dev->dev; struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ); + bool needs_deletion = false; char *buf = NULL; ulong lock_flags; int rc = 0; @@ -527,6 +528,7 @@ static int send_tmf(struct cxlflash_cfg *cfg, struct scsi_device *sdev, if (!to) { dev_err(dev, "%s: TMF timed out\n", __func__); rc = -ETIMEDOUT; + needs_deletion = true; } else if (cmd->cmd_aborted) { dev_err(dev, "%s: TMF aborted\n", __func__); rc = -EAGAIN; @@ -537,6 +539,12 @@ static int send_tmf(struct cxlflash_cfg *cfg, struct scsi_device *sdev, } cfg->tmf_active = false; spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags); + + if (needs_deletion) { + spin_lock_irqsave(&hwq->hsq_slock, lock_flags); + list_del(&cmd->list); + spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags); + } out: kfree(buf); return rc; @@ -2284,6 +2292,7 @@ static int send_afu_cmd(struct afu *afu, struct sisl_ioarcb *rcb) struct device *dev = &cfg->dev->dev; struct afu_cmd *cmd = NULL; struct hwq *hwq = get_hwq(afu, PRIMARY_HWQ); + ulong lock_flags; char *buf = NULL; int rc = 0; int nretry = 0; @@ -2329,6 +2338,11 @@ retry: case -ETIMEDOUT: rc = afu->context_reset(hwq); if (rc) { + /* Delete the command from pending_cmds list */ + spin_lock_irqsave(&hwq->hsq_slock, lock_flags); + list_del(&cmd->list); + spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags); + cxlflash_schedule_async_reset(cfg); break; } -- cgit v1.2.3 From d2d354a606d5309fbfe81d5fca01122159e38c6e Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Mon, 26 Mar 2018 11:35:42 -0500 Subject: scsi: cxlflash: Handle spurious interrupts The following Oops can occur when there is heavy I/O traffic and the host is reset by a tool such as sg_reset. [c000200fff3fbc90] c00800001690117c process_cmd_doneq+0x104/0x500 [cxlflash] (unreliable) [c000200fff3fbd80] c008000016901648 cxlflash_rrq_irq+0xd0/0x150 [cxlflash] [c000200fff3fbde0] c000000000193130 __handle_irq_event_percpu+0xa0/0x310 [c000200fff3fbea0] c0000000001933d8 handle_irq_event_percpu+0x38/0x90 [c000200fff3fbee0] c000000000193494 handle_irq_event+0x64/0xb0 [c000200fff3fbf10] c000000000198ea0 handle_fasteoi_irq+0xc0/0x230 [c000200fff3fbf40] c00000000019182c generic_handle_irq+0x4c/0x70 [c000200fff3fbf60] c00000000001794c __do_irq+0x7c/0x1c0 [c000200fff3fbf90] c00000000002a390 call_do_irq+0x14/0x24 [c000200e5828fab0] c000000000017b2c do_IRQ+0x9c/0x130 [c000200e5828fb00] c000000000009b04 h_virt_irq_common+0x114/0x120 When a context is reset, the pending commands are flushed and the AFU is notified. Before the AFU handles this request there could be command completion interrupts queued to PHB which are yet to be delivered to the context. In this scenario, a context could receive an interrupt for a command that has been flushed, leading to a possible crash when the memory for the flushed command is accessed. To resolve this problem, a boolean will indicate if the hardware queue is ready to process interrupts or not. This can be evaluated in the interrupt handler before proessing an interrupt. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/common.h | 1 + drivers/scsi/cxlflash/main.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index fffa2c1699ca..89240b84745c 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -224,6 +224,7 @@ struct hwq { u64 *hrrq_end; u64 *hrrq_curr; bool toggle; + bool hrrq_online; s64 room; diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index c9203282d943..a24d7e6e51c1 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -801,6 +801,10 @@ static void term_mc(struct cxlflash_cfg *cfg, u32 index) WARN_ON(cfg->ops->release_context(hwq->ctx_cookie)); hwq->ctx_cookie = NULL; + spin_lock_irqsave(&hwq->hrrq_slock, lock_flags); + hwq->hrrq_online = false; + spin_unlock_irqrestore(&hwq->hrrq_slock, lock_flags); + spin_lock_irqsave(&hwq->hsq_slock, lock_flags); flush_pending_cmds(hwq); spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags); @@ -1475,6 +1479,12 @@ static irqreturn_t cxlflash_rrq_irq(int irq, void *data) spin_lock_irqsave(&hwq->hrrq_slock, hrrq_flags); + /* Silently drop spurious interrupts when queue is not online */ + if (!hwq->hrrq_online) { + spin_unlock_irqrestore(&hwq->hrrq_slock, hrrq_flags); + return IRQ_HANDLED; + } + if (afu_is_irqpoll_enabled(afu)) { irq_poll_sched(&hwq->irqpoll); spin_unlock_irqrestore(&hwq->hrrq_slock, hrrq_flags); @@ -1781,6 +1791,7 @@ static int init_global(struct cxlflash_cfg *cfg) writeq_be((u64) hwq->hrrq_start, &hmap->rrq_start); writeq_be((u64) hwq->hrrq_end, &hmap->rrq_end); + hwq->hrrq_online = true; if (afu_is_sq_cmd_mode(afu)) { writeq_be((u64)hwq->hsq_start, &hmap->sq_start); -- cgit v1.2.3 From 8b8d66531555006a18d1532546dadbea8d16df95 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Sat, 24 Mar 2018 00:05:08 +0800 Subject: scsi: hisi_sas: make SAS address of SATA disks unique When directly connected with SATA disks in different SAS cores, fill SAS address with scsi_host's id to make it's fake SAS address unique. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index f89fb9a49ea9..89b9505c0654 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3295,6 +3295,7 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p) sas_phy->oob_mode = SATA_OOB_MODE; /* Make up some unique SAS address */ attached_sas_addr[0] = 0x50; + attached_sas_addr[6] = hisi_hba->shost->host_no; attached_sas_addr[7] = phy_no; memcpy(sas_phy->attached_sas_addr, attached_sas_addr, SAS_ADDR_SIZE); memcpy(sas_phy->frame_rcvd, fis, sizeof(struct dev_to_host_fis)); -- cgit v1.2.3 From 61573630918bf29a1974e08ef5d4b8889fb9dd7f Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Sat, 24 Mar 2018 00:05:09 +0800 Subject: scsi: hisi_sas: update RAS feature for later revision of v3 HW There is an modification for later revision of v3 hw. More HW errors are reported through RAS interrupt. These errors were originally reported only through MSI. When report to RAS, some combinations are done to port AXI errors and FIFO OMIT errors. For example, each port has 4 AXI errors, and they are combined to one when report to RAS. This patch does two things: 1. Enable RAS interrupt of these errors and handle them in PCI error handlers. 2. Disable MSI interrupts of these errors for this later revision hw. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 60 ++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 6f3e5ba6b472..df5414a0bdd9 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -216,6 +216,9 @@ #define SAS_RAS_INTR1 (RAS_BASE + 0x04) #define SAS_RAS_INTR0_MASK (RAS_BASE + 0x08) #define SAS_RAS_INTR1_MASK (RAS_BASE + 0x0c) +#define CFG_SAS_RAS_INTR_MASK (RAS_BASE + 0x1c) +#define SAS_RAS_INTR2 (RAS_BASE + 0x20) +#define SAS_RAS_INTR2_MASK (RAS_BASE + 0x24) /* HW dma structures */ /* Delivery queue header */ @@ -392,6 +395,7 @@ static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba, static void init_reg_v3_hw(struct hisi_hba *hisi_hba) { + struct pci_dev *pdev = hisi_hba->pci_dev; int i; /* Global registers init */ @@ -409,7 +413,10 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, ENT_INT_SRC3, 0xffffffff); hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1, 0xfefefefe); hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK2, 0xfefefefe); - hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xfffe20ff); + if (pdev->revision >= 0x21) + hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xffff7fff); + else + hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, 0xfffe20ff); hisi_sas_write32(hisi_hba, CHNL_PHYUPDOWN_INT_MSK, 0x0); hisi_sas_write32(hisi_hba, CHNL_ENT_INT_MSK, 0x0); hisi_sas_write32(hisi_hba, HGC_COM_INT_MSK, 0x0); @@ -428,7 +435,12 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT2, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, RXOP_CHECK_CFG_H, 0x1000); - hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, 0xff87ffff); + if (pdev->revision >= 0x21) + hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, + 0xffffffff); + else + hisi_sas_phy_write32(hisi_hba, i, CHL_INT1_MSK, + 0xff87ffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT2_MSK, 0xffffbfe); hisi_sas_phy_write32(hisi_hba, i, PHY_CTRL_RDY_MSK, 0x0); hisi_sas_phy_write32(hisi_hba, i, PHYCTRL_NOT_RDY_MSK, 0x0); @@ -503,6 +515,8 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) /* RAS registers init */ hisi_sas_write32(hisi_hba, SAS_RAS_INTR0_MASK, 0x0); hisi_sas_write32(hisi_hba, SAS_RAS_INTR1_MASK, 0x0); + hisi_sas_write32(hisi_hba, SAS_RAS_INTR2_MASK, 0x0); + hisi_sas_write32(hisi_hba, CFG_SAS_RAS_INTR_MASK, 0x0); } static void config_phy_opt_mode_v3_hw(struct hisi_hba *hisi_hba, int phy_no) @@ -1319,6 +1333,13 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) CHL_INT1); u32 irq_value2 = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2); + u32 irq_msk1 = hisi_sas_phy_read32(hisi_hba, phy_no, + CHL_INT1_MSK); + u32 irq_msk2 = hisi_sas_phy_read32(hisi_hba, phy_no, + CHL_INT2_MSK); + + irq_value1 &= ~irq_msk1; + irq_value2 &= ~irq_msk2; if ((irq_msk & (4 << (phy_no * 4))) && irq_value1) { @@ -1448,6 +1469,7 @@ static irqreturn_t fatal_axi_int_v3_hw(int irq_no, void *p) hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, irq_msk | 0x1df00); irq_value = hisi_sas_read32(hisi_hba, ENT_INT_SRC3); + irq_value &= ~irq_msk; for (i = 0; i < ARRAY_SIZE(fatal_axi_error); i++) { const struct hisi_sas_hw_error *error = &fatal_axi_error[i]; @@ -2222,6 +2244,29 @@ static const struct hisi_sas_hw_error sas_ras_intr1_nfe[] = { { .irq_msk = BIT(31), .msg = "DMAC7_RX_POISON" }, }; +static const struct hisi_sas_hw_error sas_ras_intr2_nfe[] = { + { .irq_msk = BIT(0), .msg = "DMAC0_AXI_BUS_ERR" }, + { .irq_msk = BIT(1), .msg = "DMAC1_AXI_BUS_ERR" }, + { .irq_msk = BIT(2), .msg = "DMAC2_AXI_BUS_ERR" }, + { .irq_msk = BIT(3), .msg = "DMAC3_AXI_BUS_ERR" }, + { .irq_msk = BIT(4), .msg = "DMAC4_AXI_BUS_ERR" }, + { .irq_msk = BIT(5), .msg = "DMAC5_AXI_BUS_ERR" }, + { .irq_msk = BIT(6), .msg = "DMAC6_AXI_BUS_ERR" }, + { .irq_msk = BIT(7), .msg = "DMAC7_AXI_BUS_ERR" }, + { .irq_msk = BIT(8), .msg = "DMAC0_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(9), .msg = "DMAC1_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(10), .msg = "DMAC2_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(11), .msg = "DMAC3_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(12), .msg = "DMAC4_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(13), .msg = "DMAC5_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(14), .msg = "DMAC6_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(15), .msg = "DMAC7_FIFO_OMIT_ERR" }, + { .irq_msk = BIT(16), .msg = "HGC_RLSE_SLOT_UNMATCH" }, + { .irq_msk = BIT(17), .msg = "HGC_LM_ADD_FCH_LIST_ERR" }, + { .irq_msk = BIT(18), .msg = "HGC_AXI_BUS_ERR" }, + { .irq_msk = BIT(19), .msg = "HGC_FIFO_OMIT_ERR" }, +}; + static bool process_non_fatal_error_v3_hw(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; @@ -2252,6 +2297,17 @@ static bool process_non_fatal_error_v3_hw(struct hisi_hba *hisi_hba) } hisi_sas_write32(hisi_hba, SAS_RAS_INTR1, irq_value); + irq_value = hisi_sas_read32(hisi_hba, SAS_RAS_INTR2); + for (i = 0; i < ARRAY_SIZE(sas_ras_intr2_nfe); i++) { + ras_error = &sas_ras_intr2_nfe[i]; + if (ras_error->irq_msk & irq_value) { + dev_warn(dev, "SAS_RAS_INTR2: %s(irq_value=0x%x) found.\n", + ras_error->msg, irq_value); + need_reset = true; + } + } + hisi_sas_write32(hisi_hba, SAS_RAS_INTR2, irq_value); + return need_reset; } -- cgit v1.2.3 From 5df41af4b18720061b1024f93f54b957864cdd44 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Sat, 24 Mar 2018 00:05:10 +0800 Subject: scsi: hisi_sas: delete timer when removing hisi_sas driver Delete timer for v1 and v3 hw when removing hisi_sas driver. Signed-off-by: Xiang chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 3 +++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 3 --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 49c1fa643803..a21679574142 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -2177,6 +2177,9 @@ int hisi_sas_remove(struct platform_device *pdev) struct hisi_hba *hisi_hba = sha->lldd_ha; struct Scsi_Host *shost = sha->core.shost; + if (timer_pending(&hisi_hba->timer)) + del_timer(&hisi_hba->timer); + sas_unregister_ha(sha); sas_remove_host(sha->core.shost); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 89b9505c0654..bed6afb324a1 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3599,9 +3599,6 @@ static int hisi_sas_v2_remove(struct platform_device *pdev) struct sas_ha_struct *sha = platform_get_drvdata(pdev); struct hisi_hba *hisi_hba = sha->lldd_ha; - if (timer_pending(&hisi_hba->timer)) - del_timer(&hisi_hba->timer); - hisi_sas_kill_tasklets(hisi_hba); return hisi_sas_remove(pdev); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index df5414a0bdd9..efe64bcfa4f2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2183,6 +2183,9 @@ static void hisi_sas_v3_remove(struct pci_dev *pdev) struct hisi_hba *hisi_hba = sha->lldd_ha; struct Scsi_Host *shost = sha->core.shost; + if (timer_pending(&hisi_hba->timer)) + del_timer(&hisi_hba->timer); + sas_unregister_ha(sha); sas_remove_host(sha->core.shost); -- cgit v1.2.3 From 4f4e21b8ff3e706f79e1adb2a475c3f5ee6b57f9 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Sat, 24 Mar 2018 00:05:11 +0800 Subject: scsi: hisi_sas: use dma_zalloc_coherent() This is a warning coming from Coccinelle, and need to use new interface dma_zalloc_coherent() instead of dma_alloc_coherent()/memset(). Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index a21679574142..9563dfa72181 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1822,13 +1822,11 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) goto err_out; s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); - hisi_hba->itct = dma_alloc_coherent(dev, s, &hisi_hba->itct_dma, + hisi_hba->itct = dma_zalloc_coherent(dev, s, &hisi_hba->itct_dma, GFP_KERNEL); if (!hisi_hba->itct) goto err_out; - memset(hisi_hba->itct, 0, s); - hisi_hba->slot_info = devm_kcalloc(dev, max_command_entries, sizeof(struct hisi_sas_slot), GFP_KERNEL); -- cgit v1.2.3 From 3ff0f0b657eb414c1f8f1f22b91d52b892d8a850 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Sat, 24 Mar 2018 00:05:12 +0800 Subject: scsi: hisi_sas: consolidate command check in hisi_sas_get_ata_protocol() Currently we check the fis->command value in 2 locations in hisi_sas_get_ata_protocol() switch statement. Fix this by consolidating the check for fis->command value to 1 location only. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 9563dfa72181..8557fd08ed8e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -78,22 +78,23 @@ u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction) case ATA_CMD_STANDBYNOW1: case ATA_CMD_ZAC_MGMT_OUT: return HISI_SAS_SATA_PROTOCOL_NONDATA; + + case ATA_CMD_SET_MAX: + switch (fis->features) { + case ATA_SET_MAX_PASSWD: + case ATA_SET_MAX_LOCK: + return HISI_SAS_SATA_PROTOCOL_PIO; + + case ATA_SET_MAX_PASSWD_DMA: + case ATA_SET_MAX_UNLOCK_DMA: + return HISI_SAS_SATA_PROTOCOL_DMA; + + default: + return HISI_SAS_SATA_PROTOCOL_NONDATA; + } + default: { - if (fis->command == ATA_CMD_SET_MAX) { - switch (fis->features) { - case ATA_SET_MAX_PASSWD: - case ATA_SET_MAX_LOCK: - return HISI_SAS_SATA_PROTOCOL_PIO; - - case ATA_SET_MAX_PASSWD_DMA: - case ATA_SET_MAX_UNLOCK_DMA: - return HISI_SAS_SATA_PROTOCOL_DMA; - - default: - return HISI_SAS_SATA_PROTOCOL_NONDATA; - } - } if (direction == DMA_NONE) return HISI_SAS_SATA_PROTOCOL_NONDATA; return HISI_SAS_SATA_PROTOCOL_PIO; -- cgit v1.2.3 From 327f242fa806c199a183c2197592e94d336c8266 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Sat, 24 Mar 2018 00:05:13 +0800 Subject: scsi: hisi_sas: check IPTT is valid before using it for v3 hw There is a bug of v3 hw development version. When AXI error happen, hw may return an abnormal CQ that IPTT value is 0xffff. This will cause IPTT out-of-bounds reference. This patch adds a check of IPTT in cq_tasklet_v3_hw() and discards invalid slot. This workaround scheme is just to enhance fault-tolerance of the driver. So, we will apply this scheme for all version of v3 hw, although release version has fixed this SoC bug. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index efe64bcfa4f2..aa52d5e424f7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1731,15 +1731,19 @@ static void cq_tasklet_v3_hw(unsigned long val) while (rd_point != wr_point) { struct hisi_sas_complete_v3_hdr *complete_hdr; + struct device *dev = hisi_hba->dev; int iptt; complete_hdr = &complete_queue[rd_point]; iptt = (complete_hdr->dw1) & CMPLT_HDR_IPTT_MSK; - slot = &hisi_hba->slot_info[iptt]; - slot->cmplt_queue_slot = rd_point; - slot->cmplt_queue = queue; - slot_complete_v3_hw(hisi_hba, slot); + if (likely(iptt < HISI_SAS_COMMAND_ENTRIES_V3_HW)) { + slot = &hisi_hba->slot_info[iptt]; + slot->cmplt_queue_slot = rd_point; + slot->cmplt_queue = queue; + slot_complete_v3_hw(hisi_hba, slot); + } else + dev_err(dev, "IPTT %d is invalid, discard it.\n", iptt); if (++rd_point >= HISI_SAS_QUEUE_SLOTS) rd_point = 0; -- cgit v1.2.3 From 381ed6c081ae423b03d82ce1a0bb79bbec2b033e Mon Sep 17 00:00:00 2001 From: John Garry Date: Sat, 24 Mar 2018 00:05:14 +0800 Subject: scsi: hisi_sas: print device id for errors When we find an erroneous slot completion, to help aid debugging add the device index to the current debug log. Signed-off-by: John Garry Reviewed-by: Xiang Chen Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 4 ++-- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index bed6afb324a1..a5abde855cb2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2459,10 +2459,10 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) slot_err_v2_hw(hisi_hba, task, slot, 2); if (ts->stat != SAS_DATA_UNDERRUN) - dev_info(dev, "erroneous completion iptt=%d task=%p " + dev_info(dev, "erroneous completion iptt=%d task=%p dev id=%d " "CQ hdr: 0x%x 0x%x 0x%x 0x%x " "Error info: 0x%x 0x%x 0x%x 0x%x\n", - slot->idx, task, + slot->idx, task, sas_dev->device_id, complete_hdr->dw0, complete_hdr->dw1, complete_hdr->act, complete_hdr->dw3, error_info[0], error_info[1], diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index aa52d5e424f7..760724ae4d6f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1641,10 +1641,10 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) slot_err_v3_hw(hisi_hba, task, slot); if (ts->stat != SAS_DATA_UNDERRUN) - dev_info(dev, "erroneous completion iptt=%d task=%p " + dev_info(dev, "erroneous completion iptt=%d task=%p dev id=%d " "CQ hdr: 0x%x 0x%x 0x%x 0x%x " "Error info: 0x%x 0x%x 0x%x 0x%x\n", - slot->idx, task, + slot->idx, task, sas_dev->device_id, complete_hdr->dw0, complete_hdr->dw1, complete_hdr->act, complete_hdr->dw3, error_info[0], error_info[1], -- cgit v1.2.3 From c90a0bea4f645d561b87becd2bd99f7934402510 Mon Sep 17 00:00:00 2001 From: John Garry Date: Sat, 24 Mar 2018 00:05:15 +0800 Subject: scsi: hisi_sas: remove some unneeded structure members This patch removes unneeded structure elements: - hisi_sas_phy.dev_sas_addr: only ever written - Also remove associated function which writes it, hisi_sas_init_add(). - hisi_sas_device.attached_phy: only ever written - Also remove code to set it in hisi_sas_dev_found() Signed-off-by: John Garry Reviewed-by: Xiang Chen Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 3 --- drivers/scsi/hisi_sas/hisi_sas_main.c | 17 +---------------- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 2 -- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index d1153e8e846b..d413d05fda26 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -137,7 +137,6 @@ struct hisi_sas_phy { struct asd_sas_phy sas_phy; struct sas_identify identify; u64 port_id; /* from hw */ - u64 dev_sas_addr; u64 frame_rcvd_size; u8 frame_rcvd[32]; u8 phy_attached; @@ -174,7 +173,6 @@ struct hisi_sas_device { struct completion *completion; struct hisi_sas_dq *dq; struct list_head list; - u64 attached_phy; enum sas_device_type dev_type; int device_id; int sata_idx; @@ -440,7 +438,6 @@ extern struct scsi_transport_template *hisi_sas_stt; extern struct scsi_host_template *hisi_sas_sht; extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba); -extern void hisi_sas_init_add(struct hisi_hba *hisi_hba); extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost); extern void hisi_sas_free(struct hisi_hba *hisi_hba); extern u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 8557fd08ed8e..d1a61b1e591b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -577,10 +577,8 @@ static int hisi_sas_dev_found(struct domain_device *device) for (phy_no = 0; phy_no < phy_num; phy_no++) { phy = &parent_dev->ex_dev.ex_phy[phy_no]; if (SAS_ADDR(phy->attached_sas_addr) == - SAS_ADDR(device->sas_addr)) { - sas_dev->attached_phy = phy_no; + SAS_ADDR(device->sas_addr)) break; - } } if (phy_no == phy_num) { @@ -2079,17 +2077,6 @@ err_out: return NULL; } -void hisi_sas_init_add(struct hisi_hba *hisi_hba) -{ - int i; - - for (i = 0; i < hisi_hba->n_phy; i++) - memcpy(&hisi_hba->phy[i].dev_sas_addr, - hisi_hba->sas_addr, - SAS_ADDR_SIZE); -} -EXPORT_SYMBOL_GPL(hisi_sas_init_add); - int hisi_sas_probe(struct platform_device *pdev, const struct hisi_sas_hw *hw) { @@ -2143,8 +2130,6 @@ int hisi_sas_probe(struct platform_device *pdev, sha->sas_port[i] = &hisi_hba->port[i].sas_port; } - hisi_sas_init_add(hisi_hba); - rc = scsi_add_host(shost, &pdev->dev); if (rc) goto err_out_ha; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 760724ae4d6f..33735a7082b6 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2134,8 +2134,6 @@ hisi_sas_v3_probe(struct pci_dev *pdev, const struct pci_device_id *id) sha->sas_port[i] = &hisi_hba->port[i].sas_port; } - hisi_sas_init_add(hisi_hba); - rc = scsi_add_host(shost, dev); if (rc) goto err_out_ha; -- cgit v1.2.3 From b6240a4df0186c03e5ffff6f61570ed31a1a5172 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Mon, 26 Mar 2018 17:27:41 +0800 Subject: scsi: libsas: add transport class for ATA devices Now ata devices attached with sas controller do not have transport class, so that we can not see any information of these ata devices in /sys/class/ata_port(or ata_link or ata_device). Add transport class for the ata devices attached with sas controller. The /sys/class directory will show the infomation of the ata devices as follows: localhost:/sys/class # ls ata* ata_device: dev1.0 dev2.0 ata_link: link1 link2 ata_port: ata1 ata2 No functional change of the device scanning and io path. The ata transport class was deleted when destroying the sas devices. Signed-off-by: Jason Yan CC: Dan Williams CC: Tejun Heo Acked-by: Tejun Heo Signed-off-by: Martin K. Petersen --- drivers/ata/libata-scsi.c | 12 ++++++++++++ drivers/scsi/libsas/sas_ata.c | 5 +++++ drivers/scsi/libsas/sas_discover.c | 1 + include/linux/libata.h | 2 ++ 4 files changed, 20 insertions(+) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 89a9d4a2efc8..1c9f80fbc51c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -5051,6 +5051,18 @@ int ata_sas_port_init(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_init); +int ata_sas_tport_add(struct device *parent, struct ata_port *ap) +{ + return ata_tport_add(parent, ap); +} +EXPORT_SYMBOL_GPL(ata_sas_tport_add); + +void ata_sas_tport_delete(struct ata_port *ap) +{ + ata_tport_delete(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_tport_delete); + /** * ata_sas_port_destroy - Destroy a SATA port allocated by ata_sas_port_alloc * @ap: SATA port to destroy diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 0cc1567eacc1..ff1d612f6fb9 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -577,6 +577,11 @@ int sas_ata_init(struct domain_device *found_dev) ata_sas_port_destroy(ap); return rc; } + rc = ata_sas_tport_add(found_dev->sata_dev.ata_host.dev, ap); + if (rc) { + ata_sas_port_destroy(ap); + return rc; + } found_dev->sata_dev.ap = ap; return 0; diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index a0fa7ef3a071..1ffca28fe6a8 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -314,6 +314,7 @@ void sas_free_device(struct kref *kref) kfree(dev->ex_dev.ex_phy); if (dev_is_sata(dev) && dev->sata_dev.ap) { + ata_sas_tport_delete(dev->sata_dev.ap); ata_sas_port_destroy(dev->sata_dev.ap); dev->sata_dev.ap = NULL; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 1795fecdea17..0619ebf4d475 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1130,6 +1130,8 @@ extern void ata_sas_async_probe(struct ata_port *ap); extern int ata_sas_sync_probe(struct ata_port *ap); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); +extern int ata_sas_tport_add(struct device *parent, struct ata_port *ap); +extern void ata_sas_tport_delete(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); extern int ata_sas_slave_configure(struct scsi_device *, struct ata_port *); extern int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap); -- cgit v1.2.3 From 63273cb40101b6f303a5493f1bdf629d4ab3746b Mon Sep 17 00:00:00 2001 From: Long Li Date: Tue, 27 Mar 2018 17:48:38 -0700 Subject: scsi: vmbus: Add function to report available ring buffer to write in total ring size percentage Netvsc has a function to calculate how much ring buffer in percentage is available to write. This function is also useful for storvsc and other vmbus devices. Define a similar function in vmbus to be used by other vmbus devices. Signed-off-by: Long Li Acked-by: David S. Miller Signed-off-by: Martin K. Petersen --- drivers/hv/ring_buffer.c | 2 ++ include/linux/hyperv.h | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 8699bb969e7e..3c836c099a8f 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -227,6 +227,8 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, ring_info->ring_buffer->feature_bits.value = 1; ring_info->ring_size = page_cnt << PAGE_SHIFT; + ring_info->ring_size_div10_reciprocal = + reciprocal_value(ring_info->ring_size / 10); ring_info->ring_datasize = ring_info->ring_size - sizeof(struct hv_ring_buffer); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 192ed8fbc403..9ac954ee577e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -35,6 +35,7 @@ #include #include #include +#include #define MAX_PAGE_BUFFER_COUNT 32 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ @@ -120,6 +121,7 @@ struct hv_ring_buffer { struct hv_ring_buffer_info { struct hv_ring_buffer *ring_buffer; u32 ring_size; /* Include the shared header */ + struct reciprocal_value ring_size_div10_reciprocal; spinlock_t ring_lock; u32 ring_datasize; /* < ring_size */ @@ -154,6 +156,16 @@ static inline u32 hv_get_bytes_to_write(const struct hv_ring_buffer_info *rbi) return write; } +static inline u32 hv_get_avail_to_write_percent( + const struct hv_ring_buffer_info *rbi) +{ + u32 avail_write = hv_get_bytes_to_write(rbi); + + return reciprocal_divide( + (avail_write << 3) + (avail_write << 1), + rbi->ring_size_div10_reciprocal); +} + /* * VMBUS version is 32 bit entity broken up into * two 16 bit quantities: major_number. minor_number. -- cgit v1.2.3 From 6b1f8376dc34fb3b728672ed6e06751f26b3b225 Mon Sep 17 00:00:00 2001 From: Long Li Date: Tue, 27 Mar 2018 17:48:39 -0700 Subject: scsi: netvsc: Use the vmbus function to calculate ring buffer percentage In Vmbus, we have defined a function to calculate available ring buffer percentage to write. Use that function and remove netvsc's private version. [mkp: typo] Signed-off-by: Long Li Acked-by: David S. Miller Signed-off-by: Martin K. Petersen --- drivers/net/hyperv/hyperv_net.h | 1 - drivers/net/hyperv/netvsc.c | 18 +++--------------- drivers/net/hyperv/netvsc_drv.c | 3 --- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 960f06141472..d965d022a941 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -189,7 +189,6 @@ struct netvsc_device; struct net_device_context; extern u32 netvsc_ring_bytes; -extern struct reciprocal_value netvsc_ring_reciprocal; struct netvsc_device *netvsc_device_add(struct hv_device *device, const struct netvsc_device_info *info); diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 04f611e6f678..96bf712ddd31 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -31,7 +31,6 @@ #include #include #include -#include #include @@ -634,17 +633,6 @@ void netvsc_device_remove(struct hv_device *device) #define RING_AVAIL_PERCENT_HIWATER 20 #define RING_AVAIL_PERCENT_LOWATER 10 -/* - * Get the percentage of available bytes to write in the ring. - * The return value is in range from 0 to 100. - */ -static u32 hv_ringbuf_avail_percent(const struct hv_ring_buffer_info *ring_info) -{ - u32 avail_write = hv_get_bytes_to_write(ring_info); - - return reciprocal_divide(avail_write * 100, netvsc_ring_reciprocal); -} - static inline void netvsc_free_send_slot(struct netvsc_device *net_device, u32 index) { @@ -696,8 +684,8 @@ static void netvsc_send_tx_complete(struct netvsc_device *net_device, struct netdev_queue *txq = netdev_get_tx_queue(ndev, q_idx); if (netif_tx_queue_stopped(txq) && - (hv_ringbuf_avail_percent(&channel->outbound) > RING_AVAIL_PERCENT_HIWATER || - queue_sends < 1)) { + (hv_get_avail_to_write_percent(&channel->outbound) > + RING_AVAIL_PERCENT_HIWATER || queue_sends < 1)) { netif_tx_wake_queue(txq); ndev_ctx->eth_stats.wake_queue++; } @@ -805,7 +793,7 @@ static inline int netvsc_send_pkt( struct netdev_queue *txq = netdev_get_tx_queue(ndev, packet->q_idx); u64 req_id; int ret; - u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound); + u32 ring_avail = hv_get_avail_to_write_percent(&out_channel->outbound); nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT; if (skb) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index ecc84954c511..895a54f96c6c 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -35,7 +35,6 @@ #include #include #include -#include #include #include @@ -58,7 +57,6 @@ static unsigned int ring_size __ro_after_init = 128; module_param(ring_size, uint, 0444); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); unsigned int netvsc_ring_bytes __ro_after_init; -struct reciprocal_value netvsc_ring_reciprocal __ro_after_init; static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFUP | @@ -2218,7 +2216,6 @@ static int __init netvsc_drv_init(void) ring_size); } netvsc_ring_bytes = ring_size * PAGE_SIZE; - netvsc_ring_reciprocal = reciprocal_value(netvsc_ring_bytes); ret = vmbus_driver_register(&netvsc_drv); if (ret) -- cgit v1.2.3 From 3c6c122cfcbc2264d8414d292ebe2803b7e20215 Mon Sep 17 00:00:00 2001 From: Himanshu Jha Date: Fri, 6 Apr 2018 02:02:10 -0700 Subject: scsi: megaraid_sas: Use zeroing memory allocator than allocator/memset Use pci_zalloc_consistent for allocating zeroed memory and remove unnecessary memset function. Done using Coccinelle. Generated by: scripts/coccinelle/api/alloc/kzalloc-simple.cocci Suggested-by: Luis R. Rodriguez Signed-off-by: Himanshu Jha Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 25 ++++++++++--------------- drivers/scsi/megaraid/megaraid_sas_fusion.c | 5 ++--- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index b89c6e6c0589..026fad818b2a 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -2224,9 +2224,9 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, sizeof(struct MR_LD_VF_AFFILIATION_111)); else { new_affiliation_111 = - pci_alloc_consistent(instance->pdev, - sizeof(struct MR_LD_VF_AFFILIATION_111), - &new_affiliation_111_h); + pci_zalloc_consistent(instance->pdev, + sizeof(struct MR_LD_VF_AFFILIATION_111), + &new_affiliation_111_h); if (!new_affiliation_111) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " "memory for new affiliation for scsi%d\n", @@ -2234,8 +2234,6 @@ static int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, megasas_return_cmd(instance, cmd); return -ENOMEM; } - memset(new_affiliation_111, 0, - sizeof(struct MR_LD_VF_AFFILIATION_111)); } memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -2333,10 +2331,10 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, sizeof(struct MR_LD_VF_AFFILIATION)); else { new_affiliation = - pci_alloc_consistent(instance->pdev, - (MAX_LOGICAL_DRIVES + 1) * - sizeof(struct MR_LD_VF_AFFILIATION), - &new_affiliation_h); + pci_zalloc_consistent(instance->pdev, + (MAX_LOGICAL_DRIVES + 1) * + sizeof(struct MR_LD_VF_AFFILIATION), + &new_affiliation_h); if (!new_affiliation) { dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " "memory for new affiliation for scsi%d\n", @@ -2344,8 +2342,6 @@ static int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, megasas_return_cmd(instance, cmd); return -ENOMEM; } - memset(new_affiliation, 0, (MAX_LOGICAL_DRIVES + 1) * - sizeof(struct MR_LD_VF_AFFILIATION)); } memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -5636,16 +5632,15 @@ megasas_get_seq_num(struct megasas_instance *instance, } dcmd = &cmd->frame->dcmd; - el_info = pci_alloc_consistent(instance->pdev, - sizeof(struct megasas_evt_log_info), - &el_info_h); + el_info = pci_zalloc_consistent(instance->pdev, + sizeof(struct megasas_evt_log_info), + &el_info_h); if (!el_info) { megasas_return_cmd(instance, cmd); return -ENOMEM; } - memset(el_info, 0, sizeof(*el_info)); memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); dcmd->cmd = MFI_CMD_DCMD; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index ce97cde3b41c..0f4d30359729 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -684,15 +684,14 @@ megasas_alloc_rdpq_fusion(struct megasas_instance *instance) array_size = sizeof(struct MPI2_IOC_INIT_RDPQ_ARRAY_ENTRY) * MAX_MSIX_QUEUES_FUSION; - fusion->rdpq_virt = pci_alloc_consistent(instance->pdev, array_size, - &fusion->rdpq_phys); + fusion->rdpq_virt = pci_zalloc_consistent(instance->pdev, array_size, + &fusion->rdpq_phys); if (!fusion->rdpq_virt) { dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM; } - memset(fusion->rdpq_virt, 0, array_size); msix_count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; fusion->reply_frames_desc_pool = dma_pool_create("mr_rdpq", -- cgit v1.2.3 From 3239b8cd28fd849a2023483257d35d68c5876c74 Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Fri, 6 Apr 2018 02:02:11 -0700 Subject: scsi: megaraid_sas: Increase timeout by 1 sec for non-RAID fastpath IOs Hardware could time out Fastpath IOs one second earlier than the timeout provided by the host. For non-RAID devices, driver provides timeout value based on OS provided timeout value. Under certain scenarios, if the OS provides a timeout value of 1 second, due to above behavior hardware will timeout immediately. Increase timeout value for non-RAID fastpath IOs by 1 second. Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_fusion.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index 0f4d30359729..358d42df22e1 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -2980,6 +2980,9 @@ megasas_build_syspd_fusion(struct megasas_instance *instance, pRAID_Context->timeout_value = cpu_to_le16(os_timeout_value); pRAID_Context->virtual_disk_tgt_id = cpu_to_le16(device_id); } else { + if (os_timeout_value) + os_timeout_value++; + /* system pd Fast Path */ io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; timeout_limit = (scmd->device->type == TYPE_DISK) ? -- cgit v1.2.3 From 67c5490acebec5036b1bad199061633917d3c0ef Mon Sep 17 00:00:00 2001 From: Shivasharan S Date: Fri, 6 Apr 2018 02:02:12 -0700 Subject: scsi: megaraid_sas: driver version upgrade Signed-off-by: Shivasharan S Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 27fab8235ea5..75dc25f78336 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -35,8 +35,8 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "07.704.04.00-rc1" -#define MEGASAS_RELDATE "December 7, 2017" +#define MEGASAS_VERSION "07.705.02.00-rc1" +#define MEGASAS_RELDATE "April 4, 2018" /* * Device IDs -- cgit v1.2.3 From 118c0415eeec96669219536ed8fdaabecc5619fb Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:21 -0700 Subject: scsi: lpfc: Fix multiple PRLI completion error path Nodelist entry for SCSI array ends up in UNMAPPED state. This is due to illegal discovery State machine transition because of two PRLIs and the first one failing with LS_RJT. Also, the error path was designed assuming the PRLIs complete in the order they were sent, FCP first, then NVME. In a failing case, the array thinks about the first PRLI (FCP), but issues LS_RJT for the 2nd PRLI immediately. Fix PRLI completion error path for the ordering expectation. Ensure the discovery state machine update is not set until all outstanding PRLIs are complete. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nportdisc.c | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 022060636ae1..e790c0bc64fc 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1936,31 +1936,14 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, goto out; } - /* When the rport rejected the FCP PRLI as unsupported. - * This should only happen in Pt2Pt so an NVME PRLI - * should be outstanding still. - */ - if (npr && ndlp->nlp_flag & NLP_FCP_PRLI_RJT) { + /* Adjust the nlp_type accordingly if the PRLI failed */ + if (npr) ndlp->nlp_fc4_type &= ~NLP_FC4_FCP; - goto out_err; - } - - /* The LS Req had some error. Don't let this be a - * target. - */ - if ((ndlp->fc4_prli_sent == 1) && - (ndlp->nlp_state == NLP_STE_PRLI_ISSUE) && - (ndlp->nlp_type & (NLP_FCP_TARGET | NLP_FCP_INITIATOR))) - /* The FCP PRLI completed successfully but - * the NVME PRLI failed. Since they are sent in - * succession, allow the FCP to complete. - */ - goto out_err; + if (nvpr) + ndlp->nlp_fc4_type &= ~NLP_FC4_NVME; - ndlp->nlp_prev_state = NLP_STE_PRLI_ISSUE; - ndlp->nlp_type |= NLP_FCP_INITIATOR; - lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE); - return ndlp->nlp_state; + /* We can't set the DSM state till BOTH PRLIs complete */ + goto out_err; } if (npr && (npr->acceptRspCode == PRLI_REQ_EXECUTED) && -- cgit v1.2.3 From f91bc594ba963a9354c9da8bb85c21606c2b6289 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:22 -0700 Subject: scsi: lpfc: Correct target queue depth application changes The max_scsicmpl_time parameter can be used to perform scsi cmd queue depth mgmt based on io completion time: the queue depth is reduced to make completion time shorter. However, as soon as an io completes and the completion time is within limits, the code immediately bumps the queue depth limit back up to the target queue depth. Thus the procedure restarts, effectively limiting the usefulness of adjusting queue depth to help completion time. This patch makes the following changes: - Removes the code at io completion that resets the queue depth as soon as within limits. - As the code removed was where the target queue depth was first applied, change target queue depth application so that it occurs when the parameter is changed. - Makes target queue depth a standard parameter: both a module parameter and a sysfs parameter. - Optimizes the command pending count by using atomics rather than locks. - Updates the debugfs nodelist stats to allow better debugging of pending command counts. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 2 -- drivers/scsi/lpfc/lpfc_attr.c | 45 ++++++++++++++++++++++++++++++++++++++-- drivers/scsi/lpfc/lpfc_debugfs.c | 20 ++++++++++++++++-- drivers/scsi/lpfc/lpfc_scsi.c | 19 +++++------------ 4 files changed, 66 insertions(+), 20 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 6c0d351c0d0d..be4abe52f289 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -64,8 +64,6 @@ struct lpfc_sli2_slim; #define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */ #define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */ #define LPFC_VNAME_LEN 100 /* vport symbolic name length */ -#define LPFC_TGTQ_INTERVAL 40000 /* Min amount of time between tgt - queue depth change in millisecs */ #define LPFC_TGTQ_RAMPUP_PCENT 5 /* Target queue rampup in percentage */ #define LPFC_MIN_TGT_QDEPTH 10 #define LPFC_MAX_TGT_QDEPTH 0xFFFF diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 2ac1d21c553f..c89ffad1f43d 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -3469,8 +3469,49 @@ LPFC_VPORT_ATTR_R(lun_queue_depth, 30, 1, 512, # tgt_queue_depth: This parameter is used to limit the number of outstanding # commands per target port. Value range is [10,65535]. Default value is 65535. */ -LPFC_VPORT_ATTR_RW(tgt_queue_depth, 65535, 10, 65535, - "Max number of FCP commands we can queue to a specific target port"); +static uint lpfc_tgt_queue_depth = LPFC_MAX_TGT_QDEPTH; +module_param(lpfc_tgt_queue_depth, uint, 0444); +MODULE_PARM_DESC(lpfc_tgt_queue_depth, "Set max Target queue depth"); +lpfc_vport_param_show(tgt_queue_depth); +lpfc_vport_param_init(tgt_queue_depth, LPFC_MAX_TGT_QDEPTH, + LPFC_MIN_TGT_QDEPTH, LPFC_MAX_TGT_QDEPTH); + +/** + * lpfc_tgt_queue_depth_store: Sets an attribute value. + * @phba: pointer the the adapter structure. + * @val: integer attribute value. + * + * Description: Sets the parameter to the new value. + * + * Returns: + * zero on success + * -EINVAL if val is invalid + */ +static int +lpfc_tgt_queue_depth_set(struct lpfc_vport *vport, uint val) +{ + struct Scsi_Host *shost = lpfc_shost_from_vport(vport); + struct lpfc_nodelist *ndlp; + + if (!lpfc_rangecheck(val, LPFC_MIN_TGT_QDEPTH, LPFC_MAX_TGT_QDEPTH)) + return -EINVAL; + + if (val == vport->cfg_tgt_queue_depth) + return 0; + + spin_lock_irq(shost->host_lock); + vport->cfg_tgt_queue_depth = val; + + /* Next loop thru nodelist and change cmd_qdepth */ + list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) + ndlp->cmd_qdepth = vport->cfg_tgt_queue_depth; + + spin_unlock_irq(shost->host_lock); + return 0; +} + +lpfc_vport_param_store(tgt_queue_depth); +static DEVICE_ATTR_RW(lpfc_tgt_queue_depth); /* # hba_queue_depth: This parameter is used to limit the number of outstanding diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index fb0dc2aeed91..50c11acf73a8 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -544,7 +544,7 @@ static int lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) { int len = 0; - int cnt; + int i, iocnt, outio, cnt; struct Scsi_Host *shost = lpfc_shost_from_vport(vport); struct lpfc_hba *phba = vport->phba; struct lpfc_nodelist *ndlp; @@ -554,10 +554,12 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) struct nvme_fc_remote_port *nrport; cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE); + outio = 0; len += snprintf(buf+len, size-len, "\nFCP Nodelist Entries ...\n"); spin_lock_irq(shost->host_lock); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { + iocnt = 0; if (!cnt) { len += snprintf(buf+len, size-len, "Missing Nodelist Entries\n"); @@ -585,9 +587,11 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) break; case NLP_STE_UNMAPPED_NODE: statep = "UNMAP "; + iocnt = 1; break; case NLP_STE_MAPPED_NODE: statep = "MAPPED"; + iocnt = 1; break; case NLP_STE_NPR_NODE: statep = "NPR "; @@ -614,8 +618,10 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf+len, size-len, "UNKNOWN_TYPE "); if (ndlp->nlp_type & NLP_FC_NODE) len += snprintf(buf+len, size-len, "FC_NODE "); - if (ndlp->nlp_type & NLP_FABRIC) + if (ndlp->nlp_type & NLP_FABRIC) { len += snprintf(buf+len, size-len, "FABRIC "); + iocnt = 0; + } if (ndlp->nlp_type & NLP_FCP_TARGET) len += snprintf(buf+len, size-len, "FCP_TGT sid:%d ", ndlp->nlp_sid); @@ -632,10 +638,20 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) ndlp->nlp_usg_map); len += snprintf(buf+len, size-len, "refcnt:%x", kref_read(&ndlp->kref)); + if (iocnt) { + i = atomic_read(&ndlp->cmd_pending); + len += snprintf(buf + len, size - len, + " OutIO:x%x Qdepth x%x", + i, ndlp->cmd_qdepth); + outio += i; + } len += snprintf(buf+len, size-len, "\n"); } spin_unlock_irq(shost->host_lock); + len += snprintf(buf + len, size - len, + "\nOutstanding IO x%x\n", outio); + if (phba->nvmet_support && phba->targetport && (vport == phba->pport)) { tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; len += snprintf(buf + len, size - len, diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 050f04418f5f..8570486013f3 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -3983,9 +3983,6 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } #endif - if (pnode && NLP_CHK_NODE_ACT(pnode)) - atomic_dec(&pnode->cmd_pending); - if (lpfc_cmd->status) { if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && (lpfc_cmd->result & IOERR_DRVR_MASK)) @@ -4125,6 +4122,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) { spin_lock_irqsave(shost->host_lock, flags); if (pnode && NLP_CHK_NODE_ACT(pnode)) { + atomic_dec(&pnode->cmd_pending); if (pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) && (atomic_read(&pnode->cmd_pending) > @@ -4138,16 +4136,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } spin_unlock_irqrestore(shost->host_lock, flags); } else if (pnode && NLP_CHK_NODE_ACT(pnode)) { - if ((pnode->cmd_qdepth != vport->cfg_tgt_queue_depth) && - time_after(jiffies, pnode->last_change_time + - msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) { - spin_lock_irqsave(shost->host_lock, flags); - pnode->cmd_qdepth = vport->cfg_tgt_queue_depth; - pnode->last_change_time = jiffies; - spin_unlock_irqrestore(shost->host_lock, flags); - } + atomic_dec(&pnode->cmd_pending); } - lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); spin_lock_irqsave(&phba->hbalock, flags); @@ -4591,6 +4581,8 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) ndlp->nlp_portname.u.wwn[7]); goto out_tgt_busy; } + atomic_inc(&ndlp->cmd_pending); + lpfc_cmd = lpfc_get_scsi_buf(phba, ndlp); if (lpfc_cmd == NULL) { lpfc_rampdown_queue_depth(phba); @@ -4643,11 +4635,9 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp); - atomic_inc(&ndlp->cmd_pending); err = lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB); if (err) { - atomic_dec(&ndlp->cmd_pending); lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, "3376 FCP could not issue IOCB err %x" "FCP cmd x%x <%d/%llu> " @@ -4691,6 +4681,7 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); lpfc_release_scsi_buf(phba, lpfc_cmd); out_host_busy: + atomic_dec(&ndlp->cmd_pending); return SCSI_MLQUEUE_HOST_BUSY; out_tgt_busy: -- cgit v1.2.3 From 66a210ffb877dc93644d02b688d5d8586aab4e60 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:23 -0700 Subject: scsi: lpfc: Add per io channel NVME IO statistics When debugging various issues, per IO channel IO statistics were useful to understand what was happening. However, many of the stats were on a port basis rather than an io channel basis. Move statistics to an io channel basis. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 6 --- drivers/scsi/lpfc/lpfc_attr.c | 40 +++++++++------ drivers/scsi/lpfc/lpfc_debugfs.c | 65 ++++++++++++++++-------- drivers/scsi/lpfc/lpfc_init.c | 36 ++++++++----- drivers/scsi/lpfc/lpfc_nvme.c | 107 ++++++++++++++++++++++++++------------- drivers/scsi/lpfc/lpfc_nvme.h | 10 ++++ 6 files changed, 173 insertions(+), 91 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index be4abe52f289..2b47c69c1732 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -920,12 +920,6 @@ struct lpfc_hba { atomic_t fc4ScsiOutputRequests; atomic_t fc4ScsiControlRequests; atomic_t fc4ScsiIoCmpls; - atomic_t fc4NvmeInputRequests; - atomic_t fc4NvmeOutputRequests; - atomic_t fc4NvmeControlRequests; - atomic_t fc4NvmeIoCmpls; - atomic_t fc4NvmeLsRequests; - atomic_t fc4NvmeLsCmpls; uint64_t bg_guard_err_cnt; uint64_t bg_apptag_err_cnt; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index c89ffad1f43d..3a6b1be18886 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -151,8 +151,11 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, struct lpfc_nvme_lport *lport; struct lpfc_nodelist *ndlp; struct nvme_fc_remote_port *nrport; - uint64_t data1, data2, data3, tot; + struct lpfc_nvme_ctrl_stat *cstat; + uint64_t data1, data2, data3; + uint64_t totin, totout, tot; char *statep; + int i; int len = 0; if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) { @@ -364,11 +367,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, } spin_unlock_irq(shost->host_lock); + if (!lport) + return len; + len += snprintf(buf + len, PAGE_SIZE - len, "\nNVME Statistics\n"); len += snprintf(buf+len, PAGE_SIZE-len, "LS: Xmt %010x Cmpl %010x Abort %08x\n", - atomic_read(&phba->fc4NvmeLsRequests), - atomic_read(&phba->fc4NvmeLsCmpls), + atomic_read(&lport->fc4NvmeLsRequests), + atomic_read(&lport->fc4NvmeLsCmpls), atomic_read(&lport->xmt_ls_abort)); len += snprintf(buf + len, PAGE_SIZE - len, @@ -377,27 +383,31 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, atomic_read(&lport->cmpl_ls_xb), atomic_read(&lport->cmpl_ls_err)); - tot = atomic_read(&phba->fc4NvmeIoCmpls); - data1 = atomic_read(&phba->fc4NvmeInputRequests); - data2 = atomic_read(&phba->fc4NvmeOutputRequests); - data3 = atomic_read(&phba->fc4NvmeControlRequests); + totin = 0; + totout = 0; + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + tot = atomic_read(&cstat->fc4NvmeIoCmpls); + totin += tot; + data1 = atomic_read(&cstat->fc4NvmeInputRequests); + data2 = atomic_read(&cstat->fc4NvmeOutputRequests); + data3 = atomic_read(&cstat->fc4NvmeControlRequests); + totout += (data1 + data2 + data3); + } len += snprintf(buf+len, PAGE_SIZE-len, - "FCP: Rd %016llx Wr %016llx IO %016llx\n", - data1, data2, data3); + "Total FCP Cmpl %016llx Issue %016llx " + "OutIO %016llx\n", + totin, totout, totout - totin); len += snprintf(buf+len, PAGE_SIZE-len, - " noxri %08x nondlp %08x qdepth %08x " + " abort %08x noxri %08x nondlp %08x qdepth %08x " "wqerr %08x\n", + atomic_read(&lport->xmt_fcp_abort), atomic_read(&lport->xmt_fcp_noxri), atomic_read(&lport->xmt_fcp_bad_ndlp), atomic_read(&lport->xmt_fcp_qdepth), atomic_read(&lport->xmt_fcp_wqerr)); - len += snprintf(buf + len, PAGE_SIZE - len, - " Cmpl %016llx Outstanding %016llx Abort %08x\n", - tot, ((data1 + data2 + data3) - tot), - atomic_read(&lport->xmt_fcp_abort)); - len += snprintf(buf + len, PAGE_SIZE - len, "FCP CMPL: xb %08x Err %08x\n", atomic_read(&lport->cmpl_fcp_xb), diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 50c11acf73a8..cd3eb6b71398 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -767,10 +767,12 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) struct lpfc_nvmet_tgtport *tgtp; struct lpfc_nvmet_rcv_ctx *ctxp, *next_ctxp; struct nvme_fc_local_port *localport; + struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_nvme_lport *lport; - uint64_t tot, data1, data2, data3; + uint64_t data1, data2, data3; + uint64_t tot, totin, totout; + int cnt, i, maxch; int len = 0; - int cnt; if (phba->nvmet_support) { if (!phba->targetport) @@ -896,33 +898,52 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) if (!(phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME)) return len; + localport = vport->localport; + if (!localport) + return len; + lport = (struct lpfc_nvme_lport *)localport->private; + if (!lport) + return len; + len += snprintf(buf + len, size - len, "\nNVME Lport Statistics\n"); len += snprintf(buf + len, size - len, "LS: Xmt %016x Cmpl %016x\n", - atomic_read(&phba->fc4NvmeLsRequests), - atomic_read(&phba->fc4NvmeLsCmpls)); - - tot = atomic_read(&phba->fc4NvmeIoCmpls); - data1 = atomic_read(&phba->fc4NvmeInputRequests); - data2 = atomic_read(&phba->fc4NvmeOutputRequests); - data3 = atomic_read(&phba->fc4NvmeControlRequests); + atomic_read(&lport->fc4NvmeLsRequests), + atomic_read(&lport->fc4NvmeLsCmpls)); - len += snprintf(buf + len, size - len, - "FCP: Rd %016llx Wr %016llx IO %016llx\n", - data1, data2, data3); - - len += snprintf(buf + len, size - len, - " Cmpl %016llx Outstanding %016llx\n", - tot, (data1 + data2 + data3) - tot); + if (phba->cfg_nvme_io_channel < 32) + maxch = phba->cfg_nvme_io_channel; + else + maxch = 32; + totin = 0; + totout = 0; + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + tot = atomic_read(&cstat->fc4NvmeIoCmpls); + totin += tot; + data1 = atomic_read(&cstat->fc4NvmeInputRequests); + data2 = atomic_read(&cstat->fc4NvmeOutputRequests); + data3 = atomic_read(&cstat->fc4NvmeControlRequests); + totout += (data1 + data2 + data3); + + /* Limit to 32, debugfs display buffer limitation */ + if (i >= 32) + continue; - localport = vport->localport; - if (!localport) - return len; - lport = (struct lpfc_nvme_lport *)localport->private; - if (!lport) - return len; + len += snprintf(buf + len, PAGE_SIZE - len, + "FCP (%d): Rd %016llx Wr %016llx " + "IO %016llx ", + i, data1, data2, data3); + len += snprintf(buf + len, PAGE_SIZE - len, + "Cmpl %016llx OutIO %016llx\n", + tot, ((data1 + data2 + data3) - tot)); + } + len += snprintf(buf + len, PAGE_SIZE - len, + "Total FCP Cmpl %016llx Issue %016llx " + "OutIO %016llx\n", + totin, totout, totout - totin); len += snprintf(buf + len, size - len, "LS Xmt Err: Abrt %08x Err %08x " diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 7887468c71b4..4add398ec9cf 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1266,6 +1266,9 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) uint64_t tot, data1, data2, data3; struct lpfc_nvmet_tgtport *tgtp; struct lpfc_register reg_data; + struct nvme_fc_local_port *localport; + struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; void __iomem *eqdreg = phba->sli4_hba.u.if_type2.EQDregaddr; vports = lpfc_create_vport_work_array(phba); @@ -1299,14 +1302,25 @@ lpfc_hb_timeout_handler(struct lpfc_hba *phba) tot += atomic_read(&tgtp->xmt_fcp_release); tot = atomic_read(&tgtp->rcv_fcp_cmd_in) - tot; } else { - tot = atomic_read(&phba->fc4NvmeIoCmpls); - data1 = atomic_read( - &phba->fc4NvmeInputRequests); - data2 = atomic_read( - &phba->fc4NvmeOutputRequests); - data3 = atomic_read( - &phba->fc4NvmeControlRequests); - tot = (data1 + data2 + data3) - tot; + localport = phba->pport->localport; + if (!localport || !localport->private) + goto skip_eqdelay; + lport = (struct lpfc_nvme_lport *) + localport->private; + tot = 0; + for (i = 0; + i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + data1 = atomic_read( + &cstat->fc4NvmeInputRequests); + data2 = atomic_read( + &cstat->fc4NvmeOutputRequests); + data3 = atomic_read( + &cstat->fc4NvmeControlRequests); + tot += (data1 + data2 + data3); + tot -= atomic_read( + &cstat->fc4NvmeIoCmpls); + } } } @@ -6895,12 +6909,6 @@ lpfc_create_shost(struct lpfc_hba *phba) atomic_set(&phba->fc4ScsiOutputRequests, 0); atomic_set(&phba->fc4ScsiControlRequests, 0); atomic_set(&phba->fc4ScsiIoCmpls, 0); - atomic_set(&phba->fc4NvmeInputRequests, 0); - atomic_set(&phba->fc4NvmeOutputRequests, 0); - atomic_set(&phba->fc4NvmeControlRequests, 0); - atomic_set(&phba->fc4NvmeIoCmpls, 0); - atomic_set(&phba->fc4NvmeLsRequests, 0); - atomic_set(&phba->fc4NvmeLsCmpls, 0); vport = lpfc_create_port(phba, phba->brd_no, &phba->pcidev->dev); if (!vport) return -ENODEV; diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 378dca40ca20..1414c581c0b6 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -357,15 +357,17 @@ lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_dmabuf *buf_ptr; struct lpfc_nodelist *ndlp; - atomic_inc(&vport->phba->fc4NvmeLsCmpls); - + lport = (struct lpfc_nvme_lport *)vport->localport->private; pnvme_lsreq = (struct nvmefc_ls_req *)cmdwqe->context2; status = bf_get(lpfc_wcqe_c_status, wcqe) & LPFC_IOCB_STATUS_MASK; - if (status) { - lport = (struct lpfc_nvme_lport *)vport->localport->private; - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_ls_xb); - atomic_inc(&lport->cmpl_ls_err); + + if (lport) { + atomic_inc(&lport->fc4NvmeLsCmpls); + if (status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_ls_xb); + atomic_inc(&lport->cmpl_ls_err); + } } ndlp = (struct lpfc_nodelist *)cmdwqe->context1; @@ -570,6 +572,9 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, lport = (struct lpfc_nvme_lport *)pnvme_lport->private; rport = (struct lpfc_nvme_rport *)pnvme_rport->private; + if (unlikely(!lport) || unlikely(!rport)) + return -EINVAL; + vport = lport->vport; if (vport->load_flag & FC_UNLOADING) @@ -639,7 +644,7 @@ lpfc_nvme_ls_req(struct nvme_fc_local_port *pnvme_lport, pnvme_lsreq->rsplen, &pnvme_lsreq->rqstdma, &pnvme_lsreq->rspdma); - atomic_inc(&vport->phba->fc4NvmeLsRequests); + atomic_inc(&lport->fc4NvmeLsRequests); /* Hardcode the wait to 30 seconds. Connections are failing otherwise. * This code allows it all to work. @@ -690,6 +695,8 @@ lpfc_nvme_ls_abort(struct nvme_fc_local_port *pnvme_lport, struct lpfc_iocbq *wqe, *next_wqe; lport = (struct lpfc_nvme_lport *)pnvme_lport->private; + if (unlikely(!lport)) + return; vport = lport->vport; phba = vport->phba; @@ -949,8 +956,9 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, struct lpfc_nodelist *ndlp; struct lpfc_nvme_fcpreq_priv *freqpriv; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; unsigned long flags; - uint32_t code, status; + uint32_t code, status, idx; uint16_t cid, sqhd, data; uint32_t *ptr; @@ -961,16 +969,20 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, wcqe); return; } - atomic_inc(&phba->fc4NvmeIoCmpls); - nCmd = lpfc_ncmd->nvmeCmd; rport = lpfc_ncmd->nrport; status = bf_get(lpfc_wcqe_c_status, wcqe); - if (status) { - lport = (struct lpfc_nvme_lport *)vport->localport->private; - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_fcp_xb); - atomic_inc(&lport->cmpl_fcp_err); + + lport = (struct lpfc_nvme_lport *)vport->localport->private; + if (lport) { + idx = lpfc_ncmd->cur_iocbq.hba_wqidx; + cstat = &lport->cstat[idx]; + atomic_inc(&cstat->fc4NvmeIoCmpls); + if (status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_fcp_xb); + atomic_inc(&lport->cmpl_fcp_err); + } } lpfc_nvmeio_data(phba, "NVME FCP CMPL: xri x%x stat x%x parm x%x\n", @@ -1163,7 +1175,8 @@ out_err: static int lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, struct lpfc_nvme_buf *lpfc_ncmd, - struct lpfc_nodelist *pnode) + struct lpfc_nodelist *pnode, + struct lpfc_nvme_ctrl_stat *cstat) { struct lpfc_hba *phba = vport->phba; struct nvmefc_fcp_req *nCmd = lpfc_ncmd->nvmeCmd; @@ -1201,7 +1214,7 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, } else { wqe->fcp_iwrite.initial_xfer_len = 0; } - atomic_inc(&phba->fc4NvmeOutputRequests); + atomic_inc(&cstat->fc4NvmeOutputRequests); } else { /* From the iread template, initialize words 7 - 11 */ memcpy(&wqe->words[7], @@ -1214,13 +1227,13 @@ lpfc_nvme_prep_io_cmd(struct lpfc_vport *vport, /* Word 5 */ wqe->fcp_iread.rsrvd5 = 0; - atomic_inc(&phba->fc4NvmeInputRequests); + atomic_inc(&cstat->fc4NvmeInputRequests); } } else { /* From the icmnd template, initialize words 4 - 11 */ memcpy(&wqe->words[4], &lpfc_icmnd_cmd_template.words[4], sizeof(uint32_t) * 8); - atomic_inc(&phba->fc4NvmeControlRequests); + atomic_inc(&cstat->fc4NvmeControlRequests); } /* * Finish initializing those WQE fields that are independent @@ -1400,7 +1413,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, { int ret = 0; int expedite = 0; + int idx; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_vport *vport; struct lpfc_hba *phba; struct lpfc_nodelist *ndlp; @@ -1543,15 +1558,6 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, lpfc_ncmd->ndlp = ndlp; lpfc_ncmd->start_time = jiffies; - lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp); - ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); - if (ret) { - ret = -ENOMEM; - goto out_free_nvme_buf; - } - - atomic_inc(&ndlp->cmd_pending); - /* * Issue the IO on the WQ indicated by index in the hw_queue_handle. * This identfier was create in our hardware queue create callback @@ -1560,7 +1566,18 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, * index to use and that they have affinitized a CPU to this hardware * queue. A hardware queue maps to a driver MSI-X vector/EQ/CQ/WQ. */ - lpfc_ncmd->cur_iocbq.hba_wqidx = lpfc_queue_info->index; + idx = lpfc_queue_info->index; + lpfc_ncmd->cur_iocbq.hba_wqidx = idx; + cstat = &lport->cstat[idx]; + + lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp, cstat); + ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); + if (ret) { + ret = -ENOMEM; + goto out_free_nvme_buf; + } + + atomic_inc(&ndlp->cmd_pending); lpfc_nvmeio_data(phba, "NVME FCP XMIT: xri x%x idx %d to %06x\n", lpfc_ncmd->cur_iocbq.sli4_xritag, @@ -1605,11 +1622,11 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, out_free_nvme_buf: if (lpfc_ncmd->nvmeCmd->sg_cnt) { if (lpfc_ncmd->nvmeCmd->io_dir == NVMEFC_FCP_WRITE) - atomic_dec(&phba->fc4NvmeOutputRequests); + atomic_dec(&cstat->fc4NvmeOutputRequests); else - atomic_dec(&phba->fc4NvmeInputRequests); + atomic_dec(&cstat->fc4NvmeInputRequests); } else - atomic_dec(&phba->fc4NvmeControlRequests); + atomic_dec(&cstat->fc4NvmeControlRequests); lpfc_release_nvme_buf(phba, lpfc_ncmd); out_fail: return ret; @@ -2390,7 +2407,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) struct nvme_fc_port_info nfcp_info; struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; - int len; + struct lpfc_nvme_ctrl_stat *cstat; + int len, i; /* Initialize this localport instance. The vport wwn usage ensures * that NPIV is accounted for. @@ -2414,6 +2432,11 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) lpfc_nvme_template.max_sgl_segments = phba->cfg_nvme_seg_cnt + 1; lpfc_nvme_template.max_hw_queues = phba->cfg_nvme_io_channel; + cstat = kmalloc((sizeof(struct lpfc_nvme_ctrl_stat) * + phba->cfg_nvme_io_channel), GFP_KERNEL); + if (!cstat) + return -ENOMEM; + /* localport is allocated from the stack, but the registration * call allocates heap memory as well as the private area. */ @@ -2436,6 +2459,7 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) lport = (struct lpfc_nvme_lport *)localport->private; vport->localport = localport; lport->vport = vport; + lport->cstat = cstat; vport->nvmei_support = 1; atomic_set(&lport->xmt_fcp_noxri, 0); @@ -2449,6 +2473,16 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) atomic_set(&lport->cmpl_fcp_err, 0); atomic_set(&lport->cmpl_ls_xb, 0); atomic_set(&lport->cmpl_ls_err, 0); + atomic_set(&lport->fc4NvmeLsRequests, 0); + atomic_set(&lport->fc4NvmeLsCmpls, 0); + + for (i = 0; i < phba->cfg_nvme_io_channel; i++) { + cstat = &lport->cstat[i]; + atomic_set(&cstat->fc4NvmeInputRequests, 0); + atomic_set(&cstat->fc4NvmeOutputRequests, 0); + atomic_set(&cstat->fc4NvmeControlRequests, 0); + atomic_set(&cstat->fc4NvmeIoCmpls, 0); + } /* Don't post more new bufs if repost already recovered * the nvme sgls. @@ -2458,6 +2492,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) phba->sli4_hba.nvme_xri_max); vport->phba->total_nvme_bufs += len; } + } else { + kfree(cstat); } return ret; @@ -2520,6 +2556,7 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) #if (IS_ENABLED(CONFIG_NVME_FC)) struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_ctrl_stat *cstat; int ret; if (vport->nvmei_support == 0) @@ -2528,6 +2565,7 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) localport = vport->localport; vport->localport = NULL; lport = (struct lpfc_nvme_lport *)localport->private; + cstat = lport->cstat; lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME, "6011 Destroying NVME localport %p\n", @@ -2543,6 +2581,7 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) * indefinitely or succeeds */ lpfc_nvme_lport_unreg_wait(vport, lport); + kfree(cstat); /* Regardless of the unregister upcall response, clear * nvmei_support. All rports are unregistered and the diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 9216653e0441..b07188b533fb 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -36,11 +36,21 @@ struct lpfc_nvme_qhandle { uint32_t cpu_id; /* current cpu id at time of create */ }; +struct lpfc_nvme_ctrl_stat { + atomic_t fc4NvmeInputRequests; + atomic_t fc4NvmeOutputRequests; + atomic_t fc4NvmeControlRequests; + atomic_t fc4NvmeIoCmpls; +}; + /* Declare nvme-based local and remote port definitions. */ struct lpfc_nvme_lport { struct lpfc_vport *vport; struct completion lport_unreg_done; /* Add stats counters here */ + struct lpfc_nvme_ctrl_stat *cstat; + atomic_t fc4NvmeLsRequests; + atomic_t fc4NvmeLsCmpls; atomic_t xmt_fcp_noxri; atomic_t xmt_fcp_bad_ndlp; atomic_t xmt_fcp_qdepth; -- cgit v1.2.3 From 2448e484259debb1cb8f00a06a8a0a2c6edd9d80 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:24 -0700 Subject: scsi: lpfc: Enlarge nvmet asynchronous receive buffer counts Under large io load, the current sizing of asynchronous buffer counts could be exceeded, indicated by a 2885 log message: 2885 Port Status Event: port status reg 0x81800000, port smphr reg 0xc000, error 1=0x52004a01, error 2=0x0 Enlarge the async receive queue size. Allow for a configurable number of buffers to be posted to each RQ, using the new attribute lpfc_nvmet_mrq_post. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc.h | 1 + drivers/scsi/lpfc/lpfc_attr.c | 11 +++++++++++ drivers/scsi/lpfc/lpfc_nvmet.h | 6 ++++-- drivers/scsi/lpfc/lpfc_sli.c | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 2b47c69c1732..20b249a649dd 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -782,6 +782,7 @@ struct lpfc_hba { uint32_t cfg_nvme_oas; uint32_t cfg_nvme_embed_cmd; uint32_t cfg_nvme_io_channel; + uint32_t cfg_nvmet_mrq_post; uint32_t cfg_nvmet_mrq; uint32_t cfg_enable_nvmet; uint32_t cfg_nvme_enable_fb; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 3a6b1be18886..15f921d8ea56 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -3423,6 +3423,15 @@ LPFC_ATTR_R(nvmet_mrq, LPFC_NVMET_MRQ_AUTO, LPFC_NVMET_MRQ_AUTO, LPFC_NVMET_MRQ_MAX, "Specify number of RQ pairs for processing NVMET cmds"); +/* + * lpfc_nvmet_mrq_post: Specify number of RQ buffer to initially post + * to each NVMET RQ. Range 64 to 2048, default is 512. + */ +LPFC_ATTR_R(nvmet_mrq_post, + LPFC_NVMET_RQE_DEF_POST, LPFC_NVMET_RQE_MIN_POST, + LPFC_NVMET_RQE_DEF_COUNT, + "Specify number of RQ buffers to initially post"); + /* * lpfc_enable_fc4_type: Defines what FC4 types are supported. * Supported Values: 1 - register just FCP @@ -5353,6 +5362,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_lpfc_suppress_rsp, &dev_attr_lpfc_nvme_io_channel, &dev_attr_lpfc_nvmet_mrq, + &dev_attr_lpfc_nvmet_mrq_post, &dev_attr_lpfc_nvme_enable_fb, &dev_attr_lpfc_nvmet_fb_size, &dev_attr_lpfc_enable_bg, @@ -6403,6 +6413,7 @@ lpfc_get_cfgparam(struct lpfc_hba *phba) lpfc_enable_fc4_type_init(phba, lpfc_enable_fc4_type); lpfc_nvmet_mrq_init(phba, lpfc_nvmet_mrq); + lpfc_nvmet_mrq_post_init(phba, lpfc_nvmet_mrq_post); /* Initialize first burst. Target vs Initiator are different. */ lpfc_nvme_enable_fb_init(phba, lpfc_nvme_enable_fb); diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index c1bcef3f103c..81f520abfd64 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -22,8 +22,10 @@ ********************************************************************/ #define LPFC_NVMET_DEFAULT_SEGS (64 + 1) /* 256K IOs */ -#define LPFC_NVMET_RQE_DEF_COUNT 512 -#define LPFC_NVMET_SUCCESS_LEN 12 +#define LPFC_NVMET_RQE_MIN_POST 128 +#define LPFC_NVMET_RQE_DEF_POST 512 +#define LPFC_NVMET_RQE_DEF_COUNT 2048 +#define LPFC_NVMET_SUCCESS_LEN 12 #define LPFC_NVMET_MRQ_OFF 0xffff #define LPFC_NVMET_MRQ_AUTO 0 diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index cb17e2b2be81..e0a8c8008195 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -7199,7 +7199,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba) lpfc_post_rq_buffer( phba, phba->sli4_hba.nvmet_mrq_hdr[i], phba->sli4_hba.nvmet_mrq_data[i], - LPFC_NVMET_RQE_DEF_COUNT, i); + phba->cfg_nvmet_mrq_post, i); } } -- cgit v1.2.3 From 59c68eaad7303f9f1d42a83b223aa6a2c254aaa0 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:25 -0700 Subject: scsi: lpfc: Fix Abort request WQ selection When running loads that generated aborts, io errors where seen. Turns out the abort requests where not placed on the proper WQ resulting in the errors. Closer inspection inspection of this error also showed improper spinlock api use. Correct the WQ selection policy for the abort requests. Correct spin_lock/spin_lock_irq/spin_lock_irqsave usage. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_scsi.c | 12 ++++++------ drivers/scsi/lpfc/lpfc_sli.c | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 8570486013f3..7932bf30c8d7 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1021,7 +1021,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) if (lpfc_test_rrq_active(phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; - list_del(&lpfc_cmd->list); + list_del_init(&lpfc_cmd->list); found = 1; break; } @@ -1036,7 +1036,7 @@ lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) if (lpfc_test_rrq_active( phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; - list_del(&lpfc_cmd->list); + list_del_init(&lpfc_cmd->list); found = 1; break; } @@ -4716,7 +4716,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) int ret = SUCCESS, status = 0; struct lpfc_sli_ring *pring_s4; int ret_val; - unsigned long flags, iflags; + unsigned long flags; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq); status = fc_block_scsi_eh(cmnd); @@ -4816,16 +4816,16 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; abtsiocb->vport = vport; if (phba->sli_rev == LPFC_SLI_REV4) { - pring_s4 = lpfc_sli4_calc_ring(phba, iocb); + pring_s4 = lpfc_sli4_calc_ring(phba, abtsiocb); if (pring_s4 == NULL) { ret = FAILED; goto out_unlock; } /* Note: both hbalock and ring_lock must be set here */ - spin_lock_irqsave(&pring_s4->ring_lock, iflags); + spin_lock(&pring_s4->ring_lock); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, abtsiocb, 0); - spin_unlock_irqrestore(&pring_s4->ring_lock, iflags); + spin_unlock(&pring_s4->ring_lock); } else { ret_val = __lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0); diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index e0a8c8008195..38993efbe37e 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -11300,11 +11300,11 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, unsigned long iflags; struct lpfc_sli_ring *pring_s4; - spin_lock_irq(&phba->hbalock); + spin_lock_irqsave(&phba->hbalock, iflags); /* all I/Os are in process of being flushed */ if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflags); return 0; } sum = 0; @@ -11366,14 +11366,14 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, iocbq->iocb_flag |= LPFC_DRIVER_ABORTED; if (phba->sli_rev == LPFC_SLI_REV4) { - pring_s4 = lpfc_sli4_calc_ring(phba, iocbq); - if (pring_s4 == NULL) + pring_s4 = lpfc_sli4_calc_ring(phba, abtsiocbq); + if (!pring_s4) continue; /* Note: both hbalock and ring_lock must be set here */ - spin_lock_irqsave(&pring_s4->ring_lock, iflags); + spin_lock(&pring_s4->ring_lock); ret_val = __lpfc_sli_issue_iocb(phba, pring_s4->ringno, abtsiocbq, 0); - spin_unlock_irqrestore(&pring_s4->ring_lock, iflags); + spin_unlock(&pring_s4->ring_lock); } else { ret_val = __lpfc_sli_issue_iocb(phba, pring->ringno, abtsiocbq, 0); @@ -11385,7 +11385,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, else sum++; } - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, iflags); return sum; } -- cgit v1.2.3 From 0cdb84ec26e455326a8ee1b7c69ce1c281ba38cb Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:26 -0700 Subject: scsi: lpfc: Fix lingering lpfc_wq resource after driver unload After driver unloads, lpfc_wq remains active. The destroy_workqueue calls were not being made in driver unload. Additionally, SLI3 is allocating lpfc_wq resources, but never uses it. Make the destroy_workqueue calls on driver unload. Modify the SLI3 code path no longer allocate lpfc_wq resources. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4add398ec9cf..8dac676a46db 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -6420,8 +6420,11 @@ lpfc_setup_driver_resource_phase2(struct lpfc_hba *phba) return error; } - /* workqueue for deferred irq use */ - phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0); + /* The lpfc_wq workqueue for deferred irq use, is only used for SLI4 */ + if (phba->sli_rev == LPFC_SLI_REV4) + phba->wq = alloc_workqueue("lpfc_wq", WQ_MEM_RECLAIM, 0); + else + phba->wq = NULL; return 0; } @@ -6444,7 +6447,8 @@ lpfc_unset_driver_resource_phase2(struct lpfc_hba *phba) } /* Stop kernel worker thread */ - kthread_stop(phba->worker_thread); + if (phba->worker_thread) + kthread_stop(phba->worker_thread); } /** @@ -11727,6 +11731,7 @@ lpfc_pci_remove_one_s4(struct pci_dev *pdev) lpfc_nvme_free(phba); lpfc_free_iocb_list(phba); + lpfc_unset_driver_resource_phase2(phba); lpfc_sli4_driver_resource_unset(phba); /* Unmap adapter Control and Doorbell registers */ -- cgit v1.2.3 From 01466024d2de1c05652d69411461e8e7908f0d1e Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:27 -0700 Subject: scsi: lpfc: Fix NULL pointer access in lpfc_nvme_info_show After making remoteport unregister requests, the ndlp nrport pointer was stale. Track when waiting for waiting for unregister completion callback and adjust nldp pointer assignment. Add a few safety checks for NULL pointer values. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 16 ++++++++++++---- drivers/scsi/lpfc/lpfc_debugfs.c | 8 ++++++-- drivers/scsi/lpfc/lpfc_nvme.c | 13 +++++++++---- drivers/scsi/lpfc/lpfc_nvme.h | 4 ++++ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 15f921d8ea56..fd3b25317887 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -149,6 +149,7 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, struct lpfc_nvmet_tgtport *tgtp; struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; + struct lpfc_nvme_rport *rport; struct lpfc_nodelist *ndlp; struct nvme_fc_remote_port *nrport; struct lpfc_nvme_ctrl_stat *cstat; @@ -312,11 +313,14 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, localport->port_id, statep); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { - if (!ndlp->nrport) + rport = lpfc_ndlp_get_nrport(ndlp); + if (!rport) continue; /* local short-hand pointer. */ - nrport = ndlp->nrport->remoteport; + nrport = rport->remoteport; + if (!nrport) + continue; /* Port state is only one of two values for now. */ switch (nrport->port_state) { @@ -3290,6 +3294,9 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport) { struct Scsi_Host *shost; struct lpfc_nodelist *ndlp; +#if (IS_ENABLED(CONFIG_NVME_FC)) + struct lpfc_nvme_rport *rport; +#endif shost = lpfc_shost_from_vport(vport); spin_lock_irq(shost->host_lock); @@ -3299,8 +3306,9 @@ lpfc_update_rport_devloss_tmo(struct lpfc_vport *vport) if (ndlp->rport) ndlp->rport->dev_loss_tmo = vport->cfg_devloss_tmo; #if (IS_ENABLED(CONFIG_NVME_FC)) - if (ndlp->nrport) - nvme_fc_set_remoteport_devloss(ndlp->nrport->remoteport, + rport = lpfc_ndlp_get_nrport(ndlp); + if (rport) + nvme_fc_set_remoteport_devloss(rport->remoteport, vport->cfg_devloss_tmo); #endif } diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index cd3eb6b71398..afe7883c988a 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -552,6 +552,7 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) struct nvme_fc_local_port *localport; struct lpfc_nvmet_tgtport *tgtp; struct nvme_fc_remote_port *nrport; + struct lpfc_nvme_rport *rport; cnt = (LPFC_NODELIST_SIZE / LPFC_NODELIST_ENTRY_SIZE); outio = 0; @@ -695,10 +696,13 @@ lpfc_debugfs_nodelist_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf + len, size - len, "\tRport List:\n"); list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) { /* local short-hand pointer. */ - if (!ndlp->nrport) + rport = lpfc_ndlp_get_nrport(ndlp); + if (!rport) continue; - nrport = ndlp->nrport->remoteport; + nrport = rport->remoteport; + if (!nrport) + continue; /* Port state is only one of two values for now. */ switch (nrport->port_state) { diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 1414c581c0b6..1cb2c634e9f7 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -335,6 +335,7 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) remoteport); spin_lock_irq(&vport->phba->hbalock); ndlp->nrport = NULL; + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; spin_unlock_irq(&vport->phba->hbalock); /* Remove original register reference. The host transport @@ -2646,6 +2647,7 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) struct nvme_fc_local_port *localport; struct lpfc_nvme_lport *lport; struct lpfc_nvme_rport *rport; + struct lpfc_nvme_rport *oldrport; struct nvme_fc_remote_port *remote_port; struct nvme_fc_port_info rpinfo; struct lpfc_nodelist *prev_ndlp; @@ -2678,7 +2680,9 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) rpinfo.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); rpinfo.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); - if (!ndlp->nrport) + + oldrport = lpfc_ndlp_get_nrport(ndlp); + if (!oldrport) lpfc_nlp_get(ndlp); ret = nvme_fc_register_remoteport(localport, &rpinfo, &remote_port); @@ -2688,8 +2692,8 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * new rport. */ rport = remote_port->private; - if (ndlp->nrport) { - if (ndlp->nrport == remote_port->private) { + if (oldrport) { + if (oldrport == remote_port->private) { /* Same remoteport. Just reuse. */ lpfc_printf_vlog(ndlp->vport, KERN_INFO, LOG_NVME_DISC, @@ -2713,6 +2717,7 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) */ spin_lock_irq(&vport->phba->hbalock); ndlp->nrport = NULL; + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; spin_unlock_irq(&vport->phba->hbalock); rport->ndlp = NULL; rport->remoteport = NULL; @@ -2785,7 +2790,7 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) if (!lport) goto input_err; - rport = ndlp->nrport; + rport = lpfc_ndlp_get_nrport(ndlp); if (!rport) goto input_err; diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index b07188b533fb..53236974f2dd 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -30,6 +30,10 @@ #define LPFC_NVME_FB_SHIFT 9 #define LPFC_NVME_MAX_FB (1 << 20) /* 1M */ +#define lpfc_ndlp_get_nrport(ndlp) \ + ((!ndlp->nrport || (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG)) \ + ? NULL : ndlp->nrport) + struct lpfc_nvme_qhandle { uint32_t index; /* WQ index to use */ uint32_t qidx; /* queue index passed to create */ -- cgit v1.2.3 From bf316c78517d9437656293f65a70d6ecdc2ec58e Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:28 -0700 Subject: scsi: lpfc: Fix WQ/CQ creation for older asic's. The patch to enlarge WQ/CQ creation keys off of an adapter response that indicates support for the larger values. Older adapters return an incorrect response and are limited in size. Thus the adapters fail the WQ creation steps. Augment the WQ sizing checks with a check on the older adapter types and limit them to the restricted sizes. Fixes: c176ffa0841c ("scsi: lpfc: Increase CQ and WQ sizes for SCSI") Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hw4.h | 12 ++++++++++++ drivers/scsi/lpfc/lpfc_init.c | 15 +++++++++++++++ drivers/scsi/lpfc/lpfc_sli4.h | 1 + 3 files changed, 28 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 98b80559c215..9df1c8da6f52 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -104,6 +104,17 @@ struct lpfc_sli_intf { #define LPFC_SLI_INTF_IF_TYPE_VIRT 1 }; +struct lpfc_sli_asic_rev { + u32 word0; +#define LPFC_SLI_ASIC_VER_A 0x0 +#define LPFC_SLI_ASIC_VER_B 0x1 +#define LPFC_SLI_ASIC_VER_C 0x2 +#define LPFC_SLI_ASIC_VER_D 0x3 +#define lpfc_sli_asic_ver_SHIFT 4 +#define lpfc_sli_asic_ver_MASK 0x0000000F +#define lpfc_sli_asic_ver_WORD word0 +}; + #define LPFC_SLI4_MBX_EMBED true #define LPFC_SLI4_MBX_NEMBED false @@ -566,6 +577,7 @@ struct lpfc_register { /* The following BAR0 register sets are defined for if_type 0 and 2 UCNAs. */ #define LPFC_SLI_INTF 0x0058 +#define LPFC_SLI_ASIC_VER 0x009C #define LPFC_CTL_PORT_SEM_OFFSET 0x400 #define lpfc_port_smphr_perr_SHIFT 31 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 8dac676a46db..060f0e2f6ff5 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -9514,6 +9514,11 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) return error; } + if (pci_read_config_dword(pdev, LPFC_SLI_ASIC_VER, + &phba->sli4_hba.sli_asic_ver.word0)) { + return error; + } + /* There is no SLI3 failback for SLI4 devices. */ if (bf_get(lpfc_sli_intf_valid, &phba->sli4_hba.sli_intf) != LPFC_SLI_INTF_VALID) { @@ -10545,6 +10550,7 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) struct lpfc_pc_sli4_params *sli4_params; uint32_t mbox_tmo; int length; + bool exp_wqcq_pages = true; struct lpfc_sli4_parameters *mbx_sli4_parameters; /* @@ -10671,8 +10677,17 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->nvme_support, phba->nvme_embed_pbde, phba->cfg_nvme_embed_cmd, phba->cfg_suppress_rsp); + if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == + LPFC_SLI_INTF_IF_TYPE_2) && + (bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf) == + LPFC_SLI_INTF_FAMILY_LNCR_A0) && + (bf_get(lpfc_sli_asic_ver, &phba->sli4_hba.sli_asic_ver) == + LPFC_SLI_ASIC_VER_A)) + exp_wqcq_pages = false; + if ((bf_get(cfg_cqpsize, mbx_sli4_parameters) & LPFC_CQ_16K_PAGE_SZ) && (bf_get(cfg_wqpsize, mbx_sli4_parameters) & LPFC_WQ_16K_PAGE_SZ) && + exp_wqcq_pages && (sli4_params->wqsize & LPFC_WQ_SZ128_SUPPORT)) phba->enab_exp_wqcq_pages = 1; else diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index cf64aca82bd0..179e870a00b4 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -592,6 +592,7 @@ struct lpfc_sli4_hba { uint32_t ue_to_sr; uint32_t ue_to_rp; struct lpfc_register sli_intf; + struct lpfc_register sli_asic_ver; struct lpfc_pc_sli4_params pc_sli4_params; struct lpfc_bbscn_params bbscn_params; struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */ -- cgit v1.2.3 From b04744ce52a8da883c8b87b66082f9805bb4ca32 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:29 -0700 Subject: scsi: lpfc: Fix driver not recovering NVME rports during target link faults During target-side port faults, the driver would not recover all target port logins. This resulted in a loss of nvme device discovery. The driver is coded to wait for all GID_FT requests to complete before restarting discovery. A fault is seen where the outstanding GIT_FT counts are not properly decremented, thus discovery would never start. Another fault was found in the clearing of the gidft_inp counter that would be skipped in this condition. And a third fault found with lpfc_nvme_register_port that would remove a reverence on the ndlp which then allows a node swap on a port address change to prematurely remove the reference and release the ndlp. The following changes are made: - Correct the decrementing of the outstanding GID_FT counters. - In RSCN handling, no longer zero the counter before calling to issue another GID_FT. - No longer remove the reference on the dlp when the ndlp->nrport value is not yet null. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_ct.c | 5 +++++ drivers/scsi/lpfc/lpfc_els.c | 1 - drivers/scsi/lpfc/lpfc_nvme.c | 12 ++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 0617c8ea88c6..1e7889e45160 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -691,6 +691,11 @@ lpfc_cmpl_ct_cmd_gid_ft(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, vport->fc_flag &= ~FC_RSCN_DEFERRED; spin_unlock_irq(shost->host_lock); + /* This is a GID_FT completing so the gidft_inp counter was + * incremented before the GID_FT was issued to the wire. + */ + vport->gidft_inp--; + /* * Skip processing the NS response * Re-issue the NS cmd diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 74895e62aaea..6d84a10fef07 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -6268,7 +6268,6 @@ lpfc_els_handle_rscn(struct lpfc_vport *vport) * flush the RSCN. Otherwise, the outstanding requests * need to complete. */ - vport->gidft_inp = 0; if (lpfc_issue_gidft(vport) > 0) return 1; } else { diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 1cb2c634e9f7..22962b08c275 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -2721,8 +2721,16 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) spin_unlock_irq(&vport->phba->hbalock); rport->ndlp = NULL; rport->remoteport = NULL; - if (prev_ndlp) - lpfc_nlp_put(ndlp); + + /* Reference only removed if previous NDLP is no longer + * active. It might be just a swap and removing the + * reference would cause a premature cleanup. + */ + if (prev_ndlp && prev_ndlp != ndlp) { + if ((!NLP_CHK_NODE_ACT(prev_ndlp)) || + (!prev_ndlp->nrport)) + lpfc_nlp_put(prev_ndlp); + } } /* Clean bind the rport to the ndlp. */ -- cgit v1.2.3 From b15bd3e6212e747ebd1b37a542898e88ad05bb17 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:30 -0700 Subject: scsi: lpfc: Fix nvme remoteport registration race conditions On tests adding and removing a remote port, calls to nvme_info would eventually show fewer target ports discovered than were present in the san. Additionally, the following error messages were seen: 6031 RemotePort Registration failed err: -116, DID x471301 There is a race condition that exists between the driver and the nvme transport on remote port unregister vs the confirmed deletion. It's possible that the driver may rediscover the remote port and reregister the remote port before a prior unregister delete callback was made (as it rebinded to the prior remoteport structure). However, the driver was coded to expect the callback before seeing the remote port again thus a new registration. The logic results in the driver having an invalid remoteport pointer set. Correct by tracking when waiting for the delete callback. In cases where the ndlp remoteport pointer is updated, it is only cleared when the wait has not been superceded by a prior registration. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 22962b08c275..a0257478b63c 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -334,8 +334,14 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) "6146 remoteport delete of remoteport %p\n", remoteport); spin_lock_irq(&vport->phba->hbalock); - ndlp->nrport = NULL; - ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; + + /* The register rebind might have occurred before the delete + * downcall. Guard against this race. + */ + if (ndlp->upcall_flags & NLP_WAIT_FOR_UNREG) { + ndlp->nrport = NULL; + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; + } spin_unlock_irq(&vport->phba->hbalock); /* Remove original register reference. The host transport @@ -2691,6 +2697,12 @@ lpfc_nvme_register_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * a resume of the existing rport. Else this is a * new rport. */ + /* Guard against an unregister/reregister + * race that leaves the WAIT flag set. + */ + spin_lock_irq(&vport->phba->hbalock); + ndlp->upcall_flags &= ~NLP_WAIT_FOR_UNREG; + spin_unlock_irq(&vport->phba->hbalock); rport = remote_port->private; if (oldrport) { if (oldrport == remote_port->private) { -- cgit v1.2.3 From 66a85155d4851fe10e7c08c386507d92b45c0ec1 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:31 -0700 Subject: scsi: lpfc: Fix NULL pointer reference when resetting adapter Points referencing local port structures didn't accommodate cases where the localport may not be registered yet. Add NULL pointer checks to logic. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index a0257478b63c..9e0345697e1b 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -364,16 +364,18 @@ lpfc_nvme_cmpl_gen_req(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_dmabuf *buf_ptr; struct lpfc_nodelist *ndlp; - lport = (struct lpfc_nvme_lport *)vport->localport->private; pnvme_lsreq = (struct nvmefc_ls_req *)cmdwqe->context2; status = bf_get(lpfc_wcqe_c_status, wcqe) & LPFC_IOCB_STATUS_MASK; - if (lport) { - atomic_inc(&lport->fc4NvmeLsCmpls); - if (status) { - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_ls_xb); - atomic_inc(&lport->cmpl_ls_err); + if (vport->localport) { + lport = (struct lpfc_nvme_lport *)vport->localport->private; + if (lport) { + atomic_inc(&lport->fc4NvmeLsCmpls); + if (status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_ls_xb); + atomic_inc(&lport->cmpl_ls_err); + } } } @@ -980,15 +982,17 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, rport = lpfc_ncmd->nrport; status = bf_get(lpfc_wcqe_c_status, wcqe); - lport = (struct lpfc_nvme_lport *)vport->localport->private; - if (lport) { - idx = lpfc_ncmd->cur_iocbq.hba_wqidx; - cstat = &lport->cstat[idx]; - atomic_inc(&cstat->fc4NvmeIoCmpls); - if (status) { - if (bf_get(lpfc_wcqe_c_xb, wcqe)) - atomic_inc(&lport->cmpl_fcp_xb); - atomic_inc(&lport->cmpl_fcp_err); + if (vport->localport) { + lport = (struct lpfc_nvme_lport *)vport->localport->private; + if (lport) { + idx = lpfc_ncmd->cur_iocbq.hba_wqidx; + cstat = &lport->cstat[idx]; + atomic_inc(&cstat->fc4NvmeIoCmpls); + if (status) { + if (bf_get(lpfc_wcqe_c_xb, wcqe)) + atomic_inc(&lport->cmpl_fcp_xb); + atomic_inc(&lport->cmpl_fcp_err); + } } } -- cgit v1.2.3 From b0a00d8d2bb7b88980bad48f6458089878422d86 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:32 -0700 Subject: scsi: lpfc: Correct missing remoteport registration during link bounces Remote port disappearance/reappearances would cause a series of RSCN events to be delivered to the driver. During the resulting GID_FT handling, the driver clears the fc4 settings on the remote port, which makes it skip registration. As such, the nvme associations eventually fail and return io errors to the applications. Correct by not clearng the nlp_fc4_types for all nodes in lpfc_issue_gidft. Instead, when the GID_FT response is handled, clear the nlp_fc4_types of FCP and NVME prior to evaluating the fc4_type returned by the GID_FT response. This approach leaves "skipped" nodes with their nlp_fc4_types intacted. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_ct.c | 5 +++++ drivers/scsi/lpfc/lpfc_hbadisc.c | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 1e7889e45160..d4a200ae5a6f 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -471,6 +471,11 @@ lpfc_prep_node_fc4type(struct lpfc_vport *vport, uint32_t Did, uint8_t fc4_type) "Parse GID_FTrsp: did:x%x flg:x%x x%x", Did, ndlp->nlp_flag, vport->fc_flag); + /* Don't assume the rport is always the previous + * FC4 type. + */ + ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); + /* By default, the driver expects to support FCP FC4 */ if (fc4_type == FC_TYPE_FCP) ndlp->nlp_fc4_type |= NLP_FC4_FCP; diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 3e7712cd6c9a..cf2cbaa241b9 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -3876,10 +3876,6 @@ int lpfc_issue_gidft(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; - struct lpfc_nodelist *ndlp; - - list_for_each_entry(ndlp, &vport->fc_nodes, nlp_listp) - ndlp->nlp_fc4_type &= ~(NLP_FC4_FCP | NLP_FC4_NVME); /* Good status, issue CT Request to NameServer */ if ((phba->cfg_enable_fc4_type == LPFC_ENABLE_BOTH) || -- cgit v1.2.3 From 40e4a2e15c563c5663df19fa40cb638c29203ed8 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 9 Apr 2018 14:24:33 -0700 Subject: scsi: lpfc: update driver version to 12.0.0.2 Update the driver version to 12.0.0.2 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index e8b089abbfb3..0cd474bb0bdd 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "12.0.0.1" +#define LPFC_DRIVER_VERSION "12.0.0.2" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ -- cgit v1.2.3 From b15578ddaba437bf1a7bcc323069208bb92b5f86 Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Sat, 7 Apr 2018 00:15:07 +0200 Subject: scsi: devinfo: delete duplicate "Generic"/"USB Storage-SMC" device The revision field is currently unused by the devinfo pattern matching code. Combine two blacklist entries into one. $ egrep "Generic.*Storage-SMC" /proc/scsi/device_info 'Generic' 'USB Storage-SMC' 0x402 'Generic' 'USB Storage-SMC' 0x402 [mkp: tweaked commit desc] Cc: Hannes Reinecke Cc: Martin K. Petersen Cc: James E.J. Bottomley Cc: SCSI ML Signed-off-by: Xose Vazquez Perez Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index dd107dc4db0e..e5c82f13b7d9 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -168,8 +168,7 @@ static struct { {"easyRAID", "F8", NULL, BLIST_NOREPORTLUN}, {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36}, - {"Generic", "USB Storage-SMC", "0180", BLIST_FORCELUN | BLIST_INQUIRY_36}, - {"Generic", "USB Storage-SMC", "0207", BLIST_FORCELUN | BLIST_INQUIRY_36}, + {"Generic", "USB Storage-SMC", NULL, BLIST_FORCELUN | BLIST_INQUIRY_36}, /* FW: 0180 and 0207 */ {"HITACHI", "DF400", "*", BLIST_REPORTLUN2}, {"HITACHI", "DF500", "*", BLIST_REPORTLUN2}, {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_REPORTLUN2}, -- cgit v1.2.3 From 37b37d2609cb0ac267280ef27350b962d16d272e Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Sat, 7 Apr 2018 00:47:23 +0200 Subject: scsi: scsi_dh: replace too broad "TP9" string with the exact models SGI/TP9100 is not an RDAC array: ^^^ https://git.opensvc.com/gitweb.cgi?p=multipath-tools/.git;a=blob;f=libmultipath/hwtable.c;h=88b4700beb1d8940008020fbe4c3cd97d62f4a56;hb=HEAD#l235 This partially reverts commit 35204772ea03 ("[SCSI] scsi_dh_rdac : Consolidate rdac strings together") [mkp: fixed up the new entries to align with rest of struct] Cc: NetApp RDAC team Cc: Hannes Reinecke Cc: James E.J. Bottomley Cc: Martin K. Petersen Cc: SCSI ML Cc: DM ML Signed-off-by: Xose Vazquez Perez Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_dh.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c index 188f30572aa1..5a58cbf3a75d 100644 --- a/drivers/scsi/scsi_dh.c +++ b/drivers/scsi/scsi_dh.c @@ -58,7 +58,10 @@ static const struct scsi_dh_blist scsi_dh_blist[] = { {"IBM", "3526", "rdac", }, {"IBM", "3542", "rdac", }, {"IBM", "3552", "rdac", }, - {"SGI", "TP9", "rdac", }, + {"SGI", "TP9300", "rdac", }, + {"SGI", "TP9400", "rdac", }, + {"SGI", "TP9500", "rdac", }, + {"SGI", "TP9700", "rdac", }, {"SGI", "IS", "rdac", }, {"STK", "OPENstorage", "rdac", }, {"STK", "FLEXLINE 380", "rdac", }, -- cgit v1.2.3 From 3109e5ae0311e937d49a5325134e50b742ac5f4a Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Thu, 12 Apr 2018 13:53:26 +1200 Subject: scsi: zorro_esp: New driver for Amiga Zorro NCR53C9x boards New combined SCSI driver for all ESP based Zorro SCSI boards for m68k Amiga. Code largely based on board specific parts of the old drivers (blz1230.c, blz2060.c, cyberstorm.c, cyberstormII.c, fastlane.c which were removed after the 2.6 kernel series for lack of maintenance) with contributions by Tuomas Vainikka (TCQ bug tests and workaround) and Finn Thain (TCQ bugfix by use of PIO in extended message in transfer). New Kconfig option and Makefile entries for new Amiga Zorro ESP SCSI driver included in this patch. Use DMA transfers wherever possible, with board-specific DMA set-up functions copied from the old driver code. Three byte reselection messages do appear to cause DMA timeouts. So wire up a PIO transfer routine for these instead. esp_reselect_with_tag explicitly sets esp->cmd_block_dma as target address for the message bytes but PIO requires a virtual address. Substiute kernel virtual address esp->cmd_block in PIO transfer call if DMA address is esp->cmd_block_dma and phase is message in. PIO code taken from mac_esp.c where the reselection timeout issue was debugged and fixed first, with minor macro and function rename. Signed-off-by: Michael Schmitz Reviewed-by: Finn Thain Reviewed-by: Christoph Hellwig Tested-by: Christian T. Steigies Tested-by: John Paul Adrian Glaubitz Signed-off-by: Martin K. Petersen --- drivers/scsi/Kconfig | 14 + drivers/scsi/Makefile | 1 + drivers/scsi/zorro_esp.c | 1172 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1187 insertions(+) create mode 100644 drivers/scsi/zorro_esp.c diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 11e89e56b865..35c909bbf8ba 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1351,6 +1351,20 @@ config SCSI_ZORRO7XX accelerator card for the Amiga 1200, - the SCSI controller on the GVP Turbo 040/060 accelerator. +config SCSI_ZORRO_ESP + tristate "Zorro ESP SCSI support" + depends on ZORRO && SCSI + select SCSI_SPI_ATTRS + help + Support for various NCR53C9x (ESP) based SCSI controllers on Zorro + expansion boards for the Amiga. + This includes: + - the Phase5 Blizzard 1230 II and IV SCSI controllers, + - the Phase5 Blizzard 2060 SCSI controller, + - the Phase5 Blizzard Cyberstorm and Cyberstorm II SCSI + controllers, + - the Fastlane Zorro III SCSI controller. + config ATARI_SCSI tristate "Atari native SCSI support" depends on ATARI && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index e29f9b8fd66d..f31145d6d472 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o obj-$(CONFIG_ISCSI_BOOT_SYSFS) += iscsi_boot_sysfs.o obj-$(CONFIG_SCSI_A4000T) += 53c700.o a4000t.o obj-$(CONFIG_SCSI_ZORRO7XX) += 53c700.o zorro7xx.o +obj-$(CONFIG_SCSI_ZORRO_ESP) += esp_scsi.o zorro_esp.o obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o obj-$(CONFIG_GVP11_SCSI) += gvp11.o wd33c93.o diff --git a/drivers/scsi/zorro_esp.c b/drivers/scsi/zorro_esp.c new file mode 100644 index 000000000000..bb70882e6b56 --- /dev/null +++ b/drivers/scsi/zorro_esp.c @@ -0,0 +1,1172 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ESP front-end for Amiga ZORRO SCSI systems. + * + * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk) + * + * Copyright (C) 2011,2018 Michael Schmitz (schmitz@debian.org) for + * migration to ESP SCSI core + * + * Copyright (C) 2013 Tuomas Vainikka (tuomas.vainikka@aalto.fi) for + * Blizzard 1230 DMA and probe function fixes + * + * Copyright (C) 2017 Finn Thain for PIO code from Mac ESP driver adapted here + */ +/* + * ZORRO bus code from: + */ +/* + * Detection routine for the NCR53c710 based Amiga SCSI Controllers for Linux. + * Amiga MacroSystemUS WarpEngine SCSI controller. + * Amiga Technologies/DKB A4091 SCSI controller. + * + * Written 1997 by Alan Hourihane + * plus modifications of the 53c7xx.c driver to support the Amiga. + * + * Rewritten to use 53c700.c by Kars de Jong + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "esp_scsi.h" + +MODULE_AUTHOR("Michael Schmitz "); +MODULE_DESCRIPTION("Amiga Zorro NCR5C9x (ESP) driver"); +MODULE_LICENSE("GPL"); + +/* per-board register layout definitions */ + +/* Blizzard 1230 DMA interface */ + +struct blz1230_dma_registers { + unsigned char dma_addr; /* DMA address [0x0000] */ + unsigned char dmapad2[0x7fff]; + unsigned char dma_latch; /* DMA latch [0x8000] */ +}; + +/* Blizzard 1230II DMA interface */ + +struct blz1230II_dma_registers { + unsigned char dma_addr; /* DMA address [0x0000] */ + unsigned char dmapad2[0xf]; + unsigned char dma_latch; /* DMA latch [0x0010] */ +}; + +/* Blizzard 2060 DMA interface */ + +struct blz2060_dma_registers { + unsigned char dma_led_ctrl; /* DMA led control [0x000] */ + unsigned char dmapad1[0x0f]; + unsigned char dma_addr0; /* DMA address (MSB) [0x010] */ + unsigned char dmapad2[0x03]; + unsigned char dma_addr1; /* DMA address [0x014] */ + unsigned char dmapad3[0x03]; + unsigned char dma_addr2; /* DMA address [0x018] */ + unsigned char dmapad4[0x03]; + unsigned char dma_addr3; /* DMA address (LSB) [0x01c] */ +}; + +/* DMA control bits */ +#define DMA_WRITE 0x80000000 + +/* Cyberstorm DMA interface */ + +struct cyber_dma_registers { + unsigned char dma_addr0; /* DMA address (MSB) [0x000] */ + unsigned char dmapad1[1]; + unsigned char dma_addr1; /* DMA address [0x002] */ + unsigned char dmapad2[1]; + unsigned char dma_addr2; /* DMA address [0x004] */ + unsigned char dmapad3[1]; + unsigned char dma_addr3; /* DMA address (LSB) [0x006] */ + unsigned char dmapad4[0x3fb]; + unsigned char cond_reg; /* DMA cond (ro) [0x402] */ +#define ctrl_reg cond_reg /* DMA control (wo) [0x402] */ +}; + +/* DMA control bits */ +#define CYBER_DMA_WRITE 0x40 /* DMA direction. 1 = write */ +#define CYBER_DMA_Z3 0x20 /* 16 (Z2) or 32 (CHIP/Z3) bit DMA transfer */ + +/* DMA status bits */ +#define CYBER_DMA_HNDL_INTR 0x80 /* DMA IRQ pending? */ + +/* The CyberStorm II DMA interface */ +struct cyberII_dma_registers { + unsigned char cond_reg; /* DMA cond (ro) [0x000] */ +#define ctrl_reg cond_reg /* DMA control (wo) [0x000] */ + unsigned char dmapad4[0x3f]; + unsigned char dma_addr0; /* DMA address (MSB) [0x040] */ + unsigned char dmapad1[3]; + unsigned char dma_addr1; /* DMA address [0x044] */ + unsigned char dmapad2[3]; + unsigned char dma_addr2; /* DMA address [0x048] */ + unsigned char dmapad3[3]; + unsigned char dma_addr3; /* DMA address (LSB) [0x04c] */ +}; + +/* Fastlane DMA interface */ + +struct fastlane_dma_registers { + unsigned char cond_reg; /* DMA status (ro) [0x0000] */ +#define ctrl_reg cond_reg /* DMA control (wo) [0x0000] */ + char dmapad1[0x3f]; + unsigned char clear_strobe; /* DMA clear (wo) [0x0040] */ +}; + +/* + * The controller registers can be found in the Z2 config area at these + * offsets: + */ +#define FASTLANE_ESP_ADDR 0x1000001 + +/* DMA status bits */ +#define FASTLANE_DMA_MINT 0x80 +#define FASTLANE_DMA_IACT 0x40 +#define FASTLANE_DMA_CREQ 0x20 + +/* DMA control bits */ +#define FASTLANE_DMA_FCODE 0xa0 +#define FASTLANE_DMA_MASK 0xf3 +#define FASTLANE_DMA_WRITE 0x08 /* 1 = write */ +#define FASTLANE_DMA_ENABLE 0x04 /* Enable DMA */ +#define FASTLANE_DMA_EDI 0x02 /* Enable DMA IRQ ? */ +#define FASTLANE_DMA_ESI 0x01 /* Enable SCSI IRQ */ + +/* + * private data used for driver + */ +struct zorro_esp_priv { + struct esp *esp; /* our ESP instance - for Scsi_host* */ + void __iomem *board_base; /* virtual address (Zorro III board) */ + int error; /* PIO error flag */ + int zorro3; /* board is Zorro III */ + unsigned char ctrl_data; /* shadow copy of ctrl_reg */ +}; + +/* + * On all implementations except for the Oktagon, padding between ESP + * registers is three bytes. + * On Oktagon, it is one byte - use a different accessor there. + * + * Oktagon needs PDMA - currently unsupported! + */ + +static void zorro_esp_write8(struct esp *esp, u8 val, unsigned long reg) +{ + writeb(val, esp->regs + (reg * 4UL)); +} + +static u8 zorro_esp_read8(struct esp *esp, unsigned long reg) +{ + return readb(esp->regs + (reg * 4UL)); +} + +static dma_addr_t zorro_esp_map_single(struct esp *esp, void *buf, + size_t sz, int dir) +{ + return dma_map_single(esp->dev, buf, sz, dir); +} + +static int zorro_esp_map_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + return dma_map_sg(esp->dev, sg, num_sg, dir); +} + +static void zorro_esp_unmap_single(struct esp *esp, dma_addr_t addr, + size_t sz, int dir) +{ + dma_unmap_single(esp->dev, addr, sz, dir); +} + +static void zorro_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + dma_unmap_sg(esp->dev, sg, num_sg, dir); +} + +static int zorro_esp_irq_pending(struct esp *esp) +{ + /* check ESP status register; DMA has no status reg. */ + if (zorro_esp_read8(esp, ESP_STATUS) & ESP_STAT_INTR) + return 1; + + return 0; +} + +static int cyber_esp_irq_pending(struct esp *esp) +{ + struct cyber_dma_registers __iomem *dregs = esp->dma_regs; + unsigned char dma_status = readb(&dregs->cond_reg); + + /* It's important to check the DMA IRQ bit in the correct way! */ + return ((zorro_esp_read8(esp, ESP_STATUS) & ESP_STAT_INTR) && + (dma_status & CYBER_DMA_HNDL_INTR)); +} + +static int fastlane_esp_irq_pending(struct esp *esp) +{ + struct fastlane_dma_registers __iomem *dregs = esp->dma_regs; + unsigned char dma_status; + + dma_status = readb(&dregs->cond_reg); + + if (dma_status & FASTLANE_DMA_IACT) + return 0; /* not our IRQ */ + + /* Return non-zero if ESP requested IRQ */ + return ( + (dma_status & FASTLANE_DMA_CREQ) && + (!(dma_status & FASTLANE_DMA_MINT)) && + (zorro_esp_read8(esp, ESP_STATUS) & ESP_STAT_INTR)); +} + +static u32 zorro_esp_dma_length_limit(struct esp *esp, u32 dma_addr, + u32 dma_len) +{ + return dma_len > 0xFFFFFF ? 0xFFFFFF : dma_len; +} + +static void zorro_esp_reset_dma(struct esp *esp) +{ + /* nothing to do here */ +} + +static void zorro_esp_dma_drain(struct esp *esp) +{ + /* nothing to do here */ +} + +static void zorro_esp_dma_invalidate(struct esp *esp) +{ + /* nothing to do here */ +} + +static void fastlane_esp_dma_invalidate(struct esp *esp) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct fastlane_dma_registers __iomem *dregs = esp->dma_regs; + unsigned char *ctrl_data = &zep->ctrl_data; + + *ctrl_data = (*ctrl_data & FASTLANE_DMA_MASK); + writeb(0, &dregs->clear_strobe); + z_writel(0, zep->board_base); +} + +/* + * Programmed IO routines follow. + */ + +static inline unsigned int zorro_esp_wait_for_fifo(struct esp *esp) +{ + int i = 500000; + + do { + unsigned int fbytes = zorro_esp_read8(esp, ESP_FFLAGS) + & ESP_FF_FBYTES; + + if (fbytes) + return fbytes; + + udelay(2); + } while (--i); + + pr_err("FIFO is empty (sreg %02x)\n", + zorro_esp_read8(esp, ESP_STATUS)); + return 0; +} + +static inline int zorro_esp_wait_for_intr(struct esp *esp) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + int i = 500000; + + do { + esp->sreg = zorro_esp_read8(esp, ESP_STATUS); + if (esp->sreg & ESP_STAT_INTR) + return 0; + + udelay(2); + } while (--i); + + pr_err("IRQ timeout (sreg %02x)\n", esp->sreg); + zep->error = 1; + return 1; +} + +/* + * PIO macros as used in mac_esp.c. + * Note that addr and fifo arguments are local-scope variables declared + * in zorro_esp_send_pio_cmd(), the macros are only used in that function, + * and addr and fifo are referenced in each use of the macros so there + * is no need to pass them as macro parameters. + */ +#define ZORRO_ESP_PIO_LOOP(operands, reg1) \ + asm volatile ( \ + "1: moveb " operands "\n" \ + " subqw #1,%1 \n" \ + " jbne 1b \n" \ + : "+a" (addr), "+r" (reg1) \ + : "a" (fifo)); + +#define ZORRO_ESP_PIO_FILL(operands, reg1) \ + asm volatile ( \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " moveb " operands "\n" \ + " subqw #8,%1 \n" \ + " subqw #8,%1 \n" \ + : "+a" (addr), "+r" (reg1) \ + : "a" (fifo)); + +#define ZORRO_ESP_FIFO_SIZE 16 + +static void zorro_esp_send_pio_cmd(struct esp *esp, u32 addr, u32 esp_count, + u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + u8 __iomem *fifo = esp->regs + ESP_FDATA * 16; + u8 phase = esp->sreg & ESP_STAT_PMASK; + + cmd &= ~ESP_CMD_DMA; + + if (write) { + u8 *dst = (u8 *)addr; + u8 mask = ~(phase == ESP_MIP ? ESP_INTR_FDONE : ESP_INTR_BSERV); + + scsi_esp_cmd(esp, cmd); + + while (1) { + if (!zorro_esp_wait_for_fifo(esp)) + break; + + *dst++ = zorro_esp_read8(esp, ESP_FDATA); + --esp_count; + + if (!esp_count) + break; + + if (zorro_esp_wait_for_intr(esp)) + break; + + if ((esp->sreg & ESP_STAT_PMASK) != phase) + break; + + esp->ireg = zorro_esp_read8(esp, ESP_INTRPT); + if (esp->ireg & mask) { + zep->error = 1; + break; + } + + if (phase == ESP_MIP) + scsi_esp_cmd(esp, ESP_CMD_MOK); + + scsi_esp_cmd(esp, ESP_CMD_TI); + } + } else { /* unused, as long as we only handle MIP here */ + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + + if (esp_count >= ZORRO_ESP_FIFO_SIZE) + ZORRO_ESP_PIO_FILL("%0@+,%2@", esp_count) + else + ZORRO_ESP_PIO_LOOP("%0@+,%2@", esp_count) + + scsi_esp_cmd(esp, cmd); + + while (esp_count) { + unsigned int n; + + if (zorro_esp_wait_for_intr(esp)) + break; + + if ((esp->sreg & ESP_STAT_PMASK) != phase) + break; + + esp->ireg = zorro_esp_read8(esp, ESP_INTRPT); + if (esp->ireg & ~ESP_INTR_BSERV) { + zep->error = 1; + break; + } + + n = ZORRO_ESP_FIFO_SIZE - + (zorro_esp_read8(esp, ESP_FFLAGS) & ESP_FF_FBYTES); + if (n > esp_count) + n = esp_count; + + if (n == ZORRO_ESP_FIFO_SIZE) + ZORRO_ESP_PIO_FILL("%0@+,%2@", esp_count) + else { + esp_count -= n; + ZORRO_ESP_PIO_LOOP("%0@+,%2@", n) + } + + scsi_esp_cmd(esp, ESP_CMD_TI); + } + } +} + +/* Blizzard 1230/60 SCSI-IV DMA */ + +static void zorro_esp_send_blz1230_dma_cmd(struct esp *esp, u32 addr, + u32 esp_count, u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct blz1230_dma_registers __iomem *dregs = esp->dma_regs; + u8 phase = esp->sreg & ESP_STAT_PMASK; + + zep->error = 0; + /* + * Use PIO if transferring message bytes to esp->command_block_dma. + * PIO requires a virtual address, so substitute esp->command_block + * for addr. + */ + if (phase == ESP_MIP && addr == esp->command_block_dma) { + zorro_esp_send_pio_cmd(esp, (u32) esp->command_block, + esp_count, dma_count, write, cmd); + return; + } + + if (write) + /* DMA receive */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_FROM_DEVICE); + else + /* DMA send */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_TO_DEVICE); + + addr >>= 1; + if (write) + addr &= ~(DMA_WRITE); + else + addr |= DMA_WRITE; + + writeb((addr >> 24) & 0xff, &dregs->dma_latch); + writeb((addr >> 24) & 0xff, &dregs->dma_addr); + writeb((addr >> 16) & 0xff, &dregs->dma_addr); + writeb((addr >> 8) & 0xff, &dregs->dma_addr); + writeb(addr & 0xff, &dregs->dma_addr); + + scsi_esp_cmd(esp, ESP_CMD_DMA); + zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + scsi_esp_cmd(esp, cmd); +} + +/* Blizzard 1230-II DMA */ + +static void zorro_esp_send_blz1230II_dma_cmd(struct esp *esp, u32 addr, + u32 esp_count, u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct blz1230II_dma_registers __iomem *dregs = esp->dma_regs; + u8 phase = esp->sreg & ESP_STAT_PMASK; + + zep->error = 0; + /* Use PIO if transferring message bytes to esp->command_block_dma */ + if (phase == ESP_MIP && addr == esp->command_block_dma) { + zorro_esp_send_pio_cmd(esp, (u32) esp->command_block, + esp_count, dma_count, write, cmd); + return; + } + + if (write) + /* DMA receive */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_FROM_DEVICE); + else + /* DMA send */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_TO_DEVICE); + + addr >>= 1; + if (write) + addr &= ~(DMA_WRITE); + else + addr |= DMA_WRITE; + + writeb((addr >> 24) & 0xff, &dregs->dma_latch); + writeb((addr >> 16) & 0xff, &dregs->dma_addr); + writeb((addr >> 8) & 0xff, &dregs->dma_addr); + writeb(addr & 0xff, &dregs->dma_addr); + + scsi_esp_cmd(esp, ESP_CMD_DMA); + zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + scsi_esp_cmd(esp, cmd); +} + +/* Blizzard 2060 DMA */ + +static void zorro_esp_send_blz2060_dma_cmd(struct esp *esp, u32 addr, + u32 esp_count, u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct blz2060_dma_registers __iomem *dregs = esp->dma_regs; + u8 phase = esp->sreg & ESP_STAT_PMASK; + + zep->error = 0; + /* Use PIO if transferring message bytes to esp->command_block_dma */ + if (phase == ESP_MIP && addr == esp->command_block_dma) { + zorro_esp_send_pio_cmd(esp, (u32) esp->command_block, + esp_count, dma_count, write, cmd); + return; + } + + if (write) + /* DMA receive */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_FROM_DEVICE); + else + /* DMA send */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_TO_DEVICE); + + addr >>= 1; + if (write) + addr &= ~(DMA_WRITE); + else + addr |= DMA_WRITE; + + writeb(addr & 0xff, &dregs->dma_addr3); + writeb((addr >> 8) & 0xff, &dregs->dma_addr2); + writeb((addr >> 16) & 0xff, &dregs->dma_addr1); + writeb((addr >> 24) & 0xff, &dregs->dma_addr0); + + scsi_esp_cmd(esp, ESP_CMD_DMA); + zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + scsi_esp_cmd(esp, cmd); +} + +/* Cyberstorm I DMA */ + +static void zorro_esp_send_cyber_dma_cmd(struct esp *esp, u32 addr, + u32 esp_count, u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct cyber_dma_registers __iomem *dregs = esp->dma_regs; + u8 phase = esp->sreg & ESP_STAT_PMASK; + unsigned char *ctrl_data = &zep->ctrl_data; + + zep->error = 0; + /* Use PIO if transferring message bytes to esp->command_block_dma */ + if (phase == ESP_MIP && addr == esp->command_block_dma) { + zorro_esp_send_pio_cmd(esp, (u32) esp->command_block, + esp_count, dma_count, write, cmd); + return; + } + + zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + if (write) { + /* DMA receive */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_FROM_DEVICE); + addr &= ~(1); + } else { + /* DMA send */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_TO_DEVICE); + addr |= 1; + } + + writeb((addr >> 24) & 0xff, &dregs->dma_addr0); + writeb((addr >> 16) & 0xff, &dregs->dma_addr1); + writeb((addr >> 8) & 0xff, &dregs->dma_addr2); + writeb(addr & 0xff, &dregs->dma_addr3); + + if (write) + *ctrl_data &= ~(CYBER_DMA_WRITE); + else + *ctrl_data |= CYBER_DMA_WRITE; + + *ctrl_data &= ~(CYBER_DMA_Z3); /* Z2, do 16 bit DMA */ + + writeb(*ctrl_data, &dregs->ctrl_reg); + + scsi_esp_cmd(esp, cmd); +} + +/* Cyberstorm II DMA */ + +static void zorro_esp_send_cyberII_dma_cmd(struct esp *esp, u32 addr, + u32 esp_count, u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct cyberII_dma_registers __iomem *dregs = esp->dma_regs; + u8 phase = esp->sreg & ESP_STAT_PMASK; + + zep->error = 0; + /* Use PIO if transferring message bytes to esp->command_block_dma */ + if (phase == ESP_MIP && addr == esp->command_block_dma) { + zorro_esp_send_pio_cmd(esp, (u32) esp->command_block, + esp_count, dma_count, write, cmd); + return; + } + + zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + if (write) { + /* DMA receive */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_FROM_DEVICE); + addr &= ~(1); + } else { + /* DMA send */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_TO_DEVICE); + addr |= 1; + } + + writeb((addr >> 24) & 0xff, &dregs->dma_addr0); + writeb((addr >> 16) & 0xff, &dregs->dma_addr1); + writeb((addr >> 8) & 0xff, &dregs->dma_addr2); + writeb(addr & 0xff, &dregs->dma_addr3); + + scsi_esp_cmd(esp, cmd); +} + +/* Fastlane DMA */ + +static void zorro_esp_send_fastlane_dma_cmd(struct esp *esp, u32 addr, + u32 esp_count, u32 dma_count, int write, u8 cmd) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + struct fastlane_dma_registers __iomem *dregs = esp->dma_regs; + u8 phase = esp->sreg & ESP_STAT_PMASK; + unsigned char *ctrl_data = &zep->ctrl_data; + + zep->error = 0; + /* Use PIO if transferring message bytes to esp->command_block_dma */ + if (phase == ESP_MIP && addr == esp->command_block_dma) { + zorro_esp_send_pio_cmd(esp, (u32) esp->command_block, + esp_count, dma_count, write, cmd); + return; + } + + zorro_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + zorro_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + zorro_esp_write8(esp, (esp_count >> 16) & 0xff, ESP_TCHI); + + if (write) { + /* DMA receive */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_FROM_DEVICE); + addr &= ~(1); + } else { + /* DMA send */ + dma_sync_single_for_device(esp->dev, addr, esp_count, + DMA_TO_DEVICE); + addr |= 1; + } + + writeb(0, &dregs->clear_strobe); + z_writel(addr, ((addr & 0x00ffffff) + zep->board_base)); + + if (write) { + *ctrl_data = (*ctrl_data & FASTLANE_DMA_MASK) | + FASTLANE_DMA_ENABLE; + } else { + *ctrl_data = ((*ctrl_data & FASTLANE_DMA_MASK) | + FASTLANE_DMA_ENABLE | + FASTLANE_DMA_WRITE); + } + + writeb(*ctrl_data, &dregs->ctrl_reg); + + scsi_esp_cmd(esp, cmd); +} + +static int zorro_esp_dma_error(struct esp *esp) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(esp->dev); + + /* check for error in case we've been doing PIO */ + if (zep->error == 1) + return 1; + + /* do nothing - there seems to be no way to check for DMA errors */ + return 0; +} + +/* per-board ESP driver ops */ + +static const struct esp_driver_ops blz1230_esp_ops = { + .esp_write8 = zorro_esp_write8, + .esp_read8 = zorro_esp_read8, + .map_single = zorro_esp_map_single, + .map_sg = zorro_esp_map_sg, + .unmap_single = zorro_esp_unmap_single, + .unmap_sg = zorro_esp_unmap_sg, + .irq_pending = zorro_esp_irq_pending, + .dma_length_limit = zorro_esp_dma_length_limit, + .reset_dma = zorro_esp_reset_dma, + .dma_drain = zorro_esp_dma_drain, + .dma_invalidate = zorro_esp_dma_invalidate, + .send_dma_cmd = zorro_esp_send_blz1230_dma_cmd, + .dma_error = zorro_esp_dma_error, +}; + +static const struct esp_driver_ops blz1230II_esp_ops = { + .esp_write8 = zorro_esp_write8, + .esp_read8 = zorro_esp_read8, + .map_single = zorro_esp_map_single, + .map_sg = zorro_esp_map_sg, + .unmap_single = zorro_esp_unmap_single, + .unmap_sg = zorro_esp_unmap_sg, + .irq_pending = zorro_esp_irq_pending, + .dma_length_limit = zorro_esp_dma_length_limit, + .reset_dma = zorro_esp_reset_dma, + .dma_drain = zorro_esp_dma_drain, + .dma_invalidate = zorro_esp_dma_invalidate, + .send_dma_cmd = zorro_esp_send_blz1230II_dma_cmd, + .dma_error = zorro_esp_dma_error, +}; + +static const struct esp_driver_ops blz2060_esp_ops = { + .esp_write8 = zorro_esp_write8, + .esp_read8 = zorro_esp_read8, + .map_single = zorro_esp_map_single, + .map_sg = zorro_esp_map_sg, + .unmap_single = zorro_esp_unmap_single, + .unmap_sg = zorro_esp_unmap_sg, + .irq_pending = zorro_esp_irq_pending, + .dma_length_limit = zorro_esp_dma_length_limit, + .reset_dma = zorro_esp_reset_dma, + .dma_drain = zorro_esp_dma_drain, + .dma_invalidate = zorro_esp_dma_invalidate, + .send_dma_cmd = zorro_esp_send_blz2060_dma_cmd, + .dma_error = zorro_esp_dma_error, +}; + +static const struct esp_driver_ops cyber_esp_ops = { + .esp_write8 = zorro_esp_write8, + .esp_read8 = zorro_esp_read8, + .map_single = zorro_esp_map_single, + .map_sg = zorro_esp_map_sg, + .unmap_single = zorro_esp_unmap_single, + .unmap_sg = zorro_esp_unmap_sg, + .irq_pending = cyber_esp_irq_pending, + .dma_length_limit = zorro_esp_dma_length_limit, + .reset_dma = zorro_esp_reset_dma, + .dma_drain = zorro_esp_dma_drain, + .dma_invalidate = zorro_esp_dma_invalidate, + .send_dma_cmd = zorro_esp_send_cyber_dma_cmd, + .dma_error = zorro_esp_dma_error, +}; + +static const struct esp_driver_ops cyberII_esp_ops = { + .esp_write8 = zorro_esp_write8, + .esp_read8 = zorro_esp_read8, + .map_single = zorro_esp_map_single, + .map_sg = zorro_esp_map_sg, + .unmap_single = zorro_esp_unmap_single, + .unmap_sg = zorro_esp_unmap_sg, + .irq_pending = zorro_esp_irq_pending, + .dma_length_limit = zorro_esp_dma_length_limit, + .reset_dma = zorro_esp_reset_dma, + .dma_drain = zorro_esp_dma_drain, + .dma_invalidate = zorro_esp_dma_invalidate, + .send_dma_cmd = zorro_esp_send_cyberII_dma_cmd, + .dma_error = zorro_esp_dma_error, +}; + +static const struct esp_driver_ops fastlane_esp_ops = { + .esp_write8 = zorro_esp_write8, + .esp_read8 = zorro_esp_read8, + .map_single = zorro_esp_map_single, + .map_sg = zorro_esp_map_sg, + .unmap_single = zorro_esp_unmap_single, + .unmap_sg = zorro_esp_unmap_sg, + .irq_pending = fastlane_esp_irq_pending, + .dma_length_limit = zorro_esp_dma_length_limit, + .reset_dma = zorro_esp_reset_dma, + .dma_drain = zorro_esp_dma_drain, + .dma_invalidate = fastlane_esp_dma_invalidate, + .send_dma_cmd = zorro_esp_send_fastlane_dma_cmd, + .dma_error = zorro_esp_dma_error, +}; + +/* Zorro driver config data */ + +struct zorro_driver_data { + const char *name; + unsigned long offset; + unsigned long dma_offset; + int absolute; /* offset is absolute address */ + int scsi_option; + const struct esp_driver_ops *esp_ops; +}; + +/* board types */ + +enum { + ZORRO_BLZ1230, + ZORRO_BLZ1230II, + ZORRO_BLZ2060, + ZORRO_CYBER, + ZORRO_CYBERII, + ZORRO_FASTLANE, +}; + +/* per-board config data */ + +static const struct zorro_driver_data zorro_esp_boards[] = { + [ZORRO_BLZ1230] = { + .name = "Blizzard 1230", + .offset = 0x8000, + .dma_offset = 0x10000, + .scsi_option = 1, + .esp_ops = &blz1230_esp_ops, + }, + [ZORRO_BLZ1230II] = { + .name = "Blizzard 1230II", + .offset = 0x10000, + .dma_offset = 0x10021, + .scsi_option = 1, + .esp_ops = &blz1230II_esp_ops, + }, + [ZORRO_BLZ2060] = { + .name = "Blizzard 2060", + .offset = 0x1ff00, + .dma_offset = 0x1ffe0, + .esp_ops = &blz2060_esp_ops, + }, + [ZORRO_CYBER] = { + .name = "CyberStormI", + .offset = 0xf400, + .dma_offset = 0xf800, + .esp_ops = &cyber_esp_ops, + }, + [ZORRO_CYBERII] = { + .name = "CyberStormII", + .offset = 0x1ff03, + .dma_offset = 0x1ff43, + .scsi_option = 1, + .esp_ops = &cyberII_esp_ops, + }, + [ZORRO_FASTLANE] = { + .name = "Fastlane", + .offset = 0x1000001, + .dma_offset = 0x1000041, + .esp_ops = &fastlane_esp_ops, + }, +}; + +static const struct zorro_device_id zorro_esp_zorro_tbl[] = { + { /* Blizzard 1230 IV */ + .id = ZORRO_ID(PHASE5, 0x11, 0), + .driver_data = ZORRO_BLZ1230, + }, + { /* Blizzard 1230 II (Zorro II) or Fastlane (Zorro III) */ + .id = ZORRO_ID(PHASE5, 0x0B, 0), + .driver_data = ZORRO_BLZ1230II, + }, + { /* Blizzard 2060 */ + .id = ZORRO_ID(PHASE5, 0x18, 0), + .driver_data = ZORRO_BLZ2060, + }, + { /* Cyberstorm */ + .id = ZORRO_ID(PHASE5, 0x0C, 0), + .driver_data = ZORRO_CYBER, + }, + { /* Cyberstorm II */ + .id = ZORRO_ID(PHASE5, 0x19, 0), + .driver_data = ZORRO_CYBERII, + }, + { 0 } +}; +MODULE_DEVICE_TABLE(zorro, zorro_esp_zorro_tbl); + +static int zorro_esp_probe(struct zorro_dev *z, + const struct zorro_device_id *ent) +{ + struct scsi_host_template *tpnt = &scsi_esp_template; + struct Scsi_Host *host; + struct esp *esp; + const struct zorro_driver_data *zdd; + struct zorro_esp_priv *zep; + unsigned long board, ioaddr, dmaaddr; + int err; + + board = zorro_resource_start(z); + zdd = &zorro_esp_boards[ent->driver_data]; + + pr_info("%s found at address 0x%lx.\n", zdd->name, board); + + zep = kzalloc(sizeof(*zep), GFP_KERNEL); + if (!zep) { + pr_err("Can't allocate device private data!\n"); + return -ENOMEM; + } + + /* let's figure out whether we have a Zorro II or Zorro III board */ + if ((z->rom.er_Type & ERT_TYPEMASK) == ERT_ZORROIII) { + if (board > 0xffffff) + zep->zorro3 = 1; + } else { + /* + * Even though most of these boards identify as Zorro II, + * they are in fact CPU expansion slot boards and have full + * access to all of memory. Fix up DMA bitmask here. + */ + z->dev.coherent_dma_mask = DMA_BIT_MASK(32); + } + + /* + * If Zorro III and ID matches Fastlane, our device table entry + * contains data for the Blizzard 1230 II board which does share the + * same ID. Fix up device table entry here. + * TODO: Some Cyberstom060 boards also share this ID but would need + * to use the Cyberstorm I driver data ... we catch this by checking + * for presence of ESP chip later, but don't try to fix up yet. + */ + if (zep->zorro3 && ent->driver_data == ZORRO_BLZ1230II) { + pr_info("%s at address 0x%lx is Fastlane Z3, fixing data!\n", + zdd->name, board); + zdd = &zorro_esp_boards[ZORRO_FASTLANE]; + } + + if (zdd->absolute) { + ioaddr = zdd->offset; + dmaaddr = zdd->dma_offset; + } else { + ioaddr = board + zdd->offset; + dmaaddr = board + zdd->dma_offset; + } + + if (!zorro_request_device(z, zdd->name)) { + pr_err("cannot reserve region 0x%lx, abort\n", + board); + err = -EBUSY; + goto fail_free_zep; + } + + host = scsi_host_alloc(tpnt, sizeof(struct esp)); + + if (!host) { + pr_err("No host detected; board configuration problem?\n"); + err = -ENOMEM; + goto fail_release_device; + } + + host->base = ioaddr; + host->this_id = 7; + + esp = shost_priv(host); + esp->host = host; + esp->dev = &z->dev; + + esp->scsi_id = host->this_id; + esp->scsi_id_mask = (1 << esp->scsi_id); + + esp->cfreq = 40000000; + + zep->esp = esp; + + dev_set_drvdata(esp->dev, zep); + + /* additional setup required for Fastlane */ + if (zep->zorro3 && ent->driver_data == ZORRO_BLZ1230II) { + /* map full address space up to ESP base for DMA */ + zep->board_base = ioremap_nocache(board, + FASTLANE_ESP_ADDR-1); + if (!zep->board_base) { + pr_err("Cannot allocate board address space\n"); + err = -ENOMEM; + goto fail_free_host; + } + /* initialize DMA control shadow register */ + zep->ctrl_data = (FASTLANE_DMA_FCODE | + FASTLANE_DMA_EDI | FASTLANE_DMA_ESI); + } + + esp->ops = zdd->esp_ops; + + if (ioaddr > 0xffffff) + esp->regs = ioremap_nocache(ioaddr, 0x20); + else + /* ZorroII address space remapped nocache by early startup */ + esp->regs = ZTWO_VADDR(ioaddr); + + if (!esp->regs) { + err = -ENOMEM; + goto fail_unmap_fastlane; + } + + /* Check whether a Blizzard 12x0 or CyberstormII really has SCSI */ + if (zdd->scsi_option) { + zorro_esp_write8(esp, (ESP_CONFIG1_PENABLE | 7), ESP_CFG1); + if (zorro_esp_read8(esp, ESP_CFG1) != (ESP_CONFIG1_PENABLE|7)) { + err = -ENODEV; + goto fail_unmap_regs; + } + } + + if (zep->zorro3) { + /* + * Only Fastlane Z3 for now - add switch for correct struct + * dma_registers size if adding any more + */ + esp->dma_regs = ioremap_nocache(dmaaddr, + sizeof(struct fastlane_dma_registers)); + } else + /* ZorroII address space remapped nocache by early startup */ + esp->dma_regs = ZTWO_VADDR(dmaaddr); + + if (!esp->dma_regs) { + err = -ENOMEM; + goto fail_unmap_regs; + } + + esp->command_block = dma_alloc_coherent(esp->dev, 16, + &esp->command_block_dma, + GFP_KERNEL); + + if (!esp->command_block) { + err = -ENOMEM; + goto fail_unmap_dma_regs; + } + + host->irq = IRQ_AMIGA_PORTS; + err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, + "Amiga Zorro ESP", esp); + if (err < 0) { + err = -ENODEV; + goto fail_free_command_block; + } + + /* register the chip */ + err = scsi_esp_register(esp, &z->dev); + + if (err) { + err = -ENOMEM; + goto fail_free_irq; + } + + return 0; + +fail_free_irq: + free_irq(host->irq, esp); + +fail_free_command_block: + dma_free_coherent(esp->dev, 16, + esp->command_block, + esp->command_block_dma); + +fail_unmap_dma_regs: + if (zep->zorro3) + iounmap(esp->dma_regs); + +fail_unmap_regs: + if (ioaddr > 0xffffff) + iounmap(esp->regs); + +fail_unmap_fastlane: + if (zep->zorro3) + iounmap(zep->board_base); + +fail_free_host: + scsi_host_put(host); + +fail_release_device: + zorro_release_device(z); + +fail_free_zep: + kfree(zep); + + return err; +} + +static void zorro_esp_remove(struct zorro_dev *z) +{ + struct zorro_esp_priv *zep = dev_get_drvdata(&z->dev); + struct esp *esp = zep->esp; + struct Scsi_Host *host = esp->host; + + scsi_esp_unregister(esp); + + free_irq(host->irq, esp); + dma_free_coherent(esp->dev, 16, + esp->command_block, + esp->command_block_dma); + + if (zep->zorro3) { + iounmap(zep->board_base); + iounmap(esp->dma_regs); + } + + if (host->base > 0xffffff) + iounmap(esp->regs); + + scsi_host_put(host); + + zorro_release_device(z); + + kfree(zep); +} + +static struct zorro_driver zorro_esp_driver = { + .name = KBUILD_MODNAME, + .id_table = zorro_esp_zorro_tbl, + .probe = zorro_esp_probe, + .remove = zorro_esp_remove, +}; + +static int __init zorro_esp_scsi_init(void) +{ + return zorro_register_driver(&zorro_esp_driver); +} + +static void __exit zorro_esp_scsi_exit(void) +{ + zorro_unregister_driver(&zorro_esp_driver); +} + +module_init(zorro_esp_scsi_init); +module_exit(zorro_esp_scsi_exit); -- cgit v1.2.3 From 0cac8e1bba60d785d53350ab44856d9b1c4f1648 Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sun, 15 Apr 2018 00:17:41 +0530 Subject: scsi: sg: Change return type to vm_fault_t Use new return type vm_fault_t for fault handler in struct vm_operations_struct. Signed-off-by: Souptick Joarder Reviewed-by: Matthew Wilcox Acked-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/sg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index c198b96368dd..c2b7d347ede4 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1192,7 +1192,7 @@ sg_fasync(int fd, struct file *filp, int mode) return fasync_helper(fd, filp, mode, &sfp->async_qp); } -static int +static vm_fault_t sg_vma_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; -- cgit v1.2.3 From 3e58c5cf166722d568ffcdd0ff7ddc50413ba1b0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 16 Apr 2018 10:50:21 +0200 Subject: scsi: iscsi_tcp: don't set a bounce limit The default already is to never bounce, so the call is a no-op. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/iscsi_tcp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 2ba4b68fdb73..b025a0b74341 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -962,7 +962,6 @@ static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev) if (conn->datadgst_en) sdev->request_queue->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES; - blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY); blk_queue_dma_alignment(sdev->request_queue, 0); return 0; } -- cgit v1.2.3 From 9027b15d51ecd20851795787b8d98514c33fc5d2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 16 Apr 2018 10:50:22 +0200 Subject: scsi: storsvc: don't set a bounce limit The default already is to never bounce, so the call is a no-op. Signed-off-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/storvsc_drv.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 8c51d628b52e..5f2d177c3bd9 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1382,9 +1382,6 @@ static int storvsc_device_alloc(struct scsi_device *sdevice) static int storvsc_device_configure(struct scsi_device *sdevice) { - - blk_queue_bounce_limit(sdevice->request_queue, BLK_BOUNCE_ANY); - blk_queue_rq_timeout(sdevice->request_queue, (storvsc_timeout * HZ)); /* Ensure there are no gaps in presented sgls */ -- cgit v1.2.3 From 354f113205f5893f891e0eb7f1be465289bab130 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 16 Apr 2018 18:04:39 -0700 Subject: scsi: sd_zbc: Change the type of the ZBC fields into u32 This patch does not change any functionality but makes it clear that it is on purpose that these fields are 32 bits wide. Signed-off-by: Bart Van Assche Cc: Damien Le Moal Cc: Christoph Hellwig Cc: Hannes Reinecke Reviewed-by: Damien Le Moal Signed-off-by: Martin K. Petersen --- drivers/scsi/sd.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 0d663b5e45bb..392c7d078ae3 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -74,12 +74,12 @@ struct scsi_disk { struct gendisk *disk; struct opal_dev *opal_dev; #ifdef CONFIG_BLK_DEV_ZONED - unsigned int nr_zones; - unsigned int zone_blocks; - unsigned int zone_shift; - unsigned int zones_optimal_open; - unsigned int zones_optimal_nonseq; - unsigned int zones_max_open; + u32 nr_zones; + u32 zone_blocks; + u32 zone_shift; + u32 zones_optimal_open; + u32 zones_optimal_nonseq; + u32 zones_max_open; #endif atomic_t openers; sector_t capacity; /* size in logical blocks */ -- cgit v1.2.3 From c976562162a0bd8f460e5499e13118371da1a66a Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 16 Apr 2018 18:04:40 -0700 Subject: scsi: sd_zbc: Let the SCSI core handle ILLEGAL REQUEST / ASC 0x21 scsi_io_completion() translates the sense key ILLEGAL REQUEST / ASC 0x21 into ACTION_FAIL. That means that setting cmd->allowed to zero in sd_zbc_complete() for this sense code / ASC combination is not necessary. Hence remove the code that resets cmd->allowed from sd_zbc_complete(). Signed-off-by: Bart Van Assche Cc: Damien Le Moal Cc: Christoph Hellwig Cc: Hannes Reinecke Reviewed-by: Damien Le Moal Signed-off-by: Martin K. Petersen --- drivers/scsi/sd_zbc.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 41df75eea57b..2d0c06f7db3e 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -299,16 +299,6 @@ void sd_zbc_complete(struct scsi_cmnd *cmd, unsigned int good_bytes, case REQ_OP_WRITE: case REQ_OP_WRITE_ZEROES: case REQ_OP_WRITE_SAME: - - if (result && - sshdr->sense_key == ILLEGAL_REQUEST && - sshdr->asc == 0x21) - /* - * INVALID ADDRESS FOR WRITE error: It is unlikely that - * retrying write requests failed with any kind of - * alignement error will result in success. So don't. - */ - cmd->allowed = 0; break; case REQ_OP_ZONE_REPORT: -- cgit v1.2.3 From bcbba7bb54e9a7c6ed35bbd5514e0507d8c495cb Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 18 Apr 2018 12:30:38 +0100 Subject: scsi: mptfc: fix spelling mistake in macro names Rename macros MPI_FCPORTPAGE0_SUPPORT_SPEED_UKNOWN and MPI_FCPORTPAGE0_CURRENT_SPEED_UKNOWN to add in missing N in UNKNOWN Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/message/fusion/lsi/mpi_cnfg.h | 4 ++-- drivers/message/fusion/mptfc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/message/fusion/lsi/mpi_cnfg.h b/drivers/message/fusion/lsi/mpi_cnfg.h index 4e9c0ce94f27..059997f8ebce 100644 --- a/drivers/message/fusion/lsi/mpi_cnfg.h +++ b/drivers/message/fusion/lsi/mpi_cnfg.h @@ -1802,13 +1802,13 @@ typedef struct _CONFIG_PAGE_FC_PORT_0 #define MPI_FCPORTPAGE0_SUPPORT_CLASS_2 (0x00000002) #define MPI_FCPORTPAGE0_SUPPORT_CLASS_3 (0x00000004) -#define MPI_FCPORTPAGE0_SUPPORT_SPEED_UKNOWN (0x00000000) /* (SNIA)HBA_PORTSPEED_UNKNOWN 0 Unknown - transceiver incapable of reporting */ +#define MPI_FCPORTPAGE0_SUPPORT_SPEED_UNKNOWN (0x00000000) /* (SNIA)HBA_PORTSPEED_UNKNOWN 0 Unknown - transceiver incapable of reporting */ #define MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED (0x00000001) /* (SNIA)HBA_PORTSPEED_1GBIT 1 1 GBit/sec */ #define MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED (0x00000002) /* (SNIA)HBA_PORTSPEED_2GBIT 2 2 GBit/sec */ #define MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED (0x00000004) /* (SNIA)HBA_PORTSPEED_10GBIT 4 10 GBit/sec */ #define MPI_FCPORTPAGE0_SUPPORT_4GBIT_SPEED (0x00000008) /* (SNIA)HBA_PORTSPEED_4GBIT 8 4 GBit/sec */ -#define MPI_FCPORTPAGE0_CURRENT_SPEED_UKNOWN MPI_FCPORTPAGE0_SUPPORT_SPEED_UKNOWN +#define MPI_FCPORTPAGE0_CURRENT_SPEED_UNKNOWN MPI_FCPORTPAGE0_SUPPORT_SPEED_UNKNOWN #define MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED #define MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED #define MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index 6d461ca97150..06b175420be9 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -693,7 +693,7 @@ mptfc_display_port_link_speed(MPT_ADAPTER *ioc, int portnum, FCPortPage0_t *pp0d state = pp0dest->PortState; if (state != MPI_FCPORTPAGE0_PORTSTATE_OFFLINE && - new_speed != MPI_FCPORTPAGE0_CURRENT_SPEED_UKNOWN) { + new_speed != MPI_FCPORTPAGE0_CURRENT_SPEED_UNKNOWN) { old = old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT ? "1 Gbps" : old_speed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT ? "2 Gbps" : -- cgit v1.2.3 From 78a6295c71cb276f8ab0bfc786f3543a4e756a8f Mon Sep 17 00:00:00 2001 From: Lee Duncan Date: Fri, 6 Apr 2018 11:31:41 -0700 Subject: scsi: target: prefer dbroot of /etc/target over /var/target The target database root directory, dbroot, has defaulted to /var/target for a while, but its main client, targetcli-fb, has been moving it to /etc/target for quite some time. With the plethora of target drivers now appearing, it has become more difficult to initialize this attribute before use by any child drivers. If the directory /etc/target exists, use that as the DB root. Otherwise, fall back to using /var/target. The ability to override this dbroot attribute still exists via sysfs. Signed-off-by: Lee Duncan Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/target/target_core_configfs.c | 25 +++++++++++++++++++++++++ drivers/target/target_core_internal.h | 1 + 2 files changed, 26 insertions(+) diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 3f4bf126eed0..5ccef7d597fa 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -155,6 +155,8 @@ static ssize_t target_core_item_dbroot_store(struct config_item *item, mutex_unlock(&g_tf_lock); + pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root); + return read_bytes; } @@ -3213,6 +3215,27 @@ void target_setup_backend_cits(struct target_backend *tb) target_core_setup_dev_stat_cit(tb); } +static void target_init_dbroot(void) +{ + struct file *fp; + + snprintf(db_root_stage, DB_ROOT_LEN, DB_ROOT_PREFERRED); + fp = filp_open(db_root_stage, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("db_root: cannot open: %s\n", db_root_stage); + return; + } + if (!S_ISDIR(file_inode(fp)->i_mode)) { + filp_close(fp, NULL); + pr_err("db_root: not a valid directory: %s\n", db_root_stage); + return; + } + filp_close(fp, NULL); + + strncpy(db_root, db_root_stage, DB_ROOT_LEN); + pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root); +} + static int __init target_core_init_configfs(void) { struct configfs_subsystem *subsys = &target_core_fabrics; @@ -3293,6 +3316,8 @@ static int __init target_core_init_configfs(void) if (ret < 0) goto out; + target_init_dbroot(); + return 0; out: diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 1d5afc3ae017..dead30b1d32c 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -166,6 +166,7 @@ extern struct se_portal_group xcopy_pt_tpg; /* target_core_configfs.c */ #define DB_ROOT_LEN 4096 #define DB_ROOT_DEFAULT "/var/target" +#define DB_ROOT_PREFERRED "/etc/target" extern char db_root[]; -- cgit v1.2.3 From 69589c9bb9c24499ace5158e236bed5906009efa Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sun, 15 Apr 2018 00:25:57 +0530 Subject: scsi: target: Change return type to vm_fault_t Use new return type vm_fault_t for fault handler in struct vm_operations_struct. Signed-off-by: Souptick Joarder Reviewed-by: Matthew Wilcox Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 4ad89ea71a70..d6af29250d94 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1382,7 +1382,7 @@ static struct page *tcmu_try_get_block_page(struct tcmu_dev *udev, uint32_t dbi) return page; } -static int tcmu_vma_fault(struct vm_fault *vmf) +static vm_fault_t tcmu_vma_fault(struct vm_fault *vmf) { struct tcmu_dev *udev = vmf->vma->vm_private_data; struct uio_info *info = &udev->uio_info; -- cgit v1.2.3 From 2217a47de42f85b69714c2a621af13cfeae35b40 Mon Sep 17 00:00:00 2001 From: Long Li Date: Thu, 19 Apr 2018 14:54:24 -0700 Subject: scsi: storvsc: Select channel based on available percentage of ring buffer to write This is a best effort for estimating on how busy the ring buffer is for that channel, based on available buffer to write in percentage. It is still possible that at the time of actual ring buffer write, the space may not be available due to other processes may be writing at the time. Selecting a channel based on how full it is can reduce the possibility that a ring buffer write will fail, and avoid the situation a channel is over busy. Now it's possible that storvsc can use a smaller ring buffer size (e.g. 40k bytes) to take advantage of cache locality. Signed-off-by: Long Li Reviewed-by: Stephen Hemminger Signed-off-by: Martin K. Petersen --- drivers/scsi/storvsc_drv.c | 90 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 5f2d177c3bd9..d3897c438448 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -395,6 +395,12 @@ MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)"); module_param(storvsc_vcpus_per_sub_channel, int, S_IRUGO); MODULE_PARM_DESC(storvsc_vcpus_per_sub_channel, "Ratio of VCPUs to subchannels"); + +static int ring_avail_percent_lowater = 10; +module_param(ring_avail_percent_lowater, int, S_IRUGO); +MODULE_PARM_DESC(ring_avail_percent_lowater, + "Select a channel if available ring size > this in percent"); + /* * Timeout in seconds for all devices managed by this driver. */ @@ -468,6 +474,13 @@ struct storvsc_device { * Mask of CPUs bound to subchannels. */ struct cpumask alloced_cpus; + /* + * Pre-allocated struct cpumask for each hardware queue. + * struct cpumask is used by selecting out-going channels. It is a + * big structure, default to 1024k bytes when CONFIG_MAXSMP=y. + * Pre-allocate it to avoid allocation on the kernel stack. + */ + struct cpumask *cpumask_chns; /* Used for vsc/vsp channel reset process */ struct storvsc_cmd_request init_request; struct storvsc_cmd_request reset_request; @@ -872,6 +885,13 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc) if (stor_device->stor_chns == NULL) return -ENOMEM; + stor_device->cpumask_chns = kcalloc(num_possible_cpus(), + sizeof(struct cpumask), GFP_KERNEL); + if (stor_device->cpumask_chns == NULL) { + kfree(stor_device->stor_chns); + return -ENOMEM; + } + stor_device->stor_chns[device->channel->target_cpu] = device->channel; cpumask_set_cpu(device->channel->target_cpu, &stor_device->alloced_cpus); @@ -1232,6 +1252,7 @@ static int storvsc_dev_remove(struct hv_device *device) vmbus_close(device->channel); kfree(stor_device->stor_chns); + kfree(stor_device->cpumask_chns); kfree(stor_device); return 0; } @@ -1241,7 +1262,7 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device, { u16 slot = 0; u16 hash_qnum; - struct cpumask alloced_mask; + struct cpumask *alloced_mask = &stor_device->cpumask_chns[q_num]; int num_channels, tgt_cpu; if (stor_device->num_sc == 0) @@ -1257,10 +1278,10 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device, * III. Mapping is persistent. */ - cpumask_and(&alloced_mask, &stor_device->alloced_cpus, + cpumask_and(alloced_mask, &stor_device->alloced_cpus, cpumask_of_node(cpu_to_node(q_num))); - num_channels = cpumask_weight(&alloced_mask); + num_channels = cpumask_weight(alloced_mask); if (num_channels == 0) return stor_device->device->channel; @@ -1268,7 +1289,7 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device, while (hash_qnum >= num_channels) hash_qnum -= num_channels; - for_each_cpu(tgt_cpu, &alloced_mask) { + for_each_cpu(tgt_cpu, alloced_mask) { if (slot == hash_qnum) break; slot++; @@ -1285,9 +1306,9 @@ static int storvsc_do_io(struct hv_device *device, { struct storvsc_device *stor_device; struct vstor_packet *vstor_packet; - struct vmbus_channel *outgoing_channel; + struct vmbus_channel *outgoing_channel, *channel; int ret = 0; - struct cpumask alloced_mask; + struct cpumask *alloced_mask; int tgt_cpu; vstor_packet = &request->vstor_packet; @@ -1301,22 +1322,53 @@ static int storvsc_do_io(struct hv_device *device, /* * Select an an appropriate channel to send the request out. */ - if (stor_device->stor_chns[q_num] != NULL) { outgoing_channel = stor_device->stor_chns[q_num]; - if (outgoing_channel->target_cpu == smp_processor_id()) { + if (outgoing_channel->target_cpu == q_num) { /* * Ideally, we want to pick a different channel if * available on the same NUMA node. */ - cpumask_and(&alloced_mask, &stor_device->alloced_cpus, + alloced_mask = &stor_device->cpumask_chns[q_num]; + cpumask_and(alloced_mask, &stor_device->alloced_cpus, cpumask_of_node(cpu_to_node(q_num))); - for_each_cpu_wrap(tgt_cpu, &alloced_mask, - outgoing_channel->target_cpu + 1) { - if (tgt_cpu != outgoing_channel->target_cpu) { - outgoing_channel = - stor_device->stor_chns[tgt_cpu]; - break; + + for_each_cpu_wrap(tgt_cpu, alloced_mask, q_num + 1) { + if (tgt_cpu == q_num) + continue; + channel = stor_device->stor_chns[tgt_cpu]; + if (hv_get_avail_to_write_percent( + &channel->outbound) + > ring_avail_percent_lowater) { + outgoing_channel = channel; + goto found_channel; + } + } + + /* + * All the other channels on the same NUMA node are + * busy. Try to use the channel on the current CPU + */ + if (hv_get_avail_to_write_percent( + &outgoing_channel->outbound) + > ring_avail_percent_lowater) + goto found_channel; + + /* + * If we reach here, all the channels on the current + * NUMA node are busy. Try to find a channel in + * other NUMA nodes + */ + cpumask_andnot(alloced_mask, &stor_device->alloced_cpus, + cpumask_of_node(cpu_to_node(q_num))); + + for_each_cpu(tgt_cpu, alloced_mask) { + channel = stor_device->stor_chns[tgt_cpu]; + if (hv_get_avail_to_write_percent( + &channel->outbound) + > ring_avail_percent_lowater) { + outgoing_channel = channel; + goto found_channel; } } } @@ -1324,7 +1376,7 @@ static int storvsc_do_io(struct hv_device *device, outgoing_channel = get_og_chn(stor_device, q_num); } - +found_channel: vstor_packet->flags |= REQUEST_COMPLETION_FLAG; vstor_packet->vm_srb.length = (sizeof(struct vmscsi_request) - @@ -1726,8 +1778,9 @@ static int storvsc_probe(struct hv_device *device, max_sub_channels = (num_cpus / storvsc_vcpus_per_sub_channel); } - scsi_driver.can_queue = (max_outstanding_req_per_channel * - (max_sub_channels + 1)); + scsi_driver.can_queue = max_outstanding_req_per_channel * + (max_sub_channels + 1) * + (100 - ring_avail_percent_lowater) / 100; host = scsi_host_alloc(&scsi_driver, sizeof(struct hv_host_device)); @@ -1858,6 +1911,7 @@ err_out2: err_out1: kfree(stor_device->stor_chns); + kfree(stor_device->cpumask_chns); kfree(stor_device); err_out0: -- cgit v1.2.3 From dbef91ec5482239055dd2db8ec656fc13d382add Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:06 +0200 Subject: scsi: ilog2: create truly constant version for sparse Sparse emits errors about ilog2() in array indices because of the use of __ilog2_32() and __ilog2_64(), rightly so (https://www.spinics.net/lists/linux-sparse/msg03471.html). Create a const_ilog2() variant that works with sparse for this scenario. (Note: checkpatch.pl complains about missing parentheses, but that appears to be a false positive. I can get rid of the warning simply by inserting whitespace, making checkpatch "see" the whole macro). Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- include/linux/log2.h | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/include/linux/log2.h b/include/linux/log2.h index 41a1ae010993..2af7f77866d0 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -72,16 +72,13 @@ unsigned long __rounddown_pow_of_two(unsigned long n) } /** - * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value + * const_ilog2 - log base 2 of 32-bit or a 64-bit constant unsigned value * @n: parameter * - * constant-capable log of base 2 calculation - * - this can be used to initialise global variables from constant data, hence - * the massive ternary operator construction - * - * selects the appropriately-sized optimised version depending on sizeof(n) + * Use this where sparse expects a true constant expression, e.g. for array + * indices. */ -#define ilog2(n) \ +#define const_ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ (n) < 2 ? 0 : \ @@ -147,10 +144,26 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - 1 ) : \ - (sizeof(n) <= 4) ? \ - __ilog2_u32(n) : \ - __ilog2_u64(n) \ + 1) : \ + -1) + +/** + * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value + * @n: parameter + * + * constant-capable log of base 2 calculation + * - this can be used to initialise global variables from constant data, hence + * the massive ternary operator construction + * + * selects the appropriately-sized optimised version depending on sizeof(n) + */ +#define ilog2(n) \ +( \ + __builtin_constant_p(n) ? \ + const_ilog2(n) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ ) /** -- cgit v1.2.3 From 659c1c1b29ebc4af9148eed74a9f28f00b8b6dc6 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:07 +0200 Subject: scsi: devinfo: use const_ilog2 for array indices Use the just introduced const_ilog2() macro to avoid sparse errors. Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debugfs.c | 2 +- drivers/scsi/scsi_sysfs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debugfs.c b/drivers/scsi/scsi_debugfs.c index b784002ef0bd..c5a8756384bc 100644 --- a/drivers/scsi/scsi_debugfs.c +++ b/drivers/scsi/scsi_debugfs.c @@ -4,7 +4,7 @@ #include #include "scsi_debugfs.h" -#define SCSI_CMD_FLAG_NAME(name) [ilog2(SCMD_##name)] = #name +#define SCSI_CMD_FLAG_NAME(name)[const_ilog2(SCMD_##name)] = #name static const char *const scsi_cmd_flags[] = { SCSI_CMD_FLAG_NAME(TAGGED), SCSI_CMD_FLAG_NAME(UNCHECKED_ISA_DMA), diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 1e36c9a9ad17..5120e613a29c 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -968,7 +968,7 @@ sdev_show_wwid(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(wwid, S_IRUGO, sdev_show_wwid, NULL); #define BLIST_FLAG_NAME(name) \ - [ilog2((__force unsigned int)BLIST_##name)] = #name + [const_ilog2((__force unsigned int)BLIST_##name)] = #name static const char *const sdev_bflags_name[] = { #include "scsi_devinfo_tbl.c" }; -- cgit v1.2.3 From 1409880357ed33dc1c23eed080d88ea4410ed9a3 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:08 +0200 Subject: scsi: devinfo: change blist_flag_t to 64bit Space for SCSI blist flags is gradually running out. Change the type to __u64 and fix a checkpatch complaint about symbolic mode flags in scsi_devinfo.c. Make checkpatch happy by replacing simple_strtoul() with kstrtoull(). Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 18 +++++++++++----- drivers/scsi/scsi_sysfs.c | 2 +- include/scsi/scsi_device.h | 2 +- include/scsi/scsi_devinfo.h | 50 ++++++++++++++++++++++----------------------- 4 files changed, 40 insertions(+), 32 deletions(-) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index e5c82f13b7d9..bd04a33e4360 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -360,8 +360,16 @@ int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, scsi_strcpy_devinfo("model", devinfo->model, sizeof(devinfo->model), model, compatible); - if (strflags) - flags = (__force blist_flags_t)simple_strtoul(strflags, NULL, 0); + if (strflags) { + unsigned long long val; + int ret = kstrtoull(strflags, 0, &val); + + if (ret != 0) { + kfree(devinfo); + return ret; + } + flags = (__force blist_flags_t)val; + } devinfo->flags = flags; devinfo->compatible = compatible; @@ -614,7 +622,7 @@ static int devinfo_seq_show(struct seq_file *m, void *v) devinfo_table->name) seq_printf(m, "[%s]:\n", devinfo_table->name); - seq_printf(m, "'%.8s' '%.16s' 0x%x\n", + seq_printf(m, "'%.8s' '%.16s' 0x%llx\n", devinfo->vendor, devinfo->model, devinfo->flags); return 0; } @@ -733,9 +741,9 @@ MODULE_PARM_DESC(dev_flags, " list entries for vendor and model with an integer value of flags" " to the scsi device info list"); -module_param_named(default_dev_flags, scsi_default_dev_flags, int, S_IRUGO|S_IWUSR); +module_param_named(default_dev_flags, scsi_default_dev_flags, ullong, 0644); MODULE_PARM_DESC(default_dev_flags, - "scsi default device flag integer value"); + "scsi default device flag uint64_t value"); /** * scsi_exit_devinfo - remove /proc/scsi/device_info & the scsi_dev_info_list diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 5120e613a29c..7943b762c12d 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -968,7 +968,7 @@ sdev_show_wwid(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(wwid, S_IRUGO, sdev_show_wwid, NULL); #define BLIST_FLAG_NAME(name) \ - [const_ilog2((__force unsigned int)BLIST_##name)] = #name + [const_ilog2((__force __u64)BLIST_##name)] = #name static const char *const sdev_bflags_name[] = { #include "scsi_devinfo_tbl.c" }; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 7ae177c8e399..4c36af6edd79 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -15,7 +15,7 @@ struct scsi_cmnd; struct scsi_lun; struct scsi_sense_hdr; -typedef unsigned int __bitwise blist_flags_t; +typedef __u64 __bitwise blist_flags_t; struct scsi_mode_data { __u32 length; diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index ea67c32e870e..e206d299f137 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -6,55 +6,55 @@ */ /* Only scan LUN 0 */ -#define BLIST_NOLUN ((__force blist_flags_t)(1 << 0)) +#define BLIST_NOLUN ((__force blist_flags_t)(1ULL << 0)) /* Known to have LUNs, force scanning. * DEPRECATED: Use max_luns=N */ -#define BLIST_FORCELUN ((__force blist_flags_t)(1 << 1)) +#define BLIST_FORCELUN ((__force blist_flags_t)(1ULL << 1)) /* Flag for broken handshaking */ -#define BLIST_BORKEN ((__force blist_flags_t)(1 << 2)) +#define BLIST_BORKEN ((__force blist_flags_t)(1ULL << 2)) /* unlock by special command */ -#define BLIST_KEY ((__force blist_flags_t)(1 << 3)) +#define BLIST_KEY ((__force blist_flags_t)(1ULL << 3)) /* Do not use LUNs in parallel */ -#define BLIST_SINGLELUN ((__force blist_flags_t)(1 << 4)) +#define BLIST_SINGLELUN ((__force blist_flags_t)(1ULL << 4)) /* Buggy Tagged Command Queuing */ -#define BLIST_NOTQ ((__force blist_flags_t)(1 << 5)) +#define BLIST_NOTQ ((__force blist_flags_t)(1ULL << 5)) /* Non consecutive LUN numbering */ -#define BLIST_SPARSELUN ((__force blist_flags_t)(1 << 6)) +#define BLIST_SPARSELUN ((__force blist_flags_t)(1ULL << 6)) /* Avoid LUNS >= 5 */ -#define BLIST_MAX5LUN ((__force blist_flags_t)(1 << 7)) +#define BLIST_MAX5LUN ((__force blist_flags_t)(1ULL << 7)) /* Treat as (removable) CD-ROM */ -#define BLIST_ISROM ((__force blist_flags_t)(1 << 8)) +#define BLIST_ISROM ((__force blist_flags_t)(1ULL << 8)) /* LUNs past 7 on a SCSI-2 device */ -#define BLIST_LARGELUN ((__force blist_flags_t)(1 << 9)) +#define BLIST_LARGELUN ((__force blist_flags_t)(1ULL << 9)) /* override additional length field */ -#define BLIST_INQUIRY_36 ((__force blist_flags_t)(1 << 10)) +#define BLIST_INQUIRY_36 ((__force blist_flags_t)(1ULL << 10)) /* do not do automatic start on add */ -#define BLIST_NOSTARTONADD ((__force blist_flags_t)(1 << 12)) +#define BLIST_NOSTARTONADD ((__force blist_flags_t)(1ULL << 12)) /* try REPORT_LUNS even for SCSI-2 devs (if HBA supports more than 8 LUNs) */ -#define BLIST_REPORTLUN2 ((__force blist_flags_t)(1 << 17)) +#define BLIST_REPORTLUN2 ((__force blist_flags_t)(1ULL << 17)) /* don't try REPORT_LUNS scan (SCSI-3 devs) */ -#define BLIST_NOREPORTLUN ((__force blist_flags_t)(1 << 18)) +#define BLIST_NOREPORTLUN ((__force blist_flags_t)(1ULL << 18)) /* don't use PREVENT-ALLOW commands */ -#define BLIST_NOT_LOCKABLE ((__force blist_flags_t)(1 << 19)) +#define BLIST_NOT_LOCKABLE ((__force blist_flags_t)(1ULL << 19)) /* device is actually for RAID config */ -#define BLIST_NO_ULD_ATTACH ((__force blist_flags_t)(1 << 20)) +#define BLIST_NO_ULD_ATTACH ((__force blist_flags_t)(1ULL << 20)) /* select without ATN */ -#define BLIST_SELECT_NO_ATN ((__force blist_flags_t)(1 << 21)) +#define BLIST_SELECT_NO_ATN ((__force blist_flags_t)(1ULL << 21)) /* retry HARDWARE_ERROR */ -#define BLIST_RETRY_HWERROR ((__force blist_flags_t)(1 << 22)) +#define BLIST_RETRY_HWERROR ((__force blist_flags_t)(1ULL << 22)) /* maximum 512 sector cdb length */ -#define BLIST_MAX_512 ((__force blist_flags_t)(1 << 23)) +#define BLIST_MAX_512 ((__force blist_flags_t)(1ULL << 23)) /* Disable T10 PI (DIF) */ -#define BLIST_NO_DIF ((__force blist_flags_t)(1 << 25)) +#define BLIST_NO_DIF ((__force blist_flags_t)(1ULL << 25)) /* Ignore SBC-3 VPD pages */ -#define BLIST_SKIP_VPD_PAGES ((__force blist_flags_t)(1 << 26)) +#define BLIST_SKIP_VPD_PAGES ((__force blist_flags_t)(1ULL << 26)) /* Attempt to read VPD pages */ -#define BLIST_TRY_VPD_PAGES ((__force blist_flags_t)(1 << 28)) +#define BLIST_TRY_VPD_PAGES ((__force blist_flags_t)(1ULL << 28)) /* don't try to issue RSOC */ -#define BLIST_NO_RSOC ((__force blist_flags_t)(1 << 29)) +#define BLIST_NO_RSOC ((__force blist_flags_t)(1ULL << 29)) /* maximum 1024 sector cdb length */ -#define BLIST_MAX_1024 ((__force blist_flags_t)(1 << 30)) +#define BLIST_MAX_1024 ((__force blist_flags_t)(1ULL << 30)) /* Use UNMAP limit for WRITE SAME */ -#define BLIST_UNMAP_LIMIT_WS ((__force blist_flags_t)(1 << 31)) +#define BLIST_UNMAP_LIMIT_WS ((__force blist_flags_t)(1ULL << 31)) #endif -- cgit v1.2.3 From 358fda5ff4c441dfa6e66fd3b1f7d8f882ecba8f Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:09 +0200 Subject: scsi: devinfo: warn on undefined blist flags Warn if a device (or the user) sets blist flags which are unknown or have been removed. This should enable us to reuse freed blist bits in later releases. Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- drivers/scsi/Makefile | 2 +- drivers/scsi/scsi_devinfo.c | 6 ++++++ include/scsi/scsi_devinfo.h | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index f31145d6d472..eb30e558fc36 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -190,7 +190,7 @@ $(obj)/53c700.o $(MODVERDIR)/$(obj)/53c700.ver: $(obj)/53c700_d.h $(obj)/scsi_sysfs.o: $(obj)/scsi_devinfo_tbl.c quiet_cmd_bflags = GEN $@ - cmd_bflags = sed -n 's/.*BLIST_\([A-Z0-9_]*\) *.*/BLIST_FLAG_NAME(\1),/p' $< > $@ + cmd_bflags = sed -n 's/.*define *BLIST_\([A-Z0-9_]*\) *.*/BLIST_FLAG_NAME(\1),/p' $< > $@ $(obj)/scsi_devinfo_tbl.c: include/scsi/scsi_devinfo.h $(call if_changed,bflags) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index bd04a33e4360..9603f3ef18aa 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -370,6 +370,12 @@ int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, } flags = (__force blist_flags_t)val; } + if (flags & __BLIST_UNUSED_MASK) { + pr_err("scsi_devinfo (%s:%s): unsupported flags 0x%llx", + vendor, model, flags & __BLIST_UNUSED_MASK); + kfree(devinfo); + return -EINVAL; + } devinfo->flags = flags; devinfo->compatible = compatible; diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index e206d299f137..3434e143feff 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -28,8 +28,13 @@ #define BLIST_LARGELUN ((__force blist_flags_t)(1ULL << 9)) /* override additional length field */ #define BLIST_INQUIRY_36 ((__force blist_flags_t)(1ULL << 10)) +#define __BLIST_UNUSED_11 ((__force blist_flags_t)(1ULL << 11)) /* do not do automatic start on add */ #define BLIST_NOSTARTONADD ((__force blist_flags_t)(1ULL << 12)) +#define __BLIST_UNUSED_13 ((__force blist_flags_t)(1ULL << 13)) +#define __BLIST_UNUSED_14 ((__force blist_flags_t)(1ULL << 14)) +#define __BLIST_UNUSED_15 ((__force blist_flags_t)(1ULL << 15)) +#define __BLIST_UNUSED_16 ((__force blist_flags_t)(1ULL << 16)) /* try REPORT_LUNS even for SCSI-2 devs (if HBA supports more than 8 LUNs) */ #define BLIST_REPORTLUN2 ((__force blist_flags_t)(1ULL << 17)) /* don't try REPORT_LUNS scan (SCSI-3 devs) */ @@ -44,10 +49,12 @@ #define BLIST_RETRY_HWERROR ((__force blist_flags_t)(1ULL << 22)) /* maximum 512 sector cdb length */ #define BLIST_MAX_512 ((__force blist_flags_t)(1ULL << 23)) +#define __BLIST_UNUSED_24 ((__force blist_flags_t)(1ULL << 24)) /* Disable T10 PI (DIF) */ #define BLIST_NO_DIF ((__force blist_flags_t)(1ULL << 25)) /* Ignore SBC-3 VPD pages */ #define BLIST_SKIP_VPD_PAGES ((__force blist_flags_t)(1ULL << 26)) +#define __BLIST_UNUSED_27 ((__force blist_flags_t)(1ULL << 27)) /* Attempt to read VPD pages */ #define BLIST_TRY_VPD_PAGES ((__force blist_flags_t)(1ULL << 28)) /* don't try to issue RSOC */ @@ -57,4 +64,18 @@ /* Use UNMAP limit for WRITE SAME */ #define BLIST_UNMAP_LIMIT_WS ((__force blist_flags_t)(1ULL << 31)) +#define __BLIST_LAST_USED BLIST_UNMAP_LIMIT_WS + +#define __BLIST_HIGH_UNUSED (~(__BLIST_LAST_USED | \ + (__force blist_flags_t) \ + ((__force __u64)__BLIST_LAST_USED - 1ULL))) +#define __BLIST_UNUSED_MASK (__BLIST_UNUSED_11 | \ + __BLIST_UNUSED_13 | \ + __BLIST_UNUSED_14 | \ + __BLIST_UNUSED_15 | \ + __BLIST_UNUSED_16 | \ + __BLIST_UNUSED_24 | \ + __BLIST_UNUSED_27 | \ + __BLIST_HIGH_UNUSED) + #endif -- cgit v1.2.3 From 29cfc2ab71d9642c2f4fda6cd278309cc253ff82 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:10 +0200 Subject: scsi: devinfo: add BLIST_RETRY_ITF for EMC Symmetrix EMC Symmetrix returns 'internal target error' for a variety of conditions, most of which will be transient. So we should always retry it, even with failfast set. Otherwise we'd get spurious path flaps with multipath. Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 3 ++- drivers/scsi/scsi_error.c | 4 ++++ include/scsi/scsi_devinfo.h | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 9603f3ef18aa..87bc50686ac6 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -161,7 +161,8 @@ static struct { {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* EMC CLARiiON, storage on LUN 0 */ {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* EMC CLARiiON, no storage on LUN 0 */ {"EMC", "Invista", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, - {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_REPORTLUN2}, + {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | + BLIST_REPORTLUN2 | BLIST_RETRY_ITF}, {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, {"easyRAID", "16P", NULL, BLIST_NOREPORTLUN}, {"easyRAID", "X6P", NULL, BLIST_NOREPORTLUN}, diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 946039117bf4..633b198ed895 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "scsi_priv.h" @@ -525,6 +526,9 @@ int scsi_check_sense(struct scsi_cmnd *scmd) if (sshdr.asc == 0x10) /* DIF */ return SUCCESS; + if (sshdr.asc == 0x44 && sdev->sdev_bflags & BLIST_RETRY_ITF) + return ADD_TO_MLQUEUE; + return NEEDS_RETRY; case NOT_READY: case UNIT_ATTENTION: diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index 3434e143feff..91a327edf1fa 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -63,8 +63,10 @@ #define BLIST_MAX_1024 ((__force blist_flags_t)(1ULL << 30)) /* Use UNMAP limit for WRITE SAME */ #define BLIST_UNMAP_LIMIT_WS ((__force blist_flags_t)(1ULL << 31)) +/* Always retry ABORTED_COMMAND with Internal Target Failure */ +#define BLIST_RETRY_ITF ((__force blist_flags_t)(1ULL << 32)) -#define __BLIST_LAST_USED BLIST_UNMAP_LIMIT_WS +#define __BLIST_LAST_USED BLIST_RETRY_ITF #define __BLIST_HIGH_UNUSED (~(__BLIST_LAST_USED | \ (__force blist_flags_t) \ -- cgit v1.2.3 From c360652006bba40837cf16d5099ea61f7ce16c63 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:11 +0200 Subject: scsi: devinfo: BLIST_RETRY_ASC_C1 for Fujitsu ETERNUS On Fujitsu ETERNUS systems, sense code ABORTED COMMAND with ASC/Q C1/01 is used to indicate temporary condition where the storage-internal path to a target is switched from one controller to another. SCSI commands that return with this error code must be retried unconditionally (i.e. without the "maybe_retry" logic in scsi_decide_disposition); otherwise dm-multipath might initiate a failover from a healthy path e.g. for REQ_FAILFAST_DEV commands. Introduce a new blist flag for this case. [mkp: applied by hand] Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_devinfo.c | 1 + drivers/scsi/scsi_error.c | 3 +++ include/scsi/scsi_devinfo.h | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 87bc50686ac6..c4cbfd07b916 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -168,6 +168,7 @@ static struct { {"easyRAID", "X6P", NULL, BLIST_NOREPORTLUN}, {"easyRAID", "F8", NULL, BLIST_NOREPORTLUN}, {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"FUJITSU", "ETERNUS_DXM", "*", BLIST_RETRY_ASC_C1}, {"Generic", "USB SD Reader", "1.00", BLIST_FORCELUN | BLIST_INQUIRY_36}, {"Generic", "USB Storage-SMC", NULL, BLIST_FORCELUN | BLIST_INQUIRY_36}, /* FW: 0180 and 0207 */ {"HITACHI", "DF400", "*", BLIST_REPORTLUN2}, diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 633b198ed895..94d2047e0096 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -528,6 +528,9 @@ int scsi_check_sense(struct scsi_cmnd *scmd) if (sshdr.asc == 0x44 && sdev->sdev_bflags & BLIST_RETRY_ITF) return ADD_TO_MLQUEUE; + if (sshdr.asc == 0xc1 && sshdr.ascq == 0x01 && + sdev->sdev_bflags & BLIST_RETRY_ASC_C1) + return ADD_TO_MLQUEUE; return NEEDS_RETRY; case NOT_READY: diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index 91a327edf1fa..3fdb322d4c4b 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -65,8 +65,10 @@ #define BLIST_UNMAP_LIMIT_WS ((__force blist_flags_t)(1ULL << 31)) /* Always retry ABORTED_COMMAND with Internal Target Failure */ #define BLIST_RETRY_ITF ((__force blist_flags_t)(1ULL << 32)) +/* Always retry ABORTED_COMMAND with ASC 0xc1 */ +#define BLIST_RETRY_ASC_C1 ((__force blist_flags_t)(1ULL << 33)) -#define __BLIST_LAST_USED BLIST_RETRY_ITF +#define __BLIST_LAST_USED BLIST_RETRY_ASC_C1 #define __BLIST_HIGH_UNUSED (~(__BLIST_LAST_USED | \ (__force blist_flags_t) \ -- cgit v1.2.3 From 1f618aac2f00d3d9a4942cda14b8c33d28a11840 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Tue, 10 Apr 2018 20:37:59 +0800 Subject: scsi: st: Replace GFP_ATOMIC with GFP_KERNEL in st_probe st_probe() is never called in atomic context. st_probe() is only set as ".probe" in struct scsi_driver. Despite never getting called from atomic context, st_probe() calls kzalloc() with GFP_ATOMIC, which does not sleep for allocation. GFP_ATOMIC is not necessary and can be replaced with GFP_KERNEL, which can sleep and improve the possibility of sucessful allocation. This is found by a static analysis tool named DCNS written by myself. And I also manually check it. Signed-off-by: Jia-Ju Bai Signed-off-by: Martin K. Petersen --- drivers/scsi/st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 6c399480783d..4c7d39b825a3 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -4290,7 +4290,7 @@ static int st_probe(struct device *dev) goto out_buffer_free; } - tpnt = kzalloc(sizeof(struct scsi_tape), GFP_ATOMIC); + tpnt = kzalloc(sizeof(struct scsi_tape), GFP_KERNEL); if (tpnt == NULL) { sdev_printk(KERN_ERR, SDp, "st: Can't allocate device descriptor.\n"); -- cgit v1.2.3 From 4011f07660e0c909e25ea2c09ad4761c26c0b8df Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Tue, 10 Apr 2018 20:38:42 +0800 Subject: scsi: st: Replace GFP_ATOMIC with GFP_KERNEL in new_tape_buffer new_tape_buffer() is never called in atomic context. new_tape_buffer() is only called by st_probe(), which is only set as ".probe" in struct scsi_driver. Despite never getting called from atomic context, new_tape_buffer() calls kzalloc() with GFP_ATOMIC, which does not sleep for allocation. GFP_ATOMIC is not necessary and can be replaced with GFP_KERNEL, which can sleep and improve the possibility of sucessful allocation. This is found by a static analysis tool named DCNS written by myself. And I also manually check it. Signed-off-by: Jia-Ju Bai Signed-off-by: Martin K. Petersen --- drivers/scsi/st.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 4c7d39b825a3..e64489a4a9a6 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3878,7 +3878,7 @@ static struct st_buffer *new_tape_buffer(int need_dma, int max_sg) { struct st_buffer *tb; - tb = kzalloc(sizeof(struct st_buffer), GFP_ATOMIC); + tb = kzalloc(sizeof(struct st_buffer), GFP_KERNEL); if (!tb) { printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n"); return NULL; @@ -3889,7 +3889,7 @@ static struct st_buffer *new_tape_buffer(int need_dma, int max_sg) tb->buffer_size = 0; tb->reserved_pages = kzalloc(max_sg * sizeof(struct page *), - GFP_ATOMIC); + GFP_KERNEL); if (!tb->reserved_pages) { kfree(tb); return NULL; -- cgit v1.2.3 From 1e74aff12dace677ab84e0ee0e7330e0ed5a9b86 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 14 Apr 2018 10:51:03 -0700 Subject: scsi: target: target_core_transport.c: fix kernel-doc warnings Correct a function parameter's name to eliminate kernel-doc warnings in drivers/target/target_core_transport.c. Fixes these kernel-doc warnings: (tested by adding these files to a new target.rst documentation file) ../drivers/target/target_core_transport.c:1671: warning: No description found for parameter 'fabric_tmr_ptr' ../drivers/target/target_core_transport.c:1671: warning: Excess function parameter 'fabric_context' description in 'target_submit_tmr' Signed-off-by: Randy Dunlap To: "Nicholas A. Bellinger" Cc: linux-scsi@vger.kernel.org Cc: target-devel@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: Jonathan Corbet Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 4558f2e1fe1b..f649cf0bed75 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1654,7 +1654,7 @@ static bool target_lookup_lun_from_tag(struct se_session *se_sess, u64 tag, * @se_sess: associated se_sess for endpoint * @sense: pointer to SCSI sense buffer * @unpacked_lun: unpacked LUN to reference for struct se_lun - * @fabric_context: fabric context for TMR req + * @fabric_tmr_ptr: fabric context for TMR req * @tm_type: Type of TM request * @gfp: gfp type for caller * @tag: referenced task tag for TMR_ABORT_TASK -- cgit v1.2.3 From 9ad97b8b409e0c6c90d5c8f35568d189bf1e6ed5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 14 Apr 2018 10:51:04 -0700 Subject: scsi: target: target_core_transport.c: enable+fix kernel-doc For exported functions that already have near-kernel-doc notation, fix them to begin with "/**" and make a few corrections so that they don't have any kernel-doc warnings. Signed-off-by: Randy Dunlap To: "Nicholas A. Bellinger" Cc: linux-scsi@vger.kernel.org Cc: target-devel@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: Jonathan Corbet Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index f649cf0bed75..3500aa5927f2 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1431,7 +1431,7 @@ transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *sgl, return 0; } -/* +/** * target_submit_cmd_map_sgls - lookup unpacked lun and submit uninitialized * se_cmd + use pre-allocated SGL memory. * @@ -1441,7 +1441,7 @@ transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *sgl, * @sense: pointer to SCSI sense buffer * @unpacked_lun: unpacked LUN to reference for struct se_lun * @data_length: fabric expected data transfer length - * @task_addr: SAM task attribute + * @task_attr: SAM task attribute * @data_dir: DMA data direction * @flags: flags for command submission from target_sc_flags_tables * @sgl: struct scatterlist memory for unidirectional mapping @@ -1578,7 +1578,7 @@ int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess } EXPORT_SYMBOL(target_submit_cmd_map_sgls); -/* +/** * target_submit_cmd - lookup unpacked lun and submit uninitialized se_cmd * * @se_cmd: command descriptor to submit @@ -1587,7 +1587,7 @@ EXPORT_SYMBOL(target_submit_cmd_map_sgls); * @sense: pointer to SCSI sense buffer * @unpacked_lun: unpacked LUN to reference for struct se_lun * @data_length: fabric expected data transfer length - * @task_addr: SAM task attribute + * @task_attr: SAM task attribute * @data_dir: DMA data direction * @flags: flags for command submission from target_sc_flags_tables * @@ -2606,7 +2606,8 @@ int transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks) } EXPORT_SYMBOL(transport_generic_free_cmd); -/* target_get_sess_cmd - Add command to active ->sess_cmd_list +/** + * target_get_sess_cmd - Add command to active ->sess_cmd_list * @se_cmd: command descriptor to add * @ack_kref: Signal that fabric will perform an ack target_put_sess_cmd() */ @@ -2800,7 +2801,8 @@ void target_show_cmd(const char *pfx, struct se_cmd *cmd) } EXPORT_SYMBOL(target_show_cmd); -/* target_sess_cmd_list_set_waiting - Flag all commands in +/** + * target_sess_cmd_list_set_waiting - Flag all commands in * sess_cmd_list to complete cmd_wait_comp. Set * sess_tearing_down so no more commands are queued. * @se_sess: session to flag @@ -2835,7 +2837,8 @@ void target_sess_cmd_list_set_waiting(struct se_session *se_sess) } EXPORT_SYMBOL(target_sess_cmd_list_set_waiting); -/* target_wait_for_sess_cmds - Wait for outstanding descriptors +/** + * target_wait_for_sess_cmds - Wait for outstanding descriptors * @se_sess: session to wait for active I/O */ void target_wait_for_sess_cmds(struct se_session *se_sess) -- cgit v1.2.3 From 572ccdab50bb3ae9096d6947c2e78a7107acf2dd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 14 Apr 2018 10:51:05 -0700 Subject: scsi: target: target_core_user.[ch]: convert comments into DOC: Make documentation on target-supported userspace-I/O design be usable by kernel-doc by using "DOC:". This is used in the driver-api Documentation chapter. Signed-off-by: Randy Dunlap To: "Nicholas A. Bellinger" Cc: linux-scsi@vger.kernel.org Cc: target-devel@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: Jonathan Corbet Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 8 ++++++-- include/uapi/linux/target_core_user.h | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index d6af29250d94..ae0aea9a3aad 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -42,7 +42,11 @@ #include -/* +/** + * DOC: Userspace I/O + * Userspace I/O + * ------------- + * * Define a shared-memory interface for LIO to pass SCSI commands and * data to userspace for processing. This is to allow backends that * are too complex for in-kernel support to be possible. @@ -53,7 +57,7 @@ * See the .h file for how the ring is laid out. Note that while the * command ring is defined, the particulars of the data area are * not. Offset values in the command entry point to other locations - * internal to the mmap()ed area. There is separate space outside the + * internal to the mmap-ed area. There is separate space outside the * command ring for data buffers. This leaves maximum flexibility for * moving buffer allocations, or even page flipping or other * allocation techniques, without altering the command ring layout. diff --git a/include/uapi/linux/target_core_user.h b/include/uapi/linux/target_core_user.h index 0be80f72646b..6e299349b158 100644 --- a/include/uapi/linux/target_core_user.h +++ b/include/uapi/linux/target_core_user.h @@ -9,21 +9,22 @@ #define TCMU_VERSION "2.0" -/* +/** + * DOC: Ring Design * Ring Design * ----------- * * The mmaped area is divided into three parts: - * 1) The mailbox (struct tcmu_mailbox, below) - * 2) The command ring - * 3) Everything beyond the command ring (data) + * 1) The mailbox (struct tcmu_mailbox, below); + * 2) The command ring; + * 3) Everything beyond the command ring (data). * * The mailbox tells userspace the offset of the command ring from the * start of the shared memory region, and how big the command ring is. * * The kernel passes SCSI commands to userspace by putting a struct * tcmu_cmd_entry in the ring, updating mailbox->cmd_head, and poking - * userspace via uio's interrupt mechanism. + * userspace via UIO's interrupt mechanism. * * tcmu_cmd_entry contains a header. If the header type is PAD, * userspace should skip hdr->length bytes (mod cmdr_size) to find the -- cgit v1.2.3 From 6a59fd776cf44201c5c2ea1a98756083ed331f60 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 14 Apr 2018 10:51:06 -0700 Subject: scsi: target: add driver-api document Add a driver-api document for target/iSCSI interfaces. Signed-off-by: Randy Dunlap To: "Nicholas A. Bellinger" Cc: linux-scsi@vger.kernel.org Cc: target-devel@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: "James E.J. Bottomley" Cc: "Martin K. Petersen" Cc: Jonathan Corbet Signed-off-by: Martin K. Petersen --- Documentation/driver-api/index.rst | 1 + Documentation/driver-api/scsi.rst | 2 +- Documentation/driver-api/target.rst | 64 +++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 Documentation/driver-api/target.rst diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst index 6d8352c0f354..f7aca562f267 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -34,6 +34,7 @@ available subsections can be seen below. edac scsi libata + target mtdnand miscellaneous w1 diff --git a/Documentation/driver-api/scsi.rst b/Documentation/driver-api/scsi.rst index 31ad0fed6763..64b231d125e0 100644 --- a/Documentation/driver-api/scsi.rst +++ b/Documentation/driver-api/scsi.rst @@ -334,5 +334,5 @@ todo ~~~~ Parallel (fast/wide/ultra) SCSI, USB, SATA, SAS, Fibre Channel, -FireWire, ATAPI devices, Infiniband, I2O, iSCSI, Parallel ports, +FireWire, ATAPI devices, Infiniband, I2O, Parallel ports, netlink... diff --git a/Documentation/driver-api/target.rst b/Documentation/driver-api/target.rst new file mode 100644 index 000000000000..4363611dd86d --- /dev/null +++ b/Documentation/driver-api/target.rst @@ -0,0 +1,64 @@ +================================= +target and iSCSI Interfaces Guide +================================= + +Introduction and Overview +========================= + +TBD + +Target core device interfaces +============================= + +.. kernel-doc:: drivers/target/target_core_device.c + :export: + +Target core transport interfaces +================================ + +.. kernel-doc:: drivers/target/target_core_transport.c + :export: + +Target-supported userspace I/O +============================== + +.. kernel-doc:: drivers/target/target_core_user.c + :doc: Userspace I/O + +.. kernel-doc:: include/uapi/linux/target_core_user.h + :doc: Ring Design + +iSCSI helper functions +====================== + +.. kernel-doc:: drivers/scsi/libiscsi.c + :export: + + +iSCSI boot information +====================== + +.. kernel-doc:: drivers/scsi/iscsi_boot_sysfs.c + :export: + + +iSCSI transport class +===================== + +The file drivers/scsi/scsi_transport_iscsi.c defines transport +attributes for the iSCSI class, which sends SCSI packets over TCP/IP +connections. + +.. kernel-doc:: drivers/scsi/scsi_transport_iscsi.c + :export: + + +iSCSI TCP interfaces +==================== + +.. kernel-doc:: drivers/scsi/iscsi_tcp.c + :internal: + +.. kernel-doc:: drivers/scsi/libiscsi_tcp.c + :export: + -- cgit v1.2.3 From f9c25ccfc173dff6de4d68bd998e1a9fa93c5cab Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 29 Mar 2018 19:43:11 +0800 Subject: scsi: mvumi: Using module_pci_driver Remove boilerplate code by using macro module_pci_driver. Signed-off-by: YueHaibing Signed-off-by: Martin K. Petersen --- drivers/scsi/mvumi.c | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c index fe97401ad192..2e6fd864723b 100644 --- a/drivers/scsi/mvumi.c +++ b/drivers/scsi/mvumi.c @@ -2693,22 +2693,4 @@ static struct pci_driver mvumi_pci_driver = { #endif }; -/** - * mvumi_init - Driver load entry point - */ -static int __init mvumi_init(void) -{ - return pci_register_driver(&mvumi_pci_driver); -} - -/** - * mvumi_exit - Driver unload entry point - */ -static void __exit mvumi_exit(void) -{ - - pci_unregister_driver(&mvumi_pci_driver); -} - -module_init(mvumi_init); -module_exit(mvumi_exit); +module_pci_driver(mvumi_pci_driver); -- cgit v1.2.3 From feeeca4ce2942b92db90e12a8e2baf0e6e3f0191 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Apr 2018 18:02:09 +0200 Subject: scsi: esas2r: use ktime_get_real_seconds() do_gettimeofday() is deprecated because of the y2038 overflow. Here, we use the result to pass into a 32-bit field in the firmware, which still risks an overflow, but if the firmware is written to expect unsigned values, it can at least last until y2106, and there is not much we can do about it. This changes do_gettimeofday() to ktime_get_real_seconds(), which at least simplifies the code a bit, and avoids the deprecated interface. I'm adding a comment about the overflow to document what happens. Signed-off-by: Arnd Bergmann Signed-off-by: Martin K. Petersen --- drivers/scsi/esas2r/esas2r_init.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c index 9dffcb28c9b7..9db645dde35e 100644 --- a/drivers/scsi/esas2r/esas2r_init.c +++ b/drivers/scsi/esas2r/esas2r_init.c @@ -1202,8 +1202,6 @@ static bool esas2r_format_init_msg(struct esas2r_adapter *a, case ESAS2R_INIT_MSG_START: case ESAS2R_INIT_MSG_REINIT: { - struct timeval now; - do_gettimeofday(&now); esas2r_hdebug("CFG init"); esas2r_build_cfg_req(a, rq, @@ -1212,7 +1210,8 @@ static bool esas2r_format_init_msg(struct esas2r_adapter *a, NULL); ci = (struct atto_vda_cfg_init *)&rq->vrq->cfg.data.init; ci->sgl_page_size = cpu_to_le32(sgl_page_size); - ci->epoch_time = cpu_to_le32(now.tv_sec); + /* firmware interface overflows in y2106 */ + ci->epoch_time = cpu_to_le32(ktime_get_real_seconds()); rq->flags |= RF_FAILURE_OK; a->init_msg = ESAS2R_INIT_MSG_INIT; break; -- cgit v1.2.3 From f990bee3f1871616d7b255b374ca33212999201e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Apr 2018 18:04:40 +0200 Subject: scsi: ips: fix firmware timestamps for 32-bit do_gettimeofday() is deprecated since it will stop working in 2038 on 32-bit platforms, leading to incorrect times passed to the firmware. On 64-bit platforms the current code appears to be fine, as the calculation passes an 8-bit century number into the firmware that can represent times long in the future (possibly until 25599). Using ktime_get_real_seconds() to get a 64-bit seconds value and time64_to_tm() to convert it into the firmware format greatly simplifies the ips timekeeping code, makes 32-bit and 64-bit behave the same way here, and gets us closer to removing the deprecated interfaces. Signed-off-by: Arnd Bergmann Signed-off-by: Martin K. Petersen --- drivers/scsi/ips.c | 78 +++++++++++------------------------------------------- drivers/scsi/ips.h | 11 +------- 2 files changed, 17 insertions(+), 72 deletions(-) diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index e3c8857741a1..bd6ac6b5980a 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -291,7 +291,7 @@ static void ips_freescb(ips_ha_t *, ips_scb_t *); static void ips_setup_funclist(ips_ha_t *); static void ips_statinit(ips_ha_t *); static void ips_statinit_memio(ips_ha_t *); -static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time_t); +static void ips_fix_ffdc_time(ips_ha_t *, ips_scb_t *, time64_t); static void ips_ffdc_reset(ips_ha_t *, int); static void ips_ffdc_time(ips_ha_t *); static uint32_t ips_statupd_copperhead(ips_ha_t *); @@ -985,10 +985,7 @@ static int __ips_eh_reset(struct scsi_cmnd *SC) /* FFDC */ if (le32_to_cpu(ha->subsys->param[3]) & 0x300000) { - struct timeval tv; - - do_gettimeofday(&tv); - ha->last_ffdc = tv.tv_sec; + ha->last_ffdc = ktime_get_real_seconds(); ha->reset_count++; ips_ffdc_reset(ha, IPS_INTR_IORL); } @@ -2392,7 +2389,6 @@ static int ips_hainit(ips_ha_t * ha) { int i; - struct timeval tv; METHOD_TRACE("ips_hainit", 1); @@ -2407,8 +2403,7 @@ ips_hainit(ips_ha_t * ha) /* Send FFDC */ ha->reset_count = 1; - do_gettimeofday(&tv); - ha->last_ffdc = tv.tv_sec; + ha->last_ffdc = ktime_get_real_seconds(); ips_ffdc_reset(ha, IPS_INTR_IORL); if (!ips_read_config(ha, IPS_INTR_IORL)) { @@ -2548,12 +2543,9 @@ ips_next(ips_ha_t * ha, int intr) if ((ha->subsys->param[3] & 0x300000) && (ha->scb_activelist.count == 0)) { - struct timeval tv; - - do_gettimeofday(&tv); - - if (tv.tv_sec - ha->last_ffdc > IPS_SECS_8HOURS) { - ha->last_ffdc = tv.tv_sec; + time64_t now = ktime_get_real_seconds(); + if (now - ha->last_ffdc > IPS_SECS_8HOURS) { + ha->last_ffdc = now; ips_ffdc_time(ha); } } @@ -5988,59 +5980,21 @@ ips_ffdc_time(ips_ha_t * ha) /* */ /****************************************************************************/ static void -ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time_t current_time) +ips_fix_ffdc_time(ips_ha_t * ha, ips_scb_t * scb, time64_t current_time) { - long days; - long rem; - int i; - int year; - int yleap; - int year_lengths[2] = { IPS_DAYS_NORMAL_YEAR, IPS_DAYS_LEAP_YEAR }; - int month_lengths[12][2] = { {31, 31}, - {28, 29}, - {31, 31}, - {30, 30}, - {31, 31}, - {30, 30}, - {31, 31}, - {31, 31}, - {30, 30}, - {31, 31}, - {30, 30}, - {31, 31} - }; + struct tm tm; METHOD_TRACE("ips_fix_ffdc_time", 1); - days = current_time / IPS_SECS_DAY; - rem = current_time % IPS_SECS_DAY; - - scb->cmd.ffdc.hour = (rem / IPS_SECS_HOUR); - rem = rem % IPS_SECS_HOUR; - scb->cmd.ffdc.minute = (rem / IPS_SECS_MIN); - scb->cmd.ffdc.second = (rem % IPS_SECS_MIN); - - year = IPS_EPOCH_YEAR; - while (days < 0 || days >= year_lengths[yleap = IPS_IS_LEAP_YEAR(year)]) { - int newy; - - newy = year + (days / IPS_DAYS_NORMAL_YEAR); - if (days < 0) - --newy; - days -= (newy - year) * IPS_DAYS_NORMAL_YEAR + - IPS_NUM_LEAP_YEARS_THROUGH(newy - 1) - - IPS_NUM_LEAP_YEARS_THROUGH(year - 1); - year = newy; - } - - scb->cmd.ffdc.yearH = year / 100; - scb->cmd.ffdc.yearL = year % 100; - - for (i = 0; days >= month_lengths[i][yleap]; ++i) - days -= month_lengths[i][yleap]; + time64_to_tm(current_time, 0, &tm); - scb->cmd.ffdc.month = i + 1; - scb->cmd.ffdc.day = days + 1; + scb->cmd.ffdc.hour = tm.tm_hour; + scb->cmd.ffdc.minute = tm.tm_min; + scb->cmd.ffdc.second = tm.tm_sec; + scb->cmd.ffdc.yearH = (tm.tm_year + 1900) / 100; + scb->cmd.ffdc.yearL = tm.tm_year % 100; + scb->cmd.ffdc.month = tm.tm_mon + 1; + scb->cmd.ffdc.day = tm.tm_mday; } /**************************************************************************** diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index 366be3b2f9b4..db546171e97f 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -402,16 +402,7 @@ #define IPS_BIOS_HEADER 0xC0 /* time oriented stuff */ - #define IPS_IS_LEAP_YEAR(y) (((y % 4 == 0) && ((y % 100 != 0) || (y % 400 == 0))) ? 1 : 0) - #define IPS_NUM_LEAP_YEARS_THROUGH(y) ((y) / 4 - (y) / 100 + (y) / 400) - - #define IPS_SECS_MIN 60 - #define IPS_SECS_HOUR 3600 #define IPS_SECS_8HOURS 28800 - #define IPS_SECS_DAY 86400 - #define IPS_DAYS_NORMAL_YEAR 365 - #define IPS_DAYS_LEAP_YEAR 366 - #define IPS_EPOCH_YEAR 1970 /* * Scsi_Host Template @@ -1054,7 +1045,7 @@ typedef struct ips_ha { uint8_t active; int ioctl_reset; /* IOCTL Requested Reset Flag */ uint16_t reset_count; /* number of resets */ - time_t last_ffdc; /* last time we sent ffdc info*/ + time64_t last_ffdc; /* last time we sent ffdc info*/ uint8_t slot_num; /* PCI Slot Number */ int ioctl_len; /* size of ioctl buffer */ dma_addr_t ioctl_busaddr; /* dma address of ioctl buffer*/ -- cgit v1.2.3 From 63aed100e2416256c8830285174a08a0cfb47aad Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 15 Apr 2018 16:52:37 +0200 Subject: scsi: scsi_transport_sas: don't bounce highmem pages for the smp handler All three instance of ->smp_handler deal with highmem backed requests just fine. Signed-off-by: Christoph Hellwig Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_sas.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 08acbabfae07..a22baf206071 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -223,10 +223,6 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) to_sas_host_attrs(shost)->q = q; } - /* - * by default assume old behaviour and bounce for any highmem page - */ - blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH); blk_queue_flag_set(QUEUE_FLAG_BIDI, q); return 0; } -- cgit v1.2.3 From 5f1c7211964d8da54e65da768b95a43e57f39193 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 21 Apr 2018 18:58:33 +0800 Subject: scsi: am53c974: Use module_pci_driver Remove boilerplate code by using macro module_pci_driver. Signed-off-by: YueHaibing Signed-off-by: Martin K. Petersen --- drivers/scsi/am53c974.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/scsi/am53c974.c b/drivers/scsi/am53c974.c index beea30e5a34a..d81ca66e24d6 100644 --- a/drivers/scsi/am53c974.c +++ b/drivers/scsi/am53c974.c @@ -556,15 +556,7 @@ static struct pci_driver am53c974_driver = { .remove = pci_esp_remove_one, }; -static int __init am53c974_module_init(void) -{ - return pci_register_driver(&am53c974_driver); -} - -static void __exit am53c974_module_exit(void) -{ - pci_unregister_driver(&am53c974_driver); -} +module_pci_driver(am53c974_driver); MODULE_DESCRIPTION("AM53C974 SCSI driver"); MODULE_AUTHOR("Hannes Reinecke "); @@ -577,6 +569,3 @@ MODULE_PARM_DESC(am53c974_debug, "Enable debugging"); module_param(am53c974_fenab, bool, 0444); MODULE_PARM_DESC(am53c974_fenab, "Enable 24-bit DMA transfer sizes"); - -module_init(am53c974_module_init); -module_exit(am53c974_module_exit); -- cgit v1.2.3 From 3e1bbc5685677eda6c19e3139aa7a6a7a9eb4066 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 21 Apr 2018 18:58:34 +0800 Subject: scsi: wd719x: Use module_pci_driver Remove boilerplate code by using macro module_pci_driver. Signed-off-by: YueHaibing Signed-off-by: Martin K. Petersen --- drivers/scsi/wd719x.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/scsi/wd719x.c b/drivers/scsi/wd719x.c index 2ba2b7b47f41..974bfb3f30f4 100644 --- a/drivers/scsi/wd719x.c +++ b/drivers/scsi/wd719x.c @@ -978,18 +978,7 @@ static struct pci_driver wd719x_pci_driver = { .remove = wd719x_pci_remove, }; -static int __init wd719x_init(void) -{ - return pci_register_driver(&wd719x_pci_driver); -} - -static void __exit wd719x_exit(void) -{ - pci_unregister_driver(&wd719x_pci_driver); -} - -module_init(wd719x_init); -module_exit(wd719x_exit); +module_pci_driver(wd719x_pci_driver); MODULE_DESCRIPTION("Western Digital WD7193/7197/7296 SCSI driver"); MODULE_AUTHOR("Ondrej Zary, Aaron Dewell, Juergen Gaertner"); -- cgit v1.2.3 From 9407253f4af772464608c0c15a6dca47b06cbd31 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Sat, 21 Apr 2018 18:58:35 +0800 Subject: scsi: a100u2w: Use module_pci_driver Remove boilerplate code by using macro module_pci_driver. Signed-off-by: YueHaibing Signed-off-by: Martin K. Petersen --- drivers/scsi/a100u2w.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c index 8086bd0ac9fd..b2942ec3d455 100644 --- a/drivers/scsi/a100u2w.c +++ b/drivers/scsi/a100u2w.c @@ -1222,19 +1222,8 @@ static struct pci_driver inia100_pci_driver = { .remove = inia100_remove_one, }; -static int __init inia100_init(void) -{ - return pci_register_driver(&inia100_pci_driver); -} - -static void __exit inia100_exit(void) -{ - pci_unregister_driver(&inia100_pci_driver); -} +module_pci_driver(inia100_pci_driver); MODULE_DESCRIPTION("Initio A100U2W SCSI driver"); MODULE_AUTHOR("Initio Corporation"); MODULE_LICENSE("Dual BSD/GPL"); - -module_init(inia100_init); -module_exit(inia100_exit); -- cgit v1.2.3 From 35dc0b07b3b115298768f261caa76b5b2586fad7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 25 Apr 2018 11:58:43 +0100 Subject: scsi: isci: remove redundant check on in_connection_align_insertion_frequency The sanity check on u->in_connection_align_insertion_frequency is being performed twice and hence the first check can be removed since it is redundant. Cleans up cppcheck warning: drivers/scsi/ibmvscsi/ibmvscsi.c:1711: (warning) Identical inner 'if' condition is always true. Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/isci/init.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c index 922e3e56c90d..05cf4daf8788 100644 --- a/drivers/scsi/isci/init.c +++ b/drivers/scsi/isci/init.c @@ -433,9 +433,6 @@ static enum sci_status sci_user_parameters_set(struct isci_host *ihost, (u->max_speed_generation > SCIC_SDS_PARM_NO_SPEED))) return SCI_FAILURE_INVALID_PARAMETER_VALUE; - if (u->in_connection_align_insertion_frequency < 3) - return SCI_FAILURE_INVALID_PARAMETER_VALUE; - if ((u->in_connection_align_insertion_frequency < 3) || (u->align_insertion_frequency == 0) || (u->notify_enable_spin_up_insertion_frequency == 0)) -- cgit v1.2.3 From 1895bd7bcee8199ce5afae50c963fc5a4c7d8b8f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 27 Apr 2018 20:15:52 +0100 Subject: scsi: esas2r: fix spelling mistake: "asynchromous" -> "asynchronous" Trivial fix to spelling mistake in module description text Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/esas2r/esas2r_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/esas2r/esas2r_main.c b/drivers/scsi/esas2r/esas2r_main.c index e07eac5be087..c07118617d89 100644 --- a/drivers/scsi/esas2r/esas2r_main.c +++ b/drivers/scsi/esas2r/esas2r_main.c @@ -283,7 +283,7 @@ MODULE_PARM_DESC(num_requests, int num_ae_requests = 4; module_param(num_ae_requests, int, 0); MODULE_PARM_DESC(num_ae_requests, - "Number of VDA asynchromous event requests. Default 4."); + "Number of VDA asynchronous event requests. Default 4."); int cmd_per_lun = ESAS2R_DEFAULT_CMD_PER_LUN; module_param(cmd_per_lun, int, 0); -- cgit v1.2.3 From 55d9a1d241513fbc90565d81beac756c10837ae9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 29 Apr 2018 13:25:32 +0100 Subject: scsi: megaraid_sas: fix spelling mistake: "disbale" -> "disable" Trivial fix to spelling mistake in module parameter description text [mkp: applied by hand] Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 026fad818b2a..55c38bcf3c73 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -92,7 +92,7 @@ MODULE_PARM_DESC(resetwaittime, "Wait time in seconds after I/O timeout " int smp_affinity_enable = 1; module_param(smp_affinity_enable, int, S_IRUGO); -MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Default: enable(1)"); +MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)"); int rdpq_enable = 1; module_param(rdpq_enable, int, S_IRUGO); -- cgit v1.2.3 From 23b389c23139b80f5944a6d89e269974aaa9d139 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 29 Apr 2018 13:31:49 +0100 Subject: scsi: mpt3sas: fix spelling mistake: "disbale" -> "disable" Trivial fix to spelling mistake in module parameter description text [mkp: applied by hand] Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 61f93a134956..4edf1fc0e80c 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -87,7 +87,7 @@ MODULE_PARM_DESC(msix_disable, " disable msix routed interrupts (default=0)"); static int smp_affinity_enable = 1; module_param(smp_affinity_enable, int, S_IRUGO); -MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disbale Default: enable(1)"); +MODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)"); static int max_msix_vectors = -1; module_param(max_msix_vectors, int, 0); -- cgit v1.2.3 From cf6bf9710cabba1fe94a4349f4eb8db623c77ebc Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:30 -0400 Subject: scsi: mpt3sas: Bug fix for big endian systems. This patch fixes sparse warnings and bugs on big endian systems. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpi/mpi2_init.h | 2 +- drivers/scsi/mpt3sas/mpt3sas_base.c | 55 ++++++++++++++++------------- drivers/scsi/mpt3sas/mpt3sas_base.h | 4 +-- drivers/scsi/mpt3sas/mpt3sas_ctl.c | 11 +++--- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 59 +++++++++++++++----------------- drivers/scsi/mpt3sas/mpt3sas_warpdrive.c | 3 +- 6 files changed, 71 insertions(+), 63 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_init.h b/drivers/scsi/mpt3sas/mpi/mpi2_init.h index 948a3ba682d7..6213ce6791ac 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_init.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_init.h @@ -75,7 +75,7 @@ typedef struct _MPI2_SCSI_IO_CDB_EEDP32 { U8 CDB[20]; /*0x00 */ - U32 PrimaryReferenceTag; /*0x14 */ + __be32 PrimaryReferenceTag; /*0x14 */ U16 PrimaryApplicationTag; /*0x18 */ U16 PrimaryApplicationTagMask; /*0x1A */ U32 TransferLength; /*0x1C */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 4edf1fc0e80c..ca17915b2d1b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -394,13 +394,14 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, buff_ptr_phys = buffer_iomem_phys; WARN_ON(buff_ptr_phys > U32_MAX); - if (sgel->FlagsLength & + if (le32_to_cpu(sgel->FlagsLength) & (MPI2_SGE_FLAGS_HOST_TO_IOC << MPI2_SGE_FLAGS_SHIFT)) is_write = 1; for (i = 0; i < MPT_MIN_PHYS_SEGMENTS + ioc->facts.MaxChainDepth; i++) { - sgl_flags = (sgel->FlagsLength >> MPI2_SGE_FLAGS_SHIFT); + sgl_flags = + (le32_to_cpu(sgel->FlagsLength) >> MPI2_SGE_FLAGS_SHIFT); switch (sgl_flags & MPI2_SGE_FLAGS_ELEMENT_MASK) { case MPI2_SGE_FLAGS_CHAIN_ELEMENT: @@ -411,7 +412,7 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, */ sgel_next = _base_get_chain_buffer_dma_to_chain_buffer(ioc, - sgel->Address); + le32_to_cpu(sgel->Address)); if (sgel_next == NULL) return; /* @@ -426,7 +427,8 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, dst_addr_phys = _base_get_chain_phys(ioc, smid, sge_chain_count); WARN_ON(dst_addr_phys > U32_MAX); - sgel->Address = (u32)dst_addr_phys; + sgel->Address = + cpu_to_le32(lower_32_bits(dst_addr_phys)); sgel = sgel_next; sge_chain_count++; break; @@ -435,22 +437,28 @@ static void _clone_sg_entries(struct MPT3SAS_ADAPTER *ioc, if (is_scsiio_req) { _base_clone_to_sys_mem(buff_ptr, sg_virt(sg_scmd), - (sgel->FlagsLength & 0x00ffffff)); + (le32_to_cpu(sgel->FlagsLength) & + 0x00ffffff)); /* * FIXME: this relies on a a zero * PCI mem_offset. */ - sgel->Address = (u32)buff_ptr_phys; + sgel->Address = + cpu_to_le32((u32)buff_ptr_phys); } else { _base_clone_to_sys_mem(buff_ptr, ioc->config_vaddr, - (sgel->FlagsLength & 0x00ffffff)); - sgel->Address = (u32)buff_ptr_phys; + (le32_to_cpu(sgel->FlagsLength) & + 0x00ffffff)); + sgel->Address = + cpu_to_le32((u32)buff_ptr_phys); } } - buff_ptr += (sgel->FlagsLength & 0x00ffffff); - buff_ptr_phys += (sgel->FlagsLength & 0x00ffffff); - if ((sgel->FlagsLength & + buff_ptr += (le32_to_cpu(sgel->FlagsLength) & + 0x00ffffff); + buff_ptr_phys += (le32_to_cpu(sgel->FlagsLength) & + 0x00ffffff); + if ((le32_to_cpu(sgel->FlagsLength) & (MPI2_SGE_FLAGS_END_OF_BUFFER << MPI2_SGE_FLAGS_SHIFT))) goto eob_clone_chain; @@ -1433,7 +1441,7 @@ _base_interrupt(int irq, void *bus_id) cpu_to_le32(reply); if (ioc->is_mcpu_endpoint) _base_clone_reply_to_sys_mem(ioc, - cpu_to_le32(reply), + reply, ioc->reply_free_host_index); writel(ioc->reply_free_host_index, &ioc->chip->ReplyFreeHostIndex); @@ -3044,7 +3052,7 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc) for (i = 0; i < ioc->combined_reply_index_count; i++) { ioc->replyPostRegisterIndex[i] = (resource_size_t *) - ((u8 *)&ioc->chip->Doorbell + + ((u8 __force *)&ioc->chip->Doorbell + MPI25_SUP_REPLY_POST_HOST_INDEX_OFFSET + (i * MPT3_SUP_REPLY_POST_HOST_INDEX_REG_OFFSET)); } @@ -3339,7 +3347,7 @@ _base_mpi_ep_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) { unsigned long flags; - __u64 data_out = cpu_to_le64(b); + __u64 data_out = b; spin_lock_irqsave(writeq_lock, flags); writel((u32)(data_out), addr); @@ -3362,7 +3370,7 @@ _base_mpi_ep_writeq(__u64 b, volatile void __iomem *addr, static inline void _base_writeq(__u64 b, volatile void __iomem *addr, spinlock_t *writeq_lock) { - writeq(cpu_to_le64(b), addr); + writeq(b, addr); } #else static inline void @@ -3389,7 +3397,7 @@ _base_put_smid_mpi_ep_scsi_io(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 handle) __le32 *mfp = (__le32 *)mpt3sas_base_get_msg_frame(ioc, smid); _clone_sg_entries(ioc, (void *) mfp, smid); - mpi_req_iomem = (void *)ioc->chip + + mpi_req_iomem = (void __force *)ioc->chip + MPI_FRAME_START_OFFSET + (smid * ioc->request_sz); _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp, ioc->request_sz); @@ -3473,7 +3481,8 @@ mpt3sas_base_put_smid_hi_priority(struct MPT3SAS_ADAPTER *ioc, u16 smid, request_hdr = (MPI2RequestHeader_t *)mfp; /* TBD 256 is offset within sys register. */ - mpi_req_iomem = (void *)ioc->chip + MPI_FRAME_START_OFFSET + mpi_req_iomem = (void __force *)ioc->chip + + MPI_FRAME_START_OFFSET + (smid * ioc->request_sz); _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp, ioc->request_sz); @@ -3542,7 +3551,7 @@ mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid) _clone_sg_entries(ioc, (void *) mfp, smid); /* TBD 256 is offset within sys register */ - mpi_req_iomem = (void *)ioc->chip + + mpi_req_iomem = (void __force *)ioc->chip + MPI_FRAME_START_OFFSET + (smid * ioc->request_sz); _base_clone_mpi_to_sys_mem(mpi_req_iomem, (void *)mfp, ioc->request_sz); @@ -5002,7 +5011,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, /* send message 32-bits at a time */ for (i = 0, failed = 0; i < request_bytes/4 && !failed; i++) { - writel(cpu_to_le32(request[i]), &ioc->chip->Doorbell); + writel((u32)(request[i]), &ioc->chip->Doorbell); if ((_base_wait_for_doorbell_ack(ioc, 5))) failed = 1; } @@ -5023,7 +5032,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, } /* read the first two 16-bits, it gives the total length of the reply */ - reply[0] = le16_to_cpu(readl(&ioc->chip->Doorbell) + reply[0] = (u16)(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); if ((_base_wait_for_doorbell_int(ioc, 5))) { @@ -5032,7 +5041,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, ioc->name, __LINE__); return -EFAULT; } - reply[1] = le16_to_cpu(readl(&ioc->chip->Doorbell) + reply[1] = (u16)(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); @@ -5046,7 +5055,7 @@ _base_handshake_req_reply_wait(struct MPT3SAS_ADAPTER *ioc, int request_bytes, if (i >= reply_bytes/2) /* overflow case */ readl(&ioc->chip->Doorbell); else - reply[i] = le16_to_cpu(readl(&ioc->chip->Doorbell) + reply[i] = (u16)(readl(&ioc->chip->Doorbell) & MPI2_DOORBELL_DATA_MASK); writel(0, &ioc->chip->HostInterruptStatus); } @@ -6172,7 +6181,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) ioc->reply_free[i] = cpu_to_le32(reply_address); if (ioc->is_mcpu_endpoint) _base_clone_reply_to_sys_mem(ioc, - (__le32)reply_address, i); + reply_address, i); } /* initialize reply queues */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index ae36d8fb2f2b..331210523f1a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -829,8 +829,8 @@ struct _sc_list { */ struct _event_ack_list { struct list_head list; - u16 Event; - u32 EventContext; + U16 Event; + U32 EventContext; }; /** diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index d3cb387ba9f4..c1b17d64c95f 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -297,7 +297,7 @@ mpt3sas_ctl_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, nvme_error_reply = (Mpi26NVMeEncapsulatedErrorReply_t *)mpi_reply; sz = min_t(u32, NVME_ERROR_RESPONSE_SIZE, - le32_to_cpu(nvme_error_reply->ErrorResponseCount)); + le16_to_cpu(nvme_error_reply->ErrorResponseCount)); sense_data = mpt3sas_base_get_sense_buffer(ioc, smid); memcpy(ioc->ctl_cmds.sense, sense_data, sz); } @@ -803,12 +803,13 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, * Build the PRPs and set direction bits. * Send the request. */ - nvme_encap_request->ErrorResponseBaseAddress = ioc->sense_dma & - 0xFFFFFFFF00000000; + nvme_encap_request->ErrorResponseBaseAddress = + cpu_to_le64(ioc->sense_dma & 0xFFFFFFFF00000000UL); nvme_encap_request->ErrorResponseBaseAddress |= - (U64)mpt3sas_base_get_sense_buffer_dma(ioc, smid); + cpu_to_le64(le32_to_cpu( + mpt3sas_base_get_sense_buffer_dma(ioc, smid))); nvme_encap_request->ErrorResponseAllocationLength = - NVME_ERROR_RESPONSE_SIZE; + cpu_to_le16(NVME_ERROR_RESPONSE_SIZE); memset(ioc->ctl_cmds.sense, 0, NVME_ERROR_RESPONSE_SIZE); ioc->build_nvme_prp(ioc, smid, nvme_encap_request, data_out_dma, data_out_sz, data_in_dma, data_in_sz); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 8cd3782fab49..0319b34ebf6c 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -157,8 +157,8 @@ MODULE_PARM_DESC(prot_mask, " host protection capabilities mask, def=7 "); /* raid transport support */ -struct raid_template *mpt3sas_raid_template; -struct raid_template *mpt2sas_raid_template; +static struct raid_template *mpt3sas_raid_template; +static struct raid_template *mpt2sas_raid_template; /** @@ -3725,7 +3725,7 @@ _scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, if (!delayed_sc) return _scsih_check_for_pending_tm(ioc, smid); INIT_LIST_HEAD(&delayed_sc->list); - delayed_sc->handle = mpi_request_tm->DevHandle; + delayed_sc->handle = le16_to_cpu(mpi_request_tm->DevHandle); list_add_tail(&delayed_sc->list, &ioc->delayed_sc_list); dewtprintk(ioc, pr_info(MPT3SAS_FMT "DELAYED:sc:handle(0x%04x), (open)\n", @@ -3903,8 +3903,8 @@ _scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, * Context - processed in interrupt context. */ static void -_scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, u16 event, - u32 event_context) +_scsih_issue_delayed_event_ack(struct MPT3SAS_ADAPTER *ioc, u16 smid, U16 event, + U32 event_context) { Mpi2EventAckRequest_t *ack_request; int i = smid - ioc->internal_smid; @@ -3979,13 +3979,13 @@ _scsih_issue_delayed_sas_io_unit_ctrl(struct MPT3SAS_ADAPTER *ioc, dewtprintk(ioc, pr_info(MPT3SAS_FMT "sc_send:handle(0x%04x), (open), smid(%d), cb(%d)\n", - ioc->name, le16_to_cpu(handle), smid, + ioc->name, handle, smid, ioc->tm_sas_control_cb_idx)); mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t)); mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; - mpi_request->DevHandle = handle; + mpi_request->DevHandle = cpu_to_le16(handle); mpt3sas_base_put_smid_default(ioc, smid); } @@ -6108,7 +6108,7 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, if (sas_device_pg0.EnclosureHandle) { encl_pg0_rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - sas_device_pg0.EnclosureHandle); + le16_to_cpu(sas_device_pg0.EnclosureHandle)); if (encl_pg0_rc) pr_info(MPT3SAS_FMT "Enclosure Pg0 read failed for handle(0x%04x)\n", @@ -6917,7 +6917,7 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) if (pcie_device->enclosure_handle != 0) pcie_device->slot = le16_to_cpu(pcie_device_pg0.Slot); - if (le16_to_cpu(pcie_device_pg0.Flags) & + if (le32_to_cpu(pcie_device_pg0.Flags) & MPI26_PCIEDEV0_FLAGS_ENCL_LEVEL_VALID) { pcie_device->enclosure_level = pcie_device_pg0.EnclosureLevel; memcpy(&pcie_device->connector_name[0], @@ -8364,8 +8364,9 @@ Mpi2SasDevicePage0_t *sas_device_pg0) spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { - if ((sas_device->sas_address == sas_device_pg0->SASAddress) && - (sas_device->slot == sas_device_pg0->Slot)) { + if ((sas_device->sas_address == le64_to_cpu( + sas_device_pg0->SASAddress)) && (sas_device->slot == + le16_to_cpu(sas_device_pg0->Slot))) { sas_device->responding = 1; starget = sas_device->starget; if (starget && starget->hostdata) { @@ -8377,7 +8378,7 @@ Mpi2SasDevicePage0_t *sas_device_pg0) if (starget) { starget_printk(KERN_INFO, starget, "handle(0x%04x), sas_addr(0x%016llx)\n", - sas_device_pg0->DevHandle, + le16_to_cpu(sas_device_pg0->DevHandle), (unsigned long long) sas_device->sas_address); @@ -8389,7 +8390,7 @@ Mpi2SasDevicePage0_t *sas_device_pg0) sas_device->enclosure_logical_id, sas_device->slot); } - if (sas_device_pg0->Flags & + if (le16_to_cpu(sas_device_pg0->Flags) & MPI2_SAS_DEVICE0_FLAGS_ENCL_LEVEL_VALID) { sas_device->enclosure_level = sas_device_pg0->EnclosureLevel; @@ -8403,14 +8404,16 @@ Mpi2SasDevicePage0_t *sas_device_pg0) _scsih_get_enclosure_logicalid_chassis_slot(ioc, sas_device_pg0, sas_device); - if (sas_device->handle == sas_device_pg0->DevHandle) + if (sas_device->handle == le16_to_cpu( + sas_device_pg0->DevHandle)) goto out; pr_info("\thandle changed from(0x%04x)!!!\n", sas_device->handle); - sas_device->handle = sas_device_pg0->DevHandle; + sas_device->handle = le16_to_cpu( + sas_device_pg0->DevHandle); if (sas_target_priv_data) sas_target_priv_data->handle = - sas_device_pg0->DevHandle; + le16_to_cpu(sas_device_pg0->DevHandle); goto out; } } @@ -8449,15 +8452,10 @@ _scsih_search_responding_sas_devices(struct MPT3SAS_ADAPTER *ioc) MPI2_IOCSTATUS_MASK; if (ioc_status != MPI2_IOCSTATUS_SUCCESS) break; - handle = sas_device_pg0.DevHandle = - le16_to_cpu(sas_device_pg0.DevHandle); + handle = le16_to_cpu(sas_device_pg0.DevHandle); device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); if (!(_scsih_is_end_device(device_info))) continue; - sas_device_pg0.SASAddress = - le64_to_cpu(sas_device_pg0.SASAddress); - sas_device_pg0.Slot = le16_to_cpu(sas_device_pg0.Slot); - sas_device_pg0.Flags = le16_to_cpu(sas_device_pg0.Flags); _scsih_mark_responding_sas_device(ioc, &sas_device_pg0); } @@ -8487,8 +8485,9 @@ _scsih_mark_responding_pcie_device(struct MPT3SAS_ADAPTER *ioc, spin_lock_irqsave(&ioc->pcie_device_lock, flags); list_for_each_entry(pcie_device, &ioc->pcie_device_list, list) { - if ((pcie_device->wwid == pcie_device_pg0->WWID) && - (pcie_device->slot == pcie_device_pg0->Slot)) { + if ((pcie_device->wwid == le64_to_cpu(pcie_device_pg0->WWID)) + && (pcie_device->slot == le16_to_cpu( + pcie_device_pg0->Slot))) { pcie_device->responding = 1; starget = pcie_device->starget; if (starget && starget->hostdata) { @@ -8523,14 +8522,16 @@ _scsih_mark_responding_pcie_device(struct MPT3SAS_ADAPTER *ioc, pcie_device->connector_name[0] = '\0'; } - if (pcie_device->handle == pcie_device_pg0->DevHandle) + if (pcie_device->handle == le16_to_cpu( + pcie_device_pg0->DevHandle)) goto out; pr_info("\thandle changed from(0x%04x)!!!\n", pcie_device->handle); - pcie_device->handle = pcie_device_pg0->DevHandle; + pcie_device->handle = le16_to_cpu( + pcie_device_pg0->DevHandle); if (sas_target_priv_data) sas_target_priv_data->handle = - pcie_device_pg0->DevHandle; + le16_to_cpu(pcie_device_pg0->DevHandle); goto out; } } @@ -8579,10 +8580,6 @@ _scsih_search_responding_pcie_devices(struct MPT3SAS_ADAPTER *ioc) device_info = le32_to_cpu(pcie_device_pg0.DeviceInfo); if (!(_scsih_is_nvme_device(device_info))) continue; - pcie_device_pg0.WWID = le64_to_cpu(pcie_device_pg0.WWID), - pcie_device_pg0.Slot = le16_to_cpu(pcie_device_pg0.Slot); - pcie_device_pg0.Flags = le32_to_cpu(pcie_device_pg0.Flags); - pcie_device_pg0.DevHandle = handle; _scsih_mark_responding_pcie_device(ioc, &pcie_device_pg0); } out: diff --git a/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c b/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c index 6bfcee4757e0..45aa94915cbf 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c +++ b/drivers/scsi/mpt3sas/mpt3sas_warpdrive.c @@ -177,7 +177,8 @@ mpt3sas_init_warpdrive_properties(struct MPT3SAS_ADAPTER *ioc, if (mpt3sas_config_get_phys_disk_pg0(ioc, &mpi_reply, &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM, vol_pg0->PhysDisk[count].PhysDiskNum) || - pd_pg0.DevHandle == MPT3SAS_INVALID_DEVICE_HANDLE) { + le16_to_cpu(pd_pg0.DevHandle) == + MPT3SAS_INVALID_DEVICE_HANDLE) { pr_info(MPT3SAS_FMT "WarpDrive : Direct IO is " "disabled for the drive with handle(0x%04x) member" "handle retrieval failed for member number=%d\n", -- cgit v1.2.3 From cd33223b59a4938f9ae890bc5ae4196398e9abe0 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:31 -0400 Subject: scsi: mpt3sas: Pre-allocate RDPQ Array at driver boot time. Instead of allocating RDPQ array (This stores the address's of each RDPQ pools) at run time, now it will be allocated once during driver load time and same will be reused during host reset operation also (instead of allocating & freeing this buffer on the fly during every host reset operation) and then freed during driver unload. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 57 +++++++++++++++++++++++-------------- drivers/scsi/mpt3sas/mpt3sas_base.h | 3 ++ 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index ca17915b2d1b..7a7e32425293 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4162,7 +4162,14 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) } } while (ioc->rdpq_array_enable && (++i < ioc->reply_queue_count)); - + if (ioc->reply_post_free_array && + ioc->rdpq_array_enable) { + dma_pool_free(ioc->reply_post_free_array_dma_pool, + ioc->reply_post_free_array, + ioc->reply_post_free_array_dma); + ioc->reply_post_free_array = NULL; + } + dma_pool_destroy(ioc->reply_post_free_array_dma_pool); dma_pool_destroy(ioc->reply_post_free_dma_pool); kfree(ioc->reply_post); } @@ -4212,7 +4219,7 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) struct mpt3sas_facts *facts; u16 max_sge_elements; u16 chains_needed_per_io; - u32 sz, total_sz, reply_post_free_sz; + u32 sz, total_sz, reply_post_free_sz, reply_post_free_array_sz; u32 retry_sz; u16 max_request_credit, nvme_blocks_needed; unsigned short sg_tablesize; @@ -4684,6 +4691,28 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->name, (unsigned long long)ioc->reply_free_dma)); total_sz += sz; + if (ioc->rdpq_array_enable) { + reply_post_free_array_sz = ioc->reply_queue_count * + sizeof(Mpi2IOCInitRDPQArrayEntry); + ioc->reply_post_free_array_dma_pool = + dma_pool_create("reply_post_free_array pool", + &ioc->pdev->dev, reply_post_free_array_sz, 16, 0); + if (!ioc->reply_post_free_array_dma_pool) { + dinitprintk(ioc, + pr_info(MPT3SAS_FMT "reply_post_free_array pool: " + "dma_pool_create failed\n", ioc->name)); + goto out; + } + ioc->reply_post_free_array = + dma_pool_alloc(ioc->reply_post_free_array_dma_pool, + GFP_KERNEL, &ioc->reply_post_free_array_dma); + if (!ioc->reply_post_free_array) { + dinitprintk(ioc, + pr_info(MPT3SAS_FMT "reply_post_free_array pool: " + "dma_pool_alloc failed\n", ioc->name)); + goto out; + } + } ioc->config_page_sz = 512; ioc->config_page = pci_alloc_consistent(ioc->pdev, ioc->config_page_sz, &ioc->config_page_dma); @@ -5490,8 +5519,6 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) ktime_t current_time; u16 ioc_status; u32 reply_post_free_array_sz = 0; - Mpi2IOCInitRDPQArrayEntry *reply_post_free_array = NULL; - dma_addr_t reply_post_free_array_dma; dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -5525,23 +5552,14 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) if (ioc->rdpq_array_enable) { reply_post_free_array_sz = ioc->reply_queue_count * sizeof(Mpi2IOCInitRDPQArrayEntry); - reply_post_free_array = pci_alloc_consistent(ioc->pdev, - reply_post_free_array_sz, &reply_post_free_array_dma); - if (!reply_post_free_array) { - pr_err(MPT3SAS_FMT - "reply_post_free_array: pci_alloc_consistent failed\n", - ioc->name); - r = -ENOMEM; - goto out; - } - memset(reply_post_free_array, 0, reply_post_free_array_sz); + memset(ioc->reply_post_free_array, 0, reply_post_free_array_sz); for (i = 0; i < ioc->reply_queue_count; i++) - reply_post_free_array[i].RDPQBaseAddress = + ioc->reply_post_free_array[i].RDPQBaseAddress = cpu_to_le64( (u64)ioc->reply_post[i].reply_post_free_dma); mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE; mpi_request.ReplyDescriptorPostQueueAddress = - cpu_to_le64((u64)reply_post_free_array_dma); + cpu_to_le64((u64)ioc->reply_post_free_array_dma); } else { mpi_request.ReplyDescriptorPostQueueAddress = cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma); @@ -5571,7 +5589,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) if (r != 0) { pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n", ioc->name, __func__, r); - goto out; + return r; } ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; @@ -5581,11 +5599,6 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc) r = -EIO; } -out: - if (reply_post_free_array) - pci_free_consistent(ioc->pdev, reply_post_free_array_sz, - reply_post_free_array, - reply_post_free_array_dma); return r; } diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 331210523f1a..d31a1912d31b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -1315,6 +1315,9 @@ struct MPT3SAS_ADAPTER { u8 rdpq_array_enable; u8 rdpq_array_enable_assigned; struct dma_pool *reply_post_free_dma_pool; + struct dma_pool *reply_post_free_array_dma_pool; + Mpi2IOCInitRDPQArrayEntry *reply_post_free_array; + dma_addr_t reply_post_free_array_dma; u8 reply_queue_count; struct list_head reply_queue_list; -- cgit v1.2.3 From 93204b782a88f2067ff83cb732b66236644c4c56 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:32 -0400 Subject: scsi: mpt3sas: Lockless access for chain buffers. Introduces Chain lookup table/tracker and implements accessing chain buffer using smid. Removed link list based access of chain buffer which requires lock and allocated as many chains needed. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 111 +++++++++++++++++++----------------- drivers/scsi/mpt3sas/mpt3sas_base.h | 8 ++- 2 files changed, 65 insertions(+), 54 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 7a7e32425293..147524cce8d7 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -297,12 +297,15 @@ static void * _base_get_chain_buffer_dma_to_chain_buffer(struct MPT3SAS_ADAPTER *ioc, dma_addr_t chain_buffer_dma) { - u16 index; - - for (index = 0; index < ioc->chain_depth; index++) { - if (ioc->chain_lookup[index].chain_buffer_dma == - chain_buffer_dma) - return ioc->chain_lookup[index].chain_buffer; + u16 index, j; + struct chain_tracker *ct; + + for (index = 0; index < ioc->scsiio_depth; index++) { + for (j = 0; j < ioc->chains_needed_per_io; j++) { + ct = &ioc->chain_lookup[index].chains_per_smid[j]; + if (ct && ct->chain_buffer_dma == chain_buffer_dma) + return ct->chain_buffer; + } } pr_info(MPT3SAS_FMT "Provided chain_buffer_dma address is not in the lookup list\n", @@ -1679,7 +1682,8 @@ _base_add_sg_single_64(void *paddr, u32 flags_length, dma_addr_t dma_addr) * @ioc: per adapter object * @scmd: SCSI commands of the IO request * - * Returns chain tracker(from ioc->free_chain_list) + * Returns chain tracker from chain_lookup table using key as + * smid and smid's chain_offset. */ static struct chain_tracker * _base_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc, @@ -1687,20 +1691,15 @@ _base_get_chain_buffer_tracker(struct MPT3SAS_ADAPTER *ioc, { struct chain_tracker *chain_req; struct scsiio_tracker *st = scsi_cmd_priv(scmd); - unsigned long flags; + u16 smid = st->smid; + u8 chain_offset = + atomic_read(&ioc->chain_lookup[smid - 1].chain_offset); - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - if (list_empty(&ioc->free_chain_list)) { - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - dfailprintk(ioc, pr_warn(MPT3SAS_FMT - "chain buffers not available\n", ioc->name)); + if (chain_offset == ioc->chains_needed_per_io) return NULL; - } - chain_req = list_entry(ioc->free_chain_list.next, - struct chain_tracker, tracker_list); - list_del_init(&chain_req->tracker_list); - list_add_tail(&chain_req->tracker_list, &st->chain_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); + + chain_req = &ioc->chain_lookup[smid - 1].chains_per_smid[chain_offset]; + atomic_inc(&ioc->chain_lookup[smid - 1].chain_offset); return chain_req; } @@ -3281,13 +3280,7 @@ void mpt3sas_base_clear_st(struct MPT3SAS_ADAPTER *ioc, return; st->cb_idx = 0xFF; st->direct_io = 0; - if (!list_empty(&st->chain_list)) { - unsigned long flags; - - spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); - list_splice_init(&st->chain_list, &ioc->free_chain_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); - } + atomic_set(&ioc->chain_lookup[st->smid - 1].chain_offset, 0); } /** @@ -4105,6 +4098,8 @@ static void _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) { int i = 0; + int j = 0; + struct chain_tracker *ct; struct reply_post_struct *rps; dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, @@ -4195,14 +4190,18 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) kfree(ioc->hpr_lookup); kfree(ioc->internal_lookup); if (ioc->chain_lookup) { - for (i = 0; i < ioc->chain_depth; i++) { - if (ioc->chain_lookup[i].chain_buffer) - dma_pool_free(ioc->chain_dma_pool, - ioc->chain_lookup[i].chain_buffer, - ioc->chain_lookup[i].chain_buffer_dma); + for (i = 0; i < ioc->scsiio_depth; i++) { + for (j = 0; j < ioc->chains_needed_per_io; j++) { + ct = &ioc->chain_lookup[i].chains_per_smid[j]; + if (ct && ct->chain_buffer) + dma_pool_free(ioc->chain_dma_pool, + ct->chain_buffer, + ct->chain_buffer_dma); + } + kfree(ioc->chain_lookup[i].chains_per_smid); } dma_pool_destroy(ioc->chain_dma_pool); - free_pages((ulong)ioc->chain_lookup, ioc->chain_pages); + kfree(ioc->chain_lookup); ioc->chain_lookup = NULL; } } @@ -4224,7 +4223,8 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) u16 max_request_credit, nvme_blocks_needed; unsigned short sg_tablesize; u16 sge_size; - int i; + int i, j; + struct chain_tracker *ct; dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, __func__)); @@ -4505,15 +4505,24 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->name, ioc->request, ioc->scsiio_depth)); ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH); - sz = ioc->chain_depth * sizeof(struct chain_tracker); - ioc->chain_pages = get_order(sz); - ioc->chain_lookup = (struct chain_tracker *)__get_free_pages( - GFP_KERNEL, ioc->chain_pages); + sz = ioc->scsiio_depth * sizeof(struct chain_lookup); + ioc->chain_lookup = kzalloc(sz, GFP_KERNEL); if (!ioc->chain_lookup) { - pr_err(MPT3SAS_FMT "chain_lookup: __get_free_pages failed\n", - ioc->name); + pr_err(MPT3SAS_FMT "chain_lookup: __get_free_pages " + "failed\n", ioc->name); goto out; } + + sz = ioc->chains_needed_per_io * sizeof(struct chain_tracker); + for (i = 0; i < ioc->scsiio_depth; i++) { + ioc->chain_lookup[i].chains_per_smid = kzalloc(sz, GFP_KERNEL); + if (!ioc->chain_lookup[i].chains_per_smid) { + pr_err(MPT3SAS_FMT "chain_lookup: " + " kzalloc failed\n", ioc->name); + goto out; + } + } + ioc->chain_dma_pool = dma_pool_create("chain pool", &ioc->pdev->dev, ioc->chain_segment_sz, 16, 0); if (!ioc->chain_dma_pool) { @@ -4521,17 +4530,21 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->name); goto out; } - for (i = 0; i < ioc->chain_depth; i++) { - ioc->chain_lookup[i].chain_buffer = dma_pool_alloc( + for (i = 0; i < ioc->scsiio_depth; i++) { + for (j = 0; j < ioc->chains_needed_per_io; j++) { + ct = &ioc->chain_lookup[i].chains_per_smid[j]; + ct->chain_buffer = dma_pool_alloc( ioc->chain_dma_pool , GFP_KERNEL, - &ioc->chain_lookup[i].chain_buffer_dma); - if (!ioc->chain_lookup[i].chain_buffer) { - ioc->chain_depth = i; - goto chain_done; + &ct->chain_buffer_dma); + if (!ct->chain_buffer) { + pr_err(MPT3SAS_FMT "chain_lookup: " + " pci_pool_alloc failed\n", ioc->name); + goto out; + } } total_sz += ioc->chain_segment_sz; } - chain_done: + dinitprintk(ioc, pr_info(MPT3SAS_FMT "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", ioc->name, ioc->chain_depth, ioc->chain_segment_sz, @@ -6179,12 +6192,6 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) &ioc->internal_free_list); } - /* chain pool */ - INIT_LIST_HEAD(&ioc->free_chain_list); - for (i = 0; i < ioc->chain_depth; i++) - list_add_tail(&ioc->chain_lookup[i].tracker_list, - &ioc->free_chain_list); - spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); /* initialize Reply Free Queue */ diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index d31a1912d31b..eeeb3b711144 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -770,7 +770,11 @@ struct pcie_sg_list { struct chain_tracker { void *chain_buffer; dma_addr_t chain_buffer_dma; - struct list_head tracker_list; +}; + +struct chain_lookup { + struct chain_tracker *chains_per_smid; + atomic_t chain_offset; }; /** @@ -1261,7 +1265,7 @@ struct MPT3SAS_ADAPTER { u32 page_size; /* chain */ - struct chain_tracker *chain_lookup; + struct chain_lookup *chain_lookup; struct list_head free_chain_list; struct dma_pool *chain_dma_pool; ulong chain_pages; -- cgit v1.2.3 From 74522a92bbf003111852d77f1a95d961c1de7baf Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:33 -0400 Subject: scsi: mpt3sas: Optimize I/O memory consumption in driver. For every IO, memory of PAGE size is allocated for handling NVMe native PRPS. And in addition to that for every IO (chains need per IO * chain buffer size, e.g. 38 * 128byte) amount of memory is allocated for chain buffers. However, at any point of time; the IO request can be for NVMe target device (where PRP's page is used for framing PRP's) or can be for SCSI target device (where chain buffers are used for framing chain SGE's). This patch modifies the driver to reuse same pre-allocated PRP page buffers as a chain buffer for IO's targeted for SCSI target devices. No need to allocate separate buffers for chain SGE's buffers. Suppose if the number of chain buffers need for IO doesn't fit in the PRP Page size then driver maintain's separate buffers for those extra chain buffers that exceeds the PRP page size. For example consider PRP page size as 4K and chain buffer size as 128 bytes, then number of chain buffers that can fit in PRP page is 4096/128 => 32. if the number of chain buffer need per IO exceeds 32; for example consider number of chains need per IO is 36 then for remaining 4 chain buffer's driver allocates them individual. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 80 +++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 147524cce8d7..2863a3b334c3 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4191,7 +4191,8 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) kfree(ioc->internal_lookup); if (ioc->chain_lookup) { for (i = 0; i < ioc->scsiio_depth; i++) { - for (j = 0; j < ioc->chains_needed_per_io; j++) { + for (j = ioc->chains_per_prp_buffer; + j < ioc->chains_needed_per_io; j++) { ct = &ioc->chain_lookup[i].chains_per_smid[j]; if (ct && ct->chain_buffer) dma_pool_free(ioc->chain_dma_pool, @@ -4509,7 +4510,7 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->chain_lookup = kzalloc(sz, GFP_KERNEL); if (!ioc->chain_lookup) { pr_err(MPT3SAS_FMT "chain_lookup: __get_free_pages " - "failed\n", ioc->name); + "failed\n", ioc->name); goto out; } @@ -4523,33 +4524,6 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) } } - ioc->chain_dma_pool = dma_pool_create("chain pool", &ioc->pdev->dev, - ioc->chain_segment_sz, 16, 0); - if (!ioc->chain_dma_pool) { - pr_err(MPT3SAS_FMT "chain_dma_pool: dma_pool_create failed\n", - ioc->name); - goto out; - } - for (i = 0; i < ioc->scsiio_depth; i++) { - for (j = 0; j < ioc->chains_needed_per_io; j++) { - ct = &ioc->chain_lookup[i].chains_per_smid[j]; - ct->chain_buffer = dma_pool_alloc( - ioc->chain_dma_pool , GFP_KERNEL, - &ct->chain_buffer_dma); - if (!ct->chain_buffer) { - pr_err(MPT3SAS_FMT "chain_lookup: " - " pci_pool_alloc failed\n", ioc->name); - goto out; - } - } - total_sz += ioc->chain_segment_sz; - } - - dinitprintk(ioc, pr_info(MPT3SAS_FMT - "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", - ioc->name, ioc->chain_depth, ioc->chain_segment_sz, - ((ioc->chain_depth * ioc->chain_segment_sz))/1024)); - /* initialize hi-priority queue smid's */ ioc->hpr_lookup = kcalloc(ioc->hi_priority_depth, sizeof(struct request_tracker), GFP_KERNEL); @@ -4590,6 +4564,7 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) * be required for NVMe PRP's, only each set of NVMe blocks will be * contiguous, so a new set is allocated for each possible I/O. */ + ioc->chains_per_prp_buffer = 0; if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_NVME_DEVICES) { nvme_blocks_needed = (ioc->shost->sg_tablesize * NVME_PRP_SIZE) - 1; @@ -4612,6 +4587,11 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->name); goto out; } + + ioc->chains_per_prp_buffer = sz/ioc->chain_segment_sz; + ioc->chains_per_prp_buffer = min(ioc->chains_per_prp_buffer, + ioc->chains_needed_per_io); + for (i = 0; i < ioc->scsiio_depth; i++) { ioc->pcie_sg_lookup[i].pcie_sgl = dma_pool_alloc( ioc->pcie_sgl_dma_pool, GFP_KERNEL, @@ -4622,13 +4602,55 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->name); goto out; } + for (j = 0; j < ioc->chains_per_prp_buffer; j++) { + ct = &ioc->chain_lookup[i].chains_per_smid[j]; + ct->chain_buffer = + ioc->pcie_sg_lookup[i].pcie_sgl + + (j * ioc->chain_segment_sz); + ct->chain_buffer_dma = + ioc->pcie_sg_lookup[i].pcie_sgl_dma + + (j * ioc->chain_segment_sz); + } } dinitprintk(ioc, pr_info(MPT3SAS_FMT "PCIe sgl pool depth(%d), " "element_size(%d), pool_size(%d kB)\n", ioc->name, ioc->scsiio_depth, sz, (sz * ioc->scsiio_depth)/1024)); + dinitprintk(ioc, pr_info(MPT3SAS_FMT "Number of chains can " + "fit in a PRP page(%d)\n", ioc->name, + ioc->chains_per_prp_buffer)); total_sz += sz * ioc->scsiio_depth; } + + ioc->chain_dma_pool = dma_pool_create("chain pool", &ioc->pdev->dev, + ioc->chain_segment_sz, 16, 0); + if (!ioc->chain_dma_pool) { + pr_err(MPT3SAS_FMT "chain_dma_pool: dma_pool_create failed\n", + ioc->name); + goto out; + } + for (i = 0; i < ioc->scsiio_depth; i++) { + for (j = ioc->chains_per_prp_buffer; + j < ioc->chains_needed_per_io; j++) { + ct = &ioc->chain_lookup[i].chains_per_smid[j]; + ct->chain_buffer = dma_pool_alloc( + ioc->chain_dma_pool, GFP_KERNEL, + &ct->chain_buffer_dma); + if (!ct->chain_buffer) { + pr_err(MPT3SAS_FMT "chain_lookup: " + " pci_pool_alloc failed\n", ioc->name); + _base_release_memory_pools(ioc); + goto out; + } + } + total_sz += ioc->chain_segment_sz; + } + + dinitprintk(ioc, pr_info(MPT3SAS_FMT + "chain pool depth(%d), frame_size(%d), pool_size(%d kB)\n", + ioc->name, ioc->chain_depth, ioc->chain_segment_sz, + ((ioc->chain_depth * ioc->chain_segment_sz))/1024)); + /* sense buffers, 4 byte align */ sz = ioc->scsiio_depth * SCSI_SENSE_BUFFERSIZE; ioc->sense_dma_pool = dma_pool_create("sense pool", &ioc->pdev->dev, sz, -- cgit v1.2.3 From e21fef6f331b3977018f35ef38c91dccf2ebc5ae Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:34 -0400 Subject: scsi: mpt3sas: Enhanced handling of Sense Buffer. Enhanced DMA allocation for Sense Buffer, if the allocation does not fit within same 4GB.Introduced is_MSB_are_same function to check if allocted buffer within 4GB range or not. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 2863a3b334c3..379a4127b059 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4207,6 +4207,31 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc) } } +/** + * is_MSB_are_same - checks whether all reply queues in a set are + * having same upper 32bits in their base memory address. + * @reply_pool_start_address: Base address of a reply queue set + * @pool_sz: Size of single Reply Descriptor Post Queues pool size + * + * Returns 1 if reply queues in a set have a same upper 32bits + * in their base memory address, + * else 0 + */ + +static int +is_MSB_are_same(long reply_pool_start_address, u32 pool_sz) +{ + long reply_pool_end_address; + + reply_pool_end_address = reply_pool_start_address + pool_sz; + + if (upper_32_bits(reply_pool_start_address) == + upper_32_bits(reply_pool_end_address)) + return 1; + else + return 0; +} + /** * _base_allocate_memory_pools - allocate start of day memory pools * @ioc: per adapter object @@ -4667,6 +4692,37 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc) ioc->name); goto out; } + /* sense buffer requires to be in same 4 gb region. + * Below function will check the same. + * In case of failure, new pci pool will be created with updated + * alignment. Older allocation and pool will be destroyed. + * Alignment will be used such a way that next allocation if + * success, will always meet same 4gb region requirement. + * Actual requirement is not alignment, but we need start and end of + * DMA address must have same upper 32 bit address. + */ + if (!is_MSB_are_same((long)ioc->sense, sz)) { + //Release Sense pool & Reallocate + dma_pool_free(ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); + dma_pool_destroy(ioc->sense_dma_pool); + ioc->sense = NULL; + + ioc->sense_dma_pool = + dma_pool_create("sense pool", &ioc->pdev->dev, sz, + roundup_pow_of_two(sz), 0); + if (!ioc->sense_dma_pool) { + pr_err(MPT3SAS_FMT "sense pool: pci_pool_create failed\n", + ioc->name); + goto out; + } + ioc->sense = dma_pool_alloc(ioc->sense_dma_pool, GFP_KERNEL, + &ioc->sense_dma); + if (!ioc->sense) { + pr_err(MPT3SAS_FMT "sense pool: pci_pool_alloc failed\n", + ioc->name); + goto out; + } + } dinitprintk(ioc, pr_info(MPT3SAS_FMT "sense pool(0x%p): depth(%d), element_size(%d), pool_size" "(%d kB)\n", ioc->name, ioc->sense, ioc->scsiio_depth, -- cgit v1.2.3 From 95540b8eaf30d224ba0723f5a109aa52fd996c6f Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:35 -0400 Subject: scsi: mpt3sas: Added support for SAS Device Discovery Error Event. The SAS Device Discovery Error Event is sent to the host when discovery for a particular device is failed during discovery, even after maximum retries by the IOC. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 4 ++++ drivers/scsi/mpt3sas/mpt3sas_scsih.c | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 379a4127b059..ea07533f6676 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -1030,6 +1030,9 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, case MPI2_EVENT_ACTIVE_CABLE_EXCEPTION: desc = "Cable Event"; break; + case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR: + desc = "SAS Device Discovery Error"; + break; case MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE: desc = "PCIE Device Status Change"; break; @@ -6599,6 +6602,7 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc) _base_unmask_events(ioc, MPI2_EVENT_LOG_ENTRY_ADDED); _base_unmask_events(ioc, MPI2_EVENT_TEMP_THRESHOLD); _base_unmask_events(ioc, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); + _base_unmask_events(ioc, MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR); if (ioc->hba_mpi_version_belonged == MPI26_VERSION) { if (ioc->is_gen35_ioc) { _base_unmask_events(ioc, diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 0319b34ebf6c..05ce0227a0a2 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -7526,6 +7526,44 @@ _scsih_sas_discovery_event(struct MPT3SAS_ADAPTER *ioc, } } +/** + * _scsih_sas_device_discovery_error_event - display SAS device discovery error + * events + * @ioc: per adapter object + * @fw_event: The fw_event_work object + * Context: user. + * + * Return nothing. + */ +static void +_scsih_sas_device_discovery_error_event(struct MPT3SAS_ADAPTER *ioc, + struct fw_event_work *fw_event) +{ + Mpi25EventDataSasDeviceDiscoveryError_t *event_data = + (Mpi25EventDataSasDeviceDiscoveryError_t *)fw_event->event_data; + + switch (event_data->ReasonCode) { + case MPI25_EVENT_SAS_DISC_ERR_SMP_FAILED: + pr_warn(MPT3SAS_FMT "SMP command sent to the expander" + "(handle:0x%04x, sas_address:0x%016llx," + "physical_port:0x%02x) has failed", + ioc->name, le16_to_cpu(event_data->DevHandle), + (unsigned long long)le64_to_cpu(event_data->SASAddress), + event_data->PhysicalPort); + break; + case MPI25_EVENT_SAS_DISC_ERR_SMP_TIMEOUT: + pr_warn(MPT3SAS_FMT "SMP command sent to the expander" + "(handle:0x%04x, sas_address:0x%016llx," + "physical_port:0x%02x) has timed out", + ioc->name, le16_to_cpu(event_data->DevHandle), + (unsigned long long)le64_to_cpu(event_data->SASAddress), + event_data->PhysicalPort); + break; + default: + break; + } +} + /** * _scsih_pcie_enumeration_event - handle enumeration events * @ioc: per adapter object @@ -9353,6 +9391,9 @@ _mpt3sas_fw_work(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) case MPI2_EVENT_SAS_DISCOVERY: _scsih_sas_discovery_event(ioc, fw_event); break; + case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR: + _scsih_sas_device_discovery_error_event(ioc, fw_event); + break; case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE: _scsih_sas_broadcast_primitive_event(ioc, fw_event); break; @@ -9537,6 +9578,7 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE: case MPI2_EVENT_IR_OPERATION_STATUS: case MPI2_EVENT_SAS_DISCOVERY: + case MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR: case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE: case MPI2_EVENT_IR_PHYSICAL_DISK: case MPI2_EVENT_PCIE_ENUMERATION: -- cgit v1.2.3 From 1537d1bfc58858f2e0a86c37977d92b05e0afa2e Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:36 -0400 Subject: scsi: mpt3sas: Increase event log buffer to support 24 port HBA's. For 24 port HBA's events generated by IOC are more in certain cases and the current circular buffer may be overwritten.Hence increased the event log buffer to accommodate more events. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_ctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.h b/drivers/scsi/mpt3sas/mpt3sas_ctl.h index a44046cff0f3..18b46faef6f1 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.h +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.h @@ -184,7 +184,7 @@ struct mpt3_ioctl_iocinfo { /* number of event log entries */ -#define MPT3SAS_CTL_EVENT_LOG_SIZE (50) +#define MPT3SAS_CTL_EVENT_LOG_SIZE (200) /** * struct mpt3_ioctl_eventquery - query event count and type -- cgit v1.2.3 From 79eb96d6ca6971e79a3770991a12a16fde871e90 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:37 -0400 Subject: scsi: mpt3sas: Allow processing of events during driver unload. Events were not processed during driver unload, hence unloading of driver doesn't complete when drives are disconnected while unloading of driver. So don't block events in ISR path, i,e., remove the flag ioc->remove_host so that events are getting processed during driver unload. Thus allowing driver unload to complete by processing drive removal events during driver unload. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 05ce0227a0a2..29ba7e863e74 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -3680,11 +3680,7 @@ _scsih_tm_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 ioc_state; struct _sc_list *delayed_sc; - if (ioc->remove_host) { - dewtprintk(ioc, pr_info(MPT3SAS_FMT - "%s: host has been removed\n", __func__, ioc->name)); - return 1; - } else if (ioc->pci_error_recovery) { + if (ioc->pci_error_recovery) { dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: host in pci error recovery\n", __func__, ioc->name)); @@ -3806,8 +3802,7 @@ _scsih_tm_tr_volume_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) u16 smid; struct _tr_list *delayed_tr; - if (ioc->shost_recovery || ioc->remove_host || - ioc->pci_error_recovery) { + if (ioc->pci_error_recovery) { dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", __func__, ioc->name)); @@ -3860,8 +3855,7 @@ _scsih_tm_volume_tr_complete(struct MPT3SAS_ADAPTER *ioc, u16 smid, Mpi2SCSITaskManagementReply_t *mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); - if (ioc->shost_recovery || ioc->remove_host || - ioc->pci_error_recovery) { + if (ioc->shost_recovery || ioc->pci_error_recovery) { dewtprintk(ioc, pr_info(MPT3SAS_FMT "%s: host reset in progress!\n", __func__, ioc->name)); @@ -9471,8 +9465,8 @@ mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u16 sz; Mpi26EventDataActiveCableExcept_t *ActiveCableEventData; - /* events turned off due to host reset or driver unloading */ - if (ioc->remove_host || ioc->pci_error_recovery) + /* events turned off due to host reset */ + if (ioc->pci_error_recovery) return 1; mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); -- cgit v1.2.3 From 22a923c315ba09a83ef88ff1d968413d9dd8fb75 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:38 -0400 Subject: scsi: mpt3sas: Cache enclosure pages during enclosure add. In function _scsih_add_device, for each device connected to an enclosure, driver reads the enclosure page(To get details like enclosure handle, enclosure logical ID, enclosure level etc.) With this patch, instead of reading enclosure page everytime, driver maintains a list for enclosure device(During enclosure add event, enclosure device is added to the list and removed from the list on delete events) and uses the enclosure page from the list. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 22 +++ drivers/scsi/mpt3sas/mpt3sas_base.h | 14 ++ drivers/scsi/mpt3sas/mpt3sas_scsih.c | 296 +++++++++++++++++++++++------------ 3 files changed, 236 insertions(+), 96 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index ea07533f6676..25bc36fdb380 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4089,6 +4089,27 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) ioc->temp_sensors_count = ioc->iounit_pg8.NumSensors; } +/** + * mpt3sas_free_enclosure_list - release memory + * @ioc: per adapter object + * + * Free memory allocated during encloure add. + * + * Return nothing. + */ +void +mpt3sas_free_enclosure_list(struct MPT3SAS_ADAPTER *ioc) +{ + struct _enclosure_node *enclosure_dev, *enclosure_dev_next; + + /* Free enclosure list */ + list_for_each_entry_safe(enclosure_dev, + enclosure_dev_next, &ioc->enclosure_list, list) { + list_del(&enclosure_dev->list); + kfree(enclosure_dev); + } +} + /** * _base_release_memory_pools - release memory * @ioc: per adapter object @@ -6669,6 +6690,7 @@ mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc) mpt3sas_base_stop_watchdog(ioc); mpt3sas_base_free_resources(ioc); _base_release_memory_pools(ioc); + mpt3sas_free_enclosure_list(ioc); pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->cpu_msix_table); if (ioc->is_warpdrive) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index eeeb3b711144..3fafee666789 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -741,6 +741,17 @@ struct _sas_node { struct list_head sas_port_list; }; + +/** + * struct _enclosure_node - enclosure information + * @list: list of enclosures + * @pg0: enclosure pg0; + */ +struct _enclosure_node { + struct list_head list; + Mpi2SasEnclosurePage0_t pg0; +}; + /** * enum reset_type - reset state * @FORCE_BIG_HAMMER: issue diagnostic reset @@ -1013,6 +1024,7 @@ typedef void (*MPT3SAS_FLUSH_RUNNING_CMDS)(struct MPT3SAS_ADAPTER *ioc); * @iounit_pg8: static iounit page 8 * @sas_hba: sas host object * @sas_expander_list: expander object list + * @enclosure_list: enclosure object list * @sas_node_lock: * @sas_device_list: sas device object list * @sas_device_init_list: sas device object list (used only at init time) @@ -1218,6 +1230,7 @@ struct MPT3SAS_ADAPTER { /* sas hba, expander, and device list */ struct _sas_node sas_hba; struct list_head sas_expander_list; + struct list_head enclosure_list; spinlock_t sas_node_lock; struct list_head sas_device_list; struct list_head sas_device_init_list; @@ -1391,6 +1404,7 @@ int mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_detach(struct MPT3SAS_ADAPTER *ioc); int mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_base_free_resources(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_free_enclosure_list(struct MPT3SAS_ADAPTER *ioc); int mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, enum reset_type type); diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 29ba7e863e74..27cc2e827472 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1361,6 +1361,30 @@ mpt3sas_scsih_expander_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) return r; } +/** + * mpt3sas_scsih_enclosure_find_by_handle - exclosure device search + * @ioc: per adapter object + * @handle: enclosure handle (assigned by firmware) + * Context: Calling function should acquire ioc->sas_device_lock + * + * This searches for enclosure device based on handle, then returns the + * enclosure object. + */ +static struct _enclosure_node * +mpt3sas_scsih_enclosure_find_by_handle(struct MPT3SAS_ADAPTER *ioc, u16 handle) +{ + struct _enclosure_node *enclosure_dev, *r; + + r = NULL; + list_for_each_entry(enclosure_dev, &ioc->enclosure_list, list) { + if (le16_to_cpu(enclosure_dev->pg0.EnclosureHandle) != handle) + continue; + r = enclosure_dev; + goto out; + } +out: + return r; +} /** * mpt3sas_scsih_expander_find_by_sas_address - expander device search * @ioc: per adapter object @@ -5612,10 +5636,10 @@ static int _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) { struct _sas_node *sas_expander; + struct _enclosure_node *enclosure_dev; Mpi2ConfigReply_t mpi_reply; Mpi2ExpanderPage0_t expander_pg0; Mpi2ExpanderPage1_t expander_pg1; - Mpi2SasEnclosurePage0_t enclosure_pg0; u32 ioc_status; u16 parent_handle; u64 sas_address, sas_address_parent = 0; @@ -5737,11 +5761,12 @@ _scsih_expander_add(struct MPT3SAS_ADAPTER *ioc, u16 handle) } if (sas_expander->enclosure_handle) { - if (!(mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - sas_expander->enclosure_handle))) + enclosure_dev = + mpt3sas_scsih_enclosure_find_by_handle(ioc, + sas_expander->enclosure_handle); + if (enclosure_dev) sas_expander->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); + le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID); } _scsih_expander_node_add(ioc, sas_expander); @@ -5884,52 +5909,6 @@ _scsih_check_access_status(struct MPT3SAS_ADAPTER *ioc, u64 sas_address, return rc; } -/** - * _scsih_get_enclosure_logicalid_chassis_slot - get device's - * EnclosureLogicalID and ChassisSlot information. - * @ioc: per adapter object - * @sas_device_pg0: SAS device page0 - * @sas_device: per sas device object - * - * Returns nothing. - */ -static void -_scsih_get_enclosure_logicalid_chassis_slot(struct MPT3SAS_ADAPTER *ioc, - Mpi2SasDevicePage0_t *sas_device_pg0, struct _sas_device *sas_device) -{ - Mpi2ConfigReply_t mpi_reply; - Mpi2SasEnclosurePage0_t enclosure_pg0; - - if (!sas_device_pg0 || !sas_device) - return; - - sas_device->enclosure_handle = - le16_to_cpu(sas_device_pg0->EnclosureHandle); - sas_device->is_chassis_slot_valid = 0; - - if (!le16_to_cpu(sas_device_pg0->EnclosureHandle)) - return; - - if (mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - le16_to_cpu(sas_device_pg0->EnclosureHandle))) { - pr_err(MPT3SAS_FMT - "Enclosure Pg0 read failed for handle(0x%04x)\n", - ioc->name, le16_to_cpu(sas_device_pg0->EnclosureHandle)); - return; - } - - sas_device->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - - if (le16_to_cpu(enclosure_pg0.Flags) & - MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { - sas_device->is_chassis_slot_valid = 1; - sas_device->chassis_slot = enclosure_pg0.ChassisSlot; - } -} - - /** * _scsih_check_device - checking device responsiveness * @ioc: per adapter object @@ -5947,6 +5926,7 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; struct _sas_device *sas_device; + struct _enclosure_node *enclosure_dev = NULL; u32 ioc_status; unsigned long flags; u64 sas_address; @@ -6001,8 +5981,21 @@ _scsih_check_device(struct MPT3SAS_ADAPTER *ioc, sas_device->connector_name[0] = '\0'; } - _scsih_get_enclosure_logicalid_chassis_slot(ioc, - &sas_device_pg0, sas_device); + sas_device->enclosure_handle = + le16_to_cpu(sas_device_pg0.EnclosureHandle); + sas_device->is_chassis_slot_valid = 0; + enclosure_dev = mpt3sas_scsih_enclosure_find_by_handle(ioc, + sas_device->enclosure_handle); + if (enclosure_dev) { + sas_device->enclosure_logical_id = + le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID); + if (le16_to_cpu(enclosure_dev->pg0.Flags) & + MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { + sas_device->is_chassis_slot_valid = 1; + sas_device->chassis_slot = + enclosure_dev->pg0.ChassisSlot; + } + } } /* check if device is present */ @@ -6049,12 +6042,11 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, { Mpi2ConfigReply_t mpi_reply; Mpi2SasDevicePage0_t sas_device_pg0; - Mpi2SasEnclosurePage0_t enclosure_pg0; struct _sas_device *sas_device; + struct _enclosure_node *enclosure_dev = NULL; u32 ioc_status; u64 sas_address; u32 device_info; - int encl_pg0_rc = -1; if ((mpt3sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { @@ -6100,12 +6092,12 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, } if (sas_device_pg0.EnclosureHandle) { - encl_pg0_rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - le16_to_cpu(sas_device_pg0.EnclosureHandle)); - if (encl_pg0_rc) - pr_info(MPT3SAS_FMT - "Enclosure Pg0 read failed for handle(0x%04x)\n", + enclosure_dev = + mpt3sas_scsih_enclosure_find_by_handle(ioc, + le16_to_cpu(sas_device_pg0.EnclosureHandle)); + if (enclosure_dev == NULL) + pr_info(MPT3SAS_FMT "Enclosure handle(0x%04x)" + "doesn't match with enclosure device!\n", ioc->name, sas_device_pg0.EnclosureHandle); } @@ -6146,18 +6138,16 @@ _scsih_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle, u8 phy_num, sas_device->enclosure_level = 0; sas_device->connector_name[0] = '\0'; } - - /* get enclosure_logical_id & chassis_slot */ + /* get enclosure_logical_id & chassis_slot*/ sas_device->is_chassis_slot_valid = 0; - if (encl_pg0_rc == 0) { + if (enclosure_dev) { sas_device->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - - if (le16_to_cpu(enclosure_pg0.Flags) & + le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID); + if (le16_to_cpu(enclosure_dev->pg0.Flags) & MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { sas_device->is_chassis_slot_valid = 1; sas_device->chassis_slot = - enclosure_pg0.ChassisSlot; + enclosure_dev->pg0.ChassisSlot; } } @@ -6839,8 +6829,8 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) Mpi26PCIeDevicePage0_t pcie_device_pg0; Mpi26PCIeDevicePage2_t pcie_device_pg2; Mpi2ConfigReply_t mpi_reply; - Mpi2SasEnclosurePage0_t enclosure_pg0; struct _pcie_device *pcie_device; + struct _enclosure_node *enclosure_dev; u32 pcie_device_type; u32 ioc_status; u64 wwid; @@ -6922,13 +6912,14 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) } /* get enclosure_logical_id */ - if (pcie_device->enclosure_handle && - !(mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - pcie_device->enclosure_handle))) - pcie_device->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - + if (pcie_device->enclosure_handle) { + enclosure_dev = + mpt3sas_scsih_enclosure_find_by_handle(ioc, + pcie_device->enclosure_handle); + if (enclosure_dev) + pcie_device->enclosure_logical_id = + le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID); + } /* TODO -- Add device name once FW supports it */ if (mpt3sas_config_get_pcie_device_pg2(ioc, &mpi_reply, &pcie_device_pg2, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle)) { @@ -7314,10 +7305,60 @@ static void _scsih_sas_enclosure_dev_status_change_event(struct MPT3SAS_ADAPTER *ioc, struct fw_event_work *fw_event) { + Mpi2ConfigReply_t mpi_reply; + struct _enclosure_node *enclosure_dev = NULL; + Mpi2EventDataSasEnclDevStatusChange_t *event_data = + (Mpi2EventDataSasEnclDevStatusChange_t *)fw_event->event_data; + int rc; + u16 enclosure_handle = le16_to_cpu(event_data->EnclosureHandle); + if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_enclosure_dev_status_change_event_debug(ioc, (Mpi2EventDataSasEnclDevStatusChange_t *) fw_event->event_data); + if (ioc->shost_recovery) + return; + + if (enclosure_handle) + enclosure_dev = + mpt3sas_scsih_enclosure_find_by_handle(ioc, + enclosure_handle); + switch (event_data->ReasonCode) { + case MPI2_EVENT_SAS_ENCL_RC_ADDED: + if (!enclosure_dev) { + enclosure_dev = + kzalloc(sizeof(struct _enclosure_node), + GFP_KERNEL); + if (!enclosure_dev) { + pr_info(MPT3SAS_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_dev->pg0, + MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + enclosure_handle); + + if (rc || (le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK)) { + kfree(enclosure_dev); + return; + } + + list_add_tail(&enclosure_dev->list, + &ioc->enclosure_list); + } + break; + case MPI2_EVENT_SAS_ENCL_RC_NOT_RESPONDING: + if (enclosure_dev) { + list_del(&enclosure_dev->list); + kfree(enclosure_dev); + } + break; + default: + break; + } } /** @@ -8392,8 +8433,18 @@ Mpi2SasDevicePage0_t *sas_device_pg0) struct MPT3SAS_TARGET *sas_target_priv_data = NULL; struct scsi_target *starget; struct _sas_device *sas_device = NULL; + struct _enclosure_node *enclosure_dev = NULL; unsigned long flags; + if (sas_device_pg0->EnclosureHandle) { + enclosure_dev = + mpt3sas_scsih_enclosure_find_by_handle(ioc, + le16_to_cpu(sas_device_pg0->EnclosureHandle)); + if (enclosure_dev == NULL) + pr_info(MPT3SAS_FMT "Enclosure handle(0x%04x)" + "doesn't match with enclosure device!\n", + ioc->name, sas_device_pg0->EnclosureHandle); + } spin_lock_irqsave(&ioc->sas_device_lock, flags); list_for_each_entry(sas_device, &ioc->sas_device_list, list) { if ((sas_device->sas_address == le64_to_cpu( @@ -8433,8 +8484,19 @@ Mpi2SasDevicePage0_t *sas_device_pg0) sas_device->connector_name[0] = '\0'; } - _scsih_get_enclosure_logicalid_chassis_slot(ioc, - sas_device_pg0, sas_device); + sas_device->enclosure_handle = + le16_to_cpu(sas_device_pg0->EnclosureHandle); + sas_device->is_chassis_slot_valid = 0; + if (enclosure_dev) { + sas_device->enclosure_logical_id = le64_to_cpu( + enclosure_dev->pg0.EnclosureLogicalID); + if (le16_to_cpu(enclosure_dev->pg0.Flags) & + MPI2_SAS_ENCLS0_FLAGS_CHASSIS_SLOT_VALID) { + sas_device->is_chassis_slot_valid = 1; + sas_device->chassis_slot = + enclosure_dev->pg0.ChassisSlot; + } + } if (sas_device->handle == le16_to_cpu( sas_device_pg0->DevHandle)) @@ -8453,6 +8515,52 @@ Mpi2SasDevicePage0_t *sas_device_pg0) spin_unlock_irqrestore(&ioc->sas_device_lock, flags); } +/** + * _scsih_create_enclosure_list_after_reset - Free Existing list, + * And create enclosure list by scanning all Enclosure Page(0)s + * @ioc: per adapter object + * + * Return nothing. + */ +static void +_scsih_create_enclosure_list_after_reset(struct MPT3SAS_ADAPTER *ioc) +{ + struct _enclosure_node *enclosure_dev; + Mpi2ConfigReply_t mpi_reply; + u16 enclosure_handle; + int rc; + + /* Free existing enclosure list */ + mpt3sas_free_enclosure_list(ioc); + + /* Re constructing enclosure list after reset*/ + enclosure_handle = 0xFFFF; + do { + enclosure_dev = + kzalloc(sizeof(struct _enclosure_node), GFP_KERNEL); + if (!enclosure_dev) { + pr_err(MPT3SAS_FMT + "failure at %s:%d/%s()!\n", ioc->name, + __FILE__, __LINE__, __func__); + return; + } + rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, + &enclosure_dev->pg0, + MPI2_SAS_ENCLOS_PGAD_FORM_GET_NEXT_HANDLE, + enclosure_handle); + + if (rc || (le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK)) { + kfree(enclosure_dev); + return; + } + list_add_tail(&enclosure_dev->list, + &ioc->enclosure_list); + enclosure_handle = + le16_to_cpu(enclosure_dev->pg0.EnclosureHandle); + } while (1); +} + /** * _scsih_search_responding_sas_devices - * @ioc: per adapter object @@ -8765,22 +8873,16 @@ _scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, { struct _sas_node *sas_expander = NULL; unsigned long flags; - int i, encl_pg0_rc = -1; - Mpi2ConfigReply_t mpi_reply; - Mpi2SasEnclosurePage0_t enclosure_pg0; + int i; + struct _enclosure_node *enclosure_dev = NULL; u16 handle = le16_to_cpu(expander_pg0->DevHandle); + u16 enclosure_handle = le16_to_cpu(expander_pg0->EnclosureHandle); u64 sas_address = le64_to_cpu(expander_pg0->SASAddress); - if (le16_to_cpu(expander_pg0->EnclosureHandle)) { - encl_pg0_rc = mpt3sas_config_get_enclosure_pg0(ioc, &mpi_reply, - &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - le16_to_cpu(expander_pg0->EnclosureHandle)); - if (encl_pg0_rc) - pr_info(MPT3SAS_FMT - "Enclosure Pg0 read failed for handle(0x%04x)\n", - ioc->name, - le16_to_cpu(expander_pg0->EnclosureHandle)); - } + if (enclosure_handle) + enclosure_dev = + mpt3sas_scsih_enclosure_find_by_handle(ioc, + enclosure_handle); spin_lock_irqsave(&ioc->sas_node_lock, flags); list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) { @@ -8788,12 +8890,12 @@ _scsih_mark_responding_expander(struct MPT3SAS_ADAPTER *ioc, continue; sas_expander->responding = 1; - if (!encl_pg0_rc) + if (enclosure_dev) { sas_expander->enclosure_logical_id = - le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - - sas_expander->enclosure_handle = - le16_to_cpu(expander_pg0->EnclosureHandle); + le64_to_cpu(enclosure_dev->pg0.EnclosureLogicalID); + sas_expander->enclosure_handle = + le16_to_cpu(expander_pg0->EnclosureHandle); + } if (sas_expander->handle == handle) goto out; @@ -9315,6 +9417,7 @@ mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase) if ((!ioc->is_driver_loading) && !(disable_discovery > 0 && !ioc->sas_hba.num_phys)) { _scsih_prep_device_scan(ioc); + _scsih_create_enclosure_list_after_reset(ioc); _scsih_search_responding_sas_devices(ioc); _scsih_search_responding_pcie_devices(ioc); _scsih_search_responding_raid_devices(ioc); @@ -10546,6 +10649,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ioc->sas_device_list); INIT_LIST_HEAD(&ioc->sas_device_init_list); INIT_LIST_HEAD(&ioc->sas_expander_list); + INIT_LIST_HEAD(&ioc->enclosure_list); INIT_LIST_HEAD(&ioc->pcie_device_list); INIT_LIST_HEAD(&ioc->pcie_device_init_list); INIT_LIST_HEAD(&ioc->fw_event_list); -- cgit v1.2.3 From 3d29ed85fc9ca673fbae0f97178ef64c1314f7e2 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:39 -0400 Subject: scsi: mpt3sas: Report Firmware Package Version from HBA Driver. Added function _base_display_fwpkg_version, which sends FWUpload request to pull FW package version from FW Image Header. Now driver prints FW package version in addition to FW version if the PackageVersion is valid. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 109 +++++++++++++++++++++++++++++++++++- drivers/scsi/mpt3sas/mpt3sas_base.h | 1 + 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 25bc36fdb380..270b39f60c6d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -3827,6 +3827,105 @@ _base_display_OEMs_branding(struct MPT3SAS_ADAPTER *ioc) } } +/** + * _base_display_fwpkg_version - sends FWUpload request to pull FWPkg + * version from FW Image Header. + * @ioc: per adapter object + * + * Returns 0 for success, non-zero for failure. + */ + static int +_base_display_fwpkg_version(struct MPT3SAS_ADAPTER *ioc) +{ + Mpi2FWImageHeader_t *FWImgHdr; + Mpi25FWUploadRequest_t *mpi_request; + Mpi2FWUploadReply_t mpi_reply; + int r = 0; + void *fwpkg_data = NULL; + dma_addr_t fwpkg_data_dma; + u16 smid, ioc_status; + size_t data_length; + + dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name, + __func__)); + + if (ioc->base_cmds.status & MPT3_CMD_PENDING) { + pr_err(MPT3SAS_FMT "%s: internal command already in use\n", + ioc->name, __func__); + return -EAGAIN; + } + + data_length = sizeof(Mpi2FWImageHeader_t); + fwpkg_data = pci_alloc_consistent(ioc->pdev, data_length, + &fwpkg_data_dma); + if (!fwpkg_data) { + pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return -ENOMEM; + } + + smid = mpt3sas_base_get_smid(ioc, ioc->base_cb_idx); + if (!smid) { + pr_err(MPT3SAS_FMT "%s: failed obtaining a smid\n", + ioc->name, __func__); + r = -EAGAIN; + goto out; + } + + ioc->base_cmds.status = MPT3_CMD_PENDING; + mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); + ioc->base_cmds.smid = smid; + memset(mpi_request, 0, sizeof(Mpi25FWUploadRequest_t)); + mpi_request->Function = MPI2_FUNCTION_FW_UPLOAD; + mpi_request->ImageType = MPI2_FW_UPLOAD_ITYPE_FW_FLASH; + mpi_request->ImageSize = cpu_to_le32(data_length); + ioc->build_sg(ioc, &mpi_request->SGL, 0, 0, fwpkg_data_dma, + data_length); + init_completion(&ioc->base_cmds.done); + mpt3sas_base_put_smid_default(ioc, smid); + /* Wait for 15 seconds */ + wait_for_completion_timeout(&ioc->base_cmds.done, + FW_IMG_HDR_READ_TIMEOUT*HZ); + pr_info(MPT3SAS_FMT "%s: complete\n", + ioc->name, __func__); + if (!(ioc->base_cmds.status & MPT3_CMD_COMPLETE)) { + pr_err(MPT3SAS_FMT "%s: timeout\n", + ioc->name, __func__); + _debug_dump_mf(mpi_request, + sizeof(Mpi25FWUploadRequest_t)/4); + r = -ETIME; + } else { + memset(&mpi_reply, 0, sizeof(Mpi2FWUploadReply_t)); + if (ioc->base_cmds.status & MPT3_CMD_REPLY_VALID) { + memcpy(&mpi_reply, ioc->base_cmds.reply, + sizeof(Mpi2FWUploadReply_t)); + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { + FWImgHdr = (Mpi2FWImageHeader_t *)fwpkg_data; + if (FWImgHdr->PackageVersion.Word) { + pr_info(MPT3SAS_FMT "FW Package Version" + "(%02d.%02d.%02d.%02d)\n", + ioc->name, + FWImgHdr->PackageVersion.Struct.Major, + FWImgHdr->PackageVersion.Struct.Minor, + FWImgHdr->PackageVersion.Struct.Unit, + FWImgHdr->PackageVersion.Struct.Dev); + } + } else { + _debug_dump_mf(&mpi_reply, + sizeof(Mpi2FWUploadReply_t)/4); + } + } + } + ioc->base_cmds.status = MPT3_CMD_NOT_USED; +out: + if (fwpkg_data) + pci_free_consistent(ioc->pdev, data_length, fwpkg_data, + fwpkg_data_dma); + return r; +} + /** * _base_display_ioc_capabilities - Disply IOC's capabilities. * @ioc: per adapter object @@ -6361,12 +6460,18 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) skip_init_reply_post_host_index: _base_unmask_interrupts(ioc); + + if (ioc->hba_mpi_version_belonged != MPI2_VERSION) { + r = _base_display_fwpkg_version(ioc); + if (r) + return r; + } + + _base_static_config_pages(ioc); r = _base_event_notification(ioc); if (r) return r; - _base_static_config_pages(ioc); - if (ioc->is_driver_loading) { if (ioc->is_warpdrive && ioc->manu_pg10.OEMIdentifier diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 3fafee666789..0eceebbdc5d5 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -138,6 +138,7 @@ #define MAX_CHAIN_ELEMT_SZ 16 #define DEFAULT_NUM_FWCHAIN_ELEMTS 8 +#define FW_IMG_HDR_READ_TIMEOUT 15 /* * NVMe defines */ -- cgit v1.2.3 From 65928d1f4130a48732034c2f31b2b0790694537f Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:40 -0400 Subject: scsi: mpt3sas: Update MPI Headers Update MPI Files to support protocol level reset for NVMe device. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpi/mpi2.h | 9 ++++++--- drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h | 30 ++++++++++++++++++++++++------ drivers/scsi/mpt3sas/mpi/mpi2_ioc.h | 7 ++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2.h b/drivers/scsi/mpt3sas/mpi/mpi2.h index b015c30d2c32..1e45268a78fc 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2.h @@ -9,7 +9,7 @@ * scatter/gather formats. * Creation Date: June 21, 2006 * - * mpi2.h Version: 02.00.48 + * mpi2.h Version: 02.00.50 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -114,6 +114,8 @@ * 09-02-16 02.00.46 Bumped MPI2_HEADER_VERSION_UNIT. * 11-23-16 02.00.47 Bumped MPI2_HEADER_VERSION_UNIT. * 02-03-17 02.00.48 Bumped MPI2_HEADER_VERSION_UNIT. + * 06-13-17 02.00.49 Bumped MPI2_HEADER_VERSION_UNIT. + * 09-29-17 02.00.50 Bumped MPI2_HEADER_VERSION_UNIT. * -------------------------------------------------------------------------- */ @@ -152,8 +154,9 @@ MPI26_VERSION_MINOR) #define MPI2_VERSION_02_06 (0x0206) -/*Unit and Dev versioning for this MPI header set */ -#define MPI2_HEADER_VERSION_UNIT (0x30) + +/* Unit and Dev versioning for this MPI header set */ +#define MPI2_HEADER_VERSION_UNIT (0x32) #define MPI2_HEADER_VERSION_DEV (0x00) #define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI2_HEADER_VERSION_UNIT_SHIFT (8) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h index 0ad88deb3176..5122920a961a 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_cnfg.h @@ -7,7 +7,7 @@ * Title: MPI Configuration messages and pages * Creation Date: November 10, 2006 * - * mpi2_cnfg.h Version: 02.00.40 + * mpi2_cnfg.h Version: 02.00.42 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -219,6 +219,18 @@ * Added ChassisSlot field to SAS Enclosure Page 0. * Added ChassisSlot Valid bit (bit 5) to the Flags field * in SAS Enclosure Page 0. + * 06-13-17 02.00.41 Added MPI26_MFGPAGE_DEVID_SAS3816 and + * MPI26_MFGPAGE_DEVID_SAS3916 defines. + * Removed MPI26_MFGPAGE_DEVID_SAS4008 define. + * Added MPI26_PCIEIOUNIT1_LINKFLAGS_SRNS_EN define. + * Renamed PI26_PCIEIOUNIT1_LINKFLAGS_EN_SRIS to + * PI26_PCIEIOUNIT1_LINKFLAGS_SRIS_EN. + * Renamed MPI26_PCIEIOUNIT1_LINKFLAGS_DIS_SRIS to + * MPI26_PCIEIOUNIT1_LINKFLAGS_DIS_SEPARATE_REFCLK. + * 09-29-17 02.00.42 Added ControllerResetTO field to PCIe Device Page 2. + * Added NOIOB field to PCIe Device Page 2. + * Added MPI26_PCIEDEV2_CAP_DATA_BLK_ALIGN_AND_GRAN to + * the Capabilities field of PCIe Device Page 2. * -------------------------------------------------------------------------- */ @@ -556,7 +568,8 @@ typedef struct _MPI2_CONFIG_REPLY { #define MPI26_MFGPAGE_DEVID_SAS3616 (0x00D1) #define MPI26_MFGPAGE_DEVID_SAS3708 (0x00D2) -#define MPI26_MFGPAGE_DEVID_SAS4008 (0x00A1) +#define MPI26_MFGPAGE_DEVID_SAS3816 (0x00A1) +#define MPI26_MFGPAGE_DEVID_SAS3916 (0x00A0) /*Manufacturing Page 0 */ @@ -3864,20 +3877,25 @@ typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_0 { typedef struct _MPI26_CONFIG_PAGE_PCIEDEV_2 { MPI2_CONFIG_EXTENDED_PAGE_HEADER Header; /*0x00 */ U16 DevHandle; /*0x08 */ - U16 Reserved1; /*0x0A */ - U32 MaximumDataTransferSize;/*0x0C */ + U8 ControllerResetTO; /* 0x0A */ + U8 Reserved1; /* 0x0B */ + U32 MaximumDataTransferSize; /*0x0C */ U32 Capabilities; /*0x10 */ - U32 Reserved2; /*0x14 */ + U16 NOIOB; /* 0x14 */ + U16 Reserved2; /* 0x16 */ } MPI26_CONFIG_PAGE_PCIEDEV_2, *PTR_MPI26_CONFIG_PAGE_PCIEDEV_2, Mpi26PCIeDevicePage2_t, *pMpi26PCIeDevicePage2_t; -#define MPI26_PCIEDEVICE2_PAGEVERSION (0x00) +#define MPI26_PCIEDEVICE2_PAGEVERSION (0x01) /*defines for PCIe Device Page 2 Capabilities field */ +#define MPI26_PCIEDEV2_CAP_DATA_BLK_ALIGN_AND_GRAN (0x00000008) #define MPI26_PCIEDEV2_CAP_SGL_FORMAT (0x00000004) #define MPI26_PCIEDEV2_CAP_BIT_BUCKET_SUPPORT (0x00000002) #define MPI26_PCIEDEV2_CAP_SGL_SUPPORT (0x00000001) +/* Defines for the NOIOB field */ +#define MPI26_PCIEDEV2_NOIOB_UNSUPPORTED (0x0000) /**************************************************************************** * PCIe Link Config Pages (MPI v2.6 and later) diff --git a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h index cc2aff7aa67b..1faec3a93e69 100644 --- a/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h +++ b/drivers/scsi/mpt3sas/mpi/mpi2_ioc.h @@ -7,7 +7,7 @@ * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: October 11, 2006 * - * mpi2_ioc.h Version: 02.00.32 + * mpi2_ioc.h Version: 02.00.34 * * NOTE: Names (typedefs, defines, etc.) beginning with an MPI25 or Mpi25 * prefix are for use only on MPI v2.5 products, and must not be used @@ -167,6 +167,10 @@ * 02-02-17 02.00.32 Added MPI2_FW_DOWNLOAD_ITYPE_CBB_BACKUP. * Added MPI25_EVENT_DATA_ACTIVE_CABLE_EXCEPT and related * defines for the ReasonCode field. + * 06-13-17 02.00.33 Added MPI2_FW_DOWNLOAD_ITYPE_CPLD. + * 09-29-17 02.00.34 Added MPI26_EVENT_PCIDEV_STAT_RC_PCIE_HOT_RESET_FAILED + * to the ReasonCode field in PCIe Device Status Change + * Event Data. * -------------------------------------------------------------------------- */ @@ -1182,6 +1186,7 @@ typedef struct _MPI26_EVENT_DATA_PCIE_DEVICE_STATUS_CHANGE { #define MPI26_EVENT_PCIDEV_STAT_RC_CMP_INTERNAL_DEV_RESET (0x0E) #define MPI26_EVENT_PCIDEV_STAT_RC_CMP_TASK_ABORT_INTERNAL (0x0F) #define MPI26_EVENT_PCIDEV_STAT_RC_DEV_INIT_FAILURE (0x10) +#define MPI26_EVENT_PCIDEV_STAT_RC_PCIE_HOT_RESET_FAILED (0x11) /*PCIe Enumeration Event data (MPI v2.6 and later) */ -- cgit v1.2.3 From c1a6c5ac4278d406c112cc2f038e6e506feadff9 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:41 -0400 Subject: scsi: mpt3sas: For NVME device, issue a protocol level reset 1) Manufacturing Page 11 contains parameters to control internal firmware behavior. Based on AddlFlags2 field FW/Driver behaviour can be changed, (flag tm_custom_handling is used for this) a) For PCIe device, protocol level reset should be used if flag tm_custom_handling is 0. Since Abort Task Set, LUN reset and Target reset will result in a protocol level reset. Drivers should issue only one type of this reset, if that fails then it should escalate to a controller reset (diag reset/OCR). b) If the driver has control over the TM reset timeout value, then driver should use the value exposed in PCIe Device Page 2 for pcie device (field ControllerResetTO). Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.c | 13 ++++++ drivers/scsi/mpt3sas/mpt3sas_base.h | 26 ++++++++++-- drivers/scsi/mpt3sas/mpt3sas_ctl.c | 22 +++++++++-- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 76 ++++++++++++++++++++++++++++++------ 4 files changed, 116 insertions(+), 21 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 270b39f60c6d..bf04fa90f433 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -4142,6 +4142,7 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) Mpi2ConfigReply_t mpi_reply; u32 iounit_pg1_flags; + ioc->nvme_abort_timeout = 30; mpt3sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0); if (ioc->ir_firmware) mpt3sas_config_get_manufacturing_pg10(ioc, &mpi_reply, @@ -4160,6 +4161,18 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc) mpt3sas_config_set_manufacturing_pg11(ioc, &mpi_reply, &ioc->manu_pg11); } + if (ioc->manu_pg11.AddlFlags2 & NVME_TASK_MNGT_CUSTOM_MASK) + ioc->tm_custom_handling = 1; + else { + ioc->tm_custom_handling = 0; + if (ioc->manu_pg11.NVMeAbortTO < NVME_TASK_ABORT_MIN_TIMEOUT) + ioc->nvme_abort_timeout = NVME_TASK_ABORT_MIN_TIMEOUT; + else if (ioc->manu_pg11.NVMeAbortTO > + NVME_TASK_ABORT_MAX_TIMEOUT) + ioc->nvme_abort_timeout = NVME_TASK_ABORT_MAX_TIMEOUT; + else + ioc->nvme_abort_timeout = ioc->manu_pg11.NVMeAbortTO; + } mpt3sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2); mpt3sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3); diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 0eceebbdc5d5..7a9f0ac30359 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -146,8 +146,12 @@ #define NVME_CMD_PRP1_OFFSET 24 /* PRP1 offset in NVMe cmd */ #define NVME_CMD_PRP2_OFFSET 32 /* PRP2 offset in NVMe cmd */ #define NVME_ERROR_RESPONSE_SIZE 16 /* Max NVME Error Response */ +#define NVME_TASK_ABORT_MIN_TIMEOUT 6 +#define NVME_TASK_ABORT_MAX_TIMEOUT 60 +#define NVME_TASK_MNGT_CUSTOM_MASK (0x0010) #define NVME_PRP_PAGE_SIZE 4096 /* Page size */ + /* * reset phases */ @@ -363,7 +367,15 @@ struct Mpi2ManufacturingPage11_t { u8 EEDPTagMode; /* 09h */ u8 Reserved3; /* 0Ah */ u8 Reserved4; /* 0Bh */ - __le32 Reserved5[23]; /* 0Ch-60h*/ + __le32 Reserved5[8]; /* 0Ch-2Ch */ + u16 AddlFlags2; /* 2Ch */ + u8 AddlFlags3; /* 2Eh */ + u8 Reserved6; /* 2Fh */ + __le32 Reserved7[7]; /* 30h - 4Bh */ + u8 NVMeAbortTO; /* 4Ch */ + u8 Reserved8; /* 4Dh */ + u16 Reserved9; /* 4Eh */ + __le32 Reserved10[4]; /* 50h - 60h */ }; /** @@ -573,6 +585,7 @@ struct _pcie_device { u8 enclosure_level; u8 connector_name[4]; u8 *serial_number; + u8 reset_timeout; struct kref refcount; }; /** @@ -1211,6 +1224,10 @@ struct MPT3SAS_ADAPTER { void *event_log; u32 event_masks[MPI2_EVENT_NOTIFY_EVENTMASK_WORDS]; + u8 tm_custom_handling; + u8 nvme_abort_timeout; + + /* static config pages */ struct mpt3sas_facts facts; struct mpt3sas_port_facts *pfacts; @@ -1473,10 +1490,11 @@ u8 mpt3sas_scsih_event_callback(struct MPT3SAS_ADAPTER *ioc, u8 msix_index, u32 reply); void mpt3sas_scsih_reset_handler(struct MPT3SAS_ADAPTER *ioc, int reset_phase); -int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 lun, u8 type, u16 smid_task, u16 msix_task, ulong timeout); +int mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, + u8 type, u16 smid_task, u16 msix_task, u8 timeout, u8 tr_method); int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 lun, u8 type, u16 smid_task, u16 msix_task, ulong timeout); + u64 lun, u8 type, u16 smid_task, u16 msix_task, + u8 timeout, u8 tr_method); void mpt3sas_scsih_set_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); void mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle); diff --git a/drivers/scsi/mpt3sas/mpt3sas_ctl.c b/drivers/scsi/mpt3sas/mpt3sas_ctl.c index c1b17d64c95f..3269ef43f07e 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_ctl.c +++ b/drivers/scsi/mpt3sas/mpt3sas_ctl.c @@ -644,9 +644,10 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, MPI2RequestHeader_t *mpi_request = NULL, *request; MPI2DefaultReply_t *mpi_reply; Mpi26NVMeEncapsulatedRequest_t *nvme_encap_request = NULL; + struct _pcie_device *pcie_device = NULL; u32 ioc_state; u16 smid; - unsigned long timeout; + u8 timeout; u8 issue_reset; u32 sz, sz_arg; void *psge; @@ -659,6 +660,7 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, long ret; u16 wait_state_count; u16 device_handle = MPT3SAS_INVALID_DEVICE_HANDLE; + u8 tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; issue_reset = 0; @@ -1074,14 +1076,26 @@ _ctl_do_mpt_command(struct MPT3SAS_ADAPTER *ioc, struct mpt3_ioctl_command karg, ioc->name, le16_to_cpu(mpi_request->FunctionDependent1)); mpt3sas_halt_firmware(ioc); - mpt3sas_scsih_issue_locked_tm(ioc, - le16_to_cpu(mpi_request->FunctionDependent1), 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0, 30); + pcie_device = mpt3sas_get_pdev_by_handle(ioc, + le16_to_cpu(mpi_request->FunctionDependent1)); + if (pcie_device && (!ioc->tm_custom_handling)) + mpt3sas_scsih_issue_locked_tm(ioc, + le16_to_cpu(mpi_request->FunctionDependent1), + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 0, pcie_device->reset_timeout, + tr_method); + else + mpt3sas_scsih_issue_locked_tm(ioc, + le16_to_cpu(mpi_request->FunctionDependent1), + 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, + 0, 30, MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET); } else mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); } out: + if (pcie_device) + pcie_device_put(pcie_device); /* free memory associated with sg buffers */ if (data_in) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 27cc2e827472..3dd70de58132 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -1088,7 +1088,7 @@ _scsih_pcie_device_remove(struct MPT3SAS_ADAPTER *ioc, pcie_device->slot); if (pcie_device->connector_name[0] != '\0') pr_info(MPT3SAS_FMT - "removing enclosure level(0x%04x), connector name( %s)\n", + "removing enclosure level(0x%04x), connector name( %s)\n", ioc->name, pcie_device->enclosure_level, pcie_device->connector_name); @@ -2632,6 +2632,7 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) * @smid_task: smid assigned to the task * @msix_task: MSIX table index supplied by the OS * @timeout: timeout in seconds + * @tr_method: Target Reset Method * Context: user * * A generic API for sending task management requests to firmware. @@ -2642,8 +2643,8 @@ mpt3sas_scsih_clear_tm_flag(struct MPT3SAS_ADAPTER *ioc, u16 handle) * Return SUCCESS or FAILED. */ int -mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 lun, u8 type, u16 smid_task, u16 msix_task, ulong timeout) +mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, u64 lun, + u8 type, u16 smid_task, u16 msix_task, u8 timeout, u8 tr_method) { Mpi2SCSITaskManagementRequest_t *mpi_request; Mpi2SCSITaskManagementReply_t *mpi_reply; @@ -2689,8 +2690,8 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, } dtmprintk(ioc, pr_info(MPT3SAS_FMT - "sending tm: handle(0x%04x), task_type(0x%02x), smid(%d)\n", - ioc->name, handle, type, smid_task)); + "sending tm: handle(0x%04x), task_type(0x%02x), smid(%d), timeout(%d), tr_method(0x%x)\n", + ioc->name, handle, type, smid_task, timeout, tr_method)); ioc->tm_cmds.status = MPT3_CMD_PENDING; mpi_request = mpt3sas_base_get_msg_frame(ioc, smid); ioc->tm_cmds.smid = smid; @@ -2699,6 +2700,7 @@ mpt3sas_scsih_issue_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = type; + mpi_request->MsgFlags = tr_method; mpi_request->TaskMID = cpu_to_le16(smid_task); int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN); mpt3sas_scsih_set_tm_flag(ioc, handle); @@ -2745,13 +2747,14 @@ out: } int mpt3sas_scsih_issue_locked_tm(struct MPT3SAS_ADAPTER *ioc, u16 handle, - u64 lun, u8 type, u16 smid_task, u16 msix_task, ulong timeout) + u64 lun, u8 type, u16 smid_task, u16 msix_task, + u8 timeout, u8 tr_method) { int ret; mutex_lock(&ioc->tm_cmds.mutex); ret = mpt3sas_scsih_issue_tm(ioc, handle, lun, type, smid_task, - msix_task, timeout); + msix_task, timeout, tr_method); mutex_unlock(&ioc->tm_cmds.mutex); return ret; @@ -2854,6 +2857,8 @@ scsih_abort(struct scsi_cmnd *scmd) u16 handle; int r; + u8 timeout = 30; + struct _pcie_device *pcie_device = NULL; sdev_printk(KERN_INFO, scmd->device, "attempting task abort! scmd(%p)\n", scmd); _scsih_tm_display_info(ioc, scmd); @@ -2888,15 +2893,20 @@ scsih_abort(struct scsi_cmnd *scmd) mpt3sas_halt_firmware(ioc); handle = sas_device_priv_data->sas_target->handle; + pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); + if (pcie_device && (!ioc->tm_custom_handling)) + timeout = ioc->nvme_abort_timeout; r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, - st->smid, st->msix_io, 30); + st->smid, st->msix_io, timeout, 0); /* Command must be cleared after abort */ if (r == SUCCESS && st->cb_idx != 0xFF) r = FAILED; out: sdev_printk(KERN_INFO, scmd->device, "task abort: %s scmd(%p)\n", ((r == SUCCESS) ? "SUCCESS" : "FAILED"), scmd); + if (pcie_device) + pcie_device_put(pcie_device); return r; } @@ -2912,7 +2922,10 @@ scsih_dev_reset(struct scsi_cmnd *scmd) struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT3SAS_DEVICE *sas_device_priv_data; struct _sas_device *sas_device = NULL; + struct _pcie_device *pcie_device = NULL; u16 handle; + u8 tr_method = 0; + u8 tr_timeout = 30; int r; struct scsi_target *starget = scmd->device->sdev_target; @@ -2950,8 +2963,16 @@ scsih_dev_reset(struct scsi_cmnd *scmd) goto out; } + pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); + + if (pcie_device && (!ioc->tm_custom_handling)) { + tr_timeout = pcie_device->reset_timeout; + tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; + } else + tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; r = mpt3sas_scsih_issue_locked_tm(ioc, handle, scmd->device->lun, - MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 0, 30); + MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 0, + tr_timeout, tr_method); /* Check for busy commands after reset */ if (r == SUCCESS && atomic_read(&scmd->device->device_busy)) r = FAILED; @@ -2961,6 +2982,8 @@ scsih_dev_reset(struct scsi_cmnd *scmd) if (sas_device) sas_device_put(sas_device); + if (pcie_device) + pcie_device_put(pcie_device); return r; } @@ -2977,7 +3000,10 @@ scsih_target_reset(struct scsi_cmnd *scmd) struct MPT3SAS_ADAPTER *ioc = shost_priv(scmd->device->host); struct MPT3SAS_DEVICE *sas_device_priv_data; struct _sas_device *sas_device = NULL; + struct _pcie_device *pcie_device = NULL; u16 handle; + u8 tr_method = 0; + u8 tr_timeout = 30; int r; struct scsi_target *starget = scmd->device->sdev_target; struct MPT3SAS_TARGET *target_priv_data = starget->hostdata; @@ -3014,8 +3040,16 @@ scsih_target_reset(struct scsi_cmnd *scmd) goto out; } + pcie_device = mpt3sas_get_pdev_by_handle(ioc, handle); + + if (pcie_device && (!ioc->tm_custom_handling)) { + tr_timeout = pcie_device->reset_timeout; + tr_method = MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; + } else + tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; r = mpt3sas_scsih_issue_locked_tm(ioc, handle, 0, - MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0, 30); + MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 0, + tr_timeout, tr_method); /* Check for busy commands after reset */ if (r == SUCCESS && atomic_read(&starget->target_busy)) r = FAILED; @@ -3025,7 +3059,8 @@ scsih_target_reset(struct scsi_cmnd *scmd) if (sas_device) sas_device_put(sas_device); - + if (pcie_device) + pcie_device_put(pcie_device); return r; } @@ -3559,6 +3594,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) unsigned long flags; struct _tr_list *delayed_tr; u32 ioc_state; + u8 tr_method = 0; if (ioc->pci_error_recovery) { dewtprintk(ioc, pr_info(MPT3SAS_FMT @@ -3601,6 +3637,11 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) sas_address = pcie_device->wwid; } spin_unlock_irqrestore(&ioc->pcie_device_lock, flags); + if (pcie_device && (!ioc->tm_custom_handling)) + tr_method = + MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; + else + tr_method = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; } if (sas_target_priv_data) { dewtprintk(ioc, pr_info(MPT3SAS_FMT @@ -3664,6 +3705,7 @@ _scsih_tm_tr_send(struct MPT3SAS_ADAPTER *ioc, u16 handle) mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; mpi_request->DevHandle = cpu_to_le16(handle); mpi_request->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; + mpi_request->MsgFlags = tr_method; set_bit(handle, ioc->device_remove_in_progress); mpt3sas_base_put_smid_hi_priority(ioc, smid, 0); mpt3sas_trigger_master(ioc, MASTER_TRIGGER_DEVICE_REMOVAL); @@ -6938,6 +6980,11 @@ _scsih_pcie_add_device(struct MPT3SAS_ADAPTER *ioc, u16 handle) } pcie_device->nvme_mdts = le32_to_cpu(pcie_device_pg2.MaximumDataTransferSize); + if (pcie_device_pg2.ControllerResetTO) + pcie_device->reset_timeout = + pcie_device_pg2.ControllerResetTO; + else + pcie_device->reset_timeout = 30; if (ioc->wait_for_discovery_to_complete) _scsih_pcie_device_init_add(ioc, pcie_device); @@ -7190,6 +7237,9 @@ _scsih_pcie_device_status_change_event_debug(struct MPT3SAS_ADAPTER *ioc, case MPI26_EVENT_PCIDEV_STAT_RC_ASYNC_NOTIFICATION: reason_str = "internal async notification"; break; + case MPI26_EVENT_PCIDEV_STAT_RC_PCIE_HOT_RESET_FAILED: + reason_str = "pcie hot reset failed"; + break; default: reason_str = "unknown reason"; break; @@ -7444,7 +7494,7 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); r = mpt3sas_scsih_issue_tm(ioc, handle, lun, MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, st->smid, - st->msix_io, 30); + st->msix_io, 30, 0); if (r == FAILED) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: FAILED when sending " @@ -7485,7 +7535,7 @@ _scsih_sas_broadcast_primitive_event(struct MPT3SAS_ADAPTER *ioc, r = mpt3sas_scsih_issue_tm(ioc, handle, sdev->lun, MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, st->smid, - st->msix_io, 30); + st->msix_io, 30, 0); if (r == FAILED || st->cb_idx != 0xFF) { sdev_printk(KERN_WARNING, sdev, "mpt3sas_scsih_issue_tm: ABORT_TASK: FAILED : " -- cgit v1.2.3 From 87b3576e9eaba85644c643bb55b485b5330a2af5 Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:42 -0400 Subject: scsi: mpt3sas: fix possible memory leak. In ioctl exit path driver refers ioc_list to free memory associated with diag buffers and event_log pointer used to save events by driver. If ctl_exit() func is called after unregistering driver, then ioc_list will be empty and hence driver will not be able to free the allocated memory which in turn causes memory leak. So call ctl_exit() function before unregistering mpt3sas driver. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 3dd70de58132..b8d131a455d0 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -11287,10 +11287,10 @@ _mpt3sas_exit(void) pr_info("mpt3sas version %s unloading\n", MPT3SAS_DRIVER_VERSION); - pci_unregister_driver(&mpt3sas_driver); - mpt3sas_ctl_exit(hbas_to_enumerate); + pci_unregister_driver(&mpt3sas_driver); + scsih_exit(); } -- cgit v1.2.3 From f6972d7180909787a324b83bf3f2f0686f22286a Mon Sep 17 00:00:00 2001 From: Chaitra P B Date: Tue, 24 Apr 2018 05:28:43 -0400 Subject: scsi: mpt3sas: Update driver version "25.100.00.00" Update driver version to match OOB/internal driver version. Signed-off-by: Chaitra P B Signed-off-by: Suganath Prabu S Signed-off-by: Martin K. Petersen --- drivers/scsi/mpt3sas/mpt3sas_base.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index 7a9f0ac30359..f02974c0be4a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -74,8 +74,8 @@ #define MPT3SAS_DRIVER_NAME "mpt3sas" #define MPT3SAS_AUTHOR "Avago Technologies " #define MPT3SAS_DESCRIPTION "LSI MPT Fusion SAS 3.0 Device Driver" -#define MPT3SAS_DRIVER_VERSION "17.100.00.00" -#define MPT3SAS_MAJOR_VERSION 17 +#define MPT3SAS_DRIVER_VERSION "25.100.00.00" +#define MPT3SAS_MAJOR_VERSION 25 #define MPT3SAS_MINOR_VERSION 100 #define MPT3SAS_BUILD_VERSION 0 #define MPT3SAS_RELEASE_VERSION 00 -- cgit v1.2.3 From 625a1caefe435d5a2aca849e6baa454b7e3a3bbc Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:44 -0700 Subject: scsi: qla2xxx: Fix sending ADISC command for login This patch fixes login_retry login for ADISC command. when login_retry count reaches 0, further attempt to send ADISC command is ignored by the code. Remove this redundant login_retry count check from qla24xx_fcport_handle_login() [mkp: fix typo] Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 8f55dd44adae..b9050cb52c0e 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1167,9 +1167,6 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) fcport->login_gen, fcport->login_retry, fcport->loop_id, fcport->scan_state); - if (fcport->login_retry == 0) - return 0; - if (fcport->scan_state != QLA_FCPORT_FOUND) return 0; @@ -1194,7 +1191,8 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) return 0; } - fcport->login_retry--; + if (fcport->login_retry > 0) + fcport->login_retry--; switch (fcport->disc_state) { case DSC_DELETED: -- cgit v1.2.3 From 29528491ccf76ff017f29c6ff24f2d8945b43d7e Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:45 -0700 Subject: scsi: qla2xxx: Fix Rport and session state getting out of sync This patch fixes rport state and session state getting out of sync. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 025dc2d3f3de..85640707cceb 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -601,24 +601,18 @@ void qla2x00_async_nack_sp_done(void *s, int res) vha->fcport_count++; - if (!IS_IIDMA_CAPABLE(vha->hw) || - !vha->hw->flags.gpsc_supported) { - ql_dbg(ql_dbg_disc, vha, 0x20f3, - "%s %d %8phC post upd_fcport fcp_cnt %d\n", - __func__, __LINE__, - sp->fcport->port_name, - vha->fcport_count); - sp->fcport->disc_state = DSC_UPD_FCPORT; - qla24xx_post_upd_fcport_work(vha, sp->fcport); - } else { - ql_dbg(ql_dbg_disc, vha, 0x20f5, - "%s %d %8phC post gpsc fcp_cnt %d\n", - __func__, __LINE__, - sp->fcport->port_name, - vha->fcport_count); - - qla24xx_post_gpsc_work(vha, sp->fcport); - } + ql_dbg(ql_dbg_disc, vha, 0x20f3, + "%s %d %8phC post upd_fcport fcp_cnt %d\n", + __func__, __LINE__, + sp->fcport->port_name, + vha->fcport_count); + sp->fcport->disc_state = DSC_UPD_FCPORT; + qla24xx_post_upd_fcport_work(vha, sp->fcport); + } else { + sp->fcport->login_retry = 0; + sp->fcport->disc_state = DSC_LOGIN_COMPLETE; + sp->fcport->deleted = 0; + sp->fcport->logout_on_delete = 1; } break; -- cgit v1.2.3 From 1d317b21231bb2b81a6e0f94f708b8619ec8775b Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:46 -0700 Subject: scsi: qla2xxx: Delete session for nport id change This patch fixes regression introduced by commit a4239945b8ad ("scsi: qla2xxx: Add switch command to simplify fabric discovery") by scheduling session deletion when Nport ID changes. [mkp: clarified commit] Fixes: a4239945b8ad ("scsi: qla2xxx: Add switch command to simplify fabric discovery") Cc: Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_gs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 9e914f9c3ffb..05abe5aaab7f 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -3915,7 +3915,6 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) if (memcmp(rp->port_name, fcport->port_name, WWN_SIZE)) continue; fcport->scan_state = QLA_FCPORT_FOUND; - fcport->d_id.b24 = rp->id.b24; found = true; /* * If device was not a fabric device before. @@ -3923,7 +3922,10 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) { qla2x00_clear_loop_id(fcport); fcport->flags |= FCF_FABRIC_DEVICE; + } else if (fcport->d_id.b24 != rp->id.b24) { + qlt_schedule_sess_for_deletion(fcport); } + fcport->d_id.b24 = rp->id.b24; break; } -- cgit v1.2.3 From bee8b84686c4918354dcf7eef5481b06bde8c26e Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:47 -0700 Subject: scsi: qla2xxx: Reduce redundant ADISC command for RSCNs For each RSCN that triggers a rescan of the fabric, ADISC is used to revalidate an existing session. If the RSCN is not affecting all existing sessions, then driver should not send redundant ADISC for all existing sessions. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 1 + drivers/scsi/qla2xxx/qla_gs.c | 27 ++++++++++++++++++++++++--- drivers/scsi/qla2xxx/qla_init.c | 6 ++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index eb2ec1fb07cb..5a1900ec808d 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2346,6 +2346,7 @@ typedef struct fc_port { unsigned int login_succ:1; unsigned int query:1; unsigned int id_changed:1; + unsigned int rscn_rcvd:1; struct work_struct nvme_del_work; struct completion nvme_del_done; diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 05abe5aaab7f..939ac8435f19 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -3862,6 +3862,7 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) bool found; struct fab_scan_rp *rp; unsigned long flags; + u8 recheck = 0; ql_dbg(ql_dbg_disc, vha, 0xffff, "%s enter\n", __func__); @@ -3914,6 +3915,7 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) list_for_each_entry(fcport, &vha->vp_fcports, list) { if (memcmp(rp->port_name, fcport->port_name, WWN_SIZE)) continue; + fcport->rscn_rcvd = 0; fcport->scan_state = QLA_FCPORT_FOUND; found = true; /* @@ -3942,10 +3944,13 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) * Logout all previous fabric dev marked lost, except FCP2 devices. */ list_for_each_entry(fcport, &vha->vp_fcports, list) { - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) + if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) { + fcport->rscn_rcvd = 0; continue; + } if (fcport->scan_state != QLA_FCPORT_FOUND) { + fcport->rscn_rcvd = 0; if ((qla_dual_mode_enabled(vha) || qla_ini_mode_enabled(vha)) && atomic_read(&fcport->state) == FCS_ONLINE) { @@ -3963,15 +3968,31 @@ void qla24xx_async_gnnft_done(scsi_qla_host_t *vha, srb_t *sp) continue; } } - } else - qla24xx_fcport_handle_login(vha, fcport); + } else { + if (fcport->rscn_rcvd || + fcport->disc_state != DSC_LOGIN_COMPLETE) { + fcport->rscn_rcvd = 0; + qla24xx_fcport_handle_login(vha, fcport); + } + } } + recheck = 1; out: qla24xx_sp_unmap(vha, sp); spin_lock_irqsave(&vha->work_lock, flags); vha->scan.scan_flags &= ~SF_SCANNING; spin_unlock_irqrestore(&vha->work_lock, flags); + + if (recheck) { + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->rscn_rcvd) { + set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + break; + } + } + } } static void qla2x00_find_free_fcp_nvme_slot(struct scsi_qla_host *vha, diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index b9050cb52c0e..98d4b315d66a 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1348,6 +1348,7 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) fc_port_t *f, *tf; uint32_t id = 0, mask, rid; unsigned long flags; + fc_port_t *fcport; switch (ea->event) { case FCME_RSCN: @@ -1375,6 +1376,11 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) return; switch (ea->id.b.rsvd_1) { case RSCN_PORT_ADDR: + fcport = qla2x00_find_fcport_by_nportid + (vha, &ea->id, 1); + if (fcport) + fcport->rscn_rcvd = 1; + spin_lock_irqsave(&vha->work_lock, flags); if (vha->scan.scan_flags == 0) { ql_dbg(ql_dbg_disc, vha, 0xffff, -- cgit v1.2.3 From cc28e0ace97c5615cc2333d98827751cd0d794d1 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:48 -0700 Subject: scsi: qla2xxx: Move GPSC and GFPNID out of session management Move GPSC & GFPNID commands out of session management to reduce time lag in reporting the session state to remote port. These commands are not essential when it comes to maintaining the rport state. Delay sending these commands after rport state is set to Online. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_def.h | 3 +- drivers/scsi/qla2xxx/qla_gbl.h | 3 +- drivers/scsi/qla2xxx/qla_gs.c | 10 +---- drivers/scsi/qla2xxx/qla_init.c | 78 +++++++++++++++++++++++---------------- drivers/scsi/qla2xxx/qla_os.c | 4 ++ drivers/scsi/qla2xxx/qla_target.c | 1 - 6 files changed, 55 insertions(+), 44 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 5a1900ec808d..9442e18aef6f 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2279,8 +2279,6 @@ enum discovery_state { DSC_LOGIN_PEND, DSC_LOGIN_FAILED, DSC_GPDB, - DSC_GFPN_ID, - DSC_GPSC, DSC_UPD_FCPORT, DSC_LOGIN_COMPLETE, DSC_ADISC, @@ -3227,6 +3225,7 @@ enum qla_work_type { QLA_EVT_GNNID, QLA_EVT_GFPNID, QLA_EVT_SP_RETRY, + QLA_EVT_IIDMA, }; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 3c4c84ed0f0f..f68eb6096559 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -116,7 +116,8 @@ extern int qla2x00_post_async_prlo_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); extern int qla2x00_post_async_prlo_done_work(struct scsi_qla_host *, fc_port_t *, uint16_t *); - +int qla_post_iidma_work(struct scsi_qla_host *vha, fc_port_t *fcport); +void qla_do_iidma_work(struct scsi_qla_host *vha, fc_port_t *fcport); /* * Global Data in qla_os.c source file. */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 939ac8435f19..4bc2b66b299f 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -3175,7 +3175,6 @@ int qla24xx_async_gidpn(scsi_qla_host_t *vha, fc_port_t *fcport) done_free_sp: sp->free(sp); - fcport->flags &= ~FCF_ASYNC_SENT; done: fcport->flags &= ~FCF_ASYNC_ACTIVE; return rval; @@ -3239,7 +3238,7 @@ void qla24xx_handle_gpsc_event(scsi_qla_host_t *vha, struct event_arg *ea) return; } - qla24xx_post_upd_fcport_work(vha, ea->fcport); + qla_post_iidma_work(vha, fcport); } static void qla24xx_async_gpsc_sp_done(void *s, int res) @@ -3257,8 +3256,6 @@ static void qla24xx_async_gpsc_sp_done(void *s, int res) "Async done-%s res %x, WWPN %8phC \n", sp->name, res, fcport->port_name); - fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); - if (res == (DID_ERROR << 16)) { /* entry status error */ goto done; @@ -3327,7 +3324,6 @@ int qla24xx_async_gpsc(scsi_qla_host_t *vha, fc_port_t *fcport) if (!sp) goto done; - fcport->flags |= FCF_ASYNC_SENT; sp->type = SRB_CT_PTHRU_CMD; sp->name = "gpsc"; sp->gen1 = fcport->rscn_gen; @@ -4555,7 +4551,6 @@ int qla24xx_async_gnnid(scsi_qla_host_t *vha, fc_port_t *fcport) done_free_sp: sp->free(sp); - fcport->flags &= ~FCF_ASYNC_SENT; done: return rval; } @@ -4617,7 +4612,6 @@ static void qla2x00_async_gfpnid_sp_done(void *s, int res) struct event_arg ea; u64 wwn; - fcport->flags &= ~FCF_ASYNC_SENT; wwn = wwn_to_u64(fpn); if (wwn) memcpy(fcport->fabric_port_name, fpn, WWN_SIZE); @@ -4646,12 +4640,10 @@ int qla24xx_async_gfpnid(scsi_qla_host_t *vha, fc_port_t *fcport) if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT)) return rval; - fcport->disc_state = DSC_GFPN_ID; sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); if (!sp) goto done; - fcport->flags |= FCF_ASYNC_SENT; sp->type = SRB_CT_PTHRU_CMD; sp->name = "gfpnid"; sp->gen1 = fcport->rscn_gen; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 98d4b315d66a..792c09470c5a 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1021,30 +1021,11 @@ void __qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea) vha->fcport_count++; ea->fcport->login_succ = 1; - if (!IS_IIDMA_CAPABLE(vha->hw) || - !vha->hw->flags.gpsc_supported) { - ql_dbg(ql_dbg_disc, vha, 0x20d6, - "%s %d %8phC post upd_fcport fcp_cnt %d\n", - __func__, __LINE__, ea->fcport->port_name, - vha->fcport_count); - - qla24xx_post_upd_fcport_work(vha, ea->fcport); - } else { - if (ea->fcport->id_changed) { - ea->fcport->id_changed = 0; - ql_dbg(ql_dbg_disc, vha, 0x20d7, - "%s %d %8phC post gfpnid fcp_cnt %d\n", - __func__, __LINE__, ea->fcport->port_name, - vha->fcport_count); - qla24xx_post_gfpnid_work(vha, ea->fcport); - } else { - ql_dbg(ql_dbg_disc, vha, 0x20d7, - "%s %d %8phC post gpsc fcp_cnt %d\n", - __func__, __LINE__, ea->fcport->port_name, - vha->fcport_count); - qla24xx_post_gpsc_work(vha, ea->fcport); - } - } + ql_dbg(ql_dbg_disc, vha, 0x20d6, + "%s %d %8phC post upd_fcport fcp_cnt %d\n", + __func__, __LINE__, ea->fcport->port_name, + vha->fcport_count); + qla24xx_post_upd_fcport_work(vha, ea->fcport); } else if (ea->fcport->login_succ) { /* * We have an existing session. A late RSCN delivery @@ -5058,6 +5039,24 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) } } +void qla_do_iidma_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + qla2x00_iidma_fcport(vha, fcport); + qla24xx_update_fcport_fcp_prio(vha, fcport); +} + +int qla_post_iidma_work(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_IIDMA); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.fcport.fcport = fcport; + return qla2x00_post_work(vha, e); +} + /* qla2x00_reg_remote_port is reserved for Initiator Mode only.*/ static void qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) @@ -5126,13 +5125,14 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) if (IS_QLAFX00(vha->hw)) { qla2x00_set_fcport_state(fcport, FCS_ONLINE); - goto reg_port; + } else { + fcport->login_retry = 0; + fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); + fcport->disc_state = DSC_LOGIN_COMPLETE; + fcport->deleted = 0; + fcport->logout_on_delete = 1; + qla2x00_set_fcport_state(fcport, FCS_ONLINE); } - fcport->login_retry = 0; - fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); - fcport->disc_state = DSC_LOGIN_COMPLETE; - fcport->deleted = 0; - fcport->logout_on_delete = 1; qla2x00_set_fcport_state(fcport, FCS_ONLINE); qla2x00_iidma_fcport(vha, fcport); @@ -5144,7 +5144,6 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) qla24xx_update_fcport_fcp_prio(vha, fcport); -reg_port: switch (vha->host->active_mode) { case MODE_INITIATOR: qla2x00_reg_remote_port(vha, fcport); @@ -5163,6 +5162,23 @@ reg_port: default: break; } + + if (IS_IIDMA_CAPABLE(vha->hw) && vha->hw->flags.gpsc_supported) { + if (fcport->id_changed) { + fcport->id_changed = 0; + ql_dbg(ql_dbg_disc, vha, 0x20d7, + "%s %d %8phC post gfpnid fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, + vha->fcport_count); + qla24xx_post_gfpnid_work(vha, fcport); + } else { + ql_dbg(ql_dbg_disc, vha, 0x20d7, + "%s %d %8phC post gpsc fcp_cnt %d\n", + __func__, __LINE__, fcport->port_name, + vha->fcport_count); + qla24xx_post_gpsc_work(vha, fcport); + } + } } /* diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 15eaa6dded04..817c18a8e84d 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -5063,6 +5063,10 @@ qla2x00_do_work(struct scsi_qla_host *vha) break; case QLA_EVT_SP_RETRY: qla_sp_retry(vha, e); + break; + case QLA_EVT_IIDMA: + qla_do_iidma_work(vha, e->u.fcport.fcport); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 85640707cceb..3e8bf728e884 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4835,7 +4835,6 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, switch (sess->disc_state) { case DSC_LOGIN_PEND: case DSC_GPDB: - case DSC_GPSC: case DSC_UPD_FCPORT: case DSC_LOGIN_COMPLETE: case DSC_ADISC: -- cgit v1.2.3 From 8ea4faf829eb2ea36dd0c989965c029390d238f3 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:49 -0700 Subject: scsi: qla2xxx: Fix Inquiry command being dropped in Target mode When a connection is established, the target core session may not be created immediately. Current code will drop/terminate the command based on the session state. This patch will return BUSY status for any commands arriving on wire before the session is created. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 3e8bf728e884..5e81b64c8ef5 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -5140,10 +5140,15 @@ static int __qlt_send_busy(struct qla_qpair *qpair, struct fc_port *sess = NULL; unsigned long flags; u16 temp; + port_id_t id; + + id.b.al_pa = atio->u.isp24.fcp_hdr.s_id[2]; + id.b.area = atio->u.isp24.fcp_hdr.s_id[1]; + id.b.domain = atio->u.isp24.fcp_hdr.s_id[0]; + id.b.rsvd_1 = 0; spin_lock_irqsave(&ha->tgt.sess_lock, flags); - sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, - atio->u.isp24.fcp_hdr.s_id); + sess = qla2x00_find_fcport_by_nportid(vha, &id, 1); spin_unlock_irqrestore(&ha->tgt.sess_lock, flags); if (!sess) { qlt_send_term_exchange(qpair, NULL, atio, 1, 0); -- cgit v1.2.3 From e25f76549bd7794aa171e1d998514d8d99172b1f Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:50 -0700 Subject: scsi: qla2xxx: Use predefined get_datalen_for_atio() inline function - Uses predefine inline function to access add_cdb_len field in ATIO. - Return SS_RESIDUAL_UNDER status when sending BUSY Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 5e81b64c8ef5..a77703f655ed 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3550,13 +3550,6 @@ static int __qlt_send_term_exchange(struct qla_qpair *qpair, temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); ctio24->u.status1.ox_id = cpu_to_le16(temp); - /* Most likely, it isn't needed */ - ctio24->u.status1.residual = get_unaligned((uint32_t *) - &atio->u.isp24.fcp_cmnd.add_cdb[ - atio->u.isp24.fcp_cmnd.add_cdb_len]); - if (ctio24->u.status1.residual != 0) - ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER; - /* Memory Barrier */ wmb(); if (qpair->reqq_start_iocbs) @@ -4051,9 +4044,7 @@ static void __qlt_do_work(struct qla_tgt_cmd *cmd) fcp_task_attr = qlt_get_fcp_task_attr(vha, atio->u.isp24.fcp_cmnd.task_attr); - data_length = be32_to_cpu(get_unaligned((uint32_t *) - &atio->u.isp24.fcp_cmnd.add_cdb[ - atio->u.isp24.fcp_cmnd.add_cdb_len])); + data_length = get_datalen_for_atio(atio); ret = ha->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length, fcp_task_attr, data_dir, bidi); @@ -5187,6 +5178,12 @@ static int __qlt_send_busy(struct qla_qpair *qpair, */ ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id); ctio24->u.status1.scsi_status = cpu_to_le16(status); + + ctio24->u.status1.residual = get_datalen_for_atio(atio); + + if (ctio24->u.status1.residual != 0) + ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER; + /* Memory Barrier */ wmb(); if (qpair->reqq_start_iocbs) -- cgit v1.2.3 From 36d49c92efe6e0d149fcc5b91da93136d65d5ce3 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:51 -0700 Subject: scsi: qla2xxx: Remove stale debug value for login_retry flag Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 792c09470c5a..3405cb9031b6 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -4517,7 +4517,6 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) fcport->fw_login_state = DSC_LS_PORT_UNAVAIL; fcport->deleted = QLA_SESS_DELETED; fcport->login_retry = vha->hw->login_retry_count; - fcport->login_retry = 5; fcport->logout_on_delete = 1; if (!fcport->ct_desc.ct_sns) { -- cgit v1.2.3 From fc31b7a803bfe6548a445bd48039b56728d3ac3c Mon Sep 17 00:00:00 2001 From: "himanshu.madhani@cavium.com" Date: Tue, 1 May 2018 09:01:52 -0700 Subject: scsi: qla2xxx: Prevent relogin loop by removing stale code Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_init.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 3405cb9031b6..1aa3720ea2ed 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1331,20 +1331,6 @@ void qla2x00_fcport_event_handler(scsi_qla_host_t *vha, struct event_arg *ea) unsigned long flags; fc_port_t *fcport; - switch (ea->event) { - case FCME_RSCN: - case FCME_GIDPN_DONE: - case FCME_GPSC_DONE: - case FCME_GPNID_DONE: - case FCME_GNNID_DONE: - if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) || - test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags)) - return; - break; - default: - break; - } - switch (ea->event) { case FCME_RELOGIN: if (test_bit(UNLOADING, &vha->dpc_flags)) -- cgit v1.2.3 From 84905dfe78d28b597a1c991bfc05722a8fba1184 Mon Sep 17 00:00:00 2001 From: Quinn Tran Date: Tue, 1 May 2018 09:01:53 -0700 Subject: scsi: qla2xxx: Fix TMF and Multi-Queue config For target mode, task management command is queued to specific cpu base on where the SCSI command is residing. This prevent race condition of task management command getting ahead of regular scsi command. Signed-off-by: Quinn Tran Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_target.c | 135 ++++++++++++++++++++++++++++++------- drivers/scsi/qla2xxx/qla_target.h | 4 +- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 27 ++++++++ 3 files changed, 141 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index a77703f655ed..b85c833099ff 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1924,13 +1924,84 @@ static void abort_cmds_for_lun(struct scsi_qla_host *vha, spin_unlock_irqrestore(&vha->cmd_list_lock, flags); } +static struct qla_qpair_hint *qlt_find_qphint(struct scsi_qla_host *vha, + uint64_t unpacked_lun) +{ + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_qpair_hint *h = NULL; + + if (vha->flags.qpairs_available) { + h = btree_lookup64(&tgt->lun_qpair_map, unpacked_lun); + if (!h) + h = &tgt->qphints[0]; + } else { + h = &tgt->qphints[0]; + } + + return h; +} + +static void qlt_do_tmr_work(struct work_struct *work) +{ + struct qla_tgt_mgmt_cmd *mcmd = + container_of(work, struct qla_tgt_mgmt_cmd, work); + struct qla_hw_data *ha = mcmd->vha->hw; + int rc = EIO; + uint32_t tag; + unsigned long flags; + + switch (mcmd->tmr_func) { + case QLA_TGT_ABTS: + tag = mcmd->orig_iocb.abts.exchange_addr_to_abort; + break; + default: + tag = 0; + break; + } + + rc = ha->tgt.tgt_ops->handle_tmr(mcmd, mcmd->unpacked_lun, + mcmd->tmr_func, tag); + + if (rc != 0) { + spin_lock_irqsave(mcmd->qpair->qp_lock_ptr, flags); + switch (mcmd->tmr_func) { + case QLA_TGT_ABTS: + qlt_24xx_send_abts_resp(mcmd->qpair, + &mcmd->orig_iocb.abts, + FCP_TMF_REJECTED, false); + break; + case QLA_TGT_LUN_RESET: + case QLA_TGT_CLEAR_TS: + case QLA_TGT_ABORT_TS: + case QLA_TGT_CLEAR_ACA: + case QLA_TGT_TARGET_RESET: + qlt_send_busy(mcmd->qpair, &mcmd->orig_iocb.atio, + qla_sam_status); + break; + + case QLA_TGT_ABORT_ALL: + case QLA_TGT_NEXUS_LOSS_SESS: + case QLA_TGT_NEXUS_LOSS: + qlt_send_notify_ack(mcmd->qpair, + &mcmd->orig_iocb.imm_ntfy, 0, 0, 0, 0, 0, 0); + break; + } + spin_unlock_irqrestore(mcmd->qpair->qp_lock_ptr, flags); + + ql_dbg(ql_dbg_tgt_mgt, mcmd->vha, 0xf052, + "qla_target(%d): tgt_ops->handle_tmr() failed: %d\n", + mcmd->vha->vp_idx, rc); + mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); + } +} + /* ha->hardware_lock supposed to be held on entry */ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, struct abts_recv_from_24xx *abts, struct fc_port *sess) { struct qla_hw_data *ha = vha->hw; struct qla_tgt_mgmt_cmd *mcmd; - int rc; + struct qla_qpair_hint *h = &vha->vha_tgt.qla_tgt->qphints[0]; if (abort_cmd_for_tag(vha, abts->exchange_addr_to_abort)) { /* send TASK_ABORT response immediately */ @@ -1955,23 +2026,29 @@ static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts)); mcmd->reset_count = ha->base_qpair->chip_reset; mcmd->tmr_func = QLA_TGT_ABTS; - mcmd->qpair = ha->base_qpair; + mcmd->qpair = h->qpair; mcmd->vha = vha; /* * LUN is looked up by target-core internally based on the passed * abts->exchange_addr_to_abort tag. */ - rc = ha->tgt.tgt_ops->handle_tmr(mcmd, 0, mcmd->tmr_func, - abts->exchange_addr_to_abort); - if (rc != 0) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf052, - "qla_target(%d): tgt_ops->handle_tmr()" - " failed: %d", vha->vp_idx, rc); - mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); - return -EFAULT; + mcmd->se_cmd.cpuid = h->cpuid; + + if (ha->tgt.tgt_ops->find_cmd_by_tag) { + struct qla_tgt_cmd *abort_cmd; + + abort_cmd = ha->tgt.tgt_ops->find_cmd_by_tag(sess, + abts->exchange_addr_to_abort); + if (abort_cmd && abort_cmd->qpair) { + mcmd->qpair = abort_cmd->qpair; + mcmd->se_cmd.cpuid = abort_cmd->se_cmd.cpuid; + } } + INIT_WORK(&mcmd->work, qlt_do_tmr_work); + queue_work_on(mcmd->se_cmd.cpuid, qla_tgt_wq, &mcmd->work); + return 0; } @@ -4320,7 +4397,7 @@ static int qlt_issue_task_mgmt(struct fc_port *sess, u64 lun, struct qla_hw_data *ha = vha->hw; struct qla_tgt_mgmt_cmd *mcmd; struct atio_from_isp *a = (struct atio_from_isp *)iocb; - int res; + struct qla_qpair_hint *h = &vha->vha_tgt.qla_tgt->qphints[0]; mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC); if (!mcmd) { @@ -4340,24 +4417,36 @@ static int qlt_issue_task_mgmt(struct fc_port *sess, u64 lun, mcmd->tmr_func = fn; mcmd->flags = flags; mcmd->reset_count = ha->base_qpair->chip_reset; - mcmd->qpair = ha->base_qpair; + mcmd->qpair = h->qpair; mcmd->vha = vha; + mcmd->se_cmd.cpuid = h->cpuid; + mcmd->unpacked_lun = lun; switch (fn) { case QLA_TGT_LUN_RESET: - abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id); - break; - } + case QLA_TGT_CLEAR_TS: + case QLA_TGT_ABORT_TS: + abort_cmds_for_lun(vha, lun, a->u.isp24.fcp_hdr.s_id); + /* drop through */ + case QLA_TGT_CLEAR_ACA: + h = qlt_find_qphint(vha, mcmd->unpacked_lun); + mcmd->qpair = h->qpair; + mcmd->se_cmd.cpuid = h->cpuid; + break; - res = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, mcmd->tmr_func, 0); - if (res != 0) { - ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000b, - "qla_target(%d): tgt.tgt_ops->handle_tmr() failed: %d\n", - sess->vha->vp_idx, res); - mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); - return -EFAULT; + case QLA_TGT_TARGET_RESET: + case QLA_TGT_NEXUS_LOSS_SESS: + case QLA_TGT_NEXUS_LOSS: + case QLA_TGT_ABORT_ALL: + default: + /* no-op */ + break; } + INIT_WORK(&mcmd->work, qlt_do_tmr_work); + queue_work_on(mcmd->se_cmd.cpuid, qla_tgt_wq, + &mcmd->work); + return 0; } @@ -5097,8 +5186,6 @@ static void qlt_handle_imm_notify(struct scsi_qla_host *vha, ql_dbg(ql_dbg_tgt_mgt, vha, 0xf038, "qla_target(%d): Immediate notify task %x\n", vha->vp_idx, iocb->u.isp2x.task_flags); - if (qlt_handle_task_mgmt(vha, iocb) == 0) - send_notify_ack = 0; break; case IMM_NTFY_ELS: diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index 728ce74358e7..fecf96f0225c 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -682,7 +682,7 @@ struct qla_tgt_cmd; * target module (tcm_qla2xxx). */ struct qla_tgt_func_tmpl { - + struct qla_tgt_cmd *(*find_cmd_by_tag)(struct fc_port *, uint64_t); int (*handle_cmd)(struct scsi_qla_host *, struct qla_tgt_cmd *, unsigned char *, uint32_t, int, int, int); void (*handle_data)(struct qla_tgt_cmd *); @@ -966,6 +966,8 @@ struct qla_tgt_mgmt_cmd { unsigned int flags; uint32_t reset_count; #define QLA24XX_MGMT_SEND_NACK 1 + struct work_struct work; + uint64_t unpacked_lun; union { struct atio_from_isp atio; struct imm_ntfy_from_isp imm_ntfy; diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index aadfeaac3898..34ea4a8f98d2 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -630,6 +630,32 @@ static int tcm_qla2xxx_handle_tmr(struct qla_tgt_mgmt_cmd *mcmd, u64 lun, transl_tmr_func, GFP_ATOMIC, tag, flags); } +static struct qla_tgt_cmd *tcm_qla2xxx_find_cmd_by_tag(struct fc_port *sess, + uint64_t tag) +{ + struct qla_tgt_cmd *cmd = NULL; + struct se_cmd *secmd; + unsigned long flags; + + if (!sess->se_sess) + return NULL; + + spin_lock_irqsave(&sess->se_sess->sess_cmd_lock, flags); + list_for_each_entry(secmd, &sess->se_sess->sess_cmd_list, se_cmd_list) { + /* skip task management functions, including tmr->task_cmd */ + if (secmd->se_cmd_flags & SCF_SCSI_TMR_CDB) + continue; + + if (secmd->tag == tag) { + cmd = container_of(secmd, struct qla_tgt_cmd, se_cmd); + break; + } + } + spin_unlock_irqrestore(&sess->se_sess->sess_cmd_lock, flags); + + return cmd; +} + static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, @@ -1608,6 +1634,7 @@ static void tcm_qla2xxx_update_sess(struct fc_port *sess, port_id_t s_id, * Calls into tcm_qla2xxx used by qla2xxx LLD I/O path. */ static struct qla_tgt_func_tmpl tcm_qla2xxx_template = { + .find_cmd_by_tag = tcm_qla2xxx_find_cmd_by_tag, .handle_cmd = tcm_qla2xxx_handle_cmd, .handle_data = tcm_qla2xxx_handle_data, .handle_tmr = tcm_qla2xxx_handle_tmr, -- cgit v1.2.3 From 3f9da256020b7258bc5cf70e5d9650ee9eccbd49 Mon Sep 17 00:00:00 2001 From: "himanshu.madhani@cavium.com" Date: Tue, 1 May 2018 09:01:54 -0700 Subject: scsi: qla2xxx: Update driver version to 10.00.00.07-k Signed-off-by: Himanshu Madhani Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 0c55d7057280..1ad7582220c3 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "10.00.00.06-k" +#define QLA2XXX_VERSION "10.00.00.07-k" #define QLA_DRIVER_MAJOR_VER 10 #define QLA_DRIVER_MINOR_VER 0 -- cgit v1.2.3 From 44c7c859117fcaaa466f66db6198a466d5ed61fa Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:45 -0700 Subject: scsi: qedf: Synchronize rport restarts when multiple ELS commands time out If multiple ELS commands time out, such as aborts, they could all try to restart the same rport and the same time. This could mean multiple multiple processes trying to clean up any outstanding commands or trying to upload the same port. Add a new flag (QEDF_RPORT_IN_RESET) and check other fcport state flags before trying to reset the port. Fixes the crash: [17501.824701] ------------[ cut here ]------------ [17501.824733] kernel BUG at include/asm-generic/dma-mapping-common.h:65! [17501.824760] invalid opcode: 0000 [#1] SMP [17501.824781] Modules linked in: xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ipt_REJECT nf_reject_ipv4 tun bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter ses enclosure dm_service_time vfat fat sb_edac edac_core intel_powerclamp coretemp intel_rapl iosf_mbi kvm_intel kvm irqbypass joydev btrfs hpilo raid6_pq iTCO_wdt iTCO_vendor_support xor hpwdt ipmi_ssif sg crc32_pclmul ghash_clmulni_intel aesni_intel lrw gf128mul ioatdma lpc_ich glue_helper ablk_helper i2c_i801 shpchp cryptd ipmi_si pcspkr acpi_power_meter ipmi_devintf pcc_cpufreq dca wmi ipmi_msghandler dm_multipath nfsd auth_rpcgss nfs_acl lockd grace sunrpc ip_tables xfs libcrc32c sr_mod cdrom sd_mod [17501.825119] crc_t10dif crct10dif_generic mgag200 i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm qedf(OE) drm libfcoe ahci qedi(OE) crct10dif_pclmul libfc libahci uio crct10dif_common crc32c_intel libiscsi libata scsi_transport_iscsi scsi_transport_fc tg3 qede(OE) scsi_tgt hpsa qed(OE) i2c_core ptp scsi_transport_sas pps_core iscsi_boot_sysfs dm_mirror dm_region_hash dm_log dm_mod [17501.825292] CPU: 8 PID: 10531 Comm: kworker/u96:1 Tainted: G OE ------------ 3.10.0-693.el7.x86_64 #1 [17501.825330] Hardware name: HP ProLiant DL380 Gen9/ProLiant DL380 Gen9, BIOS P89 06/02/2016 [17501.825372] Workqueue: fc_rport_eq fc_rport_work [libfc] [17501.825395] task: ffff88101bca8000 ti: ffff881025278000 task.ti: ffff881025278000 [17501.825424] RIP: 0010:[] [] qedf_unmap_sg_list.isra.15+0x89/0x90 [qedf] [17501.825471] RSP: 0018:ffff88102527bb98 EFLAGS: 00010212 [17501.825493] RAX: ffff8800224eac00 RBX: ffffc9000cd05210 RCX: 0000000000001000 [17501.825520] RDX: 000000007e655e40 RSI: 0000000000001000 RDI: ffff88107fe3b098 [17501.826683] RBP: ffff88102527bba0 R08: ffffffff81a13200 R09: 0000000000000286 [17501.827747] R10: 0000000000000004 R11: 0000000000000005 R12: ffffc9000cd051b8 [17501.828804] R13: ffff881037640c28 R14: 0000000000000007 R15: ffffc9000cd05200 [17501.829850] FS: 0000000000000000(0000) GS:ffff88103fa00000(0000) knlGS:0000000000000000 [17501.830910] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [17501.831966] CR2: 00007f9b94005f38 CR3: 00000000019f2000 CR4: 00000000003407e0 [17501.833027] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [17501.834087] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 [17501.835142] Stack: [17501.836201] ffff881033ddbb80 ffff88102527bc30 ffffffffc042f834 0000000000002710 [17501.837264] ffff88102527bbd0 ffffffff8133d9dd ffffc9000cd052a0 ffff88102527bc30 [17501.838325] ffffffff816a9c65 0000000000000001 ffff88101bca8000 ffffffff810c4810 [17501.839388] Call Trace: [17501.840446] [] qedf_scsi_done+0x54/0x1d0 [qedf] [17501.841504] [] ? list_del+0xd/0x30 [17501.842537] [] ? wait_for_completion_timeout+0x125/0x140 [17501.843560] [] ? wake_up_state+0x20/0x20 [17501.844577] [] qedf_initiate_cleanup+0x2e1/0x310 [qedf] [17501.845587] [] qedf_flush_active_ios+0x10e/0x260 [qedf] [17501.846612] [] qedf_cleanup_fcport+0x5f/0x370 [qedf] [17501.847613] [] qedf_rport_event_handler+0x398/0x950 [qedf] [17501.848602] [] ? dequeue_entity+0x11c/0x5d0 [17501.849581] [] ? __internal_add_timer+0xab/0x130 [17501.850555] [] ? dequeue_task_fair+0x41e/0x660 [17501.851528] [] fc_rport_work+0xf4/0x6c0 [libfc] [17501.852490] [] process_one_work+0x17a/0x440 [17501.853446] [] worker_thread+0x126/0x3c0 Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf.h | 1 + drivers/scsi/qedf/qedf_els.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index c105a2e48ac1..996bd791783d 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -180,6 +180,7 @@ struct qedf_rport { spinlock_t rport_lock; #define QEDF_RPORT_SESSION_READY 1 #define QEDF_RPORT_UPLOADING_CONNECTION 2 +#define QEDF_RPORT_IN_RESET 3 unsigned long flags; unsigned long retry_delay_timestamp; struct fc_rport *rport; diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index aa22b11436ba..7024a2154ed1 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -322,6 +322,17 @@ void qedf_restart_rport(struct qedf_rport *fcport) if (!fcport) return; + if (test_bit(QEDF_RPORT_IN_RESET, &fcport->flags) || + !test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags) || + test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) { + QEDF_ERR(&(fcport->qedf->dbg_ctx), "fcport %p already in reset or not offloaded.\n", + fcport); + return; + } + + /* Set that we are now in reset */ + set_bit(QEDF_RPORT_IN_RESET, &fcport->flags); + rdata = fcport->rdata; if (rdata) { lport = fcport->qedf->lport; @@ -334,6 +345,7 @@ void qedf_restart_rport(struct qedf_rport *fcport) if (rdata) fc_rport_login(rdata); } + clear_bit(QEDF_RPORT_IN_RESET, &fcport->flags); } static void qedf_l2_els_compl(struct qedf_els_cb_arg *cb_arg) -- cgit v1.2.3 From c3ef86f3ec581b370d3fed5f5efbd52c2f489985 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:46 -0700 Subject: scsi: qedf: Increase the number of default FIP VLAN request retries to 60 Some configurations need more than 30 seconds to respond to a FIP VLAN request so increase the default to 60 seconds. Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 284ccb566b19..e14b402f25d5 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -44,10 +44,10 @@ module_param_named(debug, qedf_debug, uint, S_IRUGO); MODULE_PARM_DESC(debug, " Debug mask. Pass '1' to enable default debugging" " mask"); -static uint qedf_fipvlan_retries = 30; +static uint qedf_fipvlan_retries = 60; module_param_named(fipvlan_retries, qedf_fipvlan_retries, int, S_IRUGO); MODULE_PARM_DESC(fipvlan_retries, " Number of FIP VLAN requests to attempt " - "before giving up (default 30)"); + "before giving up (default 60)"); static uint qedf_fallback_vlan = QEDF_FALLBACK_VLAN; module_param_named(fallback_vlan, qedf_fallback_vlan, int, S_IRUGO); -- cgit v1.2.3 From f32803bb4512c73711a996e3e8e3188abeb8445e Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:47 -0700 Subject: scsi: qedf: Add missing skb frees in error path Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_fip.c | 1 + drivers/scsi/qedf/qedf_main.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/scsi/qedf/qedf_fip.c b/drivers/scsi/qedf/qedf_fip.c index 773558fc0697..aef199c12fb4 100644 --- a/drivers/scsi/qedf/qedf_fip.c +++ b/drivers/scsi/qedf/qedf_fip.c @@ -181,6 +181,7 @@ void qedf_fip_recv(struct qedf_ctx *qedf, struct sk_buff *skb) QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Dropping CVL since FCF has not been selected " "yet."); + kfree_skb(skb); return; } diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index e14b402f25d5..8dcacc5b5f60 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -2190,6 +2190,7 @@ static void qedf_recv_frame(struct qedf_ctx *qedf, if (ntoh24(&dest_mac[3]) != ntoh24(fh->fh_d_id)) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FC frame d_id mismatch with MAC %pM.\n", dest_mac); + kfree_skb(skb); return; } -- cgit v1.2.3 From 15a93de7e9a53f7112ccd1d890bf7b16bc31d543 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:48 -0700 Subject: scsi: qedf: Fix VLAN display when printing sent FIP frames Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_fip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/qedf/qedf_fip.c b/drivers/scsi/qedf/qedf_fip.c index aef199c12fb4..c6b984efa5e8 100644 --- a/drivers/scsi/qedf/qedf_fip.c +++ b/drivers/scsi/qedf/qedf_fip.c @@ -134,7 +134,7 @@ void qedf_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_LL2, "FIP frame send: " "dest=%pM op=%x sub=%x vlan=%04x.", eth_hdr->h_dest, op, sub, - ntohs(vlan_tci)); + vlan_tci); if (qedf_dump_frames) print_hex_dump(KERN_WARNING, "fip ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, false); -- cgit v1.2.3 From 766639cab01ed84968524ce6ad25b03ba6b15c1a Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:49 -0700 Subject: scsi: qedf: Add check for offload before flushing I/Os for target We need to check that a fcport is offloaded before we try to flush any requests. No doing so could lead to undefined results and most likely a crash. Fixes the oops: [ 343.971886] [0000:42:00.3]:[qedf_execute_tmf:2070]:8: wait for tm_cmpl timeout! [ 343.971933] BUG: unable to handle kernel paging request at 00000000000024a8 [ 343.971949] IP: [] qedf_flush_active_ios+0x46/0x260 [qedf] [ 343.971952] PGD 42c569067 PUD 4160fe067 PMD 0 [ 343.971954] Oops: 0000 [#1] SMP [ 343.972008] Modules linked in: qedf(OEX) qed(OEX) bnx2i cnic fuse af_packet iscsi_ibft msr xfs intel_rapl sb_edac edac_core x86_pkg_temp_thermal bnx2x geneve intel_powerclamp vxlan coretemp ipmi_ssif ipmi_devintf kvm_intel kvm libiscsi joydev irqbypass crct10dif_pclmul crc32_pclmul ghash_clmulni_intel tg3 ip6_udp_tunnel udp_tunnel mdio libcrc32c iTCO_wdt scsi_transport_iscsi uio drbg iTCO_vendor_support iscsi_boot_sysfs dcdbas(X) ipmi_si ansi_cprng aesni_intel aes_x86_64 lrw gf128mul glue_helper ablk_helper ptp pps_core pcspkr libphy lpc_ich mfd_core cryptd fjes wmi ipmi_msghandler button crc8 libfcoe libfc scsi_transport_fc mei_me mei shpchp processor acpi_pad btrfs xor hid_generic usbhid raid6_pq sd_mod sr_mod cdrom mgag200 crc32c_intel i2c_algo_bit drm_kms_helper syscopyarea sysfillrect sysimgblt [ 343.972020] fb_sys_fops ttm ahci ehci_pci libahci ehci_hcd drm libata usbcore megaraid_sas usb_common sg dm_multipath dm_mod scsi_dh_rdac scsi_dh_emc scsi_dh_alua scsi_mod autofs4 [last unloaded: qedf] [ 343.972022] Supported: Yes, External [ 343.972026] CPU: 30 PID: 12777 Comm: sg_reset Tainted: G W OE X 4.4.73-5-default #1 [ 343.972027] Hardware name: Dell Inc. PowerEdge R720/0X3D66, BIOS 2.1.3 11/20/2013 [ 343.972029] task: ffff88018dfc0e80 ti: ffff88042bd7c000 task.ti: ffff88042bd7c000 [ 343.972036] RIP: 0010:[] [] qedf_flush_active_ios+0x46/0x260 [qedf] [ 343.972038] RSP: 0018:ffff88042bd7fbe0 EFLAGS: 00010286 [ 343.972039] RAX: 0000000000000000 RBX: ffff88042ce37800 RCX: 0000000000000400 [ 343.972040] RDX: 000000000000060e RSI: ffffffffa06be830 RDI: ffff8807e5072cc0 [ 343.972041] RBP: 0000000000001000 R08: ffffffffa06bff4d R09: ffff88018dd84580 [ 343.972042] R10: 000000000000018b R11: 0000000000000002 R12: 0000000000002003 [ 343.972043] R13: 0000000000000000 R14: 0000000000000000 R15: ffff8807e5072cc0 [ 343.972046] FS: 00007fc1c8809700(0000) GS:ffff88042fbc0000(0000) knlGS:0000000000000000 [ 343.972048] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 343.972049] CR2: 00000000000024a8 CR3: 00000004236ec000 CR4: 00000000001406e0 [ 343.972050] Stack: [ 343.972053] 504c78750607e154 ffffffff810a7d10 ffff88042ce37800 0000000000000010 [ 343.972055] 0000000000002003 ffff8807ff480c48 ffff8807e5072cc0 ffffc90004ec4ff8 [ 343.972057] ffffffffa06b9b86 ffff880800000010 0000000000000282 ffff88042ce37800 [ 343.972058] Call Trace: [ 343.972094] [] qedf_initiate_tmf+0x346/0x3e0 [qedf] [ 343.972120] [] scsi_try_bus_device_reset+0x26/0x40 [scsi_mod] [ 343.972133] [] scsi_ioctl_reset+0x13e/0x260 [scsi_mod] [ 343.972145] [] scsi_ioctl+0x136/0x3d0 [scsi_mod] [ 343.972154] [] blkdev_ioctl+0x6bb/0x950 [ 343.972164] [] block_ioctl+0x3d/0x40 [ 343.972170] [] do_vfs_ioctl+0x2cd/0x4a0 [ 343.972186] [] SyS_ioctl+0x74/0x80 [ 343.972193] [] entry_SYSCALL_64_fastpath+0x12/0x6d [ 343.975285] DWARF2 unwinder stuck at entry_SYSCALL_64_fastpath+0x12/0x6d Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 50a50c4249d0..94d6455c9ddc 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1414,6 +1414,12 @@ void qedf_flush_active_ios(struct qedf_rport *fcport, int lun) if (!fcport) return; + /* Check that fcport is still offloaded */ + if (!test_bit(QEDF_RPORT_SESSION_READY, &fcport->flags)) { + QEDF_ERR(NULL, "fcport is no longer offloaded.\n"); + return; + } + qedf = fcport->qedf; cmd_mgr = qedf->cmd_mgr; -- cgit v1.2.3 From a93755cf7e6e822e6856113342fe26ea874c92cc Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:50 -0700 Subject: scsi: qedf: Sanity check FCoE/FIP priority value to make sure it's between 0 and 7 Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 8dcacc5b5f60..1a58f4a9cdf9 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -3405,6 +3405,13 @@ static int __init qedf_init(void) if (qedf_debug == QEDF_LOG_DEFAULT) qedf_debug = QEDF_DEFAULT_LOG_MASK; + /* Check that default prio for FIP/FCoE traffic is between 0..7 */ + if (qedf_default_prio > 7) { + qedf_default_prio = QEDF_DEFAULT_PRIO; + QEDF_ERR(NULL, "FCoE/FIP priority out of range, resetting to %d.\n", + QEDF_DEFAULT_PRIO); + } + /* Print driver banner */ QEDF_INFO(NULL, QEDF_LOG_INFO, "%s v%s.\n", QEDF_DESCR, QEDF_VERSION); -- cgit v1.2.3 From ba17d379c2bf859c5964cc35bab24bd0e67b0e12 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:51 -0700 Subject: scsi: qedf: Add dcbx_not_wait module parameter so we won't wait for DCBX convergence to start discovery This module parameter is to work around cases where we do not receive the DCBX handler notification from qed but discovery is still possible if we send out a FIP VLAN request irregardless of the DCBX state. [mkp: zeroday warning] Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 1a58f4a9cdf9..b96c92803588 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -89,6 +89,11 @@ module_param_named(retry_delay, qedf_retry_delay, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(retry_delay, " Enable/disable handling of FCP_RSP IU retry " "delay handling (default off)."); +static bool qedf_dcbx_no_wait; +module_param_named(dcbx_no_wait, qedf_dcbx_no_wait, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(dcbx_no_wait, " Do not wait for DCBX convergence to start " + "sending FIP VLAN requests on link up (Default: off)."); + static uint qedf_dp_module; module_param_named(dp_module, qedf_dp_module, uint, S_IRUGO); MODULE_PARM_DESC(dp_module, " bit flags control for verbose printk passed " @@ -489,7 +494,8 @@ static void qedf_link_update(void *dev, struct qed_link_output *link) atomic_set(&qedf->link_state, QEDF_LINK_UP); qedf_update_link_speed(qedf, link); - if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE) { + if (atomic_read(&qedf->dcbx) == QEDF_DCBX_DONE || + qedf_dcbx_no_wait) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "DCBx done.\n"); if (atomic_read(&qedf->link_down_tmo_valid) > 0) @@ -541,7 +547,8 @@ static void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type) atomic_set(&qedf->dcbx, QEDF_DCBX_DONE); - if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) { + if (atomic_read(&qedf->link_state) == QEDF_LINK_UP && + !qedf_dcbx_no_wait) { if (atomic_read(&qedf->link_down_tmo_valid) > 0) queue_delayed_work(qedf->link_update_wq, &qedf->link_recovery, 0); -- cgit v1.2.3 From 84b2ba6e427ce57408052bde987260c022aff96c Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:52 -0700 Subject: scsi: qedf: Honor priority from DCBX FCoE App tag We currently hard code the priority in the 8021q tag to 3 for FCoE traffic. The vast majority of the time this is fine but if the priority is something else besides 3, any VLAN ID comparison either in the non-offload path or offload path will fail and cause dropped frames where none are expected. Change the behavior so that the driver default is 3 if we do not get any DCBX convergence. If DCBX does converge, then set the FIP/FCoE priority in the following manner: 1. If the qedf_default_prio modparam is set use that 2. If the DCBX FCoE priority is not in range (0..7) use 3 3. Use the DCBX FCoE priority we get in the driver's DCBX handler Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf.h | 1 + drivers/scsi/qedf/qedf_main.c | 52 +++++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 996bd791783d..1dffe21873d0 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -301,6 +301,7 @@ struct qedf_ctx { #define QEDF_FALLBACK_VLAN 1002 #define QEDF_DEFAULT_PRIO 3 int vlan_id; + u8 prio; struct qed_dev *cdev; struct qed_dev_fcoe_info dev_info; struct qed_int_info int_info; diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index b96c92803588..28f37113bc9b 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -54,10 +54,10 @@ module_param_named(fallback_vlan, qedf_fallback_vlan, int, S_IRUGO); MODULE_PARM_DESC(fallback_vlan, " VLAN ID to try if fip vlan request fails " "(default 1002)."); -static uint qedf_default_prio = QEDF_DEFAULT_PRIO; +static int qedf_default_prio = -1; module_param_named(default_prio, qedf_default_prio, int, S_IRUGO); -MODULE_PARM_DESC(default_prio, " Default 802.1q priority for FIP and FCoE" - " traffic (default 3)."); +MODULE_PARM_DESC(default_prio, " Override 802.1q priority for FIP and FCoE" + " traffic (value between 0 and 7, default 3)."); uint qedf_dump_frames; module_param_named(dump_frames, qedf_dump_frames, int, S_IRUGO | S_IWUSR); @@ -114,9 +114,9 @@ static struct kmem_cache *qedf_io_work_cache; void qedf_set_vlan_id(struct qedf_ctx *qedf, int vlan_id) { qedf->vlan_id = vlan_id; - qedf->vlan_id |= qedf_default_prio << VLAN_PRIO_SHIFT; + qedf->vlan_id |= qedf->prio << VLAN_PRIO_SHIFT; QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Setting vlan_id=%04x " - "prio=%d.\n", vlan_id, qedf_default_prio); + "prio=%d.\n", vlan_id, qedf->prio); } /* Returns true if we have a valid vlan, false otherwise */ @@ -521,7 +521,7 @@ static void qedf_link_update(void *dev, struct qed_link_output *link) "Starting link down tmo.\n"); atomic_set(&qedf->link_down_tmo_valid, 1); } - qedf->vlan_id = 0; + qedf->vlan_id = 0; qedf_update_link_speed(qedf, link); queue_delayed_work(qedf->link_update_wq, &qedf->link_update, qedf_link_down_tmo * HZ); @@ -532,6 +532,7 @@ static void qedf_link_update(void *dev, struct qed_link_output *link) static void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type) { struct qedf_ctx *qedf = (struct qedf_ctx *)dev; + u8 tmp_prio; QEDF_ERR(&(qedf->dbg_ctx), "DCBx event valid=%d enabled=%d fcoe " "prio=%d.\n", get->operational.valid, get->operational.enabled, @@ -547,6 +548,24 @@ static void qedf_dcbx_handler(void *dev, struct qed_dcbx_get *get, u32 mib_type) atomic_set(&qedf->dcbx, QEDF_DCBX_DONE); + /* + * Set the 8021q priority in the following manner: + * + * 1. If a modparam is set use that + * 2. If the value is not between 0..7 use the default + * 3. Use the priority we get from the DCBX app tag + */ + tmp_prio = get->operational.app_prio.fcoe; + if (qedf_default_prio > -1) + qedf->prio = qedf_default_prio; + else if (tmp_prio < 0 || tmp_prio > 7) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, + "FIP/FCoE prio %d out of range, setting to %d.\n", + tmp_prio, QEDF_DEFAULT_PRIO); + qedf->prio = QEDF_DEFAULT_PRIO; + } else + qedf->prio = tmp_prio; + if (atomic_read(&qedf->link_state) == QEDF_LINK_UP && !qedf_dcbx_no_wait) { if (atomic_read(&qedf->link_down_tmo_valid) > 0) @@ -1114,7 +1133,7 @@ static int qedf_offload_connection(struct qedf_ctx *qedf, conn_info.vlan_tag = qedf->vlan_id << FCOE_CONN_OFFLOAD_RAMROD_DATA_VLAN_ID_SHIFT; conn_info.vlan_tag |= - qedf_default_prio << FCOE_CONN_OFFLOAD_RAMROD_DATA_PRIORITY_SHIFT; + qedf->prio << FCOE_CONN_OFFLOAD_RAMROD_DATA_PRIORITY_SHIFT; conn_info.flags |= (FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_MASK << FCOE_CONN_OFFLOAD_RAMROD_DATA_B_VLAN_FLAG_SHIFT); @@ -2985,8 +3004,9 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) qedf->link_update_wq = create_workqueue(host_buf); INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update); INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery); - qedf->fipvlan_retries = qedf_fipvlan_retries; + /* Set a default prio in case DCBX doesn't converge */ + qedf->prio = QEDF_DEFAULT_PRIO; /* * Common probe. Takes care of basic hardware init and pci_* @@ -3412,12 +3432,16 @@ static int __init qedf_init(void) if (qedf_debug == QEDF_LOG_DEFAULT) qedf_debug = QEDF_DEFAULT_LOG_MASK; - /* Check that default prio for FIP/FCoE traffic is between 0..7 */ - if (qedf_default_prio > 7) { - qedf_default_prio = QEDF_DEFAULT_PRIO; - QEDF_ERR(NULL, "FCoE/FIP priority out of range, resetting to %d.\n", - QEDF_DEFAULT_PRIO); - } + /* + * Check that default prio for FIP/FCoE traffic is between 0..7 if a + * value has been set + */ + if (qedf_default_prio > -1) + if (qedf_default_prio > 7) { + qedf_default_prio = QEDF_DEFAULT_PRIO; + QEDF_ERR(NULL, "FCoE/FIP priority out of range, resetting to %d.\n", + QEDF_DEFAULT_PRIO); + } /* Print driver banner */ QEDF_INFO(NULL, QEDF_LOG_INFO, "%s v%s.\n", QEDF_DESCR, -- cgit v1.2.3 From adf488425294de6434b9b49ee27643ece10423da Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:53 -0700 Subject: scsi: qedf: Release RRQ reference correctly when RRQ command times out When an RRQ request times out the reference is not getting decremented correctly as there are still ELS commands leftover when we flush any pending I/Os during offload: [ 281.788553] [0000:21:00.3]:[qedf_cmd_timeout:58]:4: ELS timeout, xid=0x96a. ... [ 281.788553] [0000:21:00.3]:[qedf_cmd_timeout:58]:4: ELS timeout, xid=0x96a. [ 281.788772] [0000:21:00.3]:[qedf_rrq_compl:182]:4: Entered. [ 281.788774] [0000:21:00.3]:[qedf_rrq_compl:200]:4: rrq_compl: orig io = ffffc90004c556f8, orig xid = 0x81b, rrq_xid = 0x96a, refcount=1 ... [ 331.448032] [0000:21:00.3]:[qedf_flush_els_req:1512]:4: Flushing ELS request xid=0x96a refcount=2. The fix is to call kref_put on the rrq_req in case of timeout as the timeout handler will call rrq_compl directly vs. a normal completion where it is call from els_compl. Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_els.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index 7024a2154ed1..816f693ef4a8 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -201,6 +201,14 @@ static void qedf_rrq_compl(struct qedf_els_cb_arg *cb_arg) kref_put(&orig_io_req->refcount, qedf_release_cmd); out_free: + /* + * Release a reference to the rrq request if we timed out as the + * rrq completion handler is called directly from the timeout handler + * and not from els_compl where the reference would have normally been + * released. + */ + if (rrq_req->event == QEDF_IOREQ_EV_ELS_TMO) + kref_put(&rrq_req->refcount, qedf_release_cmd); kfree(cb_arg); } -- cgit v1.2.3 From a8f192bce17430106157100746699bfabbd6e892 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:54 -0700 Subject: scsi: qedf: Return request as DID_NO_CONNECT if MSI-X is not enabled Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 94d6455c9ddc..78cbd126c7e5 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -931,6 +931,15 @@ qedf_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc_cmd) return 0; } + if (!qedf->pdev->msix_enabled) { + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, + "Completing sc_cmd=%p DID_NO_CONNECT as MSI-X is not enabled.\n", + sc_cmd); + sc_cmd->result = DID_NO_CONNECT << 16; + sc_cmd->scsi_done(sc_cmd); + return 0; + } + rval = fc_remote_port_chkready(rport); if (rval) { sc_cmd->result = rval; -- cgit v1.2.3 From 3f9de7f0413df53efa47e1571433f0c951d26e46 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:55 -0700 Subject: scsi: qedf: Check if link is already up when receiving a link up event from qed [mkp: typo] Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 28f37113bc9b..c3fbab059599 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -485,6 +485,11 @@ static void qedf_link_update(void *dev, struct qed_link_output *link) struct qedf_ctx *qedf = (struct qedf_ctx *)dev; if (link->link_up) { + if (atomic_read(&qedf->link_state) == QEDF_LINK_UP) { + QEDF_INFO((&qedf->dbg_ctx), QEDF_LOG_DISC, + "Ignoring link up event as link is already up.\n"); + return; + } QEDF_ERR(&(qedf->dbg_ctx), "LINK UP (%d GB/s).\n", link->speed / 1000); -- cgit v1.2.3 From 8025c84208ef53a8a57f04d3d44a091e272510d3 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:56 -0700 Subject: scsi: qedf: Add task id to kref_get_unless_zero() debug messages when flushing requests Helps to corroborate which requests we can't get reference on and if it's real bug or not. Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 78cbd126c7e5..589414f06376 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1445,8 +1445,8 @@ void qedf_flush_active_ios(struct qedf_rport *fcport, int lun) rc = kref_get_unless_zero(&io_req->refcount); if (!rc) { QEDF_ERR(&(qedf->dbg_ctx), - "Could not get kref for io_req=0x%p.\n", - io_req); + "Could not get kref for ELS io_req=0x%p xid=0x%x.\n", + io_req, io_req->xid); continue; } qedf_flush_els_req(qedf, io_req); @@ -1472,7 +1472,7 @@ void qedf_flush_active_ios(struct qedf_rport *fcport, int lun) rc = kref_get_unless_zero(&io_req->refcount); if (!rc) { QEDF_ERR(&(qedf->dbg_ctx), "Could not get kref for " - "io_req=0x%p\n", io_req); + "io_req=0x%p xid=0x%x\n", io_req, io_req->xid); continue; } QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_IO, -- cgit v1.2.3 From f9a4a7f2c0f79d782aea04c05eb75fcee9f3e506 Mon Sep 17 00:00:00 2001 From: Saurav Kashyap Date: Wed, 25 Apr 2018 06:08:57 -0700 Subject: scsi: qedf: Remove setting DCBX pending during soft context reset PROBLEM DESCRIPTION: According to the logs, STAG was changing and it was triggering soft reset. In soft reset we used to virtual link down and up and also we were disabling DCBx flag. Since this was virtual link flap, DCBx never used to converge again. SOLUTION: Code change is to remove disabling DCBx flag from soft reset. Signed-off-by: Saurav Kashyap Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index c3fbab059599..2da6ba82cc6f 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -734,7 +734,6 @@ static void qedf_ctx_soft_reset(struct fc_lport *lport) /* For host reset, essentially do a soft link up/down */ atomic_set(&qedf->link_state, QEDF_LINK_DOWN); - atomic_set(&qedf->dcbx, QEDF_DCBX_PENDING); queue_delayed_work(qedf->link_update_wq, &qedf->link_update, 0); qedf_wait_for_upload(qedf); -- cgit v1.2.3 From 4b9b7fabb39b3e9d7682d1bdff0521982fe80fbd Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:58 -0700 Subject: scsi: qedf: Improve firmware debug dump handling Get all firmware debug data instead of just a grc dump. Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf.h | 2 ++ drivers/scsi/qedf/qedf_dbg.c | 2 +- drivers/scsi/qedf/qedf_main.c | 13 ++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 1dffe21873d0..44770f849632 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -367,6 +367,7 @@ struct qedf_ctx { #define QEDF_IO_WORK_MIN 64 mempool_t *io_mempool; struct workqueue_struct *dpc_wq; + struct delayed_work grcdump_work; u32 slow_sge_ios; u32 fast_sge_ios; @@ -499,6 +500,7 @@ extern void qedf_process_seq_cleanup_compl(struct qedf_ctx *qedf, struct fcoe_cqe *cqe, struct qedf_ioreq *io_req); extern int qedf_send_flogi(struct qedf_ctx *qedf); extern void qedf_fp_io_handler(struct work_struct *work); +extern void qedf_wq_grcdump(struct work_struct *work); #define FCOE_WORD_TO_BYTE 4 #define QEDF_MAX_TASK_NUM 0xFFFF diff --git a/drivers/scsi/qedf/qedf_dbg.c b/drivers/scsi/qedf/qedf_dbg.c index bd1cef25a900..e02c7a497de9 100644 --- a/drivers/scsi/qedf/qedf_dbg.c +++ b/drivers/scsi/qedf/qedf_dbg.c @@ -147,7 +147,7 @@ qedf_get_grc_dump(struct qed_dev *cdev, const struct qed_common_ops *common, if (!*buf) return -EINVAL; - return common->dbg_grc(cdev, *buf, grcsize); + return common->dbg_all_data(cdev, *buf); } void diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 2da6ba82cc6f..3f4243f42821 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -3008,6 +3008,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) qedf->link_update_wq = create_workqueue(host_buf); INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update); INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery); + INIT_DELAYED_WORK(&qedf->grcdump_work, qedf_wq_grcdump); qedf->fipvlan_retries = qedf_fipvlan_retries; /* Set a default prio in case DCBX doesn't converge */ qedf->prio = QEDF_DEFAULT_PRIO; @@ -3240,7 +3241,8 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) * unload process. */ if (mode != QEDF_MODE_RECOVERY) { - qedf->grcdump_size = qed_ops->common->dbg_grc_size(qedf->cdev); + qedf->grcdump_size = + qed_ops->common->dbg_all_data_size(qedf->cdev); if (qedf->grcdump_size) { rc = qedf_alloc_grc_dump_buf(&qedf->grcdump, qedf->grcdump_size); @@ -3424,6 +3426,15 @@ static void qedf_remove(struct pci_dev *pdev) __qedf_remove(pdev, QEDF_MODE_NORMAL); } +void qedf_wq_grcdump(struct work_struct *work) +{ + struct qedf_ctx *qedf = + container_of(work, struct qedf_ctx, grcdump_work.work); + + QEDF_ERR(&(qedf->dbg_ctx), "Collecting GRC dump.\n"); + qedf_capture_grc_dump(qedf); +} + /* * Module Init/Remove */ -- cgit v1.2.3 From 65b7beca42a34b965602db4a1c06aa344cd62bc8 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:08:59 -0700 Subject: scsi: qedf: Honor default_prio module parameter even if DCBX does not converge Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 3f4243f42821..777ab14d2998 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -3011,7 +3011,14 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) INIT_DELAYED_WORK(&qedf->grcdump_work, qedf_wq_grcdump); qedf->fipvlan_retries = qedf_fipvlan_retries; /* Set a default prio in case DCBX doesn't converge */ - qedf->prio = QEDF_DEFAULT_PRIO; + if (qedf_default_prio > -1) { + /* + * This is the case where we pass a modparam in so we want to + * honor it even if dcbx doesn't converge. + */ + qedf->prio = qedf_default_prio; + } else + qedf->prio = QEDF_DEFAULT_PRIO; /* * Common probe. Takes care of basic hardware init and pci_* -- cgit v1.2.3 From 96673e1e224c8ecb44effc5aaf32455456b7c3e9 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:09:00 -0700 Subject: scsi: qedf: If qed fails to enable MSI-X fail PCI probe Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 777ab14d2998..23849f56210b 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -2134,7 +2134,8 @@ static int qedf_setup_int(struct qedf_ctx *qedf) QEDF_SIMD_HANDLER_NUM, qedf_simd_int_handler); qedf->int_info.used_cnt = 1; - return 0; + QEDF_ERR(&qedf->dbg_ctx, "Only MSI-X supported. Failing probe.\n"); + return -EINVAL; } /* Main function for libfc frame reception */ -- cgit v1.2.3 From 92bbccdf72ae7f605dbbae050e92dda8971c592d Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:09:01 -0700 Subject: scsi: qedf: Add additional checks when restarting an rport due to ABTS timeout There are a couple of kernel cases when we restart a remote port due to ABTS timeout that we need to handle: 1. Flush any outstanding ABTS requests when flushing I/Os so that we do not hold up the eh_abort handler indefinitely causing process hangs. 2. Check if we are currently uploading a connection before issuing an ABTS. Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 589414f06376..f669df03f37d 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1457,6 +1457,31 @@ void qedf_flush_active_ios(struct qedf_rport *fcport, int lun) goto free_cmd; } + if (io_req->cmd_type == QEDF_ABTS) { + rc = kref_get_unless_zero(&io_req->refcount); + if (!rc) { + QEDF_ERR(&(qedf->dbg_ctx), + "Could not get kref for abort io_req=0x%p xid=0x%x.\n", + io_req, io_req->xid); + continue; + } + QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_IO, + "Flushing abort xid=0x%x.\n", io_req->xid); + + clear_bit(QEDF_CMD_IN_ABORT, &io_req->flags); + + if (io_req->sc_cmd) { + if (io_req->return_scsi_cmd_on_abts) + qedf_scsi_done(qedf, io_req, DID_ERROR); + } + + /* Notify eh_abort handler that ABTS is complete */ + complete(&io_req->abts_done); + kref_put(&io_req->refcount, qedf_release_cmd); + + goto free_cmd; + } + if (!io_req->sc_cmd) continue; if (lun > 0) { @@ -1534,6 +1559,11 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) goto abts_err; } + if (test_bit(QEDF_RPORT_UPLOADING_CONNECTION, &fcport->flags)) { + QEDF_ERR(&qedf->dbg_ctx, "fcport is uploading.\n"); + rc = 1; + goto out; + } kref_get(&io_req->refcount); @@ -1573,6 +1603,7 @@ abts_err: * task at the firmware. */ qedf_initiate_cleanup(io_req, return_scsi_cmd_on_abts); +out: return rc; } -- cgit v1.2.3 From 4f4616ceebaf045c59e8a6aa01f08826d18d5c63 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:09:02 -0700 Subject: scsi: qedf: Set the UNLOADING flag when removing a vport Similar to what we do when we remove a PCI function, set the QEDF_UNLOADING flag to prevent any requests from being queued while a vport is being deleted. This prevents any requests from getting stuck in limbo when the vport is unloaded or deleted. Fixes the crash: PID: 106676 TASK: ffff9a436aa90000 CPU: 12 COMMAND: "multipathd" #0 [ffff9a43567d3550] machine_kexec+522 at ffffffffaca60b2a #1 [ffff9a43567d35b0] __crash_kexec+114 at ffffffffacb13512 #2 [ffff9a43567d3680] crash_kexec+48 at ffffffffacb13600 #3 [ffff9a43567d3698] oops_end+168 at ffffffffad117768 #4 [ffff9a43567d36c0] no_context+645 at ffffffffad106f52 #5 [ffff9a43567d3710] __bad_area_nosemaphore+116 at ffffffffad106fe9 #6 [ffff9a43567d3760] bad_area+70 at ffffffffad107379 #7 [ffff9a43567d3788] __do_page_fault+1247 at ffffffffad11a8cf #8 [ffff9a43567d37f0] do_page_fault+53 at ffffffffad11a915 #9 [ffff9a43567d3820] page_fault+40 at ffffffffad116768 [exception RIP: qedf_init_task+61] RIP: ffffffffc0e13c2d RSP: ffff9a43567d38d0 RFLAGS: 00010046 RAX: 0000000000000000 RBX: ffffbe920472c738 RCX: ffff9a434fa0e3e8 RDX: ffff9a434f695280 RSI: ffffbe920472c738 RDI: ffff9a43aa359c80 RBP: ffff9a43567d3950 R8: 0000000000000c15 R9: ffff9a3fb09b9880 R10: ffff9a434fa0e3e8 R11: ffff9a43567d35ce R12: 0000000000000000 R13: ffff9a434f695280 R14: ffff9a43aa359c80 R15: ffff9a3fb9e005c0 ORIG_RAX: ffffffffffffffff CS: 0010 SS: 0018 Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 23849f56210b..5f9ae6e9897e 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -1677,6 +1677,15 @@ static int qedf_vport_destroy(struct fc_vport *vport) struct Scsi_Host *shost = vport_to_shost(vport); struct fc_lport *n_port = shost_priv(shost); struct fc_lport *vn_port = vport->dd_data; + struct qedf_ctx *qedf = lport_priv(vn_port); + + if (!qedf) { + QEDF_ERR(NULL, "qedf is NULL.\n"); + goto out; + } + + /* Set unloading bit on vport qedf_ctx to prevent more I/O */ + set_bit(QEDF_UNLOADING, &qedf->flags); mutex_lock(&n_port->lp_mutex); list_del(&vn_port->list); @@ -1703,6 +1712,7 @@ static int qedf_vport_destroy(struct fc_vport *vport) if (vn_port->host) scsi_host_put(vn_port->host); +out: return 0; } -- cgit v1.2.3 From f3690a89f918031a5eed17da78af51c329efe93a Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:09:03 -0700 Subject: scsi: qedf: Add more defensive checks for concurrent error conditions During an uplink toggle test all error handling is done via timeout and firmware error conditions which can occur concurrently: - SCSI layer timeouts - Error detect CQEs - Firmware detected underruns - ABTS timeouts All these concurrent events require more defensive checks in the driver including: - Check both internally and externally generated aborts to make sure the xid is not already been aborted in another context or in cleanup. - Check back pointers in qedf_cmd_timeout to verify the context of the io_req, fcport and qedf_ctx - Check rport state in host reset handler to not reset the whole host if the rport is already uploaded or in the process of relogin - Check to state for an fcport before initiating a middle path ELS request Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_els.c | 13 +++++++++++-- drivers/scsi/qedf/qedf_io.c | 33 +++++++++++++++++++++++++++++++-- drivers/scsi/qedf/qedf_main.c | 26 ++++++++++++++++---------- 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index 816f693ef4a8..3053270aa473 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -14,8 +14,8 @@ static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op, void (*cb_func)(struct qedf_els_cb_arg *cb_arg), struct qedf_els_cb_arg *cb_arg, uint32_t timer_msec) { - struct qedf_ctx *qedf = fcport->qedf; - struct fc_lport *lport = qedf->lport; + struct qedf_ctx *qedf; + struct fc_lport *lport; struct qedf_ioreq *els_req; struct qedf_mp_req *mp_req; struct fc_frame_header *fc_hdr; @@ -29,6 +29,15 @@ static int qedf_initiate_els(struct qedf_rport *fcport, unsigned int op, unsigned long flags; u16 sqe_idx; + if (!fcport) { + QEDF_ERR(NULL, "fcport is NULL"); + rc = -EINVAL; + goto els_err; + } + + qedf = fcport->qedf; + lport = qedf->lport; + QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_ELS, "Sending ELS\n"); rc = fc_remote_port_chkready(fcport->rport); diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index f669df03f37d..07925f8e65bf 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -23,12 +23,31 @@ static void qedf_cmd_timeout(struct work_struct *work) struct qedf_ioreq *io_req = container_of(work, struct qedf_ioreq, timeout_work.work); - struct qedf_ctx *qedf = io_req->fcport->qedf; - struct qedf_rport *fcport = io_req->fcport; + struct qedf_ctx *qedf; + struct qedf_rport *fcport; u8 op = 0; + if (io_req == NULL) { + QEDF_INFO(NULL, QEDF_LOG_IO, "io_req is NULL.\n"); + return; + } + + fcport = io_req->fcport; + if (io_req->fcport == NULL) { + QEDF_INFO(NULL, QEDF_LOG_IO, "fcport is NULL.\n"); + return; + } + + qedf = fcport->qedf; + switch (io_req->cmd_type) { case QEDF_ABTS: + if (qedf == NULL) { + QEDF_INFO(NULL, QEDF_LOG_IO, "qedf is NULL for xid=0x%x.\n", + io_req->xid); + return; + } + QEDF_ERR((&qedf->dbg_ctx), "ABTS timeout, xid=0x%x.\n", io_req->xid); /* Cleanup timed out ABTS */ @@ -1565,6 +1584,16 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) goto out; } + if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || + test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) || + test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) { + QEDF_ERR(&(qedf->dbg_ctx), "io_req xid=0x%x already in " + "cleanup or abort processing or already " + "completed.\n", io_req->xid); + rc = 1; + goto out; + } + kref_get(&io_req->refcount); xid = io_req->xid; diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 5f9ae6e9897e..7e88c7a41a01 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -643,16 +643,6 @@ static int qedf_eh_abort(struct scsi_cmnd *sc_cmd) goto out; } - if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || - test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) || - test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) { - QEDF_ERR(&(qedf->dbg_ctx), "io_req xid=0x%x already in " - "cleanup or abort processing or already " - "completed.\n", io_req->xid); - rc = SUCCESS; - goto out; - } - QEDF_ERR(&(qedf->dbg_ctx), "Aborting io_req sc_cmd=%p xid=0x%x " "fp_idx=%d.\n", sc_cmd, io_req->xid, io_req->fp_idx); @@ -748,6 +738,22 @@ static int qedf_eh_host_reset(struct scsi_cmnd *sc_cmd) { struct fc_lport *lport; struct qedf_ctx *qedf; + struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device)); + struct fc_rport_libfc_priv *rp = rport->dd_data; + struct qedf_rport *fcport = (struct qedf_rport *)&rp[1]; + int rval; + + rval = fc_remote_port_chkready(rport); + + if (rval) { + QEDF_ERR(NULL, "device_reset rport not ready\n"); + return FAILED; + } + + if (fcport == NULL) { + QEDF_ERR(NULL, "device_reset: rport is NULL\n"); + return FAILED; + } lport = shost_priv(sc_cmd->device->host); qedf = lport_priv(lport); -- cgit v1.2.3 From 5d1c8b5ba074a45abd545d3e1087b3864931f341 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:09:04 -0700 Subject: scsi: qedf: Update copyright for 2018 Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/drv_fcoe_fw_funcs.c | 2 +- drivers/scsi/qedf/drv_fcoe_fw_funcs.h | 2 +- drivers/scsi/qedf/drv_scsi_fw_funcs.c | 2 +- drivers/scsi/qedf/drv_scsi_fw_funcs.h | 2 +- drivers/scsi/qedf/qedf.h | 2 +- drivers/scsi/qedf/qedf_attr.c | 2 +- drivers/scsi/qedf/qedf_dbg.c | 2 +- drivers/scsi/qedf/qedf_dbg.h | 2 +- drivers/scsi/qedf/qedf_debugfs.c | 2 +- drivers/scsi/qedf/qedf_els.c | 2 +- drivers/scsi/qedf/qedf_fip.c | 2 +- drivers/scsi/qedf/qedf_hsi.h | 2 +- drivers/scsi/qedf/qedf_io.c | 2 +- drivers/scsi/qedf/qedf_main.c | 2 +- drivers/scsi/qedf/qedf_version.h | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.c b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c index a980ef756a67..5bd10b534c99 100644 --- a/drivers/scsi/qedf/drv_fcoe_fw_funcs.c +++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.c @@ -1,5 +1,5 @@ /* QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/drv_fcoe_fw_funcs.h b/drivers/scsi/qedf/drv_fcoe_fw_funcs.h index b5c236efd465..42fde55ac735 100644 --- a/drivers/scsi/qedf/drv_fcoe_fw_funcs.h +++ b/drivers/scsi/qedf/drv_fcoe_fw_funcs.h @@ -1,5 +1,5 @@ /* QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/drv_scsi_fw_funcs.c b/drivers/scsi/qedf/drv_scsi_fw_funcs.c index 5d5095e3d96d..29a55257224f 100644 --- a/drivers/scsi/qedf/drv_scsi_fw_funcs.c +++ b/drivers/scsi/qedf/drv_scsi_fw_funcs.c @@ -1,5 +1,5 @@ /* QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/drv_scsi_fw_funcs.h b/drivers/scsi/qedf/drv_scsi_fw_funcs.h index 8fbe6e4d0b4f..bf102204fe56 100644 --- a/drivers/scsi/qedf/drv_scsi_fw_funcs.h +++ b/drivers/scsi/qedf/drv_scsi_fw_funcs.h @@ -1,5 +1,5 @@ /* QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 44770f849632..2372a40326f8 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_attr.c b/drivers/scsi/qedf/qedf_attr.c index fa6727685627..0487b7237104 100644 --- a/drivers/scsi/qedf/qedf_attr.c +++ b/drivers/scsi/qedf/qedf_attr.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_dbg.c b/drivers/scsi/qedf/qedf_dbg.c index e02c7a497de9..f2397ee9ba69 100644 --- a/drivers/scsi/qedf/qedf_dbg.c +++ b/drivers/scsi/qedf/qedf_dbg.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_dbg.h b/drivers/scsi/qedf/qedf_dbg.h index 77c27e888969..dd0109653aa3 100644 --- a/drivers/scsi/qedf/qedf_dbg.h +++ b/drivers/scsi/qedf/qedf_dbg.h @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_debugfs.c b/drivers/scsi/qedf/qedf_debugfs.c index c539a7ae3a7e..99783402df5c 100644 --- a/drivers/scsi/qedf/qedf_debugfs.c +++ b/drivers/scsi/qedf/qedf_debugfs.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 QLogic Corporation + * Copyright (c) 2016-2018 QLogic Corporation * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_els.c b/drivers/scsi/qedf/qedf_els.c index 3053270aa473..04f0c4d2e256 100644 --- a/drivers/scsi/qedf/qedf_els.c +++ b/drivers/scsi/qedf/qedf_els.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_fip.c b/drivers/scsi/qedf/qedf_fip.c index c6b984efa5e8..53599e9fd2ef 100644 --- a/drivers/scsi/qedf/qedf_fip.c +++ b/drivers/scsi/qedf/qedf_fip.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_hsi.h b/drivers/scsi/qedf/qedf_hsi.h index 503c1ae3ccd0..f6f634e48d69 100644 --- a/drivers/scsi/qedf/qedf_hsi.h +++ b/drivers/scsi/qedf/qedf_hsi.h @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 07925f8e65bf..690df97432d2 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 7e88c7a41a01..9597d75d73ce 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of diff --git a/drivers/scsi/qedf/qedf_version.h b/drivers/scsi/qedf/qedf_version.h index c2478056356a..4a19e6f6d843 100644 --- a/drivers/scsi/qedf/qedf_version.h +++ b/drivers/scsi/qedf/qedf_version.h @@ -1,6 +1,6 @@ /* * QLogic FCoE Offload Driver - * Copyright (c) 2016-2017 Cavium Inc. + * Copyright (c) 2016-2018 Cavium Inc. * * This software is available under the terms of the GNU General Public License * (GPL) Version 2, available from the file COPYING in the main directory of -- cgit v1.2.3 From ab7ad49d01a5f03c5b1705334dc68f770f0a74f8 Mon Sep 17 00:00:00 2001 From: Chad Dupuis Date: Wed, 25 Apr 2018 06:09:05 -0700 Subject: scsi: qedf: Update version number to 8.33.16.20 Signed-off-by: Chad Dupuis Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/qedf/qedf_version.h b/drivers/scsi/qedf/qedf_version.h index 4a19e6f6d843..9455faacd5de 100644 --- a/drivers/scsi/qedf/qedf_version.h +++ b/drivers/scsi/qedf/qedf_version.h @@ -7,9 +7,9 @@ * this source tree. */ -#define QEDF_VERSION "8.33.0.20" +#define QEDF_VERSION "8.33.16.20" #define QEDF_DRIVER_MAJOR_VER 8 #define QEDF_DRIVER_MINOR_VER 33 -#define QEDF_DRIVER_REV_VER 0 +#define QEDF_DRIVER_REV_VER 16 #define QEDF_DRIVER_ENG_VER 20 -- cgit v1.2.3 From cd2400715c1250db78f5b54795edd1960815820a Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:50 -0700 Subject: scsi: lpfc: Change IO submit return to EBUSY if remote port is recovering I/O submission paths in the lpfc nvme path are rejecting the io with an error code that reflects back to the callee as a hard io failure. Many of these conditions are transient and would likely resolve if retried. Correct by returning -EBUSY, which the FC transport triggers off of to return busy status codes to the blk-mq layer. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 9e0345697e1b..2d80c7207869 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1452,8 +1452,8 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, if (unlikely(!hw_queue_handle)) { lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS, - "6129 Fail Abort, NULL hw_queue_handle\n"); - ret = -EINVAL; + "6117 Fail Abort, NULL hw_queue_handle\n"); + ret = -EBUSY; goto out_fail; } @@ -1499,7 +1499,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, "6066 Missing node for DID %x\n", pnvme_rport->port_id); atomic_inc(&lport->xmt_fcp_bad_ndlp); - ret = -ENODEV; + ret = -EBUSY; goto out_fail; } } @@ -1509,11 +1509,12 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, "6036 rport %p, DID x%06x not ready for " - "IO. State x%x, Type x%x\n", + "IO. State x%x, Type x%x Flg x%x\n", rport, pnvme_rport->port_id, - ndlp->nlp_state, ndlp->nlp_type); + ndlp->nlp_state, ndlp->nlp_type, + ndlp->upcall_flags); atomic_inc(&lport->xmt_fcp_bad_ndlp); - ret = -ENODEV; + ret = -EBUSY; goto out_fail; } -- cgit v1.2.3 From 48f8fdb4b4269c0435238604d2ba3ca4f67f5620 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:51 -0700 Subject: scsi: lpfc: enhance LE data structure copies to hardware The driver builds the control structures in host memory using definitions that are based on 32-bit words. After building the structure it is then written to the adapter. This patch slightly optimizes LE hosts by copying the structures via 64-bit copies. This is doable as the adapter interface is LE thus there is no byteswapping as the copy is performed. The same optimization would be nice on BE systems, but when byteswapping occurs, it swaps 32-bit words as well, thus trashing the control structure. Given amount of code that is dependent upon the 32-bit word definition, it was decided to not change things for the minor optimization. Thus PPC 64-bit systems sticks with doing 32-bit copies. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_sli.c | 56 +++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 38993efbe37e..30480e47913f 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -96,6 +96,34 @@ lpfc_get_iocb_from_iocbq(struct lpfc_iocbq *iocbq) return &iocbq->iocb; } +#if defined(CONFIG_64BIT) && defined(__LITTLE_ENDIAN) +/** + * lpfc_sli4_pcimem_bcopy - SLI4 memory copy function + * @srcp: Source memory pointer. + * @destp: Destination memory pointer. + * @cnt: Number of words required to be copied. + * Must be a multiple of sizeof(uint64_t) + * + * This function is used for copying data between driver memory + * and the SLI WQ. This function also changes the endianness + * of each word if native endianness is different from SLI + * endianness. This function can be called with or without + * lock. + **/ +void +lpfc_sli4_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt) +{ + uint64_t *src = srcp; + uint64_t *dest = destp; + int i; + + for (i = 0; i < (int)cnt; i += sizeof(uint64_t)) + *dest++ = *src++; +} +#else +#define lpfc_sli4_pcimem_bcopy(a, b, c) lpfc_sli_pcimem_bcopy(a, b, c) +#endif + /** * lpfc_sli4_wq_put - Put a Work Queue Entry on an Work Queue * @q: The Work Queue to operate on. @@ -137,7 +165,7 @@ lpfc_sli4_wq_put(struct lpfc_queue *q, union lpfc_wqe128 *wqe) bf_set(wqe_wqec, &wqe->generic.wqe_com, 0); if (q->phba->sli3_options & LPFC_SLI4_PHWQ_ENABLED) bf_set(wqe_wqid, &wqe->generic.wqe_com, q->queue_id); - lpfc_sli_pcimem_bcopy(wqe, temp_wqe, q->entry_size); + lpfc_sli4_pcimem_bcopy(wqe, temp_wqe, q->entry_size); if (q->dpp_enable && q->phba->cfg_enable_dpp) { /* write to DPP aperture taking advatage of Combined Writes */ tmp = (uint8_t *)temp_wqe; @@ -240,7 +268,7 @@ lpfc_sli4_mq_put(struct lpfc_queue *q, struct lpfc_mqe *mqe) /* If the host has not yet processed the next entry then we are done */ if (((q->host_index + 1) % q->entry_count) == q->hba_index) return -ENOMEM; - lpfc_sli_pcimem_bcopy(mqe, temp_mqe, q->entry_size); + lpfc_sli4_pcimem_bcopy(mqe, temp_mqe, q->entry_size); /* Save off the mailbox pointer for completion */ q->phba->mbox = (MAILBOX_t *)temp_mqe; @@ -663,8 +691,8 @@ lpfc_sli4_rq_put(struct lpfc_queue *hq, struct lpfc_queue *dq, /* If the host has not yet processed the next entry then we are done */ if (((hq_put_index + 1) % hq->entry_count) == hq->hba_index) return -EBUSY; - lpfc_sli_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size); - lpfc_sli_pcimem_bcopy(drqe, temp_drqe, dq->entry_size); + lpfc_sli4_pcimem_bcopy(hrqe, temp_hrqe, hq->entry_size); + lpfc_sli4_pcimem_bcopy(drqe, temp_drqe, dq->entry_size); /* Update the host index to point to the next slot */ hq->host_index = ((hq_put_index + 1) % hq->entry_count); @@ -8185,8 +8213,8 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) */ mbx_cmnd = bf_get(lpfc_mqe_command, mb); memset(phba->sli4_hba.bmbx.avirt, 0, sizeof(struct lpfc_bmbx_create)); - lpfc_sli_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt, - sizeof(struct lpfc_mqe)); + lpfc_sli4_pcimem_bcopy(mb, phba->sli4_hba.bmbx.avirt, + sizeof(struct lpfc_mqe)); /* Post the high mailbox dma address to the port and wait for ready. */ dma_address = &phba->sli4_hba.bmbx.dma_address; @@ -8210,11 +8238,11 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) * If so, update the mailbox status so that the upper layers * can complete the request normally. */ - lpfc_sli_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb, - sizeof(struct lpfc_mqe)); + lpfc_sli4_pcimem_bcopy(phba->sli4_hba.bmbx.avirt, mb, + sizeof(struct lpfc_mqe)); mbox_rgn = (struct lpfc_bmbx_create *) phba->sli4_hba.bmbx.avirt; - lpfc_sli_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe, - sizeof(struct lpfc_mcqe)); + lpfc_sli4_pcimem_bcopy(&mbox_rgn->mcqe, &mboxq->mcqe, + sizeof(struct lpfc_mcqe)); mcqe_status = bf_get(lpfc_mcqe_status, &mbox_rgn->mcqe); /* * When the CQE status indicates a failure and the mailbox status @@ -12830,7 +12858,7 @@ lpfc_sli4_sp_handle_mbox_event(struct lpfc_hba *phba, struct lpfc_mcqe *mcqe) /* Move mbox data to caller's mailbox region, do endian swapping */ if (pmb->mbox_cmpl && mbox) - lpfc_sli_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe)); + lpfc_sli4_pcimem_bcopy(mbox, mqe, sizeof(struct lpfc_mqe)); /* * For mcqe errors, conditionally move a modified error code to @@ -12913,7 +12941,7 @@ lpfc_sli4_sp_handle_mcqe(struct lpfc_hba *phba, struct lpfc_cqe *cqe) bool workposted; /* Copy the mailbox MCQE and convert endian order as needed */ - lpfc_sli_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe)); + lpfc_sli4_pcimem_bcopy(cqe, &mcqe, sizeof(struct lpfc_mcqe)); /* Invoke the proper event handling routine */ if (!bf_get(lpfc_trailer_async, &mcqe)) @@ -13173,7 +13201,7 @@ lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, bool workposted = false; /* Copy the work queue CQE and convert endian order if needed */ - lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe)); + lpfc_sli4_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe)); /* Check and process for different type of WCQE and dispatch */ switch (bf_get(lpfc_cqe_code, &cqevt)) { @@ -13581,7 +13609,7 @@ lpfc_sli4_fp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq, bool workposted = false; /* Copy the work queue CQE and convert endian order if needed */ - lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe)); + lpfc_sli4_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe)); /* Check and process for different type of WCQE and dispatch */ switch (bf_get(lpfc_wcqe_c_code, &wcqe)) { -- cgit v1.2.3 From a72d56b2a688843592285745206fd10be1a3984a Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:52 -0700 Subject: scsi: lpfc: Correct fw download error message In situations when the firmware image in inappropriate for the chip type, initial validation checks were light, allowing the checks to pass, thus allowing the firmware to be downloaded. Eventually, after the download, the chip rejects the firmware but it is logged as a generic firmware download error. Revise the initial checks to validate the image vs asic type so that the correct message is displayed and the download process is avoided. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hw4.h | 3 +++ drivers/scsi/lpfc/lpfc_init.c | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 9df1c8da6f52..571099fbde28 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -4628,6 +4628,9 @@ union lpfc_wqe128 { struct send_frame_wqe send_frame; }; +#define MAGIC_NUMER_G6 0xFEAA0003 +#define MAGIC_NUMER_G7 0xFEAA0005 + struct lpfc_grp_hdr { uint32_t size; uint32_t magic_number; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 060f0e2f6ff5..70583e1b227d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -11349,7 +11349,11 @@ lpfc_log_write_firmware_error(struct lpfc_hba *phba, uint32_t offset, uint32_t magic_number, uint32_t ftype, uint32_t fid, uint32_t fsize, const struct firmware *fw) { - if (offset == ADD_STATUS_FW_NOT_SUPPORTED) + if ((offset == ADD_STATUS_FW_NOT_SUPPORTED) || + (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G6_FC && + magic_number != MAGIC_NUMER_G6) || + (phba->pcidev->device == PCI_DEVICE_ID_LANCER_G7_FC && + magic_number != MAGIC_NUMER_G7)) lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "3030 This firmware version is not supported on " "this HBA model. Device:%x Magic:%x Type:%x " -- cgit v1.2.3 From 23288b78a140a6a527187730754021a6a0c0cea6 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:53 -0700 Subject: scsi: lpfc: Handle new link fault code returned by adapter firmware. The driver encounters a link event ACQE with a fault code it doesn't recognize, it logs an "Invalid" fault type and futher treats the unknown value as a mailbox command failure. First off, there is no "invalid" value, only values that are unknown. Secondly, the fault code doesn't indicate status - the rest of the ACQE contains that status so there is no reason to "fail the commands". Change the "Invalid" to "Unknown". There is no "invalid" code value. Separate fault code parsing and message genaration from any mbx handling status. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hw4.h | 1 + drivers/scsi/lpfc/lpfc_init.c | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 571099fbde28..eed8dea42f74 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -3924,6 +3924,7 @@ struct lpfc_acqe_link { #define LPFC_ASYNC_LINK_FAULT_NONE 0x0 #define LPFC_ASYNC_LINK_FAULT_LOCAL 0x1 #define LPFC_ASYNC_LINK_FAULT_REMOTE 0x2 +#define LPFC_ASYNC_LINK_FAULT_LR_LRR 0x3 #define lpfc_acqe_logical_link_speed_SHIFT 16 #define lpfc_acqe_logical_link_speed_MASK 0x0000FFFF #define lpfc_acqe_logical_link_speed_WORD word1 diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 70583e1b227d..0ba49e605a61 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -4279,32 +4279,24 @@ lpfc_sli4_fcf_redisc_wait_tmo(struct timer_list *t) * @phba: pointer to lpfc hba data structure. * @acqe_link: pointer to the async link completion queue entry. * - * This routine is to parse the SLI4 link-attention link fault code and - * translate it into the base driver's read link attention mailbox command - * status. - * - * Return: Link-attention status in terms of base driver's coding. + * This routine is to parse the SLI4 link-attention link fault code. **/ -static uint16_t +static void lpfc_sli4_parse_latt_fault(struct lpfc_hba *phba, struct lpfc_acqe_link *acqe_link) { - uint16_t latt_fault; - switch (bf_get(lpfc_acqe_link_fault, acqe_link)) { case LPFC_ASYNC_LINK_FAULT_NONE: case LPFC_ASYNC_LINK_FAULT_LOCAL: case LPFC_ASYNC_LINK_FAULT_REMOTE: - latt_fault = 0; + case LPFC_ASYNC_LINK_FAULT_LR_LRR: break; default: lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "0398 Invalid link fault code: x%x\n", + "0398 Unknown link fault code: x%x\n", bf_get(lpfc_acqe_link_fault, acqe_link)); - latt_fault = MBXERR_ERROR; break; } - return latt_fault; } /** @@ -4579,9 +4571,12 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, * the READ_TOPOLOGY completion routine to continue without actually * sending the READ_TOPOLOGY mailbox command to the port. */ - /* Parse and translate status field */ + /* Initialize completion status */ mb = &pmb->u.mb; - mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, acqe_link); + mb->mbxStatus = MBX_SUCCESS; + + /* Parse port fault information field */ + lpfc_sli4_parse_latt_fault(phba, acqe_link); /* Parse and translate link attention fields */ la = (struct lpfc_mbx_read_top *) &pmb->u.mb.un.varReadTop; @@ -4709,10 +4704,12 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc) break; } - /* Parse and translate status field */ + /* Initialize completion status */ mb = &pmb->u.mb; - mb->mbxStatus = lpfc_sli4_parse_latt_fault(phba, - (void *)acqe_fc); + mb->mbxStatus = MBX_SUCCESS; + + /* Parse port fault information field */ + lpfc_sli4_parse_latt_fault(phba, (void *)acqe_fc); /* Parse and translate link attention fields */ la = (struct lpfc_mbx_read_top *)&pmb->u.mb.un.varReadTop; -- cgit v1.2.3 From d38f33b304229b0445c3292ebf2b7bf24dda6917 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:54 -0700 Subject: scsi: lpfc: Driver NVME load fails when CPU cnt > WQ resource cnt If the cpu count is larger than the number of WQ resources available, adapter attachment eventually failes due to a WQ_CREATE failure. Calculate the number of WQs desired (which initializes to cpu count) after accounting for the number of queues the adapter supports and the number allocated to SCSI and the control/ELS path, and scale down if necessary. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 0ba49e605a61..119432035e9e 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -7790,6 +7790,40 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) phba->sli4_hba.max_cfg_param.max_wq, phba->sli4_hba.max_cfg_param.max_rq); + /* + * Calculate NVME queue resources based on how + * many WQ/CQs are available. + */ + if (phba->cfg_enable_fc4_type & LPFC_ENABLE_NVME) { + length = phba->sli4_hba.max_cfg_param.max_wq; + if (phba->sli4_hba.max_cfg_param.max_cq < + phba->sli4_hba.max_cfg_param.max_wq) + length = phba->sli4_hba.max_cfg_param.max_cq; + + /* + * Whats left after this can go toward NVME. + * The minus 6 accounts for ELS, NVME LS, MBOX + * fof plus a couple extra. When configured for + * NVMET, FCP io channel WQs are not created. + */ + length -= 6; + if (!phba->nvmet_support) + length -= phba->cfg_fcp_io_channel; + + if (phba->cfg_nvme_io_channel > length) { + lpfc_printf_log( + phba, KERN_ERR, LOG_SLI, + "2005 Reducing NVME IO channel to %d: " + "WQ %d CQ %d NVMEIO %d FCPIO %d\n", + length, + phba->sli4_hba.max_cfg_param.max_wq, + phba->sli4_hba.max_cfg_param.max_cq, + phba->cfg_nvme_io_channel, + phba->cfg_fcp_io_channel); + + phba->cfg_nvme_io_channel = length; + } + } } if (rc) -- cgit v1.2.3 From 44c2757b7673d79154b322814d5774e1d4f8009a Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:56 -0700 Subject: scsi: lpfc: Fix up log messages and stats counters in IO submit code path Fix up log messages and add an fcp error stat counter in the IO submit code path to make diagnosing problems easier Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 3 ++- drivers/scsi/lpfc/lpfc_debugfs.c | 3 ++- drivers/scsi/lpfc/lpfc_nvme.c | 53 ++++++++++++++++++++++++---------------- drivers/scsi/lpfc/lpfc_nvme.h | 1 + 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index fd3b25317887..e79724564b2e 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -405,11 +405,12 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, len += snprintf(buf+len, PAGE_SIZE-len, " abort %08x noxri %08x nondlp %08x qdepth %08x " - "wqerr %08x\n", + "wqerr %08x err %08x\n", atomic_read(&lport->xmt_fcp_abort), atomic_read(&lport->xmt_fcp_noxri), atomic_read(&lport->xmt_fcp_bad_ndlp), atomic_read(&lport->xmt_fcp_qdepth), + atomic_read(&lport->xmt_fcp_err), atomic_read(&lport->xmt_fcp_wqerr)); len += snprintf(buf + len, PAGE_SIZE - len, diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index afe7883c988a..ccc342247782 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -959,11 +959,12 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) len += snprintf(buf + len, size - len, "FCP Xmt Err: noxri %06x nondlp %06x " - "qdepth %06x wqerr %06x Abrt %06x\n", + "qdepth %06x wqerr %06x err %06x Abrt %06x\n", atomic_read(&lport->xmt_fcp_noxri), atomic_read(&lport->xmt_fcp_bad_ndlp), atomic_read(&lport->xmt_fcp_qdepth), atomic_read(&lport->xmt_fcp_wqerr), + atomic_read(&lport->xmt_fcp_err), atomic_read(&lport->xmt_fcp_abort)); len += snprintf(buf + len, size - len, diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 2d80c7207869..9fba34d5d5e1 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -1451,8 +1451,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, vport = lport->vport; if (unlikely(!hw_queue_handle)) { - lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_ABTS, - "6117 Fail Abort, NULL hw_queue_handle\n"); + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6117 Fail IO, NULL hw_queue_handle\n"); + atomic_inc(&lport->xmt_fcp_err); ret = -EBUSY; goto out_fail; } @@ -1465,12 +1466,18 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, } if (vport->load_flag & FC_UNLOADING) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6124 Fail IO, Driver unload\n"); + atomic_inc(&lport->xmt_fcp_err); ret = -ENODEV; goto out_fail; } freqpriv = pnvme_fcreq->private; if (unlikely(!freqpriv)) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6158 Fail IO, NULL request data\n"); + atomic_inc(&lport->xmt_fcp_err); ret = -EINVAL; goto out_fail; } @@ -1488,29 +1495,22 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, */ ndlp = rport->ndlp; if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6053 rport %p, ndlp %p, DID x%06x " - "ndlp not ready.\n", + lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_NVME_IOERR, + "6053 Fail IO, ndlp not ready: rport %p " + "ndlp %p, DID x%06x\n", rport, ndlp, pnvme_rport->port_id); - - ndlp = lpfc_findnode_did(vport, pnvme_rport->port_id); - if (!ndlp) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, - "6066 Missing node for DID %x\n", - pnvme_rport->port_id); - atomic_inc(&lport->xmt_fcp_bad_ndlp); - ret = -EBUSY; - goto out_fail; - } + atomic_inc(&lport->xmt_fcp_err); + ret = -EBUSY; + goto out_fail; } /* The remote node has to be a mapped target or it's an error. */ if ((ndlp->nlp_type & NLP_NVME_TARGET) && (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { - lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6036 rport %p, DID x%06x not ready for " + lpfc_printf_vlog(vport, KERN_INFO, LOG_NODE | LOG_NVME_IOERR, + "6036 Fail IO, DID x%06x not ready for " "IO. State x%x, Type x%x Flg x%x\n", - rport, pnvme_rport->port_id, + pnvme_rport->port_id, ndlp->nlp_state, ndlp->nlp_type, ndlp->upcall_flags); atomic_inc(&lport->xmt_fcp_bad_ndlp); @@ -1535,6 +1535,10 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, */ if ((atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) && !expedite) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6174 Fail IO, ndlp qdepth exceeded: " + "idx %d DID %x\n", + lpfc_queue_info->index, ndlp->nlp_DID); atomic_inc(&lport->xmt_fcp_qdepth); ret = -EBUSY; goto out_fail; @@ -1544,8 +1548,9 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, if (lpfc_ncmd == NULL) { atomic_inc(&lport->xmt_fcp_noxri); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, - "6065 driver's buffer pool is empty, " - "IO failed\n"); + "6065 Fail IO, driver buffer pool is empty: " + "idx %d DID %x\n", + lpfc_queue_info->index, ndlp->nlp_DID); ret = -EBUSY; goto out_fail; } @@ -1585,6 +1590,11 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, lpfc_nvme_prep_io_cmd(vport, lpfc_ncmd, ndlp, cstat); ret = lpfc_nvme_prep_io_dma(vport, lpfc_ncmd); if (ret) { + lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, + "6175 Fail IO, Prep DMA: " + "idx %d DID %x\n", + lpfc_queue_info->index, ndlp->nlp_DID); + atomic_inc(&lport->xmt_fcp_err); ret = -ENOMEM; goto out_free_nvme_buf; } @@ -1600,7 +1610,7 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, atomic_inc(&lport->xmt_fcp_wqerr); atomic_dec(&ndlp->cmd_pending); lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, - "6113 FCP could not issue WQE err %x " + "6113 Fail IO, Could not issue WQE err %x " "sid: x%x did: x%x oxid: x%x\n", ret, vport->fc_myDID, ndlp->nlp_DID, lpfc_ncmd->cur_iocbq.sli4_xritag); @@ -2477,6 +2487,7 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) atomic_set(&lport->xmt_fcp_noxri, 0); atomic_set(&lport->xmt_fcp_bad_ndlp, 0); atomic_set(&lport->xmt_fcp_qdepth, 0); + atomic_set(&lport->xmt_fcp_err, 0); atomic_set(&lport->xmt_fcp_wqerr, 0); atomic_set(&lport->xmt_fcp_abort, 0); atomic_set(&lport->xmt_ls_abort, 0); diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 53236974f2dd..129189324c31 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -59,6 +59,7 @@ struct lpfc_nvme_lport { atomic_t xmt_fcp_bad_ndlp; atomic_t xmt_fcp_qdepth; atomic_t xmt_fcp_wqerr; + atomic_t xmt_fcp_err; atomic_t xmt_fcp_abort; atomic_t xmt_ls_abort; atomic_t xmt_ls_err; -- cgit v1.2.3 From 11f0e34ff406aea38ab319c904fafddc8002829a Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:57 -0700 Subject: scsi: lpfc: Enhance log messages when reporting CQE errors Enhance log messages for CQEs as they were not reporting certain fields. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_sli.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 30480e47913f..48ba9eb8a96b 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -12972,6 +12972,17 @@ lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, int txcmplq_cnt = 0; int fcp_txcmplq_cnt = 0; + /* Check for response status */ + if (unlikely(bf_get(lpfc_wcqe_c_status, wcqe))) { + /* Log the error status */ + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "0357 ELS CQE error: status=x%x: " + "CQE: %08x %08x %08x %08x\n", + bf_get(lpfc_wcqe_c_status, wcqe), + wcqe->word0, wcqe->total_data_placed, + wcqe->parameter, wcqe->word3); + } + /* Get an irspiocbq for later ELS response processing use */ irspiocbq = lpfc_sli_get_iocbq(phba); if (!irspiocbq) { @@ -13392,14 +13403,12 @@ lpfc_sli4_fp_handle_fcp_wcqe(struct lpfc_hba *phba, struct lpfc_queue *cq, phba->lpfc_rampdown_queue_depth(phba); /* Log the error status */ - lpfc_printf_log(phba, KERN_WARNING, LOG_SLI, - "0373 FCP complete error: status=x%x, " - "hw_status=x%x, total_data_specified=%d, " - "parameter=x%x, word3=x%x\n", + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "0373 FCP CQE error: status=x%x: " + "CQE: %08x %08x %08x %08x\n", bf_get(lpfc_wcqe_c_status, wcqe), - bf_get(lpfc_wcqe_c_hw_status, wcqe), - wcqe->total_data_placed, wcqe->parameter, - wcqe->word3); + wcqe->word0, wcqe->total_data_placed, + wcqe->parameter, wcqe->word3); } /* Look up the FCP command IOCB and create pseudo response IOCB */ -- cgit v1.2.3 From e65d8c3411f7255964994d97d69ce6fc5e838341 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:58 -0700 Subject: scsi: lpfc: update driver version to 12.0.0.3 Update the driver version to 12.0.0.3 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 0cd474bb0bdd..666ffa179653 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "12.0.0.2" +#define LPFC_DRIVER_VERSION "12.0.0.3" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ -- cgit v1.2.3 From 3e21d1cb0f9011225ed8aba29339ba27fc757524 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 4 May 2018 20:37:59 -0700 Subject: scsi: lpfc: Comment cleanup regarding Broadcom copyright header Fix small formatting and wording nits in Broadcom copyright header Signed-off-by: Dick Kennedy Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 2 +- drivers/scsi/lpfc/lpfc_debugfs.c | 2 +- drivers/scsi/lpfc/lpfc_hw4.h | 2 +- drivers/scsi/lpfc/lpfc_init.c | 2 +- drivers/scsi/lpfc/lpfc_nvme.c | 2 +- drivers/scsi/lpfc/lpfc_nvme.h | 2 +- drivers/scsi/lpfc/lpfc_scsi.c | 2 +- drivers/scsi/lpfc/lpfc_scsi.h | 2 +- drivers/scsi/lpfc/lpfc_sli.c | 2 +- drivers/scsi/lpfc/lpfc_version.h | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index e79724564b2e..a66c4fbc7690 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index ccc342247782..9df0c051349f 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2007-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index eed8dea42f74..807901af9bbe 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2009-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 119432035e9e..3d89b08896d1 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 9fba34d5d5e1..f5f90d19b215 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_nvme.h b/drivers/scsi/lpfc/lpfc_nvme.h index 129189324c31..04bd463dd043 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.h +++ b/drivers/scsi/lpfc/lpfc_nvme.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 7932bf30c8d7..a94fb9f8bb44 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 8e38e0204c47..c38e4da71f5f 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 48ba9eb8a96b..6b709cd4140b 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 666ffa179653..9fca71d7c297 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -2,7 +2,7 @@ * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2017-2018 Broadcom. All Rights Reserved. The term * - * “Broadcom” refers to Broadcom Limited and/or its subsidiaries. * + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.broadcom.com * -- cgit v1.2.3 From 24cf43612d4d851e660ac85f36cf389650c5a3ba Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:24 +0800 Subject: scsi: hisi_sas: optimise the usage of DQ locking In the DQ tasklet processing it is not necessary to take the DQ lock, as there is no contention between adding slots to the CQ and removing slots from the matching DQ. In addition, since we run each DQ in a separate tasklet context, there would be no possible contention between DQ processing running for the same queue in parallel. It is still necessary to take hisi_hba lock when free'ing slots. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 3 --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 3 --- 2 files changed, 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index a5abde855cb2..384e4ef50b24 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3151,14 +3151,12 @@ static void cq_tasklet_v2_hw(unsigned long val) struct hisi_sas_complete_v2_hdr *complete_queue; u32 rd_point = cq->rd_point, wr_point, dev_id; int queue = cq->id; - struct hisi_sas_dq *dq = &hisi_hba->dq[queue]; if (unlikely(hisi_hba->reject_stp_links_msk)) phys_try_accept_stp_links_v2_hw(hisi_hba); complete_queue = hisi_hba->complete_hdr[queue]; - spin_lock(&dq->lock); wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR + (0x14 * queue)); @@ -3208,7 +3206,6 @@ static void cq_tasklet_v2_hw(unsigned long val) /* update rd_point */ cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); - spin_unlock(&dq->lock); } static irqreturn_t cq_interrupt_v2_hw(int irq_no, void *p) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 33735a7082b6..afc1242abdcf 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1721,11 +1721,9 @@ static void cq_tasklet_v3_hw(unsigned long val) struct hisi_sas_complete_v3_hdr *complete_queue; u32 rd_point = cq->rd_point, wr_point; int queue = cq->id; - struct hisi_sas_dq *dq = &hisi_hba->dq[queue]; complete_queue = hisi_hba->complete_hdr[queue]; - spin_lock(&dq->lock); wr_point = hisi_sas_read32(hisi_hba, COMPL_Q_0_WR_PTR + (0x14 * queue)); @@ -1752,7 +1750,6 @@ static void cq_tasklet_v3_hw(unsigned long val) /* update rd_point */ cq->rd_point = rd_point; hisi_sas_write32(hisi_hba, COMPL_Q_0_RD_PTR + (0x14 * queue), rd_point); - spin_unlock(&dq->lock); } static irqreturn_t cq_interrupt_v3_hw(int irq_no, void *p) -- cgit v1.2.3 From b81b6cce58b7912e0d35f0b5bf526cb798f8e7aa Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:25 +0800 Subject: scsi: hisi_sas: Add some checks to avoid free'ing a sas_task twice If the SCSI host enters EH, any pending IO will be processed by SCSI EH. However it is possible that SCSI EH will try to abort the IO and also at the same time the IO completes in the driver. In this situation there is a small chance of freeing the sas_task twice. Then if another IO re-uses freed sas_task before the second time of free'ing sas_task, it is possible to free incorrect sas_task. To avoid this situation, add some checks to increase reliability. The sas_task task state flag SAS_TASK_STATE_ABORTED is used to mutually protect the LLDD and libsas freeing the task. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 4 ++++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 22 +++++++--------------- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 21 +++++++-------------- 3 files changed, 18 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index d1a61b1e591b..52746e2e7f6f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1174,10 +1174,14 @@ static int hisi_sas_abort_task(struct sas_task *task) return TMF_RESP_FUNC_FAILED; } + 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); rc = TMF_RESP_FUNC_COMPLETE; goto out; } + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); sas_dev->dev_status = HISI_SAS_DEV_EH; if (task->lldd_task && task->task_proto & SAS_PROTOCOL_SSP) { diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 384e4ef50b24..8ca0044e09be 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2386,7 +2386,6 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) struct hisi_sas_complete_v2_hdr *complete_hdr = &complete_queue[slot->cmplt_queue_slot]; unsigned long flags; - int aborted; if (unlikely(!task || !task->lldd_task || !task->dev)) return -EINVAL; @@ -2396,7 +2395,6 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) sas_dev = device->lldd_dev; spin_lock_irqsave(&task->task_state_lock, flags); - aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED; task->task_state_flags &= ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR); spin_unlock_irqrestore(&task->task_state_lock, flags); @@ -2404,15 +2402,6 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) memset(ts, 0, sizeof(*ts)); ts->resp = SAS_TASK_COMPLETE; - if (unlikely(aborted)) { - dev_dbg(dev, "slot_complete: task(%p) aborted\n", task); - ts->stat = SAS_ABORTED_TASK; - spin_lock_irqsave(&hisi_hba->lock, flags); - hisi_sas_slot_task_free(hisi_hba, task, slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - return ts->stat; - } - if (unlikely(!sas_dev)) { dev_dbg(dev, "slot complete: port has no device\n"); ts->stat = SAS_PHY_DOWN; @@ -2523,13 +2512,16 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) } out: + hisi_sas_slot_task_free(hisi_hba, task, slot); + sts = ts->stat; spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + dev_info(dev, "slot complete: task(%p) aborted\n", task); + return SAS_ABORTED_TASK; + } task->task_state_flags |= SAS_TASK_STATE_DONE; spin_unlock_irqrestore(&task->task_state_lock, flags); - spin_lock_irqsave(&hisi_hba->lock, flags); - hisi_sas_slot_task_free(hisi_hba, task, slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - sts = ts->stat; if (task->task_done) task->task_done(task); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index afc1242abdcf..734611046d3e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1576,7 +1576,6 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) hisi_hba->complete_hdr[slot->cmplt_queue]; struct hisi_sas_complete_v3_hdr *complete_hdr = &complete_queue[slot->cmplt_queue_slot]; - int aborted; unsigned long flags; if (unlikely(!task || !task->lldd_task || !task->dev)) @@ -1587,21 +1586,12 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) sas_dev = device->lldd_dev; spin_lock_irqsave(&task->task_state_lock, flags); - aborted = task->task_state_flags & SAS_TASK_STATE_ABORTED; task->task_state_flags &= ~(SAS_TASK_STATE_PENDING | SAS_TASK_AT_INITIATOR); spin_unlock_irqrestore(&task->task_state_lock, flags); memset(ts, 0, sizeof(*ts)); ts->resp = SAS_TASK_COMPLETE; - if (unlikely(aborted)) { - dev_dbg(dev, "slot complete: task(%p) aborted\n", task); - ts->stat = SAS_ABORTED_TASK; - spin_lock_irqsave(&hisi_hba->lock, flags); - hisi_sas_slot_task_free(hisi_hba, task, slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - return ts->stat; - } if (unlikely(!sas_dev)) { dev_dbg(dev, "slot complete: port has not device\n"); @@ -1699,13 +1689,16 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) } out: + hisi_sas_slot_task_free(hisi_hba, task, slot); + sts = ts->stat; spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + spin_unlock_irqrestore(&task->task_state_lock, flags); + dev_info(dev, "slot complete: task(%p) aborted\n", task); + return SAS_ABORTED_TASK; + } task->task_state_flags |= SAS_TASK_STATE_DONE; spin_unlock_irqrestore(&task->task_state_lock, flags); - spin_lock_irqsave(&hisi_hba->lock, flags); - hisi_sas_slot_task_free(hisi_hba, task, slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - sts = ts->stat; if (task->task_done) task->task_done(task); -- cgit v1.2.3 From cd938e535e909b80948f26d284ba7475adee0c08 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:26 +0800 Subject: scsi: hisi_sas: check host frozen before calling "done" function When the host is frozen in SCSI EH state, at any point after the LLDD sets SAS_TASK_STATE_DONE for the sas_task task state, libsas may free the task; see sas_scsi_find_task(). This puts the LLDD in a difficult position, in that once it sets SAS_TASK_STATE_DONE for the task state it should not reference the sas_task again. But the LLDD needs will check the sas_task indirectly in calling task->task_done()->sas_scsi_task_done() or sas_ata_task_done() (to check if the host is frozen state actually). And the LLDD cannot set SAS_TASK_STATE_DONE for the task state after task->task_done() is called (as the sas_task is free'd at this point). This situation would seem to be a problem made by libsas. To work around, check in the LLDD whether the host is in frozen state to ensure it is ok to call task->task_done() function. If in the frozen state, we rely on SCSI EH and libsas to free the sas_task directly. We do not do this for the following IO types: - SMP - they are managed in libsas directly, outside SCSI EH - Any internally originated IO, for similar reason Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 3 +++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 14 ++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 14 ++++++++++++++ 4 files changed, 32 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index d413d05fda26..147cfafdad9f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -196,6 +196,7 @@ struct hisi_sas_slot { dma_addr_t cmd_hdr_dma; struct work_struct abort_slot; struct timer_list internal_abort_timer; + bool is_internal; }; struct hisi_sas_tmf_task { diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 52746e2e7f6f..8f8e6424ee1d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -382,6 +382,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot]; slot->task = task; slot->port = port; + if (is_tmf) + slot->is_internal = true; task->lldd_task = slot; INIT_WORK(&slot->abort_slot, hisi_sas_slot_abort); @@ -1486,6 +1488,7 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot]; slot->task = task; slot->port = port; + slot->is_internal = true; task->lldd_task = slot; slot->buf = dma_pool_alloc(hisi_hba->buffer_pool, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 8ca0044e09be..6dda6eb50918 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2380,18 +2380,21 @@ slot_complete_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) struct device *dev = hisi_hba->dev; struct task_status_struct *ts; struct domain_device *device; + struct sas_ha_struct *ha; enum exec_status sts; struct hisi_sas_complete_v2_hdr *complete_queue = hisi_hba->complete_hdr[slot->cmplt_queue]; struct hisi_sas_complete_v2_hdr *complete_hdr = &complete_queue[slot->cmplt_queue_slot]; unsigned long flags; + bool is_internal = slot->is_internal; if (unlikely(!task || !task->lldd_task || !task->dev)) return -EINVAL; ts = &task->task_status; device = task->dev; + ha = device->port->ha; sas_dev = device->lldd_dev; spin_lock_irqsave(&task->task_state_lock, flags); @@ -2523,6 +2526,17 @@ out: task->task_state_flags |= SAS_TASK_STATE_DONE; spin_unlock_irqrestore(&task->task_state_lock, flags); + if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) { + spin_lock_irqsave(&device->done_lock, flags); + if (test_bit(SAS_HA_FROZEN, &ha->state)) { + spin_unlock_irqrestore(&device->done_lock, flags); + dev_info(dev, "slot complete: task(%p) ignored\n ", + task); + return sts; + } + spin_unlock_irqrestore(&device->done_lock, flags); + } + if (task->task_done) task->task_done(task); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 734611046d3e..5c0d9683630b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1571,18 +1571,21 @@ slot_complete_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) struct device *dev = hisi_hba->dev; struct task_status_struct *ts; struct domain_device *device; + struct sas_ha_struct *ha; enum exec_status sts; struct hisi_sas_complete_v3_hdr *complete_queue = hisi_hba->complete_hdr[slot->cmplt_queue]; struct hisi_sas_complete_v3_hdr *complete_hdr = &complete_queue[slot->cmplt_queue_slot]; unsigned long flags; + bool is_internal = slot->is_internal; if (unlikely(!task || !task->lldd_task || !task->dev)) return -EINVAL; ts = &task->task_status; device = task->dev; + ha = device->port->ha; sas_dev = device->lldd_dev; spin_lock_irqsave(&task->task_state_lock, flags); @@ -1700,6 +1703,17 @@ out: task->task_state_flags |= SAS_TASK_STATE_DONE; spin_unlock_irqrestore(&task->task_state_lock, flags); + if (!is_internal && (task->task_proto != SAS_PROTOCOL_SMP)) { + spin_lock_irqsave(&device->done_lock, flags); + if (test_bit(SAS_HA_FROZEN, &ha->state)) { + spin_unlock_irqrestore(&device->done_lock, flags); + dev_info(dev, "slot complete: task(%p) ignored\n ", + task); + return sts; + } + spin_unlock_irqrestore(&device->done_lock, flags); + } + if (task->task_done) task->task_done(task); -- cgit v1.2.3 From a14da7a20d499c54ebe53e430283e3bc17d3e89f Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:27 +0800 Subject: scsi: hisi_sas: fix PI memory size There are 28 bytes of protection information record of SSP for v3 hw, 16 bytes for v2 hw, and probably 24 for v1 hw (forgotten now). So use a value big enough in hisi_sas_command_table_ssp.prot to cover all cases. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 147cfafdad9f..04a40c427a20 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -410,7 +410,7 @@ struct hisi_sas_command_table_ssp { union { struct { struct ssp_command_iu task; - u32 prot[6]; + u32 prot[7]; }; struct ssp_tmf_iu ssp_task; struct xfer_rdy_iu xfer_rdy; -- cgit v1.2.3 From c6ef895472696cac8e50f0ce69b301cc10233a67 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:28 +0800 Subject: scsi: hisi_sas: check sas_dev gone earlier in hisi_sas_abort_task() It is possible to dereference a NULL-pointer in hisi_sas_abort_task() in special scenario when the device has been removed. If an SMP task times-out, it will call hisi_sas_abort_task() to recover. And currently there is a check in hisi_sas_abort_task() to avoid the situation of processing the abort for the removed device. However we have an ordering problem, in that we may reference a task for the removed device before checking if the device has been removed. Fix this by only referencing the sas_dev after we know it is still present. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 8f8e6424ee1d..24416bb66027 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1166,15 +1166,16 @@ static int hisi_sas_abort_task(struct sas_task *task) struct hisi_sas_tmf_task tmf_task; struct domain_device *device = task->dev; struct hisi_sas_device *sas_dev = device->lldd_dev; - struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev); - struct device *dev = hisi_hba->dev; + struct hisi_hba *hisi_hba; + struct device *dev; int rc = TMF_RESP_FUNC_FAILED; unsigned long flags; - if (!sas_dev) { - dev_warn(dev, "Device has been removed\n"); + if (!sas_dev) return TMF_RESP_FUNC_FAILED; - } + + hisi_hba = dev_to_hisi_hba(task->dev); + dev = hisi_hba->dev; spin_lock_irqsave(&task->task_state_lock, flags); if (task->task_state_flags & SAS_TASK_STATE_DONE) { -- cgit v1.2.3 From 6f7c32d6057cad05cf057b14c910659a8d06c975 Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 2 May 2018 23:56:29 +0800 Subject: scsi: hisi_sas: stop controller timer for reset We should only have the timer enabled after PHY up after controller reset, so disable prior to reset. Signed-off-by: John Garry Signed-off-by: Xiaofei Tan Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 24416bb66027..1f27f847b8b4 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1130,6 +1130,9 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) old_state = hisi_hba->hw->get_phys_state(hisi_hba); scsi_block_requests(shost); + if (timer_pending(&hisi_hba->timer)) + del_timer_sync(&hisi_hba->timer); + set_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); rc = hisi_hba->hw->soft_reset(hisi_hba); if (rc) { -- cgit v1.2.3 From c2c1d9ded0a2c06df300e244220708f5c1f1db77 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:30 +0800 Subject: scsi: hisi_sas: update PHY linkrate after a controller reset After the controller is reset, we currently may not honour the PHY max linkrate set via sysfs, in that after a reset we always revert to max linkrate of 12Gbps, ignoring the value set via sysfs. This patch modifies to policy to set the programmed PHY linkrate, honouring the max linkrate programmed via sysfs. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 16 ++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 31 ++++++++++++++++++------------- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 31 ++++++++++++++++++------------- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 04a40c427a20..44105389f2df 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -463,4 +463,5 @@ extern void hisi_sas_kill_tasklets(struct hisi_hba *hisi_hba); extern bool hisi_sas_notify_phy_event(struct hisi_sas_phy *phy, enum hisi_sas_phy_event event); extern void hisi_sas_release_tasks(struct hisi_hba *hisi_hba); +extern u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max); #endif diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 1f27f847b8b4..ff5b8d7de1d1 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -135,6 +135,22 @@ int hisi_sas_get_ncq_tag(struct sas_task *task, u32 *tag) } EXPORT_SYMBOL_GPL(hisi_sas_get_ncq_tag); +/* + * This function assumes linkrate mask fits in 8 bits, which it + * does for all HW versions supported. + */ +u8 hisi_sas_get_prog_phy_linkrate_mask(enum sas_linkrate max) +{ + u16 rate = 0; + int i; + + max -= SAS_LINK_RATE_1_5_GBPS; + for (i = 0; i <= max; i++) + rate |= 1 << (i * 2); + return rate; +} +EXPORT_SYMBOL_GPL(hisi_sas_get_prog_phy_linkrate_mask); + static struct hisi_hba *dev_to_hisi_hba(struct domain_device *device) { return device->port->ha->lldd_ha; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 6dda6eb50918..9e687319b8bc 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1216,7 +1216,22 @@ static void init_reg_v2_hw(struct hisi_hba *hisi_hba) } for (i = 0; i < hisi_hba->n_phy; i++) { - hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855); + struct hisi_sas_phy *phy = &hisi_hba->phy[i]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + u32 prog_phy_link_rate = 0x800; + + if (!sas_phy->phy || (sas_phy->phy->maximum_linkrate < + SAS_LINK_RATE_1_5_GBPS)) { + prog_phy_link_rate = 0x855; + } else { + enum sas_linkrate max = sas_phy->phy->maximum_linkrate; + + prog_phy_link_rate = + hisi_sas_get_prog_phy_linkrate_mask(max) | + 0x800; + } + hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, + prog_phy_link_rate); hisi_sas_phy_write32(hisi_hba, i, SAS_PHY_CTRL, sas_phy_ctrl); hisi_sas_phy_write32(hisi_hba, i, SL_TOUT_CFG, 0x7d7d7d7d); hisi_sas_phy_write32(hisi_hba, i, SL_CONTROL, 0x0); @@ -1585,13 +1600,10 @@ static enum sas_linkrate phy_get_max_linkrate_v2_hw(void) static void phy_set_linkrate_v2_hw(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { - u32 prog_phy_link_rate = - hisi_sas_phy_read32(hisi_hba, phy_no, PROG_PHY_LINK_RATE); struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; - int i; enum sas_linkrate min, max; - u32 rate_mask = 0; + u32 prog_phy_link_rate = 0x800; if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { max = sas_phy->phy->maximum_linkrate; @@ -1604,14 +1616,7 @@ static void phy_set_linkrate_v2_hw(struct hisi_hba *hisi_hba, int phy_no, sas_phy->phy->maximum_linkrate = max; sas_phy->phy->minimum_linkrate = min; - - max -= SAS_LINK_RATE_1_5_GBPS; - - for (i = 0; i <= max; i++) - rate_mask |= 1 << (i * 2); - - prog_phy_link_rate &= ~0xff; - prog_phy_link_rate |= rate_mask; + prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max); disable_phy_v2_hw(hisi_hba, phy_no); msleep(100); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 5c0d9683630b..ffa3cea5ca8f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -429,7 +429,22 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, HYPER_STREAM_ID_EN_CFG, 1); for (i = 0; i < hisi_hba->n_phy; i++) { - hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, 0x855); + struct hisi_sas_phy *phy = &hisi_hba->phy[i]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + u32 prog_phy_link_rate = 0x800; + + if (!sas_phy->phy || (sas_phy->phy->maximum_linkrate < + SAS_LINK_RATE_1_5_GBPS)) { + prog_phy_link_rate = 0x855; + } else { + enum sas_linkrate max = sas_phy->phy->maximum_linkrate; + + prog_phy_link_rate = + hisi_sas_get_prog_phy_linkrate_mask(max) | + 0x800; + } + hisi_sas_phy_write32(hisi_hba, i, PROG_PHY_LINK_RATE, + prog_phy_link_rate); hisi_sas_phy_write32(hisi_hba, i, SAS_RX_TRAIN_TIMER, 0x13e80); hisi_sas_phy_write32(hisi_hba, i, CHL_INT0, 0xffffffff); hisi_sas_phy_write32(hisi_hba, i, CHL_INT1, 0xffffffff); @@ -1869,13 +1884,10 @@ static int hisi_sas_v3_init(struct hisi_hba *hisi_hba) static void phy_set_linkrate_v3_hw(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { - u32 prog_phy_link_rate = - hisi_sas_phy_read32(hisi_hba, phy_no, PROG_PHY_LINK_RATE); struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; - int i; enum sas_linkrate min, max; - u32 rate_mask = 0; + u32 prog_phy_link_rate = 0x800; if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { max = sas_phy->phy->maximum_linkrate; @@ -1888,14 +1900,7 @@ static void phy_set_linkrate_v3_hw(struct hisi_hba *hisi_hba, int phy_no, sas_phy->phy->maximum_linkrate = max; sas_phy->phy->minimum_linkrate = min; - - max -= SAS_LINK_RATE_1_5_GBPS; - - for (i = 0; i <= max; i++) - rate_mask |= 1 << (i * 2); - - prog_phy_link_rate &= ~0xff; - prog_phy_link_rate |= rate_mask; + prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max); disable_phy_v3_hw(hisi_hba, phy_no); msleep(100); -- cgit v1.2.3 From 9413532788df7470297dd0475995c5dc5b07f362 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 2 May 2018 23:56:31 +0800 Subject: scsi: hisi_sas: config ATA de-reset as an constrained command for v3 hw As a unconstrained command, a command can be sent to SATA disk even if SATA disk status is BUSY, ERR or DRQ. If an ATA reset assert is successful but ATA reset de-assert fails, then it will retry the reset de-assert. If reset de- assert retry is successful, we think it is okay to probe the device but actually it still has Err status. Apparently we need to retry the ATA reset assertion and de- assertion instead for this mentioned scenario. As such, we config ATA reset assert as a constrained command, if ATA reset de-assert fails, then ATA reset de-assert retry will also fail. Then we will retry the proper process of ATA reset assert and de-assert again. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index ffa3cea5ca8f..026faeee5ead 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -351,10 +351,11 @@ struct hisi_sas_err_record_v3 { #define DIR_TO_DEVICE 2 #define DIR_RESERVED 3 -#define CMD_IS_UNCONSTRAINT(cmd) \ - ((cmd == ATA_CMD_READ_LOG_EXT) || \ - (cmd == ATA_CMD_READ_LOG_DMA_EXT) || \ - (cmd == ATA_CMD_DEV_RESET)) +#define FIS_CMD_IS_UNCONSTRAINED(fis) \ + ((fis.command == ATA_CMD_READ_LOG_EXT) || \ + (fis.command == ATA_CMD_READ_LOG_DMA_EXT) || \ + ((fis.command == ATA_CMD_DEV_RESET) && \ + ((fis.control & ATA_SRST) != 0))) static u32 hisi_sas_read32(struct hisi_hba *hisi_hba, u32 off) { @@ -1075,7 +1076,7 @@ static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, << CMD_HDR_FRAME_TYPE_OFF; dw1 |= sas_dev->device_id << CMD_HDR_DEV_ID_OFF; - if (CMD_IS_UNCONSTRAINT(task->ata_task.fis.command)) + if (FIS_CMD_IS_UNCONSTRAINED(task->ata_task.fis)) dw1 |= 1 << CMD_HDR_UNCON_CMD_OFF; hdr->dw1 = cpu_to_le32(dw1); -- cgit v1.2.3 From bf081d5da4fa3a0d0ef640868de1b9f644f633e0 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Wed, 2 May 2018 23:56:32 +0800 Subject: scsi: hisi_sas: remove redundant handling to event95 for v3 Event95 is used for DFX purpose. The relevant bit for this interrupt in the ENT_INT_SRC_MSK3 register has been disabled, so remove the processing. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 026faeee5ead..d6e705ff9dad 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1331,14 +1331,9 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) { struct hisi_hba *hisi_hba = p; struct device *dev = hisi_hba->dev; - u32 ent_msk, ent_tmp, irq_msk; + u32 irq_msk; int phy_no = 0; - ent_msk = hisi_sas_read32(hisi_hba, ENT_INT_SRC_MSK3); - ent_tmp = ent_msk; - ent_msk |= ENT_INT_SRC_MSK3_ENT95_MSK_MSK; - hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, ent_msk); - irq_msk = hisi_sas_read32(hisi_hba, CHNL_INT_STATUS) & 0xeeeeeeee; @@ -1415,8 +1410,6 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) phy_no++; } - hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK3, ent_tmp); - return IRQ_HANDLED; } -- cgit v1.2.3 From 9b8addf3024eb57a215d0af2e1c95cd44b94ccab Mon Sep 17 00:00:00 2001 From: John Garry Date: Wed, 2 May 2018 23:56:33 +0800 Subject: scsi: hisi_sas: add readl poll timeout helper wrappers It is common to use readl poll timeout helpers in the driver, so create custom wrappers. Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index d6e705ff9dad..28bb71e3b093 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -394,6 +394,20 @@ static u32 hisi_sas_phy_read32(struct hisi_hba *hisi_hba, return readl(regs); } +#define hisi_sas_read32_poll_timeout(off, val, cond, delay_us, \ + timeout_us) \ +({ \ + void __iomem *regs = hisi_hba->regs + off; \ + readl_poll_timeout(regs, val, cond, delay_us, timeout_us); \ +}) + +#define hisi_sas_read32_poll_timeout_atomic(off, val, cond, delay_us, \ + timeout_us) \ +({ \ + void __iomem *regs = hisi_hba->regs + off; \ + readl_poll_timeout_atomic(regs, val, cond, delay_us, timeout_us);\ +}) + static void init_reg_v3_hw(struct hisi_hba *hisi_hba) { struct pci_dev *pdev = hisi_hba->pci_dev; @@ -684,8 +698,8 @@ static int reset_hw_v3_hw(struct hisi_hba *hisi_hba) udelay(50); /* Ensure axi bus idle */ - ret = readl_poll_timeout(hisi_hba->regs + AXI_CFG, val, !val, - 20000, 1000000); + ret = hisi_sas_read32_poll_timeout(AXI_CFG, val, !val, + 20000, 1000000); if (ret) { dev_err(dev, "axi bus is not idle, ret = %d!\n", ret); return -EIO; @@ -1977,8 +1991,9 @@ static int soft_reset_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, AXI_MASTER_CFG_BASE + AM_CTRL_GLOBAL, 0x1); /* wait until bus idle */ - rc = readl_poll_timeout(hisi_hba->regs + AXI_MASTER_CFG_BASE + - AM_CURR_TRANS_RETURN, status, status == 0x3, 10, 100); + rc = hisi_sas_read32_poll_timeout(AXI_MASTER_CFG_BASE + + AM_CURR_TRANS_RETURN, status, + status == 0x3, 10, 100); if (rc) { dev_err(dev, "axi bus is not idle, rc = %d\n", rc); return rc; @@ -2396,8 +2411,9 @@ static int hisi_sas_v3_suspend(struct pci_dev *pdev, pm_message_t state) AM_CTRL_GLOBAL, reg_val); /* wait until bus idle */ - rc = readl_poll_timeout(hisi_hba->regs + AXI_MASTER_CFG_BASE + - AM_CURR_TRANS_RETURN, status, status == 0x3, 10, 100); + rc = hisi_sas_read32_poll_timeout(AXI_MASTER_CFG_BASE + + AM_CURR_TRANS_RETURN, status, + status == 0x3, 10, 100); if (rc) { dev_err(dev, "axi bus is not idle, rc = %d\n", rc); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); -- cgit v1.2.3 From f70c1251deb5ab17dec70119c03f2428288ebada Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Wed, 2 May 2018 23:56:34 +0800 Subject: scsi: hisi_sas: workaround a v3 hw hilink bug There is an SoC bug of v3 hw development version. When hot- unplugging a directly attached disk, the PHY down interrupt may not happen. It is very easy to appear on some boards. When this issue occurs, the controller will receive many invalid dword frames, and the "alos" fields of register HILINK_ERR_DFX can indicate that disk was unplugged. As an workaround solution, this patch detects this issue in the channel interrupt, and workaround it by following steps: - Disable the PHY - Clear error code and interrupt - Enable the PHY Then the HW will reissue PHY down interrupt. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 28bb71e3b093..492c3beea3d5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -106,6 +106,7 @@ #define COMPL_Q_0_RD_PTR 0x4f0 #define AWQOS_AWCACHE_CFG 0xc84 #define ARQOS_ARCACHE_CFG 0xc88 +#define HILINK_ERR_DFX 0xe04 /* phy registers requiring init */ #define PORT_BASE (0x2000) @@ -167,6 +168,7 @@ #define CHL_INT1_DMAC_RX_AXI_RD_ERR_OFF 22 #define CHL_INT2 (PORT_BASE + 0x1bc) #define CHL_INT2_SL_IDAF_TOUT_CONF_OFF 0 +#define CHL_INT2_RX_INVLD_DW_OFF 30 #define CHL_INT2_STP_LINK_TIMEOUT_OFF 31 #define CHL_INT0_MSK (PORT_BASE + 0x1c0) #define CHL_INT1_MSK (PORT_BASE + 0x1c4) @@ -1345,6 +1347,7 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) { struct hisi_hba *hisi_hba = p; struct device *dev = hisi_hba->dev; + struct pci_dev *pci_dev = hisi_hba->pci_dev; u32 irq_msk; int phy_no = 0; @@ -1410,8 +1413,28 @@ static irqreturn_t int_chnl_int_v3_hw(int irq_no, void *p) hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, irq_value2); - } + if ((irq_value2 & BIT(CHL_INT2_RX_INVLD_DW_OFF)) && + (pci_dev->revision == 0x20)) { + u32 reg_value; + int rc; + + rc = hisi_sas_read32_poll_timeout_atomic( + HILINK_ERR_DFX, reg_value, + !((reg_value >> 8) & BIT(phy_no)), + 1000, 10000); + if (rc) { + disable_phy_v3_hw(hisi_hba, phy_no); + hisi_sas_phy_write32(hisi_hba, phy_no, + CHL_INT2, + BIT(CHL_INT2_RX_INVLD_DW_OFF)); + hisi_sas_phy_read32(hisi_hba, phy_no, + ERR_CNT_INVLD_DW); + mdelay(1); + enable_phy_v3_hw(hisi_hba, phy_no); + } + } + } if (irq_msk & (2 << (phy_no * 4)) && irq_value0) { hisi_sas_phy_write32(hisi_hba, phy_no, -- cgit v1.2.3 From b9315530bff7ccb2be6769eb74c10e7b52eeb0bf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 2 May 2018 10:12:43 +0100 Subject: scsi: mptfusion: fix spelling mistake: "initators" -> "initiators" Trivial fix to spelling mistake in text string. Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptbase.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 51eb1b027963..72e72f93f0f5 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -7635,7 +7635,7 @@ mpt_display_event_info(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply) snprintf(evStr, EVENT_DESCR_STR_SZ, "SAS Initiator Device Table Overflow: max initiators=%02d " - "current initators=%02d", + "current initiators=%02d", max_init, current_init); break; } -- cgit v1.2.3 From 4b83cb8b06bc1faf70573a18b080976aa9aed0fb Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 2 May 2018 13:31:13 -0700 Subject: scsi: qla2xxx: remove the unused tcm_qla2xxx_cmd_wq Signed-off-by: Andrei Vagin Reviewed-by: Laurence Oberman Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 34ea4a8f98d2..0c2e82af9c0a 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -48,7 +48,6 @@ #include "tcm_qla2xxx.h" static struct workqueue_struct *tcm_qla2xxx_free_wq; -static struct workqueue_struct *tcm_qla2xxx_cmd_wq; /* * Parse WWN. @@ -2003,16 +2002,8 @@ static int tcm_qla2xxx_register_configfs(void) goto out_fabric_npiv; } - tcm_qla2xxx_cmd_wq = alloc_workqueue("tcm_qla2xxx_cmd", 0, 0); - if (!tcm_qla2xxx_cmd_wq) { - ret = -ENOMEM; - goto out_free_wq; - } - return 0; -out_free_wq: - destroy_workqueue(tcm_qla2xxx_free_wq); out_fabric_npiv: target_unregister_template(&tcm_qla2xxx_npiv_ops); out_fabric: @@ -2022,7 +2013,6 @@ out_fabric: static void tcm_qla2xxx_deregister_configfs(void) { - destroy_workqueue(tcm_qla2xxx_cmd_wq); destroy_workqueue(tcm_qla2xxx_free_wq); target_unregister_template(&tcm_qla2xxx_ops); -- cgit v1.2.3 From 7afc0ce9129e4ad4723673fdfdfc7aa63898ca22 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 3 May 2018 10:26:12 +0100 Subject: scsi: lpfc: fix spelling mistakes: "mabilbox" and "maibox" Trivial fix to spelling mistakes in lpfc_printf_log log message "mabilbox" -> "mailbox" "maibox" -> "mailbox" Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_bsg.c | 20 ++++++++++---------- drivers/scsi/lpfc/lpfc_init.c | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 0f174ca80f67..edb1a18a6414 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -3621,7 +3621,7 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) bsg_reply->result = 0; lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, - "2937 SLI_CONFIG ext-buffer maibox command " + "2937 SLI_CONFIG ext-buffer mailbox command " "(x%x/x%x) complete bsg job done, bsize:%d\n", phba->mbox_ext_buf_ctx.nembType, phba->mbox_ext_buf_ctx.mboxType, size); @@ -3632,7 +3632,7 @@ lpfc_bsg_issue_mbox_ext_handle_job(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) phba->mbox_ext_buf_ctx.mbx_dmabuf, 0); } else { lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, - "2938 SLI_CONFIG ext-buffer maibox " + "2938 SLI_CONFIG ext-buffer mailbox " "command (x%x/x%x) failure, rc:x%x\n", phba->mbox_ext_buf_ctx.nembType, phba->mbox_ext_buf_ctx.mboxType, rc); @@ -3666,7 +3666,7 @@ lpfc_bsg_issue_read_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) pmboxq->u.mb.mbxStatus = MBXERR_ERROR; lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, - "2939 SLI_CONFIG ext-buffer rd maibox command " + "2939 SLI_CONFIG ext-buffer rd mailbox command " "complete, ctxState:x%x, mbxStatus:x%x\n", phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); @@ -3706,7 +3706,7 @@ lpfc_bsg_issue_write_mbox_ext_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmboxq) pmboxq->u.mb.mbxStatus = MBXERR_ERROR; lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, - "2940 SLI_CONFIG ext-buffer wr maibox command " + "2940 SLI_CONFIG ext-buffer wr mailbox command " "complete, ctxState:x%x, mbxStatus:x%x\n", phba->mbox_ext_buf_ctx.state, pmboxq->u.mb.mbxStatus); @@ -3988,12 +3988,12 @@ lpfc_bsg_sli_cfg_read_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job, if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2947 Issued SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); return SLI_CONFIG_HANDLED; } lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "2948 Failed to issue SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); rc = -EPIPE; job_error: @@ -4147,12 +4147,12 @@ lpfc_bsg_sli_cfg_write_cmd_ext(struct lpfc_hba *phba, struct bsg_job *job, if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2955 Issued SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); return SLI_CONFIG_HANDLED; } lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "2956 Failed to issue SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); rc = -EPIPE; goto job_error; } @@ -4492,12 +4492,12 @@ lpfc_bsg_write_ebuf_set(struct lpfc_hba *phba, struct bsg_job *job, if ((rc == MBX_SUCCESS) || (rc == MBX_BUSY)) { lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC, "2969 Issued SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); return SLI_CONFIG_HANDLED; } lpfc_printf_log(phba, KERN_ERR, LOG_LIBDFC, "2970 Failed to issue SLI_CONFIG ext-buffer " - "maibox command, rc:x%x\n", rc); + "mailbox command, rc:x%x\n", rc); rc = -EPIPE; goto job_error; } diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 3d89b08896d1..83bc8d849a0d 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -5114,7 +5114,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, if (rc) { lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, - "2772 Issue FCF rediscover mabilbox " + "2772 Issue FCF rediscover mailbox " "command failed, fail through to FCF " "dead event\n"); spin_lock_irq(&phba->hbalock); @@ -5206,7 +5206,7 @@ lpfc_sli4_async_fip_evt(struct lpfc_hba *phba, lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_DISCOVERY, "2774 Issue FCF rediscover " - "mabilbox command failed, " + "mailbox command failed, " "through to CVL event\n"); spin_lock_irq(&phba->hbalock); phba->fcf.fcf_flag &= ~FCF_ACVL_DISC; -- cgit v1.2.3 From c09a21d8ddaf82adc41adc55442bed1852db6249 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 3 May 2018 11:18:07 +0100 Subject: scsi: mptsas: fix spelling mistake: "matchs" -> "matches" Trivial fix to spelling mistake in warning message Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptsas.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 231f3a1e27bf..cb7e328c7951 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -4319,7 +4319,7 @@ mptsas_hotplug_work(MPT_ADAPTER *ioc, struct fw_event_work *fw_event, if (ioc->raid_data.pIocPg2->RaidVolume[i].VolumeID == hot_plug_info->id) { printk(MYIOC_s_WARN_FMT "firmware bug: unable " - "to add hidden disk - target_id matchs " + "to add hidden disk - target_id matches " "volume_id\n", ioc->name); mptsas_free_fw_event(ioc, fw_event); return; -- cgit v1.2.3 From 27e833dabab74ee665e487e291c9afc6d71effba Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 3 May 2018 13:54:32 +0300 Subject: scsi: megaraid: silence a static checker bug If we had more than 32 megaraid cards then it would cause memory corruption. That's not likely, of course, but it's handy to enforce it and make the static checker happy. Signed-off-by: Dan Carpenter Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 7195cff51d4c..9b6f5d024dba 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -4199,6 +4199,9 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) int irq, i, j; int error = -ENODEV; + if (hba_count >= MAX_CONTROLLERS) + goto out; + if (pci_enable_device(pdev)) goto out; pci_set_master(pdev); -- cgit v1.2.3 From f13678960fb594f192fbcfffa510ef9342f3ff38 Mon Sep 17 00:00:00 2001 From: Tomohiro Kusumi Date: Fri, 4 May 2018 16:45:28 -0700 Subject: scsi: mpt3sas: remove obsolete path "drivers/scsi/mpt2sas/" from MAINTAINERS drivers/scsi/mpt2sas/ no longer exists after commit c84b06a48c ("mpt3sas: Single driver module which supports both SAS 2.0 & SAS 3.0 HBAs") merged/removed it. Signed-off-by: Tomohiro Kusumi Signed-off-by: Martin K. Petersen --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 0a1410d5a621..f2eaf4d2223b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8335,7 +8335,6 @@ L: linux-scsi@vger.kernel.org W: http://www.avagotech.com/support/ S: Supported F: drivers/message/fusion/ -F: drivers/scsi/mpt2sas/ F: drivers/scsi/mpt3sas/ LSILOGIC/SYMBIOS/NCR 53C8XX and 53C1010 PCI-SCSI drivers -- cgit v1.2.3 From a217b31165924af2989a0a454c4dff10b63ec6ea Mon Sep 17 00:00:00 2001 From: Tomohiro Kusumi Date: Fri, 4 May 2018 16:45:29 -0700 Subject: scsi: mpt3sas: fix header path in ioctl documentation MPT2_MAGIC_NUMBER as well as drivers/scsi/mpt2sas/mpt2sas_ctl.h were removed to reuse mpt3sas code since commit 09ec55ed74 ("mpt2sas: Remove .c and .h files from mpt2sas driver"). Signed-off-by: Tomohiro Kusumi Signed-off-by: Martin K. Petersen --- Documentation/ioctl/ioctl-number.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 84bb74dcae12..150e5525e16a 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -151,7 +151,7 @@ Code Seq#(hex) Include File Comments 'J' 00-1F drivers/scsi/gdth_ioctl.h 'K' all linux/kd.h 'L' 00-1F linux/loop.h conflict! -'L' 10-1F drivers/scsi/mpt2sas/mpt2sas_ctl.h conflict! +'L' 10-1F drivers/scsi/mpt3sas/mpt3sas_ctl.h conflict! 'L' 20-2F linux/lightnvm.h 'L' E0-FF linux/ppdd.h encrypted disk device driver -- cgit v1.2.3 From c9318a3e0218bc9dacc25be46b9eec363259536f Mon Sep 17 00:00:00 2001 From: Wenwen Wang Date: Mon, 7 May 2018 19:46:43 -0500 Subject: scsi: 3w-9xxx: fix a missing-check bug In twa_chrdev_ioctl(), the ioctl driver command is firstly copied from the userspace pointer 'argp' and saved to the kernel object 'driver_command'. Then a security check is performed on the data buffer size indicated by 'driver_command', which is 'driver_command.buffer_length'. If the security check is passed, the entire ioctl command is copied again from the 'argp' pointer and saved to the kernel object 'tw_ioctl'. Then, various operations are performed on 'tw_ioctl' according to the 'cmd'. Given that the 'argp' pointer resides in userspace, a malicious userspace process can race to change the buffer size between the two copies. This way, the user can bypass the security check and inject invalid data buffer size. This can cause potential security issues in the following execution. This patch checks for capable(CAP_SYS_ADMIN) in twa_chrdev_open()t o avoid the above issues. Signed-off-by: Wenwen Wang Acked-by: Adam Radford Signed-off-by: Martin K. Petersen --- drivers/scsi/3w-9xxx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index b42c9c479d4b..99ba4a770406 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -882,6 +882,11 @@ static int twa_chrdev_open(struct inode *inode, struct file *file) unsigned int minor_number; int retval = TW_IOCTL_ERROR_OS_ENODEV; + if (!capable(CAP_SYS_ADMIN)) { + retval = -EACCES; + goto out; + } + minor_number = iminor(inode); if (minor_number >= twa_device_extension_count) goto out; -- cgit v1.2.3 From 9899e4d3523faaef17c67141aa80ff2088f17871 Mon Sep 17 00:00:00 2001 From: Wenwen Wang Date: Mon, 7 May 2018 19:54:01 -0500 Subject: scsi: 3w-xxxx: fix a missing-check bug In tw_chrdev_ioctl(), the length of the data buffer is firstly copied from the userspace pointer 'argp' and saved to the kernel object 'data_buffer_length'. Then a security check is performed on it to make sure that the length is not more than 'TW_MAX_IOCTL_SECTORS * 512'. Otherwise, an error code -EINVAL is returned. If the security check is passed, the entire ioctl command is copied again from the 'argp' pointer and saved to the kernel object 'tw_ioctl'. Then, various operations are performed on 'tw_ioctl' according to the 'cmd'. Given that the 'argp' pointer resides in userspace, a malicious userspace process can race to change the buffer length between the two copies. This way, the user can bypass the security check and inject invalid data buffer length. This can cause potential security issues in the following execution. This patch checks for capable(CAP_SYS_ADMIN) in tw_chrdev_open() to avoid the above issues. Signed-off-by: Wenwen Wang Acked-by: Adam Radford Signed-off-by: Martin K. Petersen --- drivers/scsi/3w-xxxx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 33261b690774..f6179e3d6953 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1033,6 +1033,9 @@ static int tw_chrdev_open(struct inode *inode, struct file *file) dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_open()\n"); + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + minor_number = iminor(inode); if (minor_number >= tw_device_extension_count) return -ENODEV; -- cgit v1.2.3 From 0e5aee393956f14b82c42486aefc327ba594ca60 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 2 May 2018 11:13:39 +0800 Subject: scsi: tcmu: add new netlink events helpers Add new netlink events helpers tcmu_netlink_event_init() and tcmu_netlink_event_send(). These new functions intend to replace existing netlink events helper function tcmu_netlink_event(). The existing function tcmu_netlink_event() works well for events like TCMU_ADDED_DEVICE and TCMU_REMOVED_DEVICE which only has one netlink attribute. But if there is a command requires more than one attributes to send out, we have to use a struct to adapt the paremeter reconfig_data, it is hard to use one struct or a union in one struct to adapt every command with different attributes, it may get long and ugly. With the new two functions, we can call tcmu_netlink_event_init() to initialize a netlink event, then add all attributes we need by using nla_put_xxx(), at last use tcmu_netlink_event_send() to send it out. So that we don't need to use a long struct or union if we want to send mulitple attributes for different commands. [mkp: typos] Signed-off-by: Zhu Lingshan Acked-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index ae0aea9a3aad..82f424dcee98 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1657,6 +1657,65 @@ free_skb: return ret; } +static int tcmu_netlink_event_init(struct tcmu_dev *udev, + enum tcmu_genl_cmd cmd, + struct sk_buff **buf, void **hdr) +{ + struct sk_buff *skb; + void *msg_header; + int ret = -ENOMEM; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return ret; + + msg_header = genlmsg_put(skb, 0, 0, &tcmu_genl_family, 0, cmd); + if (!msg_header) + goto free_skb; + + ret = nla_put_string(skb, TCMU_ATTR_DEVICE, udev->uio_info.name); + if (ret < 0) + goto free_skb; + + ret = nla_put_u32(skb, TCMU_ATTR_MINOR, udev->uio_info.uio_dev->minor); + if (ret < 0) + goto free_skb; + + ret = nla_put_u32(skb, TCMU_ATTR_DEVICE_ID, udev->se_dev.dev_index); + if (ret < 0) + goto free_skb; + + *buf = skb; + *hdr = msg_header; + return ret; + +free_skb: + nlmsg_free(skb); + return ret; +} + +static int tcmu_netlink_event_send(struct tcmu_dev *udev, + enum tcmu_genl_cmd cmd, + struct sk_buff **buf, void **hdr) +{ + int ret = 0; + struct sk_buff *skb = *buf; + void *msg_header = *hdr; + + genlmsg_end(skb, msg_header); + + tcmu_init_genl_cmd_reply(udev, cmd); + + ret = genlmsg_multicast_allns(&tcmu_genl_family, skb, 0, + TCMU_MCGRP_CONFIG, GFP_KERNEL); + /* We don't care if no one is listening */ + if (ret == -ESRCH) + ret = 0; + if (!ret) + ret = tcmu_wait_genl_cmd_reply(udev); + return ret; +} + static int tcmu_update_uio_info(struct tcmu_dev *udev) { struct tcmu_hba *hba = udev->hba->hba_ptr; -- cgit v1.2.3 From e0c240ac3aec565360bd38fae9e991b2ce376787 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 2 May 2018 11:13:40 +0800 Subject: scsi: tcmu: refactor add_device cmd with new nl helpers use new netlink events helpers tcmu_netlink_init() and tcmu_netlink_send() to refactor netlink event TCMU_CMD_ADDED_DEVICE Signed-off-by: Zhu Lingshan Acked-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 82f424dcee98..b0e3f03f77ed 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1716,6 +1716,21 @@ static int tcmu_netlink_event_send(struct tcmu_dev *udev, return ret; } +static int tcmu_send_dev_add_event(struct tcmu_dev *udev) +{ + struct sk_buff *skb = NULL; + void *msg_header = NULL; + int ret = 0; + + ret = tcmu_netlink_event_init(udev, TCMU_CMD_ADDED_DEVICE, &skb, + &msg_header); + if (ret < 0) + return ret; + return tcmu_netlink_event_send(udev, TCMU_CMD_ADDED_DEVICE, &skb, + &msg_header); + +} + static int tcmu_update_uio_info(struct tcmu_dev *udev) { struct tcmu_hba *hba = udev->hba->hba_ptr; @@ -1825,7 +1840,7 @@ static int tcmu_configure_device(struct se_device *dev) */ kref_get(&udev->kref); - ret = tcmu_netlink_event(udev, TCMU_CMD_ADDED_DEVICE, 0, NULL); + ret = tcmu_send_dev_add_event(udev); if (ret) goto err_netlink; -- cgit v1.2.3 From f892bd8ec14226d681021ef6e4c0cd2d5ca08e8d Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 2 May 2018 11:13:41 +0800 Subject: scsi: tcmu: refactor rm_device cmd with new nl helpers use new netlink events helpers tcmu_netlink_init() and tcmu_netlink_send() to refactor netlink event TCMU_CMD_REMOVED_DEVICE Signed-off-by: Zhu Lingshan Acked-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index b0e3f03f77ed..c621364cffc5 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1731,6 +1731,20 @@ static int tcmu_send_dev_add_event(struct tcmu_dev *udev) } +static int tcmu_send_dev_remove_event(struct tcmu_dev *udev) +{ + struct sk_buff *skb = NULL; + void *msg_header = NULL; + int ret = 0; + + ret = tcmu_netlink_event_init(udev, TCMU_CMD_REMOVED_DEVICE, + &skb, &msg_header); + if (ret < 0) + return ret; + return tcmu_netlink_event_send(udev, TCMU_CMD_REMOVED_DEVICE, + &skb, &msg_header); +} + static int tcmu_update_uio_info(struct tcmu_dev *udev) { struct tcmu_hba *hba = udev->hba->hba_ptr; @@ -1890,7 +1904,7 @@ static void tcmu_destroy_device(struct se_device *dev) list_del(&udev->node); mutex_unlock(&root_udev_mutex); - tcmu_netlink_event(udev, TCMU_CMD_REMOVED_DEVICE, 0, NULL); + tcmu_send_dev_remove_event(udev); uio_unregister_device(&udev->uio_info); -- cgit v1.2.3 From 02ccfb54ba7e73c47ce401920af8cf7a0c7bcaca Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 2 May 2018 11:13:42 +0800 Subject: scsi: tcmu: refactor nl dev_cfg attr with new nl helpers use new netlink events helpers tcmu_netlink_init() and tcmu_netlink_send() to refactor netlink event attribute TCMU_ATTR_DEV_CFG(belongs to TCMU_CMD_RECONFIG_DEVICE) which is also dev_config in configFS. Signed-off-by: Zhu Lingshan Acked-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index c621364cffc5..81495683ad3c 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -2241,6 +2241,27 @@ static ssize_t tcmu_dev_config_show(struct config_item *item, char *page) return snprintf(page, PAGE_SIZE, "%s\n", udev->dev_config); } +static int tcmu_send_dev_config_event(struct tcmu_dev *udev, + const char *reconfig_data) +{ + struct sk_buff *skb = NULL; + void *msg_header = NULL; + int ret = 0; + + ret = tcmu_netlink_event_init(udev, TCMU_CMD_RECONFIG_DEVICE, + &skb, &msg_header); + if (ret < 0) + return ret; + ret = nla_put_string(skb, TCMU_ATTR_DEV_CFG, reconfig_data); + if (ret < 0) { + nlmsg_free(skb); + return ret; + } + return tcmu_netlink_event_send(udev, TCMU_CMD_RECONFIG_DEVICE, + &skb, &msg_header); +} + + static ssize_t tcmu_dev_config_store(struct config_item *item, const char *page, size_t count) { @@ -2255,8 +2276,7 @@ static ssize_t tcmu_dev_config_store(struct config_item *item, const char *page, /* Check if device has been configured before */ if (tcmu_dev_configured(udev)) { - ret = tcmu_netlink_event(udev, TCMU_CMD_RECONFIG_DEVICE, - TCMU_ATTR_DEV_CFG, page); + ret = tcmu_send_dev_config_event(udev, page); if (ret) { pr_err("Unable to reconfigure device\n"); return ret; -- cgit v1.2.3 From 84e285062848d9bca20b60827d26b7c72101c184 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 2 May 2018 11:13:43 +0800 Subject: scsi: tcmu: refactor nl dev_size attr with new helpers use new netlink events helpers tcmu_netlink_init() and tcmu_netlink_send() to refactor netlink event attribute TCMU_ATTR_DEV_SIZE(belongs to TCMU_CMD_RECONFIG_DEVICE) which is also dev_size in configFS. Signed-off-by: Zhu Lingshan Acked-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 81495683ad3c..a4c50482199c 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -2303,6 +2303,26 @@ static ssize_t tcmu_dev_size_show(struct config_item *item, char *page) return snprintf(page, PAGE_SIZE, "%zu\n", udev->dev_size); } +static int tcmu_send_dev_size_event(struct tcmu_dev *udev, u64 size) +{ + struct sk_buff *skb = NULL; + void *msg_header = NULL; + int ret = 0; + + ret = tcmu_netlink_event_init(udev, TCMU_CMD_RECONFIG_DEVICE, + &skb, &msg_header); + if (ret < 0) + return ret; + ret = nla_put_u64_64bit(skb, TCMU_ATTR_DEV_SIZE, + size, TCMU_ATTR_PAD); + if (ret < 0) { + nlmsg_free(skb); + return ret; + } + return tcmu_netlink_event_send(udev, TCMU_CMD_RECONFIG_DEVICE, + &skb, &msg_header); +} + static ssize_t tcmu_dev_size_store(struct config_item *item, const char *page, size_t count) { @@ -2318,8 +2338,7 @@ static ssize_t tcmu_dev_size_store(struct config_item *item, const char *page, /* Check if device has been configured before */ if (tcmu_dev_configured(udev)) { - ret = tcmu_netlink_event(udev, TCMU_CMD_RECONFIG_DEVICE, - TCMU_ATTR_DEV_SIZE, &val); + ret = tcmu_send_dev_size_event(udev, val); if (ret) { pr_err("Unable to reconfigure device\n"); return ret; -- cgit v1.2.3 From 33d065ccb3b21e38412cd8c7d6f2704e3b341f63 Mon Sep 17 00:00:00 2001 From: Zhu Lingshan Date: Wed, 2 May 2018 11:13:44 +0800 Subject: scsi: tcmu: refactor nl wr_cache attr with new helpers use new netlink events helpers tcmu_netlink_init() and tcmu_netlink_send() to refactor netlink event attribute TCMU_ATTR_WRITECACHE(belongs to TCMU_CMD_RECONFIG_DEVICE) which is also emulate_write_cache in configFS. Removed tcmu_netlink_event() since we have new netlink events helpers now. Signed-off-by: Zhu Lingshan Acked-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_user.c | 89 +++++++++------------------------------ 1 file changed, 20 insertions(+), 69 deletions(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index a4c50482199c..19a9c5fd7e1d 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1590,73 +1590,6 @@ static int tcmu_wait_genl_cmd_reply(struct tcmu_dev *udev) return ret; } -static int tcmu_netlink_event(struct tcmu_dev *udev, enum tcmu_genl_cmd cmd, - int reconfig_attr, const void *reconfig_data) -{ - struct sk_buff *skb; - void *msg_header; - int ret = -ENOMEM; - - skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - return ret; - - msg_header = genlmsg_put(skb, 0, 0, &tcmu_genl_family, 0, cmd); - if (!msg_header) - goto free_skb; - - ret = nla_put_string(skb, TCMU_ATTR_DEVICE, udev->uio_info.name); - if (ret < 0) - goto free_skb; - - ret = nla_put_u32(skb, TCMU_ATTR_MINOR, udev->uio_info.uio_dev->minor); - if (ret < 0) - goto free_skb; - - ret = nla_put_u32(skb, TCMU_ATTR_DEVICE_ID, udev->se_dev.dev_index); - if (ret < 0) - goto free_skb; - - if (cmd == TCMU_CMD_RECONFIG_DEVICE) { - switch (reconfig_attr) { - case TCMU_ATTR_DEV_CFG: - ret = nla_put_string(skb, reconfig_attr, reconfig_data); - break; - case TCMU_ATTR_DEV_SIZE: - ret = nla_put_u64_64bit(skb, reconfig_attr, - *((u64 *)reconfig_data), - TCMU_ATTR_PAD); - break; - case TCMU_ATTR_WRITECACHE: - ret = nla_put_u8(skb, reconfig_attr, - *((u8 *)reconfig_data)); - break; - default: - BUG(); - } - - if (ret < 0) - goto free_skb; - } - - genlmsg_end(skb, msg_header); - - tcmu_init_genl_cmd_reply(udev, cmd); - - ret = genlmsg_multicast_allns(&tcmu_genl_family, skb, 0, - TCMU_MCGRP_CONFIG, GFP_KERNEL); - /* We don't care if no one is listening */ - if (ret == -ESRCH) - ret = 0; - if (!ret) - ret = tcmu_wait_genl_cmd_reply(udev); - - return ret; -free_skb: - nlmsg_free(skb); - return ret; -} - static int tcmu_netlink_event_init(struct tcmu_dev *udev, enum tcmu_genl_cmd cmd, struct sk_buff **buf, void **hdr) @@ -2386,6 +2319,25 @@ static ssize_t tcmu_emulate_write_cache_show(struct config_item *item, return snprintf(page, PAGE_SIZE, "%i\n", da->emulate_write_cache); } +static int tcmu_send_emulate_write_cache(struct tcmu_dev *udev, u8 val) +{ + struct sk_buff *skb = NULL; + void *msg_header = NULL; + int ret = 0; + + ret = tcmu_netlink_event_init(udev, TCMU_CMD_RECONFIG_DEVICE, + &skb, &msg_header); + if (ret < 0) + return ret; + ret = nla_put_u8(skb, TCMU_ATTR_WRITECACHE, val); + if (ret < 0) { + nlmsg_free(skb); + return ret; + } + return tcmu_netlink_event_send(udev, TCMU_CMD_RECONFIG_DEVICE, + &skb, &msg_header); +} + static ssize_t tcmu_emulate_write_cache_store(struct config_item *item, const char *page, size_t count) { @@ -2401,8 +2353,7 @@ static ssize_t tcmu_emulate_write_cache_store(struct config_item *item, /* Check if device has been configured before */ if (tcmu_dev_configured(udev)) { - ret = tcmu_netlink_event(udev, TCMU_CMD_RECONFIG_DEVICE, - TCMU_ATTR_WRITECACHE, &val); + ret = tcmu_send_emulate_write_cache(udev, val); if (ret) { pr_err("Unable to reconfigure device\n"); return ret; -- cgit v1.2.3 From 97e066184b855dd2df4e82c2d1feff37282c53fd Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 2 May 2018 15:55:45 -0700 Subject: scsi: libosd: Remove VLA usage On the quest to remove all VLAs from the kernel[1] this rearranges the code to avoid a VLA warning under -Wvla (gcc doesn't recognize "const" variables as not triggering VLA creation). Additionally cleans up variable naming to avoid 80 character column limit. [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Acked-by: Boaz Harrosh Signed-off-by: Martin K. Petersen --- drivers/scsi/osd/osd_initiator.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index e18877177f1b..917a86a2ae8c 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c @@ -1842,14 +1842,14 @@ int osd_req_decode_sense_full(struct osd_request *or, case osd_sense_response_integrity_check: { struct osd_sense_response_integrity_check_descriptor - *osricd = cur_descriptor; - const unsigned len = - sizeof(osricd->integrity_check_value); - char key_dump[len*4 + 2]; /* 2nibbles+space+ASCII */ - - hex_dump_to_buffer(osricd->integrity_check_value, len, - 32, 1, key_dump, sizeof(key_dump), true); - OSD_SENSE_PRINT2("response_integrity [%s]\n", key_dump); + *d = cur_descriptor; + /* 2nibbles+space+ASCII */ + char dump[sizeof(d->integrity_check_value) * 4 + 2]; + + hex_dump_to_buffer(d->integrity_check_value, + sizeof(d->integrity_check_value), + 32, 1, dump, sizeof(dump), true); + OSD_SENSE_PRINT2("response_integrity [%s]\n", dump); } case osd_sense_attribute_identification: { -- cgit v1.2.3 From 769031c92c8b85bd886f7e064430b7b512c68728 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Wed, 21 Mar 2018 23:55:02 -0700 Subject: scsi: target: target/file: Add support of direct and async I/O There are two advantages: * Direct I/O allows to avoid the write-back cache, so it reduces affects to other processes in the system. * Async I/O allows to handle a few commands concurrently. DIO + AIO shows a better perfomance for random write operations: Mode: O_DSYNC Async: 1 $ ./fio --bs=4K --direct=1 --rw=randwrite --ioengine=libaio --iodepth=64 --name=/dev/sda --runtime=20 --numjobs=2 WRITE: bw=45.9MiB/s (48.1MB/s), 21.9MiB/s-23.0MiB/s (22.0MB/s-25.2MB/s), io=919MiB (963MB), run=20002-20020msec Mode: O_DSYNC Async: 0 $ ./fio --bs=4K --direct=1 --rw=randwrite --ioengine=libaio --iodepth=64 --name=/dev/sdb --runtime=20 --numjobs=2 WRITE: bw=1607KiB/s (1645kB/s), 802KiB/s-805KiB/s (821kB/s-824kB/s), io=31.8MiB (33.4MB), run=20280-20295msec Known issue: DIF (PI) emulation doesn't work when a target uses async I/O, because DIF metadata is saved in a separate file, and it is another non-trivial task how to synchronize writing in two files, so that a following read operation always returns a consisten metadata for a specified block. Cc: "Nicholas A. Bellinger" Cc: Christoph Hellwig Cc: Bryant G. Ly Tested-by: Bryant G. Ly Signed-off-by: Andrei Vagin Reviewed-by: Christoph Hellwig Reviewed-by: Bryant G. Ly Reviewed-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_file.c | 137 ++++++++++++++++++++++++++++++++++---- drivers/target/target_core_file.h | 1 + 2 files changed, 124 insertions(+), 14 deletions(-) diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 9b2c0c773022..16751ae55d7b 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -250,6 +250,84 @@ static void fd_destroy_device(struct se_device *dev) } } +struct target_core_file_cmd { + unsigned long len; + struct se_cmd *cmd; + struct kiocb iocb; +}; + +static void cmd_rw_aio_complete(struct kiocb *iocb, long ret, long ret2) +{ + struct target_core_file_cmd *cmd; + + cmd = container_of(iocb, struct target_core_file_cmd, iocb); + + if (ret != cmd->len) + target_complete_cmd(cmd->cmd, SAM_STAT_CHECK_CONDITION); + else + target_complete_cmd(cmd->cmd, SAM_STAT_GOOD); + + kfree(cmd); +} + +static sense_reason_t +fd_execute_rw_aio(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, + enum dma_data_direction data_direction) +{ + int is_write = !(data_direction == DMA_FROM_DEVICE); + struct se_device *dev = cmd->se_dev; + struct fd_dev *fd_dev = FD_DEV(dev); + struct file *file = fd_dev->fd_file; + struct target_core_file_cmd *aio_cmd; + struct iov_iter iter = {}; + struct scatterlist *sg; + struct bio_vec *bvec; + ssize_t len = 0; + int ret = 0, i; + + aio_cmd = kmalloc(sizeof(struct target_core_file_cmd), GFP_KERNEL); + if (!aio_cmd) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + + bvec = kcalloc(sgl_nents, sizeof(struct bio_vec), GFP_KERNEL); + if (!bvec) { + kfree(aio_cmd); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + + for_each_sg(sgl, sg, sgl_nents, i) { + bvec[i].bv_page = sg_page(sg); + bvec[i].bv_len = sg->length; + bvec[i].bv_offset = sg->offset; + + len += sg->length; + } + + iov_iter_bvec(&iter, ITER_BVEC | is_write, bvec, sgl_nents, len); + + aio_cmd->cmd = cmd; + aio_cmd->len = len; + aio_cmd->iocb.ki_pos = cmd->t_task_lba * dev->dev_attrib.block_size; + aio_cmd->iocb.ki_filp = file; + aio_cmd->iocb.ki_complete = cmd_rw_aio_complete; + aio_cmd->iocb.ki_flags = IOCB_DIRECT; + + if (is_write && (cmd->se_cmd_flags & SCF_FUA)) + aio_cmd->iocb.ki_flags |= IOCB_DSYNC; + + if (is_write) + ret = call_write_iter(file, &aio_cmd->iocb, &iter); + else + ret = call_read_iter(file, &aio_cmd->iocb, &iter); + + kfree(bvec); + + if (ret != -EIOCBQUEUED) + cmd_rw_aio_complete(&aio_cmd->iocb, ret, 0); + + return 0; +} + static int fd_do_rw(struct se_cmd *cmd, struct file *fd, u32 block_size, struct scatterlist *sgl, u32 sgl_nents, u32 data_length, int is_write) @@ -527,7 +605,7 @@ fd_execute_unmap(struct se_cmd *cmd, sector_t lba, sector_t nolb) } static sense_reason_t -fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, +fd_execute_rw_buffered(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, enum dma_data_direction data_direction) { struct se_device *dev = cmd->se_dev; @@ -536,16 +614,6 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, struct file *pfile = fd_dev->fd_prot_file; sense_reason_t rc; int ret = 0; - /* - * We are currently limited by the number of iovecs (2048) per - * single vfs_[writev,readv] call. - */ - if (cmd->data_length > FD_MAX_BYTES) { - pr_err("FILEIO: Not able to process I/O of %u bytes due to" - "FD_MAX_BYTES: %u iovec count limitation\n", - cmd->data_length, FD_MAX_BYTES); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } /* * Call vectorized fileio functions to map struct scatterlist * physical memory addresses to struct iovec virtual memory. @@ -620,14 +688,39 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, return 0; } +static sense_reason_t +fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, + enum dma_data_direction data_direction) +{ + struct se_device *dev = cmd->se_dev; + struct fd_dev *fd_dev = FD_DEV(dev); + + /* + * We are currently limited by the number of iovecs (2048) per + * single vfs_[writev,readv] call. + */ + if (cmd->data_length > FD_MAX_BYTES) { + pr_err("FILEIO: Not able to process I/O of %u bytes due to" + "FD_MAX_BYTES: %u iovec count limitation\n", + cmd->data_length, FD_MAX_BYTES); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + + if (fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO) + return fd_execute_rw_aio(cmd, sgl, sgl_nents, data_direction); + return fd_execute_rw_buffered(cmd, sgl, sgl_nents, data_direction); +} + enum { - Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, Opt_err + Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, + Opt_fd_async_io, Opt_err }; static match_table_t tokens = { {Opt_fd_dev_name, "fd_dev_name=%s"}, {Opt_fd_dev_size, "fd_dev_size=%s"}, {Opt_fd_buffered_io, "fd_buffered_io=%d"}, + {Opt_fd_async_io, "fd_async_io=%d"}, {Opt_err, NULL} }; @@ -693,6 +786,21 @@ static ssize_t fd_set_configfs_dev_params(struct se_device *dev, fd_dev->fbd_flags |= FDBD_HAS_BUFFERED_IO_WCE; break; + case Opt_fd_async_io: + ret = match_int(args, &arg); + if (ret) + goto out; + if (arg != 1) { + pr_err("bogus fd_async_io=%d value\n", arg); + ret = -EINVAL; + goto out; + } + + pr_debug("FILEIO: Using async I/O" + " operations for struct fd_dev\n"); + + fd_dev->fbd_flags |= FDBD_HAS_ASYNC_IO; + break; default: break; } @@ -709,10 +817,11 @@ static ssize_t fd_show_configfs_dev_params(struct se_device *dev, char *b) ssize_t bl = 0; bl = sprintf(b + bl, "TCM FILEIO ID: %u", fd_dev->fd_dev_id); - bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s\n", + bl += sprintf(b + bl, " File: %s Size: %llu Mode: %s Async: %d\n", fd_dev->fd_dev_name, fd_dev->fd_dev_size, (fd_dev->fbd_flags & FDBD_HAS_BUFFERED_IO_WCE) ? - "Buffered-WCE" : "O_DSYNC"); + "Buffered-WCE" : "O_DSYNC", + !!(fd_dev->fbd_flags & FDBD_HAS_ASYNC_IO)); return bl; } diff --git a/drivers/target/target_core_file.h b/drivers/target/target_core_file.h index 53be5ffd3261..929b1ecd544e 100644 --- a/drivers/target/target_core_file.h +++ b/drivers/target/target_core_file.h @@ -22,6 +22,7 @@ #define FBDF_HAS_PATH 0x01 #define FBDF_HAS_SIZE 0x02 #define FDBD_HAS_BUFFERED_IO_WCE 0x04 +#define FDBD_HAS_ASYNC_IO 0x08 #define FDBD_FORMAT_UNIT_SIZE 2048 struct fd_dev { -- cgit v1.2.3 From eaa6d0df678a37733bd373f6d851840a2349a13e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 8 May 2018 22:54:35 +0100 Subject: scsi: esas2r: fix spelling mistake: "requestss" -> "requests" Trivial fix to spelling mistake in esas2r_debug message. Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/esas2r/esas2r_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/esas2r/esas2r_ioctl.c b/drivers/scsi/esas2r/esas2r_ioctl.c index 97623002908f..34bcc8c04ff4 100644 --- a/drivers/scsi/esas2r/esas2r_ioctl.c +++ b/drivers/scsi/esas2r/esas2r_ioctl.c @@ -1849,7 +1849,7 @@ int esas2r_read_vda(struct esas2r_adapter *a, char *buf, long off, int count) /* allocate a request */ rq = esas2r_alloc_request(a); if (rq == NULL) { - esas2r_debug("esas2r_read_vda: out of requestss"); + esas2r_debug("esas2r_read_vda: out of requests"); return -EBUSY; } -- cgit v1.2.3 From 81471b07b7924422c66054d22c1aaf93d9ab5294 Mon Sep 17 00:00:00 2001 From: Wen Xiong Date: Wed, 9 May 2018 13:47:54 -0500 Subject: scsi: ipr: new IOASC update This patch adds new adapter error log for P9 system with the new AZ SAS cable. Signed-off-by: Wen Xiong Acked-by: Brian King Signed-off-by: Martin K. Petersen --- drivers/scsi/ipr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index dda1a64ab89c..6615ad8754b8 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -435,6 +435,8 @@ struct ipr_error_table_t ipr_error_table[] = { "4080: IOA exceeded maximum operating temperature"}, {0x060B8000, 0, IPR_DEFAULT_LOG_LEVEL, "4085: Service required"}, + {0x060B8100, 0, IPR_DEFAULT_LOG_LEVEL, + "4086: SAS Adapter Hardware Configuration Error"}, {0x06288000, 0, IPR_DEFAULT_LOG_LEVEL, "3140: Device bus not ready to ready transition"}, {0x06290000, 0, IPR_DEFAULT_LOG_LEVEL, -- cgit v1.2.3 From 66b7eaca548d716b86ae2417ec103a7f2f6c214c Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Tue, 24 Apr 2018 15:15:58 +0200 Subject: scsi: mptlan: Fix mpt_lan_sdu_send()'s return type The method ndo_start_xmit() is defined as returning an 'netdev_tx_t', which is a typedef for an enum type, but the implementation in this driver returns an 'int'. Fix this by returning 'netdev_tx_t' in this driver too. Signed-off-by: Luc Van Oostenryck Signed-off-by: Martin K. Petersen --- drivers/message/fusion/mptlan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index 55dd71bbdc2a..4cbed4d06aa7 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -670,7 +670,7 @@ out: } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -static int +static netdev_tx_t mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) { struct mpt_lan_priv *priv = netdev_priv(dev); -- cgit v1.2.3 From bbe21d7a979245902113e171fdb36b57812b3c3b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 2 May 2018 16:58:09 -0700 Subject: scsi: ufs: ufshcd: Remove VLA usage On the quest to remove all VLAs from the kernel[1] this moves buffers off the stack. In the second instance, this collapses two separately allocated buffers into a single buffer, since they are used consecutively, which saves 256 bytes (QUERY_DESC_MAX_SIZE + 1) of stack space. [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c5b1bf1cadcb..bbad34738bea 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5918,14 +5918,18 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) { int ret; int buff_len = hba->desc_size.pwr_desc; - u8 desc_buf[hba->desc_size.pwr_desc]; + u8 *desc_buf; + + desc_buf = kmalloc(buff_len, GFP_KERNEL); + if (!desc_buf) + return; ret = ufshcd_read_power_desc(hba, desc_buf, buff_len); if (ret) { dev_err(hba->dev, "%s: Failed reading power descriptor.len = %d ret = %d", __func__, buff_len, ret); - return; + goto out; } hba->init_prefetch_data.icc_level = @@ -5943,6 +5947,8 @@ static void ufshcd_init_icc_levels(struct ufs_hba *hba) "%s: Failed configuring bActiveICCLevel = %d ret = %d", __func__, hba->init_prefetch_data.icc_level , ret); +out: + kfree(desc_buf); } /** @@ -6012,9 +6018,17 @@ static int ufs_get_device_desc(struct ufs_hba *hba, struct ufs_dev_desc *dev_desc) { int err; + size_t buff_len; u8 model_index; - u8 str_desc_buf[QUERY_DESC_MAX_SIZE + 1] = {0}; - u8 desc_buf[hba->desc_size.dev_desc]; + u8 *desc_buf; + + buff_len = max_t(size_t, hba->desc_size.dev_desc, + QUERY_DESC_MAX_SIZE + 1); + desc_buf = kmalloc(buff_len, GFP_KERNEL); + if (!desc_buf) { + err = -ENOMEM; + goto out; + } err = ufshcd_read_device_desc(hba, desc_buf, hba->desc_size.dev_desc); if (err) { @@ -6032,7 +6046,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba, model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME]; - err = ufshcd_read_string_desc(hba, model_index, str_desc_buf, + /* Zero-pad entire buffer for string termination. */ + memset(desc_buf, 0, buff_len); + + err = ufshcd_read_string_desc(hba, model_index, desc_buf, QUERY_DESC_MAX_SIZE, true/*ASCII*/); if (err) { dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n", @@ -6040,15 +6057,16 @@ static int ufs_get_device_desc(struct ufs_hba *hba, goto out; } - str_desc_buf[QUERY_DESC_MAX_SIZE] = '\0'; - strlcpy(dev_desc->model, (str_desc_buf + QUERY_DESC_HDR_SIZE), - min_t(u8, str_desc_buf[QUERY_DESC_LENGTH_OFFSET], + desc_buf[QUERY_DESC_MAX_SIZE] = '\0'; + strlcpy(dev_desc->model, (desc_buf + QUERY_DESC_HDR_SIZE), + min_t(u8, desc_buf[QUERY_DESC_LENGTH_OFFSET], MAX_MODEL_LEN)); /* Null terminate the model string */ dev_desc->model[MAX_MODEL_LEN] = '\0'; out: + kfree(desc_buf); return err; } -- cgit v1.2.3 From 1399c5b02c568700ba50ce1ec19c12e71b25c73d Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Sun, 6 May 2018 15:44:15 +0530 Subject: scsi: ufs: add quirk to fix mishandling utrlclr/utmrlclr In the right behavior, setting the bit to '0' indicates clear and '1' indicates no change. If host controller handles this the other way, UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR can be used. [mkp: typo] Signed-off-by: Seungwon Jeon Signed-off-by: Alim Akhtar Reviewed-by: Subhash Jadavani Reviewed-by: "Asutosh Das (asd)" Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 21 +++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 5 +++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index bbad34738bea..937355b04e4a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -643,7 +643,24 @@ static inline void ufshcd_put_tm_slot(struct ufs_hba *hba, int slot) */ static inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos) { - ufshcd_writel(hba, ~(1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR); + if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR) + ufshcd_writel(hba, (1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR); + else + ufshcd_writel(hba, ~(1 << pos), + REG_UTP_TRANSFER_REQ_LIST_CLEAR); +} + +/** + * ufshcd_utmrl_clear - Clear a bit in UTRMLCLR register + * @hba: per adapter instance + * @pos: position of the bit to be cleared + */ +static inline void ufshcd_utmrl_clear(struct ufs_hba *hba, u32 pos) +{ + if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR) + ufshcd_writel(hba, (1 << pos), REG_UTP_TASK_REQ_LIST_CLEAR); + else + ufshcd_writel(hba, ~(1 << pos), REG_UTP_TASK_REQ_LIST_CLEAR); } /** @@ -5362,7 +5379,7 @@ static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) goto out; spin_lock_irqsave(hba->host->host_lock, flags); - ufshcd_writel(hba, ~(1 << tag), REG_UTP_TASK_REQ_LIST_CLEAR); + ufshcd_utmrl_clear(hba, tag); spin_unlock_irqrestore(hba->host->host_lock, flags); /* poll for max. 1 sec to clear door bell register by h/w */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 8110dcd04d22..09c285f62c6a 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -595,6 +595,11 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_PRDT_BYTE_GRAN 0x80 + /* + * Clear handling for transfer/task request list is just opposite. + */ + #define UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR 0x100 + unsigned int quirks; /* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- cgit v1.2.3 From 5ac6abc9415c3c70040b529631426b78e26108c4 Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Sun, 6 May 2018 15:44:16 +0530 Subject: scsi: ufs: add quirk to disallow reset of interrupt aggregation Some host controllers support interrupt aggregation but don't allow resetting counter and timer in software. Signed-off-by: Seungwon Jeon Signed-off-by: Alim Akhtar Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 3 ++- drivers/scsi/ufs/ufshcd.h | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 937355b04e4a..1ef4d46b25c7 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4659,7 +4659,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) * false interrupt if device completes another request after resetting * aggregation and before reading the DB. */ - if (ufshcd_is_intr_aggr_allowed(hba)) + if (ufshcd_is_intr_aggr_allowed(hba) && + !(hba->quirks & UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR)) ufshcd_reset_intr_aggr(hba); tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 09c285f62c6a..d8c922d5bbee 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -600,6 +600,12 @@ struct ufs_hba { */ #define UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR 0x100 + /* + * This quirk needs to be enabled if host controller doesn't allow + * that the interrupt aggregation timer and counter are reset by s/w. + */ + #define UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR 0x200 + unsigned int quirks; /* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- cgit v1.2.3 From 4404c5de7707859d8e54f4356ac17c82413c80e9 Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Sun, 6 May 2018 15:44:17 +0530 Subject: scsi: ufs: add quirk to enable host controller without hce Some host controllers don't support host controller enable via HCE. Signed-off-by: Seungwon Jeon Signed-off-by: Alim Akhtar Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 75 +++++++++++++++++++++++++++++++++++++++++++++-- drivers/scsi/ufs/ufshcd.h | 5 ++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 1ef4d46b25c7..bfb902380cf0 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -3364,6 +3364,52 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) "dme-link-startup: error code %d\n", ret); return ret; } +/** + * ufshcd_dme_reset - UIC command for DME_RESET + * @hba: per adapter instance + * + * DME_RESET command is issued in order to reset UniPro stack. + * This function now deal with cold reset. + * + * Returns 0 on success, non-zero value on failure + */ +static int ufshcd_dme_reset(struct ufs_hba *hba) +{ + struct uic_command uic_cmd = {0}; + int ret; + + uic_cmd.command = UIC_CMD_DME_RESET; + + ret = ufshcd_send_uic_cmd(hba, &uic_cmd); + if (ret) + dev_err(hba->dev, + "dme-reset: error code %d\n", ret); + + return ret; +} + +/** + * ufshcd_dme_enable - UIC command for DME_ENABLE + * @hba: per adapter instance + * + * DME_ENABLE command is issued in order to enable UniPro stack. + * + * Returns 0 on success, non-zero value on failure + */ +static int ufshcd_dme_enable(struct ufs_hba *hba) +{ + struct uic_command uic_cmd = {0}; + int ret; + + uic_cmd.command = UIC_CMD_DME_ENABLE; + + ret = ufshcd_send_uic_cmd(hba, &uic_cmd); + if (ret) + dev_err(hba->dev, + "dme-reset: error code %d\n", ret); + + return ret; +} static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba) { @@ -4022,7 +4068,7 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep) } /** - * ufshcd_hba_enable - initialize the controller + * ufshcd_hba_execute_hce - initialize the controller * @hba: per adapter instance * * The controller resets itself and controller firmware initialization @@ -4031,7 +4077,7 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba, bool can_sleep) * * Returns 0 on success, non-zero value on failure */ -static int ufshcd_hba_enable(struct ufs_hba *hba) +static int ufshcd_hba_execute_hce(struct ufs_hba *hba) { int retry; @@ -4086,6 +4132,31 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) return 0; } +static int ufshcd_hba_enable(struct ufs_hba *hba) +{ + int ret; + + if (hba->quirks & UFSHCI_QUIRK_BROKEN_HCE) { + ufshcd_set_link_off(hba); + ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE); + + /* enable UIC related interrupts */ + ufshcd_enable_intr(hba, UFSHCD_UIC_MASK); + ret = ufshcd_dme_reset(hba); + if (!ret) { + ret = ufshcd_dme_enable(hba); + if (!ret) + ufshcd_vops_hce_enable_notify(hba, POST_CHANGE); + if (ret) + dev_err(hba->dev, + "Host controller enable failed with non-hce\n"); + } + } else { + ret = ufshcd_hba_execute_hce(hba); + } + + return ret; +} static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) { int tx_lanes, i, err = 0; diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index d8c922d5bbee..a39cdb51ab41 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -606,6 +606,11 @@ struct ufs_hba { */ #define UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR 0x200 + /* + * This quirks needs to be enabled if host controller cannot be + * enabled via HCE register. + */ + #define UFSHCI_QUIRK_BROKEN_HCE 0x400 unsigned int quirks; /* Deviations from standard UFSHCI spec. */ /* Device deviations from standard UFS device spec. */ -- cgit v1.2.3 From 0d846e703dc8b77d31cc71e0bd8d004f187eac30 Mon Sep 17 00:00:00 2001 From: Alim Akhtar Date: Sun, 6 May 2018 15:44:18 +0530 Subject: scsi: ufs: make ufshcd_config_pwr_mode of non-static func This makes ufshcd_config_pwr_mode non-static so that other vendors like exynos can use it. Signed-off-by: Seungwon Jeon Signed-off-by: Alim Akhtar Reviewed-by: Avri Altman Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 5 ++--- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index bfb902380cf0..134e99e9f789 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -233,8 +233,6 @@ static void ufshcd_suspend_clkscaling(struct ufs_hba *hba); static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba); static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up); static irqreturn_t ufshcd_intr(int irq, void *__hba); -static int ufshcd_config_pwr_mode(struct ufs_hba *hba, - struct ufs_pa_layer_attr *desired_pwr_mode); static int ufshcd_change_power_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode); static inline bool ufshcd_valid_tag(struct ufs_hba *hba, int tag) @@ -3933,7 +3931,7 @@ static int ufshcd_change_power_mode(struct ufs_hba *hba, * @hba: per-adapter instance * @desired_pwr_mode: desired power configuration */ -static int ufshcd_config_pwr_mode(struct ufs_hba *hba, +int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode) { struct ufs_pa_layer_attr final_params = { 0 }; @@ -3951,6 +3949,7 @@ static int ufshcd_config_pwr_mode(struct ufs_hba *hba, return ret; } +EXPORT_SYMBOL_GPL(ufshcd_config_pwr_mode); /** * ufshcd_complete_dev_init() - checks device readiness diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index a39cdb51ab41..53121fd6c287 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -805,6 +805,8 @@ extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); +extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, + struct ufs_pa_layer_attr *desired_pwr_mode); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 -- cgit v1.2.3 From 7eee4b921822addfb67c2ced5772f003bb083520 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 9 May 2018 23:10:45 +0800 Subject: scsi: hisi_sas: relocate smp sg map Currently we use DQ lock to protect delivery of DQ entry one by one. To optimise to allow more than one slot to be built for a single DQ in parallel, we need to remove the DQ lock when preparing slots, prior to delivery. To achieve this, we rearrange the slot build order to ensure that once we allocate a slot for a task, we do cannot fail to deliver the task. In this patch, we rearrange the slot building for SMP tasks to ensure that sg mapping part (which can fail) happens before we allocate the slot in the DQ. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 56 ++++++++++++++++++++++++++-------- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 33 ++------------------ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 35 ++------------------- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 33 ++------------------ 4 files changed, 51 insertions(+), 106 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index ff5b8d7de1d1..0ce7c717b201 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -319,7 +319,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq struct hisi_sas_cmd_hdr *cmd_hdr_base; struct asd_sas_port *sas_port = device->port; struct device *dev = hisi_hba->dev; - int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx; + int dlvry_queue_slot, dlvry_queue, rc, slot_idx; + int n_elem = 0, n_elem_req = 0, n_elem_resp = 0; unsigned long flags; if (!sas_port) { @@ -358,6 +359,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq } if (!sas_protocol_ata(task->task_proto)) { + unsigned int req_len, resp_len; + if (task->num_scatter) { n_elem = dma_map_sg(dev, task->scatter, task->num_scatter, task->data_dir); @@ -365,6 +368,29 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq rc = -ENOMEM; goto prep_out; } + } else if (task->task_proto & SAS_PROTOCOL_SMP) { + n_elem_req = dma_map_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); + if (!n_elem_req) { + rc = -ENOMEM; + goto prep_out; + } + req_len = sg_dma_len(&task->smp_task.smp_req); + if (req_len & 0x3) { + rc = -EINVAL; + goto err_out_dma_unmap; + } + n_elem_resp = dma_map_sg(dev, &task->smp_task.smp_resp, + 1, DMA_FROM_DEVICE); + if (!n_elem_req) { + rc = -ENOMEM; + goto err_out_dma_unmap; + } + resp_len = sg_dma_len(&task->smp_task.smp_resp); + if (resp_len & 0x3) { + rc = -EINVAL; + goto err_out_dma_unmap; + } } } else n_elem = task->num_scatter; @@ -375,11 +401,9 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq device); else rc = hisi_sas_slot_index_alloc(hisi_hba, &slot_idx); - if (rc) { - spin_unlock_irqrestore(&hisi_hba->lock, flags); - goto err_out; - } spin_unlock_irqrestore(&hisi_hba->lock, flags); + if (rc) + goto err_out_dma_unmap; rc = hisi_hba->hw->get_free_slot(hisi_hba, dq); if (rc) @@ -458,14 +482,22 @@ err_out_tag: spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_index_free(hisi_hba, slot_idx); spin_unlock_irqrestore(&hisi_hba->lock, flags); -err_out: - dev_err(dev, "task prep: failed[%d]!\n", rc); - if (!sas_protocol_ata(task->task_proto)) - if (n_elem) - dma_unmap_sg(dev, task->scatter, - task->num_scatter, - task->data_dir); +err_out_dma_unmap: + if (!sas_protocol_ata(task->task_proto)) { + if (task->num_scatter) { + dma_unmap_sg(dev, task->scatter, task->num_scatter, + task->data_dir); + } else if (task->task_proto & SAS_PROTOCOL_SMP) { + if (n_elem_req) + dma_unmap_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); + if (n_elem_resp) + dma_unmap_sg(dev, &task->smp_task.smp_resp, + 1, DMA_FROM_DEVICE); + } + } prep_out: + dev_err(dev, "task prep: failed[%d]!\n", rc); return rc; } diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 84a0ccc4daf5..3769d70c9861 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -974,38 +974,17 @@ static int prep_smp_v1_hw(struct hisi_hba *hisi_hba, struct sas_task *task = slot->task; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; struct domain_device *device = task->dev; - struct device *dev = hisi_hba->dev; struct hisi_sas_port *port = slot->port; - struct scatterlist *sg_req, *sg_resp; + struct scatterlist *sg_req; struct hisi_sas_device *sas_dev = device->lldd_dev; dma_addr_t req_dma_addr; - unsigned int req_len, resp_len; - int elem, rc; + unsigned int req_len; - /* - * DMA-map SMP request, response buffers - */ /* req */ sg_req = &task->smp_task.smp_req; - elem = dma_map_sg(dev, sg_req, 1, DMA_TO_DEVICE); - if (!elem) - return -ENOMEM; req_len = sg_dma_len(sg_req); req_dma_addr = sg_dma_address(sg_req); - /* resp */ - sg_resp = &task->smp_task.smp_resp; - elem = dma_map_sg(dev, sg_resp, 1, DMA_FROM_DEVICE); - if (!elem) { - rc = -ENOMEM; - goto err_out_req; - } - resp_len = sg_dma_len(sg_resp); - if ((req_len & 0x3) || (resp_len & 0x3)) { - rc = -EINVAL; - goto err_out_resp; - } - /* create header */ /* dw0 */ hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) | @@ -1027,14 +1006,6 @@ static int prep_smp_v1_hw(struct hisi_hba *hisi_hba, hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); return 0; - -err_out_resp: - dma_unmap_sg(dev, &slot->task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); -err_out_req: - dma_unmap_sg(dev, &slot->task->smp_task.smp_req, 1, - DMA_TO_DEVICE); - return rc; } static int prep_ssp_v1_hw(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 9e687319b8bc..ac0a0b2c72c5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1721,37 +1721,16 @@ static int prep_smp_v2_hw(struct hisi_hba *hisi_hba, struct sas_task *task = slot->task; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; struct domain_device *device = task->dev; - struct device *dev = hisi_hba->dev; struct hisi_sas_port *port = slot->port; - struct scatterlist *sg_req, *sg_resp; + struct scatterlist *sg_req; struct hisi_sas_device *sas_dev = device->lldd_dev; dma_addr_t req_dma_addr; - unsigned int req_len, resp_len; - int elem, rc; + unsigned int req_len; - /* - * DMA-map SMP request, response buffers - */ /* req */ sg_req = &task->smp_task.smp_req; - elem = dma_map_sg(dev, sg_req, 1, DMA_TO_DEVICE); - if (!elem) - return -ENOMEM; - req_len = sg_dma_len(sg_req); req_dma_addr = sg_dma_address(sg_req); - - /* resp */ - sg_resp = &task->smp_task.smp_resp; - elem = dma_map_sg(dev, sg_resp, 1, DMA_FROM_DEVICE); - if (!elem) { - rc = -ENOMEM; - goto err_out_req; - } - resp_len = sg_dma_len(sg_resp); - if ((req_len & 0x3) || (resp_len & 0x3)) { - rc = -EINVAL; - goto err_out_resp; - } + req_len = sg_dma_len(&task->smp_task.smp_req); /* create header */ /* dw0 */ @@ -1775,14 +1754,6 @@ static int prep_smp_v2_hw(struct hisi_hba *hisi_hba, hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); return 0; - -err_out_resp: - dma_unmap_sg(dev, &slot->task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); -err_out_req: - dma_unmap_sg(dev, &slot->task->smp_task.smp_req, 1, - DMA_TO_DEVICE); - return rc; } static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 492c3beea3d5..cea5354c184f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -986,38 +986,17 @@ static int prep_smp_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task = slot->task; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; struct domain_device *device = task->dev; - struct device *dev = hisi_hba->dev; struct hisi_sas_port *port = slot->port; - struct scatterlist *sg_req, *sg_resp; + struct scatterlist *sg_req; struct hisi_sas_device *sas_dev = device->lldd_dev; dma_addr_t req_dma_addr; - unsigned int req_len, resp_len; - int elem, rc; + unsigned int req_len; - /* - * DMA-map SMP request, response buffers - */ /* req */ sg_req = &task->smp_task.smp_req; - elem = dma_map_sg(dev, sg_req, 1, DMA_TO_DEVICE); - if (!elem) - return -ENOMEM; req_len = sg_dma_len(sg_req); req_dma_addr = sg_dma_address(sg_req); - /* resp */ - sg_resp = &task->smp_task.smp_resp; - elem = dma_map_sg(dev, sg_resp, 1, DMA_FROM_DEVICE); - if (!elem) { - rc = -ENOMEM; - goto err_out_req; - } - resp_len = sg_dma_len(sg_resp); - if ((req_len & 0x3) || (resp_len & 0x3)) { - rc = -EINVAL; - goto err_out_resp; - } - /* create header */ /* dw0 */ hdr->dw0 = cpu_to_le32((port->id << CMD_HDR_PORT_OFF) | @@ -1040,14 +1019,6 @@ static int prep_smp_v3_hw(struct hisi_hba *hisi_hba, hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); return 0; - -err_out_resp: - dma_unmap_sg(dev, &slot->task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); -err_out_req: - dma_unmap_sg(dev, &slot->task->smp_task.smp_req, 1, - DMA_TO_DEVICE); - return rc; } static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, -- cgit v1.2.3 From a2b3820bddfbffcfbf0e8170e77de65d8b8def98 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 9 May 2018 23:10:46 +0800 Subject: scsi: hisi_sas: make return type of prep functions void Since the task prep functions now should not fail, adjust the return types to void. In addition, some checks in the task prep functions are relocated to the main module; this is specifically the check for the number of elements in an sg list exceeded the HW SGE limit. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 8 +++--- drivers/scsi/hisi_sas/hisi_sas_main.c | 45 ++++++++++++++-------------------- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 28 +++++---------------- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 45 +++++++++------------------------- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 43 +++++++++----------------------- 5 files changed, 51 insertions(+), 118 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 44105389f2df..3a1caa04407e 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -214,14 +214,14 @@ struct hisi_sas_hw { void (*sl_notify)(struct hisi_hba *hisi_hba, int phy_no); int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq); void (*start_delivery)(struct hisi_sas_dq *dq); - int (*prep_ssp)(struct hisi_hba *hisi_hba, + void (*prep_ssp)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int is_tmf, struct hisi_sas_tmf_task *tmf); - int (*prep_smp)(struct hisi_hba *hisi_hba, + void (*prep_smp)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); - int (*prep_stp)(struct hisi_hba *hisi_hba, + void (*prep_stp)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); - int (*prep_abort)(struct hisi_hba *hisi_hba, + void (*prep_abort)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int device_id, int abort_flag, int tag_to_abort); int (*slot_complete)(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 0ce7c717b201..2772e920572d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -243,30 +243,30 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, } EXPORT_SYMBOL_GPL(hisi_sas_slot_task_free); -static int hisi_sas_task_prep_smp(struct hisi_hba *hisi_hba, +static void hisi_sas_task_prep_smp(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { - return hisi_hba->hw->prep_smp(hisi_hba, slot); + hisi_hba->hw->prep_smp(hisi_hba, slot); } -static int hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba, +static void hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int is_tmf, struct hisi_sas_tmf_task *tmf) { - return hisi_hba->hw->prep_ssp(hisi_hba, slot, is_tmf, tmf); + hisi_hba->hw->prep_ssp(hisi_hba, slot, is_tmf, tmf); } -static int hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba, +static void hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { - return hisi_hba->hw->prep_stp(hisi_hba, slot); + hisi_hba->hw->prep_stp(hisi_hba, slot); } -static int hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba, +static void hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int device_id, int abort_flag, int tag_to_abort) { - return hisi_hba->hw->prep_abort(hisi_hba, slot, + hisi_hba->hw->prep_abort(hisi_hba, slot, device_id, abort_flag, tag_to_abort); } @@ -395,6 +395,13 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq } else n_elem = task->num_scatter; + if (n_elem > HISI_SAS_SGE_PAGE_CNT) { + dev_err(dev, "task prep: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT", + n_elem); + rc = -EINVAL; + goto err_out_dma_unmap; + } + spin_lock_irqsave(&hisi_hba->lock, flags); if (hisi_hba->hw->slot_index_alloc) rc = hisi_hba->hw->slot_index_alloc(hisi_hba, &slot_idx, @@ -439,28 +446,22 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq switch (task->task_proto) { case SAS_PROTOCOL_SMP: - rc = hisi_sas_task_prep_smp(hisi_hba, slot); + hisi_sas_task_prep_smp(hisi_hba, slot); break; case SAS_PROTOCOL_SSP: - rc = hisi_sas_task_prep_ssp(hisi_hba, slot, is_tmf, tmf); + hisi_sas_task_prep_ssp(hisi_hba, slot, is_tmf, tmf); break; case SAS_PROTOCOL_SATA: case SAS_PROTOCOL_STP: case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: - rc = hisi_sas_task_prep_ata(hisi_hba, slot); + hisi_sas_task_prep_ata(hisi_hba, slot); break; default: dev_err(dev, "task prep: unknown/unsupported proto (0x%x)\n", task->task_proto); - rc = -EINVAL; break; } - if (rc) { - dev_err(dev, "task prep: rc = 0x%x\n", rc); - goto err_out_buf; - } - spin_lock_irqsave(&hisi_hba->lock, flags); list_add_tail(&slot->entry, &sas_dev->list); spin_unlock_irqrestore(&hisi_hba->lock, flags); @@ -473,9 +474,6 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq return 0; -err_out_buf: - dma_pool_free(hisi_hba->buffer_pool, slot->buf, - slot->buf_dma); err_out_slot_buf: /* Nothing to be done */ err_out_tag: @@ -1554,10 +1552,8 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, memset(hisi_sas_cmd_hdr_addr_mem(slot), 0, HISI_SAS_COMMAND_TABLE_SZ); memset(hisi_sas_status_buf_addr_mem(slot), 0, HISI_SAS_STATUS_BUF_SZ); - rc = hisi_sas_task_prep_abort(hisi_hba, slot, device_id, + hisi_sas_task_prep_abort(hisi_hba, slot, device_id, abort_flag, task_tag); - if (rc) - goto err_out_buf; spin_lock_irqsave(&hisi_hba->lock, flags); list_add_tail(&slot->entry, &sas_dev->list); @@ -1574,9 +1570,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, return 0; -err_out_buf: - dma_pool_free(hisi_hba->buffer_pool, slot->buf, - slot->buf_dma); err_out_tag: spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_index_free(hisi_hba, slot_idx); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 3769d70c9861..7781a011a750 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -935,23 +935,16 @@ static void start_delivery_v1_hw(struct hisi_sas_dq *dq) dq->wr_point); } -static int prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba, +static void prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, struct hisi_sas_cmd_hdr *hdr, struct scatterlist *scatter, int n_elem) { struct hisi_sas_sge_page *sge_page = hisi_sas_sge_addr_mem(slot); - struct device *dev = hisi_hba->dev; struct scatterlist *sg; int i; - if (n_elem > HISI_SAS_SGE_PAGE_CNT) { - dev_err(dev, "prd err: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT", - n_elem); - return -EINVAL; - } - for_each_sg(scatter, sg, n_elem, i) { struct hisi_sas_sge *entry = &sge_page->sge[i]; @@ -964,11 +957,9 @@ static int prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba, hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot)); hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF); - - return 0; } -static int prep_smp_v1_hw(struct hisi_hba *hisi_hba, +static void prep_smp_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; @@ -1004,11 +995,9 @@ static int prep_smp_v1_hw(struct hisi_hba *hisi_hba, hdr->cmd_table_addr = cpu_to_le64(req_dma_addr); hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); - - return 0; } -static int prep_ssp_v1_hw(struct hisi_hba *hisi_hba, +static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int is_tmf, struct hisi_sas_tmf_task *tmf) { @@ -1019,7 +1008,7 @@ static int prep_ssp_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_port *port = slot->port; struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; - int has_data = 0, rc, priority = is_tmf; + int has_data = 0, priority = is_tmf; u8 *buf_cmd, fburst = 0; u32 dw1, dw2; @@ -1068,12 +1057,9 @@ static int prep_ssp_v1_hw(struct hisi_hba *hisi_hba, hdr->transfer_tags = cpu_to_le32(slot->idx << CMD_HDR_IPTT_OFF); - if (has_data) { - rc = prep_prd_sge_v1_hw(hisi_hba, slot, hdr, task->scatter, + if (has_data) + prep_prd_sge_v1_hw(hisi_hba, slot, hdr, task->scatter, slot->n_elem); - if (rc) - return rc; - } hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len); hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot)); @@ -1107,8 +1093,6 @@ static int prep_ssp_v1_hw(struct hisi_hba *hisi_hba, break; } } - - return 0; } /* by default, task resp is complete */ diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index ac0a0b2c72c5..9f77fd8223f7 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1682,23 +1682,16 @@ static void start_delivery_v2_hw(struct hisi_sas_dq *dq) dq->wr_point); } -static int prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba, +static void prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, struct hisi_sas_cmd_hdr *hdr, struct scatterlist *scatter, int n_elem) { struct hisi_sas_sge_page *sge_page = hisi_sas_sge_addr_mem(slot); - struct device *dev = hisi_hba->dev; struct scatterlist *sg; int i; - if (n_elem > HISI_SAS_SGE_PAGE_CNT) { - dev_err(dev, "prd err: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT", - n_elem); - return -EINVAL; - } - for_each_sg(scatter, sg, n_elem, i) { struct hisi_sas_sge *entry = &sge_page->sge[i]; @@ -1711,11 +1704,9 @@ static int prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba, hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot)); hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF); - - return 0; } -static int prep_smp_v2_hw(struct hisi_hba *hisi_hba, +static void prep_smp_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; @@ -1752,11 +1743,9 @@ static int prep_smp_v2_hw(struct hisi_hba *hisi_hba, hdr->cmd_table_addr = cpu_to_le64(req_dma_addr); hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); - - return 0; } -static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba, +static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int is_tmf, struct hisi_sas_tmf_task *tmf) { @@ -1767,7 +1756,7 @@ static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_port *port = slot->port; struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; - int has_data = 0, rc, priority = is_tmf; + int has_data = 0, priority = is_tmf; u8 *buf_cmd; u32 dw1 = 0, dw2 = 0; @@ -1809,12 +1798,9 @@ static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba, hdr->transfer_tags = cpu_to_le32(slot->idx); - if (has_data) { - rc = prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter, + if (has_data) + prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter, slot->n_elem); - if (rc) - return rc; - } hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len); hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot)); @@ -1843,8 +1829,6 @@ static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba, break; } } - - return 0; } #define TRANS_TX_ERR 0 @@ -2519,7 +2503,7 @@ out: return sts; } -static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, +static void prep_ata_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; @@ -2530,7 +2514,7 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, struct asd_sas_port *sas_port = device->port; struct hisi_sas_port *port = to_hisi_sas_port(sas_port); u8 *buf_cmd; - int has_data = 0, rc = 0, hdr_tag = 0; + int has_data = 0, hdr_tag = 0; u32 dw1 = 0, dw2 = 0; /* create header */ @@ -2578,12 +2562,9 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, /* dw3 */ hdr->transfer_tags = cpu_to_le32(slot->idx); - if (has_data) { - rc = prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter, + if (has_data) + prep_prd_sge_v2_hw(hisi_hba, slot, hdr, task->scatter, slot->n_elem); - if (rc) - return rc; - } hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len); hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot)); @@ -2595,8 +2576,6 @@ static int prep_ata_v2_hw(struct hisi_hba *hisi_hba, task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */ /* fill in command FIS */ memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis)); - - return 0; } static void hisi_sas_internal_abort_quirk_timeout(struct timer_list *t) @@ -2633,7 +2612,7 @@ static void hisi_sas_internal_abort_quirk_timeout(struct timer_list *t) } } -static int prep_abort_v2_hw(struct hisi_hba *hisi_hba, +static void prep_abort_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int device_id, int abort_flag, int tag_to_abort) { @@ -2661,8 +2640,6 @@ static int prep_abort_v2_hw(struct hisi_hba *hisi_hba, /* dw7 */ hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF); hdr->transfer_tags = cpu_to_le32(slot->idx); - - return 0; } static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index cea5354c184f..8cd13744a090 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -859,23 +859,16 @@ static void start_delivery_v3_hw(struct hisi_sas_dq *dq) dq->wr_point); } -static int prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba, +static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, struct hisi_sas_cmd_hdr *hdr, struct scatterlist *scatter, int n_elem) { struct hisi_sas_sge_page *sge_page = hisi_sas_sge_addr_mem(slot); - struct device *dev = hisi_hba->dev; struct scatterlist *sg; int i; - if (n_elem > HISI_SAS_SGE_PAGE_CNT) { - dev_err(dev, "prd err: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT", - n_elem); - return -EINVAL; - } - for_each_sg(scatter, sg, n_elem, i) { struct hisi_sas_sge *entry = &sge_page->sge[i]; @@ -888,11 +881,9 @@ static int prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba, hdr->prd_table_addr = cpu_to_le64(hisi_sas_sge_addr_dma(slot)); hdr->sg_len = cpu_to_le32(n_elem << CMD_HDR_DATA_SGL_LEN_OFF); - - return 0; } -static int prep_ssp_v3_hw(struct hisi_hba *hisi_hba, +static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int is_tmf, struct hisi_sas_tmf_task *tmf) { @@ -903,7 +894,7 @@ static int prep_ssp_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_port *port = slot->port; struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; - int has_data = 0, rc, priority = is_tmf; + int has_data = 0, priority = is_tmf; u8 *buf_cmd; u32 dw1 = 0, dw2 = 0; @@ -944,12 +935,9 @@ static int prep_ssp_v3_hw(struct hisi_hba *hisi_hba, hdr->dw2 = cpu_to_le32(dw2); hdr->transfer_tags = cpu_to_le32(slot->idx); - if (has_data) { - rc = prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter, + if (has_data) + prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter, slot->n_elem); - if (rc) - return rc; - } hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len); hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot)); @@ -976,11 +964,9 @@ static int prep_ssp_v3_hw(struct hisi_hba *hisi_hba, break; } } - - return 0; } -static int prep_smp_v3_hw(struct hisi_hba *hisi_hba, +static void prep_smp_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; @@ -1018,10 +1004,9 @@ static int prep_smp_v3_hw(struct hisi_hba *hisi_hba, hdr->cmd_table_addr = cpu_to_le64(req_dma_addr); hdr->sts_buffer_addr = cpu_to_le64(hisi_sas_status_buf_addr_dma(slot)); - return 0; } -static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, +static void prep_ata_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; @@ -1032,7 +1017,7 @@ static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, struct asd_sas_port *sas_port = device->port; struct hisi_sas_port *port = to_hisi_sas_port(sas_port); u8 *buf_cmd; - int has_data = 0, rc = 0, hdr_tag = 0; + int has_data = 0, hdr_tag = 0; u32 dw1 = 0, dw2 = 0; hdr->dw0 = cpu_to_le32(port->id << CMD_HDR_PORT_OFF); @@ -1081,12 +1066,9 @@ static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, /* dw3 */ hdr->transfer_tags = cpu_to_le32(slot->idx); - if (has_data) { - rc = prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter, + if (has_data) + prep_prd_sge_v3_hw(hisi_hba, slot, hdr, task->scatter, slot->n_elem); - if (rc) - return rc; - } hdr->data_transfer_len = cpu_to_le32(task->total_xfer_len); hdr->cmd_table_addr = cpu_to_le64(hisi_sas_cmd_hdr_addr_dma(slot)); @@ -1098,11 +1080,9 @@ static int prep_ata_v3_hw(struct hisi_hba *hisi_hba, task->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */ /* fill in command FIS */ memcpy(buf_cmd, &task->ata_task.fis, sizeof(struct host_to_dev_fis)); - - return 0; } -static int prep_abort_v3_hw(struct hisi_hba *hisi_hba, +static void prep_abort_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot, int device_id, int abort_flag, int tag_to_abort) { @@ -1127,7 +1107,6 @@ static int prep_abort_v3_hw(struct hisi_hba *hisi_hba, hdr->dw7 = cpu_to_le32(tag_to_abort << CMD_HDR_ABORT_IPTT_OFF); hdr->transfer_tags = cpu_to_le32(slot->idx); - return 0; } static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) -- cgit v1.2.3 From 3de0026dad6b8e83d8a699aef92638c50ba966f7 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 9 May 2018 23:10:47 +0800 Subject: scsi: hisi_sas: allocate slot buffer earlier Currently we allocate the slot's memory buffer after allocating the DQ slot. To aid DQ lockout reduction, and allow slots to be built in parallel, move this step (which can fail) prior to allocating the slot. Also a stray spin_unlock_irqrestore() is removed from internal task exec function. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 55 ++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 2772e920572d..58cbe1f0e0b6 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -412,14 +412,22 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq if (rc) goto err_out_dma_unmap; + slot = &hisi_hba->slot_info[slot_idx]; + memset(slot, 0, sizeof(struct hisi_sas_slot)); + + slot->buf = dma_pool_alloc(hisi_hba->buffer_pool, + GFP_ATOMIC, &slot->buf_dma); + if (!slot->buf) { + rc = -ENOMEM; + goto err_out_tag; + } + rc = hisi_hba->hw->get_free_slot(hisi_hba, dq); if (rc) - goto err_out_tag; + goto err_out_buf; dlvry_queue = dq->id; dlvry_queue_slot = dq->wr_point; - slot = &hisi_hba->slot_info[slot_idx]; - memset(slot, 0, sizeof(struct hisi_sas_slot)); slot->idx = slot_idx; slot->n_elem = n_elem; @@ -434,12 +442,6 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq task->lldd_task = slot; INIT_WORK(&slot->abort_slot, hisi_sas_slot_abort); - slot->buf = dma_pool_alloc(hisi_hba->buffer_pool, - GFP_ATOMIC, &slot->buf_dma); - if (!slot->buf) { - rc = -ENOMEM; - goto err_out_slot_buf; - } memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr)); memset(hisi_sas_cmd_hdr_addr_mem(slot), 0, HISI_SAS_COMMAND_TABLE_SZ); memset(hisi_sas_status_buf_addr_mem(slot), 0, HISI_SAS_STATUS_BUF_SZ); @@ -474,8 +476,9 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq return 0; -err_out_slot_buf: - /* Nothing to be done */ +err_out_buf: + dma_pool_free(hisi_hba->buffer_pool, slot->buf, + slot->buf_dma); err_out_tag: spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_index_free(hisi_hba, slot_idx); @@ -1519,17 +1522,26 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, } spin_unlock_irqrestore(&hisi_hba->lock, flags); + slot = &hisi_hba->slot_info[slot_idx]; + memset(slot, 0, sizeof(struct hisi_sas_slot)); + + slot->buf = dma_pool_alloc(hisi_hba->buffer_pool, + GFP_ATOMIC, &slot->buf_dma); + if (!slot->buf) { + rc = -ENOMEM; + goto err_out_tag; + } spin_lock_irqsave(&dq->lock, flags_dq); rc = hisi_hba->hw->get_free_slot(hisi_hba, dq); - if (rc) - goto err_out_tag; + if (rc) { + rc = -ENOMEM; + spin_unlock_irqrestore(&dq->lock, flags_dq); + goto err_out_buf; + } dlvry_queue = dq->id; dlvry_queue_slot = dq->wr_point; - slot = &hisi_hba->slot_info[slot_idx]; - memset(slot, 0, sizeof(struct hisi_sas_slot)); - slot->idx = slot_idx; slot->n_elem = n_elem; slot->dlvry_queue = dlvry_queue; @@ -1541,13 +1553,6 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, slot->is_internal = true; task->lldd_task = slot; - slot->buf = dma_pool_alloc(hisi_hba->buffer_pool, - GFP_ATOMIC, &slot->buf_dma); - if (!slot->buf) { - rc = -ENOMEM; - goto err_out_tag; - } - memset(slot->cmd_hdr, 0, sizeof(struct hisi_sas_cmd_hdr)); memset(hisi_sas_cmd_hdr_addr_mem(slot), 0, HISI_SAS_COMMAND_TABLE_SZ); memset(hisi_sas_status_buf_addr_mem(slot), 0, HISI_SAS_STATUS_BUF_SZ); @@ -1570,11 +1575,13 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, return 0; +err_out_buf: + dma_pool_free(hisi_hba->buffer_pool, slot->buf, + slot->buf_dma); err_out_tag: spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_index_free(hisi_hba, slot_idx); spin_unlock_irqrestore(&hisi_hba->lock, flags); - spin_unlock_irqrestore(&dq->lock, flags_dq); err_out: dev_err(dev, "internal abort task prep: failed[%d]!\n", rc); -- cgit v1.2.3 From fa222db0b036899cd4020a380568699ffb1de08d Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 9 May 2018 23:10:48 +0800 Subject: scsi: hisi_sas: Don't lock DQ for complete task sending Currently we lock the DQ to protect whole delivery process. So this stops us building slots for the same queue in parallel, and can affect performance. To optimise it, only lock the DQ during special periods, specifically when allocating a slot from the DQ and when delivering a slot to the HW. This approach is now safe, thanks to the previous patches to ensure that we always deliver a slot to the HW once allocated. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 4 ++- drivers/scsi/hisi_sas/hisi_sas_main.c | 49 ++++++++++++++++++++-------------- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 27 ++++++++++++++----- drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 29 +++++++++++++++----- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 28 ++++++++++++++----- 5 files changed, 96 insertions(+), 41 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 3a1caa04407e..52fc709dd862 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -161,7 +161,7 @@ struct hisi_sas_cq { struct hisi_sas_dq { struct hisi_hba *hisi_hba; - struct hisi_sas_slot *slot_prep; + struct list_head list; spinlock_t lock; int wr_point; int id; @@ -181,6 +181,7 @@ struct hisi_sas_device { struct hisi_sas_slot { struct list_head entry; + struct list_head delivery; struct sas_task *task; struct hisi_sas_port *port; u64 n_elem; @@ -190,6 +191,7 @@ struct hisi_sas_slot { int cmplt_queue_slot; int idx; int abort; + int ready; void *buf; dma_addr_t buf_dma; void *cmd_hdr; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 58cbe1f0e0b6..bf374a795962 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -307,9 +307,9 @@ out: task->task_done(task); } -static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq - *dq, int is_tmf, struct hisi_sas_tmf_task *tmf, - int *pass) +static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq *dq, + int is_tmf, struct hisi_sas_tmf_task *tmf, + int *pass) { struct hisi_hba *hisi_hba = dq->hisi_hba; struct domain_device *device = task->dev; @@ -321,7 +321,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq struct device *dev = hisi_hba->dev; int dlvry_queue_slot, dlvry_queue, rc, slot_idx; int n_elem = 0, n_elem_req = 0, n_elem_resp = 0; - unsigned long flags; + unsigned long flags, flags_dq; + int wr_q_index; if (!sas_port) { struct task_status_struct *ts = &task->task_status; @@ -422,12 +423,18 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq goto err_out_tag; } - rc = hisi_hba->hw->get_free_slot(hisi_hba, dq); - if (rc) + spin_lock_irqsave(&dq->lock, flags_dq); + wr_q_index = hisi_hba->hw->get_free_slot(hisi_hba, dq); + if (wr_q_index < 0) { + spin_unlock_irqrestore(&dq->lock, flags_dq); goto err_out_buf; + } + + list_add_tail(&slot->delivery, &dq->list); + spin_unlock_irqrestore(&dq->lock, flags_dq); dlvry_queue = dq->id; - dlvry_queue_slot = dq->wr_point; + dlvry_queue_slot = wr_q_index; slot->idx = slot_idx; slot->n_elem = n_elem; @@ -471,8 +478,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq task->task_state_flags |= SAS_TASK_AT_INITIATOR; spin_unlock_irqrestore(&task->task_state_lock, flags); - dq->slot_prep = slot; ++(*pass); + slot->ready = 1; return 0; @@ -518,11 +525,11 @@ static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags, return -EINVAL; /* protect task_prep and start_delivery sequence */ - spin_lock_irqsave(&dq->lock, flags); rc = hisi_sas_task_prep(task, dq, is_tmf, tmf, &pass); if (rc) dev_err(dev, "task exec: failed[%d]!\n", rc); + spin_lock_irqsave(&dq->lock, flags); if (likely(pass)) hisi_hba->hw->start_delivery(dq); spin_unlock_irqrestore(&dq->lock, flags); @@ -1503,7 +1510,8 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, struct hisi_sas_cmd_hdr *cmd_hdr_base; struct hisi_sas_dq *dq = sas_dev->dq; int dlvry_queue_slot, dlvry_queue, n_elem = 0, rc, slot_idx; - unsigned long flags, flags_dq; + unsigned long flags, flags_dq = 0; + int wr_q_index; if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) return -EINVAL; @@ -1531,16 +1539,18 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, rc = -ENOMEM; goto err_out_tag; } + spin_lock_irqsave(&dq->lock, flags_dq); - rc = hisi_hba->hw->get_free_slot(hisi_hba, dq); - if (rc) { - rc = -ENOMEM; + wr_q_index = hisi_hba->hw->get_free_slot(hisi_hba, dq); + if (wr_q_index < 0) { spin_unlock_irqrestore(&dq->lock, flags_dq); goto err_out_buf; } + list_add_tail(&slot->delivery, &dq->list); + spin_unlock_irqrestore(&dq->lock, flags_dq); dlvry_queue = dq->id; - dlvry_queue_slot = dq->wr_point; + dlvry_queue_slot = wr_q_index; slot->idx = slot_idx; slot->n_elem = n_elem; @@ -1560,18 +1570,16 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, hisi_sas_task_prep_abort(hisi_hba, slot, device_id, abort_flag, task_tag); - spin_lock_irqsave(&hisi_hba->lock, flags); - list_add_tail(&slot->entry, &sas_dev->list); - spin_unlock_irqrestore(&hisi_hba->lock, flags); spin_lock_irqsave(&task->task_state_lock, flags); task->task_state_flags |= SAS_TASK_AT_INITIATOR; spin_unlock_irqrestore(&task->task_state_lock, flags); - dq->slot_prep = slot; - + slot->ready = 1; /* send abort command to the chip */ + spin_lock_irqsave(&dq->lock, flags); + list_add_tail(&slot->entry, &sas_dev->list); hisi_hba->hw->start_delivery(dq); - spin_unlock_irqrestore(&dq->lock, flags_dq); + spin_unlock_irqrestore(&dq->lock, flags); return 0; @@ -1856,6 +1864,7 @@ int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost) /* Delivery queue structure */ spin_lock_init(&dq->lock); + INIT_LIST_HEAD(&dq->list); dq->id = i; dq->hisi_hba = hisi_hba; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 7781a011a750..abe175fddb86 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -921,18 +921,33 @@ get_free_slot_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq) return -EAGAIN; } - return 0; + dq->wr_point = (dq->wr_point + 1) % HISI_SAS_QUEUE_SLOTS; + + return w; } +/* DQ lock must be taken here */ static void start_delivery_v1_hw(struct hisi_sas_dq *dq) { struct hisi_hba *hisi_hba = dq->hisi_hba; - int dlvry_queue = dq->slot_prep->dlvry_queue; - int dlvry_queue_slot = dq->slot_prep->dlvry_queue_slot; + struct hisi_sas_slot *s, *s1; + struct list_head *dq_list; + int dlvry_queue = dq->id; + int wp, count = 0; + + dq_list = &dq->list; + list_for_each_entry_safe(s, s1, &dq->list, delivery) { + if (!s->ready) + break; + count++; + wp = (s->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS; + list_del(&s->delivery); + } + + if (!count) + return; - dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; - hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - dq->wr_point); + hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp); } static void prep_prd_sge_v1_hw(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 9f77fd8223f7..911bb76a4d0a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1663,23 +1663,38 @@ get_free_slot_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq) r = hisi_sas_read32_relaxed(hisi_hba, DLVRY_Q_0_RD_PTR + (queue * 0x14)); if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) { - dev_warn(dev, "full queue=%d r=%d w=%d\n\n", + dev_warn(dev, "full queue=%d r=%d w=%d\n", queue, r, w); return -EAGAIN; } - return 0; + dq->wr_point = (dq->wr_point + 1) % HISI_SAS_QUEUE_SLOTS; + + return w; } +/* DQ lock must be taken here */ static void start_delivery_v2_hw(struct hisi_sas_dq *dq) { struct hisi_hba *hisi_hba = dq->hisi_hba; - int dlvry_queue = dq->slot_prep->dlvry_queue; - int dlvry_queue_slot = dq->slot_prep->dlvry_queue_slot; + struct hisi_sas_slot *s, *s1; + struct list_head *dq_list; + int dlvry_queue = dq->id; + int wp, count = 0; + + dq_list = &dq->list; + list_for_each_entry_safe(s, s1, &dq->list, delivery) { + if (!s->ready) + break; + count++; + wp = (s->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS; + list_del(&s->delivery); + } + + if (!count) + return; - dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; - hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - dq->wr_point); + hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp); } static void prep_prd_sge_v2_hw(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 8cd13744a090..56f1046b244c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -840,23 +840,37 @@ get_free_slot_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq) r = hisi_sas_read32_relaxed(hisi_hba, DLVRY_Q_0_RD_PTR + (queue * 0x14)); if (r == (w+1) % HISI_SAS_QUEUE_SLOTS) { - dev_warn(dev, "full queue=%d r=%d w=%d\n\n", + dev_warn(dev, "full queue=%d r=%d w=%d\n", queue, r, w); return -EAGAIN; } - return 0; + dq->wr_point = (dq->wr_point + 1) % HISI_SAS_QUEUE_SLOTS; + + return w; } static void start_delivery_v3_hw(struct hisi_sas_dq *dq) { struct hisi_hba *hisi_hba = dq->hisi_hba; - int dlvry_queue = dq->slot_prep->dlvry_queue; - int dlvry_queue_slot = dq->slot_prep->dlvry_queue_slot; + struct hisi_sas_slot *s, *s1; + struct list_head *dq_list; + int dlvry_queue = dq->id; + int wp, count = 0; + + dq_list = &dq->list; + list_for_each_entry_safe(s, s1, &dq->list, delivery) { + if (!s->ready) + break; + count++; + wp = (s->dlvry_queue_slot + 1) % HISI_SAS_QUEUE_SLOTS; + list_del(&s->delivery); + } + + if (!count) + return; - dq->wr_point = ++dlvry_queue_slot % HISI_SAS_QUEUE_SLOTS; - hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), - dq->wr_point); + hisi_sas_write32(hisi_hba, DLVRY_Q_0_WR_PTR + (dlvry_queue * 0x14), wp); } static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba, -- cgit v1.2.3 From e85d93b21267fff56110e3a258b9fad8adbfbce3 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Wed, 9 May 2018 23:10:49 +0800 Subject: scsi: hisi_sas: Use device lock to protect slot alloc/free The IPTT of a slot is unique, and we currently use hisi_hba lock to protect it. Now slot is managed on hisi_sas_device.list, so use DQ lock to protect for allocating and freeing the slot. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 56 ++++++++++------------------------ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 3 -- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index bf374a795962..a451625b8253 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -214,6 +214,8 @@ static void hisi_sas_slot_index_init(struct hisi_hba *hisi_hba) void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, struct hisi_sas_slot *slot) { + struct hisi_sas_dq *dq = &hisi_hba->dq[slot->dlvry_queue]; + unsigned long flags; if (task) { struct device *dev = hisi_hba->dev; @@ -233,11 +235,15 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, if (slot->buf) dma_pool_free(hisi_hba->buffer_pool, slot->buf, slot->buf_dma); + spin_lock_irqsave(&dq->lock, flags); list_del_init(&slot->entry); + spin_unlock_irqrestore(&dq->lock, flags); slot->buf = NULL; slot->task = NULL; slot->port = NULL; + spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_index_free(hisi_hba, slot->idx); + spin_unlock_irqrestore(&hisi_hba->lock, flags); /* slot memory is fully zeroed when it is reused */ } @@ -286,7 +292,6 @@ static void hisi_sas_slot_abort(struct work_struct *work) struct scsi_lun lun; struct device *dev = hisi_hba->dev; int tag = abort_slot->idx; - unsigned long flags; if (!(task->task_proto & SAS_PROTOCOL_SSP)) { dev_err(dev, "cannot abort slot for non-ssp task\n"); @@ -300,9 +305,7 @@ static void hisi_sas_slot_abort(struct work_struct *work) hisi_sas_debug_issue_ssp_tmf(task->dev, lun.scsi_lun, &tmf_task); out: /* Do cleanup for this task */ - spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_slot_task_free(hisi_hba, task, abort_slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); if (task->task_done) task->task_done(task); } @@ -471,9 +474,9 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq *dq, break; } - spin_lock_irqsave(&hisi_hba->lock, flags); + spin_lock_irqsave(&dq->lock, flags); list_add_tail(&slot->entry, &sas_dev->list); - spin_unlock_irqrestore(&hisi_hba->lock, flags); + spin_unlock_irqrestore(&dq->lock, flags); spin_lock_irqsave(&task->task_state_lock, flags); task->task_state_flags |= SAS_TASK_AT_INITIATOR; spin_unlock_irqrestore(&task->task_state_lock, flags); @@ -1047,7 +1050,6 @@ static int hisi_sas_softreset_ata_disk(struct domain_device *device) struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; int s = sizeof(struct host_to_dev_fis); - unsigned long flags; ata_for_each_link(link, ap, EDGE) { int pmp = sata_srst_pmp(link); @@ -1072,11 +1074,8 @@ static int hisi_sas_softreset_ata_disk(struct domain_device *device) dev_err(dev, "ata disk reset failed\n"); } - if (rc == TMF_RESP_FUNC_COMPLETE) { - spin_lock_irqsave(&hisi_hba->lock, flags); + if (rc == TMF_RESP_FUNC_COMPLETE) hisi_sas_release_task(hisi_hba, device); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } return rc; } @@ -1173,7 +1172,6 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) struct device *dev = hisi_hba->dev; struct Scsi_Host *shost = hisi_hba->shost; u32 old_state, state; - unsigned long flags; int rc; if (!hisi_hba->hw->soft_reset) @@ -1197,9 +1195,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) scsi_unblock_requests(shost); goto out; } - spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_release_tasks(hisi_hba); - spin_unlock_irqrestore(&hisi_hba->lock, flags); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); @@ -1274,11 +1270,8 @@ static int hisi_sas_abort_task(struct sas_task *task) * will have already been completed */ if (rc == TMF_RESP_FUNC_COMPLETE && rc2 != TMF_RESP_FUNC_SUCC) { - if (task->lldd_task) { - spin_lock_irqsave(&hisi_hba->lock, flags); + if (task->lldd_task) hisi_sas_do_release_task(hisi_hba, task, slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } } } else if (task->task_proto & SAS_PROTOCOL_SATA || task->task_proto & SAS_PROTOCOL_STP) { @@ -1300,11 +1293,8 @@ static int hisi_sas_abort_task(struct sas_task *task) rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_CMD, tag); if (((rc < 0) || (rc == TMF_RESP_FUNC_FAILED)) && - task->lldd_task) { - spin_lock_irqsave(&hisi_hba->lock, flags); + task->lldd_task) hisi_sas_do_release_task(hisi_hba, task, slot); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } } out: @@ -1319,7 +1309,6 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) struct device *dev = hisi_hba->dev; struct hisi_sas_tmf_task tmf_task; int rc = TMF_RESP_FUNC_FAILED; - unsigned long flags; rc = hisi_sas_internal_task_abort(hisi_hba, device, HISI_SAS_INT_ABT_DEV, 0); @@ -1332,11 +1321,8 @@ static int hisi_sas_abort_task_set(struct domain_device *device, u8 *lun) tmf_task.tmf = TMF_ABORT_TASK_SET; rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); - if (rc == TMF_RESP_FUNC_COMPLETE) { - spin_lock_irqsave(&hisi_hba->lock, flags); + if (rc == TMF_RESP_FUNC_COMPLETE) hisi_sas_release_task(hisi_hba, device); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } return rc; } @@ -1369,7 +1355,6 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; int rc = TMF_RESP_FUNC_FAILED; - unsigned long flags; if (sas_dev->dev_status != HISI_SAS_DEV_EH) return TMF_RESP_FUNC_FAILED; @@ -1385,11 +1370,9 @@ static int hisi_sas_I_T_nexus_reset(struct domain_device *device) rc = hisi_sas_debug_I_T_nexus_reset(device); - if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV)) { - spin_lock_irqsave(&hisi_hba->lock, flags); + if ((rc == TMF_RESP_FUNC_COMPLETE) || (rc == -ENODEV)) hisi_sas_release_task(hisi_hba, device); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } + return rc; } @@ -1398,7 +1381,6 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) struct hisi_sas_device *sas_dev = device->lldd_dev; struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct device *dev = hisi_hba->dev; - unsigned long flags; int rc = TMF_RESP_FUNC_FAILED; sas_dev->dev_status = HISI_SAS_DEV_EH; @@ -1418,11 +1400,8 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) rc = sas_phy_reset(phy, 1); - if (rc == 0) { - spin_lock_irqsave(&hisi_hba->lock, flags); + if (rc == 0) hisi_sas_release_task(hisi_hba, device); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } sas_put_local_phy(phy); } else { struct hisi_sas_tmf_task tmf_task = { .tmf = TMF_LU_RESET }; @@ -1436,11 +1415,8 @@ static int hisi_sas_lu_reset(struct domain_device *device, u8 *lun) hisi_sas_dereg_device(hisi_hba, device); rc = hisi_sas_debug_issue_ssp_tmf(device, lun, &tmf_task); - if (rc == TMF_RESP_FUNC_COMPLETE) { - spin_lock_irqsave(&hisi_hba->lock, flags); + if (rc == TMF_RESP_FUNC_COMPLETE) hisi_sas_release_task(hisi_hba, device); - spin_unlock_irqrestore(&hisi_hba->lock, flags); - } } out: if (rc != TMF_RESP_FUNC_COMPLETE) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 56f1046b244c..c013673ee8ac 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2373,7 +2373,6 @@ static int hisi_sas_v3_suspend(struct pci_dev *pdev, pm_message_t state) u32 device_state, status; int rc; u32 reg_val; - unsigned long flags; if (!pdev->pm_cap) { dev_err(dev, "PCI PM not supported\n"); @@ -2418,9 +2417,7 @@ static int hisi_sas_v3_suspend(struct pci_dev *pdev, pm_message_t state) pci_disable_device(pdev); pci_set_power_state(pdev, device_state); - spin_lock_irqsave(&hisi_hba->lock, flags); hisi_sas_release_tasks(hisi_hba); - spin_unlock_irqrestore(&hisi_hba->lock, flags); sas_suspend_ha(sha); return 0; -- cgit v1.2.3 From 2f6bca202b78e2b63a62b46ff5dc26f300785e23 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Wed, 9 May 2018 23:10:50 +0800 Subject: scsi: hisi_sas: add check of device in hisi_sas_task_exec() Currently we don't check that device is not gone before dereferencing its elements in the function hisi_sas_task_exec() (specifically, the DQ pointer). This patch fixes this issue by filling in the DQ pointer in hisi_sas_task_prep() after we check that the device pointer is still safe to reference. [mkp: typo] Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index a451625b8253..39f694eb7b00 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -310,12 +310,13 @@ out: task->task_done(task); } -static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq *dq, +static int hisi_sas_task_prep(struct sas_task *task, + struct hisi_sas_dq **dq_pointer, int is_tmf, struct hisi_sas_tmf_task *tmf, int *pass) { - struct hisi_hba *hisi_hba = dq->hisi_hba; struct domain_device *device = task->dev; + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct hisi_sas_device *sas_dev = device->lldd_dev; struct hisi_sas_port *port; struct hisi_sas_slot *slot; @@ -323,8 +324,9 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq *dq, struct asd_sas_port *sas_port = device->port; struct device *dev = hisi_hba->dev; int dlvry_queue_slot, dlvry_queue, rc, slot_idx; - int n_elem = 0, n_elem_req = 0, n_elem_resp = 0; + int n_elem = 0, n_elem_req = 0, n_elem_resp = 0; unsigned long flags, flags_dq; + struct hisi_sas_dq *dq; int wr_q_index; if (!sas_port) { @@ -352,6 +354,8 @@ static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq *dq, return -ECOMM; } + *dq_pointer = dq = sas_dev->dq; + port = to_hisi_sas_port(sas_port); if (port && !port->port_attached) { dev_info(dev, "task prep: %s port%d not attach device\n", @@ -520,22 +524,21 @@ static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags, unsigned long flags; struct hisi_hba *hisi_hba = dev_to_hisi_hba(task->dev); struct device *dev = hisi_hba->dev; - struct domain_device *device = task->dev; - struct hisi_sas_device *sas_dev = device->lldd_dev; - struct hisi_sas_dq *dq = sas_dev->dq; + struct hisi_sas_dq *dq = NULL; if (unlikely(test_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags))) return -EINVAL; /* protect task_prep and start_delivery sequence */ - rc = hisi_sas_task_prep(task, dq, is_tmf, tmf, &pass); + rc = hisi_sas_task_prep(task, &dq, is_tmf, tmf, &pass); if (rc) dev_err(dev, "task exec: failed[%d]!\n", rc); - spin_lock_irqsave(&dq->lock, flags); - if (likely(pass)) + if (likely(pass)) { + spin_lock_irqsave(&dq->lock, flags); hisi_hba->hw->start_delivery(dq); - spin_unlock_irqrestore(&dq->lock, flags); + spin_unlock_irqrestore(&dq->lock, flags); + } return rc; } -- cgit v1.2.3 From e0f76ad13085b730091256d7a2fbe3555f0686df Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 11 May 2018 14:04:46 -0500 Subject: scsi: cxlflash: Yield to active send threads The following Oops may be encountered if the device is reset, i.e. EEH recovery, while there is heavy I/O traffic: 59:mon> t [c000200db64bb680] c008000009264c40 cxlflash_queuecommand+0x3b8/0x500 [cxlflash] [c000200db64bb770] c00000000090d3b0 scsi_dispatch_cmd+0x130/0x2f0 [c000200db64bb7f0] c00000000090fdd8 scsi_request_fn+0x3c8/0x8d0 [c000200db64bb900] c00000000067f528 __blk_run_queue+0x68/0xb0 [c000200db64bb930] c00000000067ab80 __elv_add_request+0x140/0x3c0 [c000200db64bb9b0] c00000000068daac blk_execute_rq_nowait+0xec/0x1a0 [c000200db64bba00] c00000000068dbb0 blk_execute_rq+0x50/0xe0 [c000200db64bba50] c0000000006b2040 sg_io+0x1f0/0x520 [c000200db64bbaf0] c0000000006b2e94 scsi_cmd_ioctl+0x534/0x610 [c000200db64bbc20] c000000000926208 sd_ioctl+0x118/0x280 [c000200db64bbcc0] c00000000069f7ac blkdev_ioctl+0x7fc/0xe30 [c000200db64bbd20] c000000000439204 block_ioctl+0x84/0xa0 [c000200db64bbd40] c0000000003f8514 do_vfs_ioctl+0xd4/0xa00 [c000200db64bbde0] c0000000003f8f04 SyS_ioctl+0xc4/0x130 [c000200db64bbe30] c00000000000b184 system_call+0x58/0x6c When there is no room to send the I/O request, the cached room is refreshed by reading the memory mapped command room value from the AFU. The AFU register mapping is refreshed during a reset, creating a race condition that can lead to the Oops above. During a device reset, the AFU should not be unmapped until all the active send threads quiesce. An atomic counter, cmds_active, is currently used to track internal AFU commands and quiesce during reset. This same counter can also be used for the active send threads. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index a24d7e6e51c1..dad2be6c29c3 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -616,6 +616,7 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp) rc = 0; goto out; default: + atomic_inc(&afu->cmds_active); break; } @@ -641,6 +642,7 @@ static int cxlflash_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scp) memcpy(cmd->rcb.cdb, scp->cmnd, sizeof(cmd->rcb.cdb)); rc = afu->send_cmd(afu, cmd); + atomic_dec(&afu->cmds_active); out: return rc; } -- cgit v1.2.3 From d58188c306a010d32a250ae532c3daaae8c57346 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 11 May 2018 14:05:08 -0500 Subject: scsi: cxlflash: Limit the debug logs in the IO path The kernel log can get filled with debug messages from send_cmd_ioarrin() when dynamic debug is enabled for the cxlflash module and there is a lot of legacy I/O traffic. While these messages are necessary to debug issues that involve command tracking, the abundance of data can overwrite other useful data in the log. The best option available is to limit the messages that should serve most of the common use cases. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index dad2be6c29c3..cf0b407f6ebb 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -339,8 +339,8 @@ static int send_cmd_ioarrin(struct afu *afu, struct afu_cmd *cmd) writeq_be((u64)&cmd->rcb, &hwq->host_map->ioarrin); out: spin_unlock_irqrestore(&hwq->hsq_slock, lock_flags); - dev_dbg(dev, "%s: cmd=%p len=%u ea=%016llx rc=%d\n", __func__, - cmd, cmd->rcb.data_len, cmd->rcb.data_ea, rc); + dev_dbg_ratelimited(dev, "%s: cmd=%p len=%u ea=%016llx rc=%d\n", + __func__, cmd, cmd->rcb.data_len, cmd->rcb.data_ea, rc); return rc; } -- cgit v1.2.3 From 32a9ae415b8a4258140312f91c71324950d9eba4 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 11 May 2018 14:05:22 -0500 Subject: scsi: cxlflash: Acquire semaphore before invoking ioctl services When a superpipe process that makes use of virtual LUNs is terminated or killed abruptly, there is a possibility that the cxlflash driver could hang and deprive other operations on the adapter. The release fop registered to be invoked on a context close, detaches every LUN associated with the context. The underlying service to detach the LUN assumes it has been called with the read semaphore held, and releases the semaphore before any operation that could be time consuming. When invoked without holding the read semaphore, an opportunity is created for the semaphore's count to become negative when it is temporarily released during one of these potential lengthy operations. This negative count results in subsequent acquisition attempts taking forever, leading to the hang. To support the current design point of holding the semaphore on the ioctl() paths, the release fop should acquire it before invoking any ioctl services. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/superpipe.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index 04a3bf9dc85f..5ba6e625666d 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -988,6 +988,10 @@ static int cxlflash_disk_detach(struct scsi_device *sdev, * theoretically never occur), every call into this routine results * in a complete freeing of a context. * + * Detaching the LUN is typically an ioctl() operation and the underlying + * code assumes that ioctl_rwsem has been acquired as a reader. To support + * that design point, the semaphore is acquired and released around detach. + * * Return: 0 on success */ static int cxlflash_cxl_release(struct inode *inode, struct file *file) @@ -1026,9 +1030,11 @@ static int cxlflash_cxl_release(struct inode *inode, struct file *file) dev_dbg(dev, "%s: close for ctxid=%d\n", __func__, ctxid); + down_read(&cfg->ioctl_rwsem); detach.context_id = ctxi->ctxid; list_for_each_entry_safe(lun_access, t, &ctxi->luns, list) _cxlflash_disk_detach(lun_access->sdev, ctxi, &detach); + up_read(&cfg->ioctl_rwsem); out_release: cfg->ops->fd_release(inode, file); out: -- cgit v1.2.3 From e63a8d886d346e83607e4495ec21f1a0ca6398a2 Mon Sep 17 00:00:00 2001 From: "Matthew R. Ochs" Date: Fri, 11 May 2018 14:05:37 -0500 Subject: scsi: cxlflash: Use local mutex for AFU serialization AFUs can only process a single AFU command at a time. This is enforced with a global mutex situated within the AFU send routine. As this mutex has a global scope, it has the potential to unnecessarily block commands destined for other AFUs. Instead of using a global mutex, transition the mutex to be per-AFU. This will allow commands to only be blocked by siblings of the same AFU. Signed-off-by: Matthew R. Ochs Signed-off-by: Uma Krishnan Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/common.h | 1 + drivers/scsi/cxlflash/main.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/cxlflash/common.h b/drivers/scsi/cxlflash/common.h index 89240b84745c..8908a20065c8 100644 --- a/drivers/scsi/cxlflash/common.h +++ b/drivers/scsi/cxlflash/common.h @@ -240,6 +240,7 @@ struct afu { struct cxlflash_afu_map __iomem *afu_map; /* entire MMIO map */ atomic_t cmds_active; /* Number of currently active AFU commands */ + struct mutex sync_active; /* Mutex to serialize AFU commands */ u64 hb; u32 internal_lun; /* User-desired LUN mode for this AFU */ diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index cf0b407f6ebb..c91e9127fc79 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -2126,6 +2126,7 @@ static int init_afu(struct cxlflash_cfg *cfg) cfg->ops->perst_reloads_same_image(cfg->afu_cookie, true); + mutex_init(&afu->sync_active); afu->num_hwqs = afu->desired_hwqs; for (i = 0; i < afu->num_hwqs; i++) { rc = init_mc(cfg, i); @@ -2309,7 +2310,6 @@ static int send_afu_cmd(struct afu *afu, struct sisl_ioarcb *rcb) char *buf = NULL; int rc = 0; int nretry = 0; - static DEFINE_MUTEX(sync_active); if (cfg->state != STATE_NORMAL) { dev_dbg(dev, "%s: Sync not required state=%u\n", @@ -2317,7 +2317,7 @@ static int send_afu_cmd(struct afu *afu, struct sisl_ioarcb *rcb) return 0; } - mutex_lock(&sync_active); + mutex_lock(&afu->sync_active); atomic_inc(&afu->cmds_active); buf = kmalloc(sizeof(*cmd) + __alignof__(*cmd) - 1, GFP_KERNEL); if (unlikely(!buf)) { @@ -2372,7 +2372,7 @@ retry: *rcb->ioasa = cmd->sa; out: atomic_dec(&afu->cmds_active); - mutex_unlock(&sync_active); + mutex_unlock(&afu->sync_active); kfree(buf); dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); return rc; -- cgit v1.2.3 From 5e12397a9706391543e87cf088fff89fe05f0ad0 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 11 May 2018 14:05:51 -0500 Subject: scsi: cxlflash: Add include guards to backend.h The new header file, backend.h, that was recently added is missing the include guards. This commit adds the guards. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/backend.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/scsi/cxlflash/backend.h b/drivers/scsi/cxlflash/backend.h index bcd8a6c588d3..55638d19c2fd 100644 --- a/drivers/scsi/cxlflash/backend.h +++ b/drivers/scsi/cxlflash/backend.h @@ -12,6 +12,9 @@ * 2 of the License, or (at your option) any later version. */ +#ifndef _CXLFLASH_BACKEND_H +#define _CXLFLASH_BACKEND_H + extern const struct cxlflash_backend_ops cxlflash_cxl_ops; extern const struct cxlflash_backend_ops cxlflash_ocxl_ops; @@ -45,3 +48,5 @@ struct cxlflash_backend_ops { int (*fd_mmap)(struct file *file, struct vm_area_struct *vm); int (*fd_release)(struct inode *inode, struct file *file); }; + +#endif /* _CXLFLASH_BACKEND_H */ -- cgit v1.2.3 From de5d35aff5b34bc91a83adb0bf1788d3d23e92f4 Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 11 May 2018 14:06:05 -0500 Subject: scsi: cxlflash: Abstract hardware dependent assignments As a staging cleanup to support transport specific builds of the cxlflash module, relocate device dependent assignments to header files. This will avoid littering the core driver with conditional compilation logic. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/main.c | 7 ++----- drivers/scsi/cxlflash/main.h | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index c91e9127fc79..cd7dcc578dfb 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -3708,11 +3708,8 @@ static int cxlflash_probe(struct pci_dev *pdev, cfg->init_state = INIT_STATE_NONE; cfg->dev = pdev; cfg->cxl_fops = cxlflash_cxl_fops; - - if (ddv->flags & CXLFLASH_OCXL_DEV) - cfg->ops = &cxlflash_ocxl_ops; - else - cfg->ops = &cxlflash_cxl_ops; + cfg->ops = cxlflash_assign_ops(ddv); + WARN_ON_ONCE(!cfg->ops); /* * Promoted LUNs move to the top of the LUN table. The rest stay on diff --git a/drivers/scsi/cxlflash/main.h b/drivers/scsi/cxlflash/main.h index 6f1be621e473..ed4908e5dffb 100644 --- a/drivers/scsi/cxlflash/main.h +++ b/drivers/scsi/cxlflash/main.h @@ -20,6 +20,8 @@ #include #include +#include "backend.h" + #define CXLFLASH_NAME "cxlflash" #define CXLFLASH_ADAPTER_NAME "IBM POWER CXL Flash Adapter" #define CXLFLASH_MAX_ADAPTERS 32 @@ -100,6 +102,19 @@ struct dev_dependent_vals { #define CXLFLASH_OCXL_DEV 0x0000000000000004ULL }; +static inline const struct cxlflash_backend_ops * +cxlflash_assign_ops(struct dev_dependent_vals *ddv) +{ + const struct cxlflash_backend_ops *ops = NULL; + + if (ddv->flags & CXLFLASH_OCXL_DEV) + ops = &cxlflash_ocxl_ops; + if (!(ddv->flags & CXLFLASH_OCXL_DEV)) + ops = &cxlflash_cxl_ops; + + return ops; +} + struct asyc_intr_info { u64 status; char *desc; -- cgit v1.2.3 From cd43c221bb5eb5a6400a62ae44a9979c8fc6c87e Mon Sep 17 00:00:00 2001 From: Uma Krishnan Date: Fri, 11 May 2018 14:06:19 -0500 Subject: scsi: cxlflash: Isolate external module dependencies Depending on the underlying transport, cxlflash has a dependency on either the CXL or OCXL drivers, which are enabled via their Kconfig option. Instead of having a module wide dependency on these config options, it is better to isolate the object modules that are dependent on the CXL and OCXL drivers and adjust the module dependencies accordingly. This commit isolates the object files that are dependent on CXL and/or OCXL. The cxl/ocxl fops used in the core driver are tucked under an ifdef to avoid compilation errors. Signed-off-by: Uma Krishnan Acked-by: Matthew R. Ochs Signed-off-by: Martin K. Petersen --- drivers/scsi/cxlflash/Kconfig | 2 +- drivers/scsi/cxlflash/Makefile | 4 +++- drivers/scsi/cxlflash/lunmgt.c | 4 +++- drivers/scsi/cxlflash/main.c | 2 -- drivers/scsi/cxlflash/main.h | 5 +++++ drivers/scsi/cxlflash/superpipe.c | 3 ++- drivers/scsi/cxlflash/vlun.c | 3 ++- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/cxlflash/Kconfig b/drivers/scsi/cxlflash/Kconfig index e2a3a1ba26fa..f1b17e3efb3f 100644 --- a/drivers/scsi/cxlflash/Kconfig +++ b/drivers/scsi/cxlflash/Kconfig @@ -4,7 +4,7 @@ config CXLFLASH tristate "Support for IBM CAPI Flash" - depends on PCI && SCSI && CXL && OCXL && EEH + depends on PCI && SCSI && (CXL || OCXL) && EEH select IRQ_POLL default m help diff --git a/drivers/scsi/cxlflash/Makefile b/drivers/scsi/cxlflash/Makefile index 5124c68f8d88..283377d8f6fb 100644 --- a/drivers/scsi/cxlflash/Makefile +++ b/drivers/scsi/cxlflash/Makefile @@ -1,2 +1,4 @@ obj-$(CONFIG_CXLFLASH) += cxlflash.o -cxlflash-y += main.o superpipe.o lunmgt.o vlun.o cxl_hw.o ocxl_hw.o +cxlflash-y += main.o superpipe.o lunmgt.o vlun.o +cxlflash-$(CONFIG_CXL) += cxl_hw.o +cxlflash-$(CONFIG_OCXL) += ocxl_hw.o diff --git a/drivers/scsi/cxlflash/lunmgt.c b/drivers/scsi/cxlflash/lunmgt.c index 4d232e271af6..edea1255fdab 100644 --- a/drivers/scsi/cxlflash/lunmgt.c +++ b/drivers/scsi/cxlflash/lunmgt.c @@ -12,9 +12,11 @@ * 2 of the License, or (at your option) any later version. */ -#include #include +#include +#include + #include #include diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index cd7dcc578dfb..6637116529aa 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -19,8 +19,6 @@ #include -#include - #include #include #include diff --git a/drivers/scsi/cxlflash/main.h b/drivers/scsi/cxlflash/main.h index ed4908e5dffb..2a3977823812 100644 --- a/drivers/scsi/cxlflash/main.h +++ b/drivers/scsi/cxlflash/main.h @@ -107,10 +107,15 @@ cxlflash_assign_ops(struct dev_dependent_vals *ddv) { const struct cxlflash_backend_ops *ops = NULL; +#ifdef CONFIG_OCXL if (ddv->flags & CXLFLASH_OCXL_DEV) ops = &cxlflash_ocxl_ops; +#endif + +#ifdef CONFIG_CXL if (!(ddv->flags & CXLFLASH_OCXL_DEV)) ops = &cxlflash_cxl_ops; +#endif return ops; } diff --git a/drivers/scsi/cxlflash/superpipe.c b/drivers/scsi/cxlflash/superpipe.c index 5ba6e625666d..e489d89cbb45 100644 --- a/drivers/scsi/cxlflash/superpipe.c +++ b/drivers/scsi/cxlflash/superpipe.c @@ -14,8 +14,9 @@ #include #include +#include +#include #include -#include #include #include diff --git a/drivers/scsi/cxlflash/vlun.c b/drivers/scsi/cxlflash/vlun.c index 5deef57a7834..66e445a17d6c 100644 --- a/drivers/scsi/cxlflash/vlun.c +++ b/drivers/scsi/cxlflash/vlun.c @@ -12,8 +12,9 @@ * 2 of the License, or (at your option) any later version. */ +#include +#include #include -#include #include #include -- cgit v1.2.3 From df30781699f53e4fd4c494c6f7dd16e3d5c21d30 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:43 +0200 Subject: scsi: zfcp: fix missing SCSI trace for result of eh_host_reset_handler For problem determination we need to see whether and why we were successful or not. This allows deduction of scsi_eh escalation. Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : schrh_r SCSI host reset handler result Request ID : 0x0000000000000000 none (invalid) SCSI ID : 0xffffffff none (invalid) SCSI LUN : 0xffffffff none (invalid) SCSI LUN high : 0xffffffff none (invalid) SCSI result : 0x00002002 field re-used for midlayer value: SUCCESS or in other cases: 0x2009 == FAST_IO_FAIL SCSI retries : 0xff none (invalid) SCSI allowed : 0xff none (invalid) SCSI scribble : 0xffffffffffffffff none (invalid) SCSI opcode : ffffffff ffffffff ffffffff ffffffff none (invalid) FCP rsp inf cod: 0xff none (invalid) FCP rsp IU : 00000000 00000000 00000000 00000000 none (invalid) 00000000 00000000 v2.6.35 commit a1dbfddd02d2 ("[SCSI] zfcp: Pass return code from fc_block_scsi_eh to scsi eh") introduced the first return with something other than the previously hardcoded single SUCCESS return path. Signed-off-by: Steffen Maier Fixes: a1dbfddd02d2 ("[SCSI] zfcp: Pass return code from fc_block_scsi_eh to scsi eh") Cc: #2.6.38+ Reviewed-by: Jens Remus Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/s390/scsi/zfcp_ext.h | 2 ++ drivers/s390/scsi/zfcp_scsi.c | 11 ++++++----- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index a8b831000b2d..1e5ea5e4992b 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -643,6 +643,46 @@ void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc, spin_unlock_irqrestore(&dbf->scsi_lock, flags); } +/** + * zfcp_dbf_scsi_eh() - Trace event for special cases of scsi_eh callbacks. + * @tag: Identifier for event. + * @adapter: Pointer to zfcp adapter as context for this event. + * @scsi_id: SCSI ID/target to indicate scope of task management function (TMF). + * @ret: Return value of calling function. + * + * This SCSI trace variant does not depend on any of: + * scsi_cmnd, zfcp_fsf_req, scsi_device. + */ +void zfcp_dbf_scsi_eh(char *tag, struct zfcp_adapter *adapter, + unsigned int scsi_id, int ret) +{ + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_scsi *rec = &dbf->scsi_buf; + unsigned long flags; + static int const level = 1; + + if (unlikely(!debug_level_enabled(adapter->dbf->scsi, level))) + return; + + spin_lock_irqsave(&dbf->scsi_lock, flags); + memset(rec, 0, sizeof(*rec)); + + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_SCSI_CMND; + rec->scsi_result = ret; /* re-use field, int is 4 bytes and fits */ + rec->scsi_retries = ~0; + rec->scsi_allowed = ~0; + rec->fcp_rsp_info = ~0; + rec->scsi_id = scsi_id; + rec->scsi_lun = (u32)ZFCP_DBF_INVALID_LUN; + rec->scsi_lun_64_hi = (u32)(ZFCP_DBF_INVALID_LUN >> 32); + rec->host_scribble = ~0; + memset(rec->scsi_opcode, 0xff, ZFCP_DBF_SCSI_OPCODE); + + debug_event(dbf->scsi, level, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->scsi_lock, flags); +} + static debug_info_t *zfcp_dbf_reg(const char *name, int size, int rec_size) { struct debug_info *d; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index bf8ea4df2bb8..e55f42ce1168 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -49,6 +49,8 @@ extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *, struct zfcp_fsf_req *); +extern void zfcp_dbf_scsi_eh(char *tag, struct zfcp_adapter *adapter, + unsigned int scsi_id, int ret); /* zfcp_erp.c */ extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 4d2ba5682493..a62357f5e8b4 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -323,15 +323,16 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; - int ret; + int ret = SUCCESS, fc_ret; zfcp_erp_adapter_reopen(adapter, 0, "schrh_1"); zfcp_erp_wait(adapter); - ret = fc_block_scsi_eh(scpnt); - if (ret) - return ret; + fc_ret = fc_block_scsi_eh(scpnt); + if (fc_ret) + ret = fc_ret; - return SUCCESS; + zfcp_dbf_scsi_eh("schrh_r", adapter, ~0, ret); + return ret; } struct scsi_transport_template *zfcp_scsi_transport_template; -- cgit v1.2.3 From 81979ae63e872ef650a7197f6ce6590059d37172 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:44 +0200 Subject: scsi: zfcp: fix missing SCSI trace for retry of abort / scsi_eh TMF We already have a SCSI trace for the end of abort and scsi_eh TMF. Due to zfcp_erp_wait() and fc_block_scsi_eh() time can pass between the start of our eh callback and an actual send/recv of an abort / TMF request. In order to see the temporal sequence including any abort / TMF send retries, add a trace before the above two blocking functions. This supports problem determination with scsi_eh and parallel zfcp ERP. No need to explicitly trace the beginning of our eh callback, since we typically can send an abort / TMF and see its HBA response (in the worst case, it's a pseudo response on dismiss all of adapter recovery, e.g. due to an FSF request timeout [fsrth_1] of the abort / TMF). If we cannot send, we now get a trace record for the first "abrt_wt" or "[lt]r_wait" which denotes almost the beginning of the callback. No need to explicitly trace the wakeup after the above two blocking functions because the next retry loop causes another trace in any case and that is sufficient. Example trace records formatted with zfcpdbf from s390-tools: Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : abrt_wt abort, before zfcp_erp_wait() Request ID : 0x0000000000000000 none (invalid) SCSI ID : 0x SCSI LUN : 0x SCSI LUN high : 0x SCSI result : 0x SCSI retries : 0x SCSI allowed : 0x SCSI scribble : 0x SCSI opcode : FCP rsp inf cod: 0x.. none (invalid) FCP rsp IU : ... none (invalid) Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : lr_wait LUN reset, before zfcp_erp_wait() Request ID : 0x0000000000000000 none (invalid) SCSI ID : 0x SCSI LUN : 0x SCSI LUN high : 0x SCSI result : 0x... unrelated SCSI retries : 0x.. unrelated SCSI allowed : 0x.. unrelated SCSI scribble : 0x... unrelated SCSI opcode : ... unrelated FCP rsp inf cod: 0x.. none (invalid) FCP rsp IU : ... none (invalid) Signed-off-by: Steffen Maier Fixes: 63caf367e1c9 ("[SCSI] zfcp: Improve reliability of SCSI eh handlers in zfcp") Fixes: af4de36d911a ("[SCSI] zfcp: Block scsi_eh thread for rport state BLOCKED") Cc: #2.6.38+ Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_scsi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index a62357f5e8b4..4fdb1665b0e6 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -181,6 +181,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt) if (abrt_req) break; + zfcp_dbf_scsi_abort("abrt_wt", scpnt, NULL); zfcp_erp_wait(adapter); ret = fc_block_scsi_eh(scpnt); if (ret) { @@ -277,6 +278,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) if (fsf_req) break; + zfcp_dbf_scsi_devreset("wait", scpnt, tm_flags, NULL); zfcp_erp_wait(adapter); ret = fc_block_scsi_eh(scpnt); if (ret) { -- cgit v1.2.3 From 512857a795cbbda5980efa4cdb3c0b6602330408 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:45 +0200 Subject: scsi: zfcp: fix misleading REC trigger trace where erp_action setup failed If a SCSI device is deleted during scsi_eh host reset, we cannot get a reference to the SCSI device anymore since scsi_device_get returns !=0 by design. Assuming the recovery of adapter and port(s) was successful, zfcp_erp_strategy_followup_success() attempts to trigger a LUN reset for the half-gone SCSI device. Unfortunately, it causes the following confusing trace record which states that zfcp will do a LUN recovery as "ERP need" is ZFCP_ERP_ACTION_REOPEN_LUN == 1 and equals "ERP want". Old example trace record formatted with zfcpdbf from s390-tools: Tag: : ersfs_3 ERP, trigger, unit reopen, port reopen succeeded LUN : 0x WWPN : 0x D_ID : 0x Adapter status : 0x5400050b Port status : 0x54000001 LUN status : 0x40000000 ZFCP_STATUS_COMMON_RUNNING but not ZFCP_STATUS_COMMON_UNBLOCKED as it was closed on close part of adapter reopen ERP want : 0x01 ERP need : 0x01 misleading However, zfcp_erp_setup_act() returns NULL as it cannot get the reference. Hence, zfcp_erp_action_enqueue() takes an early goto out and _NO_ recovery actually happens. We always do want the recovery trigger trace record even if no erp_action could be enqueued as in this case. For other cases where we did not enqueue an erp_action, 'need' has always been zero to indicate this. In order to indicate above goto out, introduce an eyecatcher "flag" to mark the "ERP need" as 'not needed' but still keep the information which erp_action type, that zfcp_erp_required_act() had decided upon, is needed. 0xc_ is chosen to be visibly different from 0x0_ in "ERP want". New example trace record formatted with zfcpdbf from s390-tools: Tag: : ersfs_3 ERP, trigger, unit reopen, port reopen succeeded LUN : 0x WWPN : 0x D_ID : 0x Adapter status : 0x5400050b Port status : 0x54000001 LUN status : 0x40000000 ERP want : 0x01 ERP need : 0xc1 would need LUN ERP, but no action set up ^ Before v2.6.38 commit ae0904f60fab ("[SCSI] zfcp: Redesign of the debug tracing for recovery actions.") we could detect this case because the "erp_action" field in the trace was NULL. The rework removed erp_action as argument and field from the trace. This patch here is for tracing. A fix to allow LUN recovery in the case at hand is a topic for a separate patch. See also commit fdbd1c5e27da ("[SCSI] zfcp: Allow running unit/LUN shutdown without acquiring reference") for a similar case and background info. Signed-off-by: Steffen Maier Fixes: ae0904f60fab ("[SCSI] zfcp: Redesign of the debug tracing for recovery actions.") Cc: #2.6.38+ Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 1d91a32db08e..d9cd25b56cfa 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -35,11 +35,23 @@ enum zfcp_erp_steps { ZFCP_ERP_STEP_LUN_OPENING = 0x2000, }; +/** + * enum zfcp_erp_act_type - Type of ERP action object. + * @ZFCP_ERP_ACTION_REOPEN_LUN: LUN recovery. + * @ZFCP_ERP_ACTION_REOPEN_PORT: Port recovery. + * @ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: Forced port recovery. + * @ZFCP_ERP_ACTION_REOPEN_ADAPTER: Adapter recovery. + * @ZFCP_ERP_ACTION_NONE: Eyecatcher pseudo flag to bitwise or-combine with + * either of the other enum values. + * Used to indicate that an ERP action could not be + * set up despite a detected need for some recovery. + */ enum zfcp_erp_act_type { ZFCP_ERP_ACTION_REOPEN_LUN = 1, ZFCP_ERP_ACTION_REOPEN_PORT = 2, ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3, ZFCP_ERP_ACTION_REOPEN_ADAPTER = 4, + ZFCP_ERP_ACTION_NONE = 0xc0, }; enum zfcp_erp_act_state { @@ -257,8 +269,10 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, goto out; act = zfcp_erp_setup_act(need, act_status, adapter, port, sdev); - if (!act) + if (!act) { + need |= ZFCP_ERP_ACTION_NONE; /* marker for trace */ goto out; + } atomic_or(ZFCP_STATUS_ADAPTER_ERP_PENDING, &adapter->status); ++adapter->erp_total_count; list_add_tail(&act->list, &adapter->erp_ready_head); -- cgit v1.2.3 From 96d9270499471545048ed8a6d7f425a49762283d Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:46 +0200 Subject: scsi: zfcp: fix missing REC trigger trace on terminate_rport_io early return get_device() and its internally used kobject_get() only return NULL if they get passed NULL as argument. zfcp_get_port_by_wwpn() loops over adapter->port_list so the iteration variable port is always non-NULL. Struct device is embedded in struct zfcp_port so &port->dev is always non-NULL. This is the argument to get_device(). However, if we get an fc_rport in terminate_rport_io() for which we cannot find a match within zfcp_get_port_by_wwpn(), the latter can return NULL. v2.6.30 commit 70932935b61e ("[SCSI] zfcp: Fix oops when port disappears") introduced an early return without adding a trace record for this case. Even if we don't need recovery in this case, for debugging we should still see that our callback was invoked originally by scsi_transport_fc. Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : sctrpin SCSI terminate rport I/O, no zfcp port LUN : 0xffffffffffffffff none (invalid) WWPN : 0x WWPN D_ID : 0x N_Port-ID Adapter status : 0x... Port status : 0xffffffff unknown (-1) LUN status : 0x00000000 none (invalid) Ready count : 0x... Running count : 0x... ERP want : 0x03 ZFCP_ERP_ACTION_REOPEN_PORT_FORCED ERP need : 0xc0 ZFCP_ERP_ACTION_NONE Signed-off-by: Steffen Maier Fixes: 70932935b61e ("[SCSI] zfcp: Fix oops when port disappears") Cc: #2.6.38+ Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 20 ++++++++++++++++++++ drivers/s390/scsi/zfcp_ext.h | 3 +++ drivers/s390/scsi/zfcp_scsi.c | 5 +++++ 3 files changed, 28 insertions(+) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index d9cd25b56cfa..3489b1bc9121 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -283,6 +283,26 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, return retval; } +void zfcp_erp_port_forced_no_port_dbf(char *id, struct zfcp_adapter *adapter, + u64 port_name, u32 port_id) +{ + unsigned long flags; + static /* don't waste stack */ struct zfcp_port tmpport; + + write_lock_irqsave(&adapter->erp_lock, flags); + /* Stand-in zfcp port with fields just good enough for + * zfcp_dbf_rec_trig() and zfcp_dbf_set_common(). + * Under lock because tmpport is static. + */ + atomic_set(&tmpport.status, -1); /* unknown */ + tmpport.wwpn = port_name; + tmpport.d_id = port_id; + zfcp_dbf_rec_trig(id, adapter, &tmpport, NULL, + ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, + ZFCP_ERP_ACTION_NONE); + write_unlock_irqrestore(&adapter->erp_lock, flags); +} + static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask, char *id) { diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index e55f42ce1168..3299bd345076 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -55,6 +55,9 @@ extern void zfcp_dbf_scsi_eh(char *tag, struct zfcp_adapter *adapter, /* zfcp_erp.c */ extern void zfcp_erp_set_adapter_status(struct zfcp_adapter *, u32); extern void zfcp_erp_clear_adapter_status(struct zfcp_adapter *, u32); +extern void zfcp_erp_port_forced_no_port_dbf(char *id, + struct zfcp_adapter *adapter, + u64 port_name, u32 port_id); extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, char *); extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, char *); extern void zfcp_erp_set_port_status(struct zfcp_port *, u32); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 4fdb1665b0e6..478e7ef9ea2f 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -605,6 +605,11 @@ static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport) if (port) { zfcp_erp_port_forced_reopen(port, 0, "sctrpi1"); put_device(&port->dev); + } else { + zfcp_erp_port_forced_no_port_dbf( + "sctrpin", adapter, + rport->port_name /* zfcp_scsi_rport_register */, + rport->port_id /* zfcp_scsi_rport_register */); } } -- cgit v1.2.3 From d70aab55924b44f213fec2b900b095430b33eec6 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:47 +0200 Subject: scsi: zfcp: fix missing REC trigger trace on terminate_rport_io for ERP_FAILED For problem determination we always want to see when we were invoked on the terminate_rport_io callback whether we perform something or not. Temporal event sequence of interest with a long fast_io_fail_tmo of 27 sec: loose remote port t workqueue [s] zfcp_q_ IRQ zfcperp === ================== =================== ============================ 0 recv RSCN q p.test_link_work block rport start fast_io_fail_tmo send ADISC ELS 4 recv ADISC fail block zfcp_port port forced reopen send open port 12 recv open port fail q p.gid_pn_work zfcp_erp_wakeup (zfcp_erp_wait would return) GID_PN fail Before this point, we got a SCSI trace with tag "sctrpi1" on fast_io_fail, e.g. with the typical 5 sec setting. port.status |= ERP_FAILED If fast_io_fail_tmo triggers after this point, we missed a SCSI trace. workqueue fc_dl_ ================== 27 fc_timeout_fail_rport_io fc_terminate_rport_io zfcp_scsi_terminate_rport_io zfcp_erp_port_forced_reopen _zfcp_erp_port_forced_reopen if (port.status & ERP_FAILED) return; Therefore, write a trace before above early return. Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 ZFCP_DBF_REC_TRIG Tag : sctrpi1 SCSI terminate rport I/O LUN : 0xffffffffffffffff none (invalid) WWPN : 0x D_ID : 0x Adapter status : 0x... Port status : 0x... LUN status : 0x00000000 none (invalid) Ready count : 0x... Running count : 0x... ERP want : 0x03 ZFCP_ERP_ACTION_REOPEN_PORT_FORCED ERP need : 0xe0 ZFCP_ERP_ACTION_FAILED Signed-off-by: Steffen Maier Cc: #2.6.38+ Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 3489b1bc9121..5c368cdfc455 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -42,9 +42,13 @@ enum zfcp_erp_steps { * @ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: Forced port recovery. * @ZFCP_ERP_ACTION_REOPEN_ADAPTER: Adapter recovery. * @ZFCP_ERP_ACTION_NONE: Eyecatcher pseudo flag to bitwise or-combine with - * either of the other enum values. + * either of the first four enum values. * Used to indicate that an ERP action could not be * set up despite a detected need for some recovery. + * @ZFCP_ERP_ACTION_FAILED: Eyecatcher pseudo flag to bitwise or-combine with + * either of the first four enum values. + * Used to indicate that ERP not needed because + * the object has ZFCP_STATUS_COMMON_ERP_FAILED. */ enum zfcp_erp_act_type { ZFCP_ERP_ACTION_REOPEN_LUN = 1, @@ -52,6 +56,7 @@ enum zfcp_erp_act_type { ZFCP_ERP_ACTION_REOPEN_PORT_FORCED = 3, ZFCP_ERP_ACTION_REOPEN_ADAPTER = 4, ZFCP_ERP_ACTION_NONE = 0xc0, + ZFCP_ERP_ACTION_FAILED = 0xe0, }; enum zfcp_erp_act_state { @@ -379,8 +384,12 @@ static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, zfcp_erp_port_block(port, clear); zfcp_scsi_schedule_rport_block(port); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { + zfcp_dbf_rec_trig(id, port->adapter, port, NULL, + ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, + ZFCP_ERP_ACTION_FAILED); return; + } zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, port->adapter, port, NULL, id, 0); -- cgit v1.2.3 From 8c3d20aada70042a39c6a6625be037c1472ca610 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:48 +0200 Subject: scsi: zfcp: fix missing REC trigger trace for all objects in ERP_FAILED That other commit introduced an inconsistency because it would trace on ERP_FAILED for all callers of port forced reopen triggers (not just terminate_rport_io), but it would not trace on ERP_FAILED for all callers of other ERP triggers such as adapter, port regular, LUN. Therefore, generalize that other commit. zfcp_erp_action_enqueue() already had two early outs which re-used the one zfcp_dbf_rec_trig() call. All ERP trigger functions finally run through zfcp_erp_action_enqueue(). So move the special handling for ZFCP_STATUS_COMMON_ERP_FAILED into zfcp_erp_action_enqueue() and add another early out with new trace marker for pseudo ERP need in this case. This removes all early returns from all ERP trigger functions so we always end up at zfcp_dbf_rec_trig(). Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 ZFCP_DBF_REC_TRIG Tag : ....... LUN : 0x... WWPN : 0x... D_ID : 0x... Adapter status : 0x... Port status : 0x... LUN status : 0x... Ready count : 0x... Running count : 0x... ERP want : 0x0. ZFCP_ERP_ACTION_REOPEN_... ERP need : 0xe0 ZFCP_ERP_ACTION_FAILED Signed-off-by: Steffen Maier Cc: #2.6.38+ Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 79 ++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 5c368cdfc455..20fe59300d0e 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -143,6 +143,49 @@ static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter) } } +static int zfcp_erp_handle_failed(int want, struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev) +{ + int need = want; + struct zfcp_scsi_dev *zsdev; + + switch (want) { + case ZFCP_ERP_ACTION_REOPEN_LUN: + zsdev = sdev_to_zfcp(sdev); + if (atomic_read(&zsdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + need = 0; + break; + case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: + if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) + need = 0; + break; + case ZFCP_ERP_ACTION_REOPEN_PORT: + if (atomic_read(&port->status) & + ZFCP_STATUS_COMMON_ERP_FAILED) { + need = 0; + /* ensure propagation of failed status to new devices */ + zfcp_erp_set_port_status( + port, ZFCP_STATUS_COMMON_ERP_FAILED); + } + break; + case ZFCP_ERP_ACTION_REOPEN_ADAPTER: + if (atomic_read(&adapter->status) & + ZFCP_STATUS_COMMON_ERP_FAILED) { + need = 0; + /* ensure propagation of failed status to new devices */ + zfcp_erp_set_adapter_status( + adapter, ZFCP_STATUS_COMMON_ERP_FAILED); + } + break; + default: + need = 0; + break; + } + + return need; +} + static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter, struct zfcp_port *port, struct scsi_device *sdev) @@ -266,6 +309,12 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, int retval = 1, need; struct zfcp_erp_action *act; + need = zfcp_erp_handle_failed(want, adapter, port, sdev); + if (!need) { + need = ZFCP_ERP_ACTION_FAILED; /* marker for trace */ + goto out; + } + if (!adapter->erp_thread) return -EIO; @@ -314,12 +363,6 @@ static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, zfcp_erp_adapter_block(adapter, clear_mask); zfcp_scsi_schedule_rports_block(adapter); - /* ensure propagation of failed status to new devices */ - if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { - zfcp_erp_set_adapter_status(adapter, - ZFCP_STATUS_COMMON_ERP_FAILED); - return -EIO; - } return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, NULL, NULL, id, 0); } @@ -338,12 +381,8 @@ void zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear, char *id) zfcp_scsi_schedule_rports_block(adapter); write_lock_irqsave(&adapter->erp_lock, flags); - if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_ERP_FAILED) - zfcp_erp_set_adapter_status(adapter, - ZFCP_STATUS_COMMON_ERP_FAILED); - else - zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, - NULL, NULL, id, 0); + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, adapter, + NULL, NULL, id, 0); write_unlock_irqrestore(&adapter->erp_lock, flags); } @@ -384,13 +423,6 @@ static void _zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, zfcp_erp_port_block(port, clear); zfcp_scsi_schedule_rport_block(port); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { - zfcp_dbf_rec_trig(id, port->adapter, port, NULL, - ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, - ZFCP_ERP_ACTION_FAILED); - return; - } - zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT_FORCED, port->adapter, port, NULL, id, 0); } @@ -416,12 +448,6 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) zfcp_erp_port_block(port, clear); zfcp_scsi_schedule_rport_block(port); - if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_FAILED) { - /* ensure propagation of failed status to new devices */ - zfcp_erp_set_port_status(port, ZFCP_STATUS_COMMON_ERP_FAILED); - return -EIO; - } - return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, port->adapter, port, NULL, id, 0); } @@ -461,9 +487,6 @@ static void _zfcp_erp_lun_reopen(struct scsi_device *sdev, int clear, char *id, zfcp_erp_lun_block(sdev, clear); - if (atomic_read(&zfcp_sdev->status) & ZFCP_STATUS_COMMON_ERP_FAILED) - return; - zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_LUN, adapter, zfcp_sdev->port, sdev, id, act_status); } -- cgit v1.2.3 From 6a76550841d412330bd86aed3238d1888ba70f0e Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:49 +0200 Subject: scsi: zfcp: fix missing REC trigger trace on enqueue without ERP thread Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 ZFCP_DBF_REC_TRIG Tag : ....... LUN : 0x... WWPN : 0x... D_ID : 0x... Adapter status : 0x... Port status : 0x... LUN status : 0x... Ready count : 0x... Running count : 0x... ERP want : 0x0. ZFCP_ERP_ACTION_REOPEN_... ERP need : 0xc0 ZFCP_ERP_ACTION_NONE Signed-off-by: Steffen Maier Cc: #2.6.38+ Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 20fe59300d0e..69dfb328dba4 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -315,8 +315,11 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, goto out; } - if (!adapter->erp_thread) - return -EIO; + if (!adapter->erp_thread) { + need = ZFCP_ERP_ACTION_NONE; /* marker for trace */ + retval = -EIO; + goto out; + } need = zfcp_erp_required_act(want, adapter, port, sdev); if (!need) -- cgit v1.2.3 From 8221211863750b1afb1f464a264c05383b077a06 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:50 +0200 Subject: scsi: zfcp: decouple SCSI traces for scsi_eh / TMF from scsi_cmnd The SCSI command pointer passed to scsi_eh callbacks is just one arbitrary command of potentially many that are in the eh queue to be processed. The command is only used to indirectly pass the TMF scope in terms of SCSI ID/target and SCSI LUN for LUN reset. Hence, zfcp had filled in SCSI trace record fields which do not really belong to the TMF. This was confusing. Therefore, refactor the TMF tracing to work without SCSI command. Since the FCP channel always requires a valid LUN handle, we use SCSI device as common context for any TMF (even target reset). To make it even clearer, we set all bits to 1 for the fields, which do not belong to the TMF, to indicate that these fields are invalid. The old zfcp_dbf_scsi() became zfcp_dbf_scsi_common() to now handle both SCSI commands and TMFs. The old argument scsi_cmnd is now optional and can be NULL with TMFs. The new argument scsi_device is mandatory to carry context, as well as SCSI ID/target and SCSI LUN in case of TMFs. New example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : [lt]r_.... Request ID : 0x ID of FSF FCP request with TM flag For cases without FSF request: 0x0 for none (invalid) SCSI ID : 0x SCSI ID/target denoting scope SCSI LUN : 0x SCSI LUN denoting scope SCSI LUN high : 0x SCSI LUN denoting scope SCSI result : 0xffffffff none (invalid) SCSI retries : 0xff none (invalid) SCSI allowed : 0xff none (invalid) SCSI scribble : 0xffffffffffffffff none (invalid) SCSI opcode : ffffffff ffffffff ffffffff ffffffff none (invalid) FCP rsp inf cod: 0x00 FCP_RSP info code of TMF FCP rsp IU : 00000000 00000000 00000100 00000000 ext FCP_RSP IU 00000000 00000008 ext FCP_RSP IU FCP rsp IU len : 32 FCP_RSP IU length Payload time : ... FCP rsp IU all : 00000000 00000000 00000100 00000000 full FCP_RSP IU 00000000 00000008 00000000 00000000 full FCP_RSP IU Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 48 ++++++++++++++++++++++++++++--------------- drivers/s390/scsi/zfcp_dbf.h | 21 ++++++++++++------- drivers/s390/scsi/zfcp_ext.h | 5 +++-- drivers/s390/scsi/zfcp_scsi.c | 15 +++++++------- 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 1e5ea5e4992b..bb3373260169 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -578,16 +578,18 @@ void zfcp_dbf_san_in_els(char *tag, struct zfcp_fsf_req *fsf) } /** - * zfcp_dbf_scsi - trace event for scsi commands - * @tag: identifier for event - * @sc: pointer to struct scsi_cmnd - * @fsf: pointer to struct zfcp_fsf_req + * zfcp_dbf_scsi_common() - Common trace event helper for scsi. + * @tag: Identifier for event. + * @level: trace level of event. + * @sdev: Pointer to SCSI device as context for this event. + * @sc: Pointer to SCSI command, or NULL with task management function (TMF). + * @fsf: Pointer to FSF request, or NULL. */ -void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc, - struct zfcp_fsf_req *fsf) +void zfcp_dbf_scsi_common(char *tag, int level, struct scsi_device *sdev, + struct scsi_cmnd *sc, struct zfcp_fsf_req *fsf) { struct zfcp_adapter *adapter = - (struct zfcp_adapter *) sc->device->host->hostdata[0]; + (struct zfcp_adapter *) sdev->host->hostdata[0]; struct zfcp_dbf *dbf = adapter->dbf; struct zfcp_dbf_scsi *rec = &dbf->scsi_buf; struct fcp_resp_with_ext *fcp_rsp; @@ -599,16 +601,28 @@ void zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *sc, memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); rec->id = ZFCP_DBF_SCSI_CMND; - rec->scsi_result = sc->result; - rec->scsi_retries = sc->retries; - rec->scsi_allowed = sc->allowed; - rec->scsi_id = sc->device->id; - rec->scsi_lun = (u32)sc->device->lun; - rec->scsi_lun_64_hi = (u32)(sc->device->lun >> 32); - rec->host_scribble = (unsigned long)sc->host_scribble; - - memcpy(rec->scsi_opcode, sc->cmnd, - min((int)sc->cmd_len, ZFCP_DBF_SCSI_OPCODE)); + if (sc) { + rec->scsi_result = sc->result; + rec->scsi_retries = sc->retries; + rec->scsi_allowed = sc->allowed; + rec->scsi_id = sc->device->id; + rec->scsi_lun = (u32)sc->device->lun; + rec->scsi_lun_64_hi = (u32)(sc->device->lun >> 32); + rec->host_scribble = (unsigned long)sc->host_scribble; + + memcpy(rec->scsi_opcode, sc->cmnd, + min_t(int, sc->cmd_len, ZFCP_DBF_SCSI_OPCODE)); + } else { + rec->scsi_result = ~0; + rec->scsi_retries = ~0; + rec->scsi_allowed = ~0; + rec->scsi_id = sdev->id; + rec->scsi_lun = (u32)sdev->lun; + rec->scsi_lun_64_hi = (u32)(sdev->lun >> 32); + rec->host_scribble = ~0; + + memset(rec->scsi_opcode, 0xff, ZFCP_DBF_SCSI_OPCODE); + } if (fsf) { rec->fsf_req_id = fsf->req_id; diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index e2a973cd2573..d116c07ed77a 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -359,7 +359,7 @@ void _zfcp_dbf_scsi(char *tag, int level, struct scsi_cmnd *scmd, scmd->device->host->hostdata[0]; if (debug_level_enabled(adapter->dbf->scsi, level)) - zfcp_dbf_scsi(tag, level, scmd, req); + zfcp_dbf_scsi_common(tag, level, scmd->device, scmd, req); } /** @@ -402,16 +402,23 @@ void zfcp_dbf_scsi_abort(char *tag, struct scsi_cmnd *scmd, } /** - * zfcp_dbf_scsi_devreset - trace event for Logical Unit or Target Reset - * @tag: tag indicating success or failure of reset operation - * @scmnd: SCSI command which caused this error recovery - * @flag: indicates type of reset (Target Reset, Logical Unit Reset) + * zfcp_dbf_scsi_devreset() - Trace event for Logical Unit or Target Reset. + * @tag: Tag indicating success or failure of reset operation. + * @sdev: Pointer to SCSI device as context for this event. + * @flag: Indicates type of reset (Target Reset, Logical Unit Reset). + * @fsf_req: Pointer to FSF request representing the TMF, or NULL. */ static inline -void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag, +void zfcp_dbf_scsi_devreset(char *tag, struct scsi_device *sdev, u8 flag, struct zfcp_fsf_req *fsf_req) { + struct zfcp_adapter *adapter = (struct zfcp_adapter *) + sdev->host->hostdata[0]; char tmp_tag[ZFCP_DBF_TAG_LEN]; + static int const level = 1; + + if (unlikely(!debug_level_enabled(adapter->dbf->scsi, level))) + return; if (flag == FCP_TMF_TGT_RESET) memcpy(tmp_tag, "tr_", 3); @@ -419,7 +426,7 @@ void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag, memcpy(tmp_tag, "lr_", 3); memcpy(&tmp_tag[3], tag, 4); - _zfcp_dbf_scsi(tmp_tag, 1, scmnd, fsf_req); + zfcp_dbf_scsi_common(tmp_tag, level, sdev, NULL, fsf_req); } /** diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 3299bd345076..78bcd80d0509 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -47,8 +47,9 @@ extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *); extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); -extern void zfcp_dbf_scsi(char *, int, struct scsi_cmnd *, - struct zfcp_fsf_req *); +extern void zfcp_dbf_scsi_common(char *tag, int level, struct scsi_device *sdev, + struct scsi_cmnd *sc, + struct zfcp_fsf_req *fsf); extern void zfcp_dbf_scsi_eh(char *tag, struct zfcp_adapter *adapter, unsigned int scsi_id, int ret); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 478e7ef9ea2f..0afc546b71df 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -267,7 +267,8 @@ static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags) static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) { - struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scpnt->device); + struct scsi_device *sdev = scpnt->device; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; struct zfcp_fsf_req *fsf_req = NULL; int retval = SUCCESS, ret; @@ -278,32 +279,32 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) if (fsf_req) break; - zfcp_dbf_scsi_devreset("wait", scpnt, tm_flags, NULL); + zfcp_dbf_scsi_devreset("wait", sdev, tm_flags, NULL); zfcp_erp_wait(adapter); ret = fc_block_scsi_eh(scpnt); if (ret) { - zfcp_dbf_scsi_devreset("fiof", scpnt, tm_flags, NULL); + zfcp_dbf_scsi_devreset("fiof", sdev, tm_flags, NULL); return ret; } if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) { - zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags, NULL); + zfcp_dbf_scsi_devreset("nres", sdev, tm_flags, NULL); return SUCCESS; } } if (!fsf_req) { - zfcp_dbf_scsi_devreset("reqf", scpnt, tm_flags, NULL); + zfcp_dbf_scsi_devreset("reqf", sdev, tm_flags, NULL); return FAILED; } wait_for_completion(&fsf_req->completion); if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) { - zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags, fsf_req); + zfcp_dbf_scsi_devreset("fail", sdev, tm_flags, fsf_req); retval = FAILED; } else { - zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags, fsf_req); + zfcp_dbf_scsi_devreset("okay", sdev, tm_flags, fsf_req); zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags); } -- cgit v1.2.3 From 266883f2f7d505a56e2b736f80ef5c088c4c5e21 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:51 +0200 Subject: scsi: zfcp: decouple TMF response handler from scsi_cmnd Originally, I planned for TMF handling to have different context data in fsf_req->data depending on the TMF scope in fcp_cmnd->fc_tm_flags: * scsi_device if FCP_TMF_LUN_RESET, * zfcp_port if FCP_TMF_TGT_RESET. However, the FCP channel requires a valid LUN handle so we now use scsi_device as context data with any TMF for the time being. Regular SCSI I/O FCP requests continue using scsi_cmnd as req->data. Hence, the callers of zfcp_fsf_fcp_handler_common() must resolve req->data and pass scsi_device as common context. While at it, remove the detour zfcp_sdev->port->adapter and use the more direct req->adapter as elsewhere in this function already. Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index b12cb81ad8a2..a95070c7cad8 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -2036,10 +2036,14 @@ static void zfcp_fsf_req_trace(struct zfcp_fsf_req *req, struct scsi_cmnd *scsi) sizeof(blktrc)); } -static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) +/** + * zfcp_fsf_fcp_handler_common() - FCP response handler common to I/O and TMF. + * @req: Pointer to FSF request. + * @sdev: Pointer to SCSI device as request context. + */ +static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req, + struct scsi_device *sdev) { - struct scsi_cmnd *scmnd = req->data; - struct scsi_device *sdev = scmnd->device; struct zfcp_scsi_dev *zfcp_sdev; struct fsf_qtcb_header *header = &req->qtcb->header; @@ -2051,7 +2055,7 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) switch (header->fsf_status) { case FSF_HANDLE_MISMATCH: case FSF_PORT_HANDLE_NOT_VALID: - zfcp_erp_adapter_reopen(zfcp_sdev->port->adapter, 0, "fssfch1"); + zfcp_erp_adapter_reopen(req->adapter, 0, "fssfch1"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_FCPLUN_NOT_VALID: @@ -2069,8 +2073,7 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) req->qtcb->bottom.io.data_direction, (unsigned long long)zfcp_scsi_dev_lun(sdev), (unsigned long long)zfcp_sdev->port->wwpn); - zfcp_erp_adapter_shutdown(zfcp_sdev->port->adapter, 0, - "fssfch3"); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fssfch3"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_CMND_LENGTH_NOT_VALID: @@ -2080,8 +2083,7 @@ static void zfcp_fsf_fcp_handler_common(struct zfcp_fsf_req *req) req->qtcb->bottom.io.fcp_cmnd_length, (unsigned long long)zfcp_scsi_dev_lun(sdev), (unsigned long long)zfcp_sdev->port->wwpn); - zfcp_erp_adapter_shutdown(zfcp_sdev->port->adapter, 0, - "fssfch4"); + zfcp_erp_adapter_shutdown(req->adapter, 0, "fssfch4"); req->status |= ZFCP_STATUS_FSFREQ_ERROR; break; case FSF_PORT_BOXED: @@ -2120,7 +2122,7 @@ static void zfcp_fsf_fcp_cmnd_handler(struct zfcp_fsf_req *req) return; } - zfcp_fsf_fcp_handler_common(req); + zfcp_fsf_fcp_handler_common(req, scpnt->device); if (unlikely(req->status & ZFCP_STATUS_FSFREQ_ERROR)) { set_host_byte(scpnt, DID_TRANSPORT_DISRUPTED); @@ -2297,10 +2299,11 @@ out: static void zfcp_fsf_fcp_task_mgmt_handler(struct zfcp_fsf_req *req) { + struct scsi_device *sdev = req->data; struct fcp_resp_with_ext *fcp_rsp; struct fcp_resp_rsp_info *rsp_info; - zfcp_fsf_fcp_handler_common(req); + zfcp_fsf_fcp_handler_common(req, sdev); fcp_rsp = &req->qtcb->bottom.io.fcp_rsp.iu; rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1]; @@ -2341,7 +2344,7 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, goto out; } - req->data = scmnd; + req->data = scmnd->device; req->handler = zfcp_fsf_fcp_task_mgmt_handler; req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; req->qtcb->header.port_handle = zfcp_sdev->port->handle; -- cgit v1.2.3 From e0116c91c7d80c841833296555555daf7470b70e Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:52 +0200 Subject: scsi: zfcp: split FCP_CMND IU setup between SCSI I/O and TMF again This reverts commit 2443c8b23aea ("[SCSI] zfcp: Merge FCP task management setup with regular FCP command setup"), because this introduced a dependency on the unsuitable SCSI command for scsi_eh / TMF. Signed-off-by: Steffen Maier Reviewed-by: Hannes Reinecke Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fc.h | 22 ++++++++++++++-------- drivers/s390/scsi/zfcp_fsf.c | 4 ++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h index 6a397ddaadf0..3cd74729cfb9 100644 --- a/drivers/s390/scsi/zfcp_fc.h +++ b/drivers/s390/scsi/zfcp_fc.h @@ -207,21 +207,14 @@ struct zfcp_fc_wka_ports { * zfcp_fc_scsi_to_fcp - setup FCP command with data from scsi_cmnd * @fcp: fcp_cmnd to setup * @scsi: scsi_cmnd where to get LUN, task attributes/flags and CDB - * @tm: task management flags to setup task management command */ static inline -void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi, - u8 tm_flags) +void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi) { u32 datalen; int_to_scsilun(scsi->device->lun, (struct scsi_lun *) &fcp->fc_lun); - if (unlikely(tm_flags)) { - fcp->fc_tm_flags = tm_flags; - return; - } - fcp->fc_pri_ta = FCP_PTA_SIMPLE; if (scsi->sc_data_direction == DMA_FROM_DEVICE) @@ -240,6 +233,19 @@ void zfcp_fc_scsi_to_fcp(struct fcp_cmnd *fcp, struct scsi_cmnd *scsi, } } +/** + * zfcp_fc_fcp_tm() - Setup FCP command as task management command. + * @fcp: Pointer to FCP_CMND IU to set up. + * @dev: Pointer to SCSI_device where to send the task management command. + * @tm_flags: Task management flags to setup tm command. + */ +static inline +void zfcp_fc_fcp_tm(struct fcp_cmnd *fcp, struct scsi_device *dev, u8 tm_flags) +{ + int_to_scsilun(dev->lun, (struct scsi_lun *) &fcp->fc_lun); + fcp->fc_tm_flags = tm_flags; +} + /** * zfcp_fc_evap_fcp_rsp - evaluate FCP RSP IU and update scsi_cmnd accordingly * @fcp_rsp: FCP RSP IU to evaluate diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index a95070c7cad8..8bc768a01ef5 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -2260,7 +2260,7 @@ int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *scsi_cmnd) BUILD_BUG_ON(sizeof(struct fcp_cmnd) > FSF_FCP_CMND_SIZE); fcp_cmnd = &req->qtcb->bottom.io.fcp_cmnd.iu; - zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd, 0); + zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd); if ((scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) && scsi_prot_sg_count(scsi_cmnd)) { @@ -2355,7 +2355,7 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); fcp_cmnd = &req->qtcb->bottom.io.fcp_cmnd.iu; - zfcp_fc_scsi_to_fcp(fcp_cmnd, scmnd, tm_flags); + zfcp_fc_fcp_tm(fcp_cmnd, scmnd->device, tm_flags); zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT); if (!zfcp_fsf_req_send(req)) -- cgit v1.2.3 From 39abb11aca00f0629a7484ed3b5e22d4b99f8f22 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:53 +0200 Subject: scsi: zfcp: decouple FSF request setup of TMF from scsi_cmnd In zfcp_fsf_fcp_task_mgmt() resolve the still old argument scsi_cmnd into scsi_device very early and only depend on scsi_device and derived objects in the function body. This prepares to later change the function signature replacing the scsi_cmnd argument with scsi_device. Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 8bc768a01ef5..5bc84eaa6948 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -2324,7 +2324,8 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, { struct zfcp_fsf_req *req = NULL; struct fcp_cmnd *fcp_cmnd; - struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(scmnd->device); + struct scsi_device *sdev = scmnd->device; + struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_qdio *qdio = zfcp_sdev->port->adapter->qdio; if (unlikely(!(atomic_read(&zfcp_sdev->status) & @@ -2344,7 +2345,8 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, goto out; } - req->data = scmnd->device; + req->data = sdev; + req->handler = zfcp_fsf_fcp_task_mgmt_handler; req->qtcb->header.lun_handle = zfcp_sdev->lun_handle; req->qtcb->header.port_handle = zfcp_sdev->port->handle; @@ -2355,7 +2357,7 @@ struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, zfcp_qdio_set_sbale_last(qdio, &req->qdio_req); fcp_cmnd = &req->qtcb->bottom.io.fcp_cmnd.iu; - zfcp_fc_fcp_tm(fcp_cmnd, scmnd->device, tm_flags); + zfcp_fc_fcp_tm(fcp_cmnd, sdev, tm_flags); zfcp_fsf_start_timer(req, ZFCP_SCSI_ER_TIMEOUT); if (!zfcp_fsf_req_send(req)) -- cgit v1.2.3 From 26f5fa9d47c12661f282f8009c4a64e449dd36c5 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:54 +0200 Subject: scsi: zfcp: decouple SCSI setup of TMF from scsi_cmnd Actually change the signature of zfcp_fsf_fcp_task_mgmt(). Since it was prepared in the previous patch, we only need to delete a local auto variable which is now the intended argument. Prepare zfcp_fsf_fcp_task_mgmt's caller zfcp_task_mgmt_function() to have its function body only depend on a scsi_device and derived objects. Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_ext.h | 3 ++- drivers/s390/scsi/zfcp_fsf.c | 12 ++++++------ drivers/s390/scsi/zfcp_scsi.c | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 78bcd80d0509..e317c4b513c9 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -123,7 +123,8 @@ extern int zfcp_fsf_send_els(struct zfcp_adapter *, u32, struct zfcp_fsf_ct_els *, unsigned int); extern int zfcp_fsf_fcp_cmnd(struct scsi_cmnd *); extern void zfcp_fsf_req_free(struct zfcp_fsf_req *); -extern struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *, u8); +extern struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_device *sdev, + u8 tm_flags); extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_cmnd(struct scsi_cmnd *); extern void zfcp_fsf_reqid_check(struct zfcp_qdio *, int); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 5bc84eaa6948..d86c3bf71664 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -2314,17 +2314,17 @@ static void zfcp_fsf_fcp_task_mgmt_handler(struct zfcp_fsf_req *req) } /** - * zfcp_fsf_fcp_task_mgmt - send SCSI task management command - * @scmnd: SCSI command to send the task management command for - * @tm_flags: unsigned byte for task management flags - * Returns: on success pointer to struct fsf_req, NULL otherwise + * zfcp_fsf_fcp_task_mgmt() - Send SCSI task management command (TMF). + * @sdev: Pointer to SCSI device to send the task management command to. + * @tm_flags: Unsigned byte for task management flags. + * + * Return: On success pointer to struct zfcp_fsf_req, %NULL otherwise. */ -struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_cmnd *scmnd, +struct zfcp_fsf_req *zfcp_fsf_fcp_task_mgmt(struct scsi_device *sdev, u8 tm_flags) { struct zfcp_fsf_req *req = NULL; struct fcp_cmnd *fcp_cmnd; - struct scsi_device *sdev = scmnd->device; struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_qdio *qdio = zfcp_sdev->port->adapter->qdio; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 0afc546b71df..e77e43a0630a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -275,7 +275,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) int retry = 3; while (retry--) { - fsf_req = zfcp_fsf_fcp_task_mgmt(scpnt, tm_flags); + fsf_req = zfcp_fsf_fcp_task_mgmt(sdev, tm_flags); if (fsf_req) break; -- cgit v1.2.3 From 42afc6527d43fdb8c74a170de9735b07019b104c Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:55 +0200 Subject: scsi: zfcp: decouple TMFs from scsi_cmnd by using fc_block_rport Intentionally retrieve the rport by walking SCSI common code objects rather than zfcp_sdev->port->rport. The latter is used for pairing the calls to fc_remote_port_add() and fc_remote_port_delete(). [see v2.6.31 commit 379d6bf6573e ("[SCSI] zfcp: Add port only once to FC transport class")] zfcp_scsi_rport_register() sets zfcp_port.rport to what fc_remote_port_add() returned. zfcp_scsi_rport_block() sets zfcp_port.rport = NULL after having called fc_remote_port_delete(). Hence, while an rport is blocked (or in any subsequent state due to scsi_transport_fc timeouts such as fast_io_fail_tmo or dev_loss_tmo), zfcp_port.rport is NULL and cannot serve as argument to fc_block_rport(). During zfcp recovery, a just recovered zfcp_port can have the UNBLOCKED status flag, but an async rport unblocking has only started via zfcp_scsi_schedule_rport_register() in zfcp_erp_try_rport_unblock() [see v4.10 commit 6f2ce1c6af37 ("scsi: zfcp: fix rport unblock race with LUN recovery")] in zfcp_erp_action_cleanup(). Now zfcp_erp_wait() can return. This would be sufficient to successfully send a TMF. But the rport can still be blocked and zfcp_port.rport can still be NULL until zfcp_port.rport_work was scheduled and has actually called fc_remote_port_add() and assigned its return value to zfcp_port.rport. We need an unblocked rport for a successful scsi_eh TUR. Similarly, for a zfcp_port which has just lost its UNBLOCKED status flag, the return of zfcp_erp_wait() can race with zfcp_port.rport_work queued by zfcp_scsi_schedule_rport_block(). Therefore we cannot reliably access zfcp_port.rport. However, we'd like to get fc_rport_block()'s opinion on when fast_io_fail_tmo triggered. While we might use flush_work(&port->rport_work) to sync with the work item, we can simply use the other way to get an rport pointer. Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_scsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index e77e43a0630a..e0c5735cf3db 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -270,6 +270,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) struct scsi_device *sdev = scpnt->device; struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; + struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); struct zfcp_fsf_req *fsf_req = NULL; int retval = SUCCESS, ret; int retry = 3; @@ -281,7 +282,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) zfcp_dbf_scsi_devreset("wait", sdev, tm_flags, NULL); zfcp_erp_wait(adapter); - ret = fc_block_scsi_eh(scpnt); + ret = fc_block_rport(rport); if (ret) { zfcp_dbf_scsi_devreset("fiof", sdev, tm_flags, NULL); return ret; -- cgit v1.2.3 From 674595d8519fdf4f78e1a4cceb2130ffa46bcdeb Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:56 +0200 Subject: scsi: zfcp: decouple our scsi_eh callbacks from scsi_cmnd Note: zfcp_scsi_eh_host_reset_handler() will be converted in a later patch. zfcp_scsi_eh_device_reset_handler() now only depends on scsi_device. zfcp_scsi_eh_target_reset_handler() now only depends on scsi_target. All derive other objects from these intended callback arguments. zfcp_scsi_eh_target_reset_handler() is special: The FCP channel requires a valid LUN handle so we try to find ourselves a stand-in scsi_device as suggested by Hannes Reinecke. If it cannot find a stand-in scsi device, trace a record like the following (formatted with zfcpdbf from s390-tools): Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : tr_nosd target reset, no SCSI device Request ID : 0x0000000000000000 none (invalid) SCSI ID : 0x00000000 SCSI ID/target denoting scope SCSI LUN : 0xffffffff none (invalid) SCSI LUN high : 0xffffffff none (invalid) SCSI result : 0x00002003 field re-used for midlayer value: FAILED SCSI retries : 0xff none (invalid) SCSI allowed : 0xff none (invalid) SCSI scribble : 0xffffffffffffffff none (invalid) SCSI opcode : ffffffff ffffffff ffffffff ffffffff none (invalid) FCP rsp inf cod: 0xff none (invalid) FCP rsp IU : 00000000 00000000 00000000 00000000 none (invalid) 00000000 00000000 Actually change the signature of zfcp_task_mgmt_function() used by zfcp_scsi_eh_device_reset_handler() & zfcp_scsi_eh_target_reset_handler(). Since it was prepared in a previous patch, we only need to delete a local auto variable which is now the intended argument. Suggested-by: Hannes Reinecke Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_scsi.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index e0c5735cf3db..fcc832b73960 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -265,9 +265,14 @@ static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags) write_unlock_irqrestore(&adapter->abort_lock, flags); } -static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) +/** + * zfcp_task_mgmt_function() - Synchronously send a task management function. + * @sdev: Pointer to SCSI device to send the task management command to. + * @tm_flags: Task management flags, + * here we only handle %FCP_TMF_TGT_RESET or %FCP_TMF_LUN_RESET. + */ +static int zfcp_task_mgmt_function(struct scsi_device *sdev, u8 tm_flags) { - struct scsi_device *sdev = scpnt->device; struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); @@ -315,12 +320,40 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags) static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) { - return zfcp_task_mgmt_function(scpnt, FCP_TMF_LUN_RESET); + struct scsi_device *sdev = scpnt->device; + + return zfcp_task_mgmt_function(sdev, FCP_TMF_LUN_RESET); } static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt) { - return zfcp_task_mgmt_function(scpnt, FCP_TMF_TGT_RESET); + struct scsi_target *starget = scsi_target(scpnt->device); + struct fc_rport *rport = starget_to_rport(starget); + struct Scsi_Host *shost = rport_to_shost(rport); + struct scsi_device *sdev = NULL, *tmp_sdev; + struct zfcp_adapter *adapter = + (struct zfcp_adapter *)shost->hostdata[0]; + int ret; + + shost_for_each_device(tmp_sdev, shost) { + if (tmp_sdev->id == starget->id) { + sdev = tmp_sdev; + break; + } + } + if (!sdev) { + ret = FAILED; + zfcp_dbf_scsi_eh("tr_nosd", adapter, starget->id, ret); + return ret; + } + + ret = zfcp_task_mgmt_function(sdev, FCP_TMF_TGT_RESET); + + /* release reference from above shost_for_each_device */ + if (sdev) + scsi_device_put(tmp_sdev); + + return ret; } static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) -- cgit v1.2.3 From 5c750d58e9d78987e2bda6b65441e6f6b961a01e Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:57 +0200 Subject: scsi: zfcp: workqueue: set description for port work items with their WWPN as context As a prerequisite, complement commit 3d1cb2059d93 ("workqueue: include workqueue info when printing debug dump of a worker task") to be usable with kernel modules by exporting the symbol set_worker_desc(). Current built-in user was introduced with commit ef3b101925f2 ("writeback: set worker desc to identify writeback workers in task dumps"). Can help distinguishing work items which do not have adapter scope. Description is printed out with task dump for debugging on WARN, BUG, panic, or magic-sysrq [show-task-states(t)]. Example: $ echo 0 >| /sys/bus/ccw/drivers/zfcp/0.0.1880/0x50050763031bd327/failed & $ echo 't' >| /proc/sysrq-trigger $ dmesg sysrq: SysRq : Show State task PC stack pid father ... zfcp_q_0.0.1880 S14640 2165 2 0x02000000 Call Trace: ([<00000000009df464>] __schedule+0xbf4/0xc78) [<00000000009df57c>] schedule+0x94/0xc0 [<0000000000168654>] rescuer_thread+0x33c/0x3a0 [<000000000016f8be>] kthread+0x166/0x178 [<00000000009e71f2>] kernel_thread_starter+0x6/0xc [<00000000009e71ec>] kernel_thread_starter+0x0/0xc no locks held by zfcp_q_0.0.1880/2165. ... kworker/u512:2 D11280 2193 2 0x02000000 Workqueue: zfcp_q_0.0.1880 zfcp_scsi_rport_work [zfcp] (zrpd-50050763031bd327) ^^^^^^^^^^^^^^^^^^^^^ Call Trace: ([<00000000009df464>] __schedule+0xbf4/0xc78) [<00000000009df57c>] schedule+0x94/0xc0 [<00000000009e50c0>] schedule_timeout+0x488/0x4d0 [<00000000001e425c>] msleep+0x5c/0x78 >>test code only<< [<000003ff8008a21e>] zfcp_scsi_rport_work+0xbe/0x100 [zfcp] [<0000000000167154>] process_one_work+0x3b4/0x718 [<000000000016771c>] worker_thread+0x264/0x408 [<000000000016f8be>] kthread+0x166/0x178 [<00000000009e71f2>] kernel_thread_starter+0x6/0xc [<00000000009e71ec>] kernel_thread_starter+0x0/0xc 2 locks held by kworker/u512:2/2193: #0: (name){++++.+}, at: [<0000000000166f4e>] process_one_work+0x1ae/0x718 #1: ((&(&port->rport_work)->work)){+.+.+.}, at: [<0000000000166f4e>] process_one_work+0x1ae/0x718 ... ============================================= Showing busy workqueues and worker pools: workqueue zfcp_q_0.0.1880: flags=0x2000a pwq 512: cpus=0-255 flags=0x4 nice=0 active=1/1 in-flight: 2193:zfcp_scsi_rport_work [zfcp] pool 512: cpus=0-255 flags=0x4 nice=0 hung=0s workers=4 idle: 5 2354 2311 Work items with adapter scope are already identified by the workqueue name "zfcp_q_" and the work item function name. Signed-off-by: Steffen Maier Cc: Tejun Heo Cc: Lai Jiangshan Reviewed-by: Benjamin Block Acked-by: Tejun Heo Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fc.c | 2 ++ drivers/s390/scsi/zfcp_scsi.c | 3 +++ kernel/workqueue.c | 1 + 3 files changed, 6 insertions(+) diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 6162cf57a20a..2ad80c43f674 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -425,6 +425,7 @@ void zfcp_fc_port_did_lookup(struct work_struct *work) struct zfcp_port *port = container_of(work, struct zfcp_port, gid_pn_work); + set_worker_desc("zgidpn%16llx", port->wwpn); /* < WORKER_DESC_LEN=24 */ ret = zfcp_fc_ns_gid_pn(port); if (ret) { /* could not issue gid_pn for some reason */ @@ -559,6 +560,7 @@ void zfcp_fc_link_test_work(struct work_struct *work) container_of(work, struct zfcp_port, test_link_work); int retval; + set_worker_desc("zadisc%16llx", port->wwpn); /* < WORKER_DESC_LEN=24 */ get_device(&port->dev); port->rport_task = RPORT_DEL; zfcp_scsi_rport_work(&port->rport_work); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index fcc832b73960..0f7830ffd40a 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -730,6 +730,9 @@ void zfcp_scsi_rport_work(struct work_struct *work) struct zfcp_port *port = container_of(work, struct zfcp_port, rport_work); + set_worker_desc("zrp%c-%16llx", + (port->rport_task == RPORT_ADD) ? 'a' : 'd', + port->wwpn); /* < WORKER_DESC_LEN=24 */ while (port->rport_task) { if (port->rport_task == RPORT_ADD) { port->rport_task = RPORT_NONE; diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ca7959be8aaa..4e6fa755ebdc 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -4350,6 +4350,7 @@ void set_worker_desc(const char *fmt, ...) worker->desc_valid = true; } } +EXPORT_SYMBOL_GPL(set_worker_desc); /** * print_worker_info - print out worker information and description -- cgit v1.2.3 From d39eda54b70de0a1cb24962c472a87b5d9bc8dcb Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:58 +0200 Subject: scsi: zfcp: consistently use function name space prefix I've been mixing up zfcp_task_mgmt_function() [SCSI] and zfcp_fsf_fcp_task_mgmt() [FSF] so often lately that I wanted to fix this. SCSI changes complement v2.6.27 commit f76af7d7e363 ("[SCSI] zfcp: Cleanup of code in zfcp_scsi.c"). While at it, also fixup the other inconsistencies elsewhere. ERP changes complement v2.6.27 commit 287ac01acf22 ("[SCSI] zfcp: Cleanup code in zfcp_erp.c") which introduced status_change_set(). FC changes complement v2.6.32 commit 6f53a2d2ecae ("[SCSI] zfcp: Apply common naming conventions to zfcp_fc"). by renaming a leftover introduced with v2.6.27 commit cc8c282963bd ("[SCSI] zfcp: Automatically attach remote ports"). FSF changes fixup v2.6.32 commit a4623c467ff7 ("[SCSI] zfcp: Improve request allocation through mempools"). which replaced zfcp_fsf_alloc_qtcb() introduced with v2.6.27 commit c41f8cbddd4e ("[SCSI] zfcp: zfcp_fsf cleanup."). SCSI fc_host statistics were introduced with v2.6.16 commit f6cd94b126aa ("[SCSI] zfcp: transport class adaptations"). SCSI fc_host port_state was introduced with v2.6.27 commit 85a82392fe6f ("[SCSI] zfcp: Add port_state attribute to sysfs"). SCSI rport setter for dev_loss_tmo was introduced with v2.6.18 commit 338151e06608 ("[SCSI] zfcp: make use of fc_remote_port_delete when target port is unavailable"). Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 11 +++++++---- drivers/s390/scsi/zfcp_fc.c | 4 ++-- drivers/s390/scsi/zfcp_fsf.c | 7 ++++--- drivers/s390/scsi/zfcp_scsi.c | 46 ++++++++++++++++++++++--------------------- 4 files changed, 37 insertions(+), 31 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 69dfb328dba4..9be629607dc0 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -551,21 +551,23 @@ void zfcp_erp_lun_shutdown_wait(struct scsi_device *sdev, char *id) zfcp_erp_wait(adapter); } -static int status_change_set(unsigned long mask, atomic_t *status) +static int zfcp_erp_status_change_set(unsigned long mask, atomic_t *status) { return (atomic_read(status) ^ mask) & mask; } static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter) { - if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status)) + if (zfcp_erp_status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, + &adapter->status)) zfcp_dbf_rec_run("eraubl1", &adapter->erp_action); atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status); } static void zfcp_erp_port_unblock(struct zfcp_port *port) { - if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status)) + if (zfcp_erp_status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, + &port->status)) zfcp_dbf_rec_run("erpubl1", &port->erp_action); atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status); } @@ -574,7 +576,8 @@ static void zfcp_erp_lun_unblock(struct scsi_device *sdev) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); - if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status)) + if (zfcp_erp_status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, + &zfcp_sdev->status)) zfcp_dbf_rec_run("erlubl1", &sdev_to_zfcp(sdev)->erp_action); atomic_or(ZFCP_STATUS_COMMON_UNBLOCKED, &zfcp_sdev->status); } diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 2ad80c43f674..54186943896b 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -598,7 +598,7 @@ void zfcp_fc_test_link(struct zfcp_port *port) put_device(&port->dev); } -static struct zfcp_fc_req *zfcp_alloc_sg_env(int buf_num) +static struct zfcp_fc_req *zfcp_fc_alloc_sg_env(int buf_num) { struct zfcp_fc_req *fc_req; @@ -750,7 +750,7 @@ void zfcp_fc_scan_ports(struct work_struct *work) if (zfcp_fc_wka_port_get(&adapter->gs->ds)) return; - fc_req = zfcp_alloc_sg_env(buf_num); + fc_req = zfcp_fc_alloc_sg_env(buf_num); if (!fc_req) goto out; diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index d86c3bf71664..049fdd968130 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -662,7 +662,7 @@ static struct zfcp_fsf_req *zfcp_fsf_alloc(mempool_t *pool) return req; } -static struct fsf_qtcb *zfcp_qtcb_alloc(mempool_t *pool) +static struct fsf_qtcb *zfcp_fsf_qtcb_alloc(mempool_t *pool) { struct fsf_qtcb *qtcb; @@ -701,9 +701,10 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio, if (likely(fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS)) { if (likely(pool)) - req->qtcb = zfcp_qtcb_alloc(adapter->pool.qtcb_pool); + req->qtcb = zfcp_fsf_qtcb_alloc( + adapter->pool.qtcb_pool); else - req->qtcb = zfcp_qtcb_alloc(NULL); + req->qtcb = zfcp_fsf_qtcb_alloc(NULL); if (unlikely(!req->qtcb)) { zfcp_fsf_req_free(req); diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 0f7830ffd40a..b4e1f1b82503 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -266,12 +266,12 @@ static void zfcp_scsi_forget_cmnds(struct zfcp_scsi_dev *zsdev, u8 tm_flags) } /** - * zfcp_task_mgmt_function() - Synchronously send a task management function. + * zfcp_scsi_task_mgmt_function() - Send a task management function (sync). * @sdev: Pointer to SCSI device to send the task management command to. * @tm_flags: Task management flags, * here we only handle %FCP_TMF_TGT_RESET or %FCP_TMF_LUN_RESET. */ -static int zfcp_task_mgmt_function(struct scsi_device *sdev, u8 tm_flags) +static int zfcp_scsi_task_mgmt_function(struct scsi_device *sdev, u8 tm_flags) { struct zfcp_scsi_dev *zfcp_sdev = sdev_to_zfcp(sdev); struct zfcp_adapter *adapter = zfcp_sdev->port->adapter; @@ -322,7 +322,7 @@ static int zfcp_scsi_eh_device_reset_handler(struct scsi_cmnd *scpnt) { struct scsi_device *sdev = scpnt->device; - return zfcp_task_mgmt_function(sdev, FCP_TMF_LUN_RESET); + return zfcp_scsi_task_mgmt_function(sdev, FCP_TMF_LUN_RESET); } static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt) @@ -347,7 +347,7 @@ static int zfcp_scsi_eh_target_reset_handler(struct scsi_cmnd *scpnt) return ret; } - ret = zfcp_task_mgmt_function(sdev, FCP_TMF_TGT_RESET); + ret = zfcp_scsi_task_mgmt_function(sdev, FCP_TMF_TGT_RESET); /* release reference from above shost_for_each_device */ if (sdev) @@ -468,7 +468,7 @@ void zfcp_scsi_adapter_unregister(struct zfcp_adapter *adapter) } static struct fc_host_statistics* -zfcp_init_fc_host_stats(struct zfcp_adapter *adapter) +zfcp_scsi_init_fc_host_stats(struct zfcp_adapter *adapter) { struct fc_host_statistics *fc_stats; @@ -482,9 +482,9 @@ zfcp_init_fc_host_stats(struct zfcp_adapter *adapter) return adapter->fc_stats; } -static void zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats, - struct fsf_qtcb_bottom_port *data, - struct fsf_qtcb_bottom_port *old) +static void zfcp_scsi_adjust_fc_host_stats(struct fc_host_statistics *fc_stats, + struct fsf_qtcb_bottom_port *data, + struct fsf_qtcb_bottom_port *old) { fc_stats->seconds_since_last_reset = data->seconds_since_last_reset - old->seconds_since_last_reset; @@ -515,8 +515,8 @@ static void zfcp_adjust_fc_host_stats(struct fc_host_statistics *fc_stats, fc_stats->fcp_output_megabytes = data->output_mb - old->output_mb; } -static void zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats, - struct fsf_qtcb_bottom_port *data) +static void zfcp_scsi_set_fc_host_stats(struct fc_host_statistics *fc_stats, + struct fsf_qtcb_bottom_port *data) { fc_stats->seconds_since_last_reset = data->seconds_since_last_reset; fc_stats->tx_frames = data->tx_frames; @@ -540,7 +540,8 @@ static void zfcp_set_fc_host_stats(struct fc_host_statistics *fc_stats, fc_stats->fcp_output_megabytes = data->output_mb; } -static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host) +static struct fc_host_statistics * +zfcp_scsi_get_fc_host_stats(struct Scsi_Host *host) { struct zfcp_adapter *adapter; struct fc_host_statistics *fc_stats; @@ -548,7 +549,7 @@ static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host) int ret; adapter = (struct zfcp_adapter *)host->hostdata[0]; - fc_stats = zfcp_init_fc_host_stats(adapter); + fc_stats = zfcp_scsi_init_fc_host_stats(adapter); if (!fc_stats) return NULL; @@ -565,16 +566,16 @@ static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host) if (adapter->stats_reset && ((jiffies/HZ - adapter->stats_reset) < data->seconds_since_last_reset)) - zfcp_adjust_fc_host_stats(fc_stats, data, - adapter->stats_reset_data); + zfcp_scsi_adjust_fc_host_stats(fc_stats, data, + adapter->stats_reset_data); else - zfcp_set_fc_host_stats(fc_stats, data); + zfcp_scsi_set_fc_host_stats(fc_stats, data); kfree(data); return fc_stats; } -static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost) +static void zfcp_scsi_reset_fc_host_stats(struct Scsi_Host *shost) { struct zfcp_adapter *adapter; struct fsf_qtcb_bottom_port *data; @@ -596,7 +597,7 @@ static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost) } } -static void zfcp_get_host_port_state(struct Scsi_Host *shost) +static void zfcp_scsi_get_host_port_state(struct Scsi_Host *shost) { struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0]; @@ -613,7 +614,8 @@ static void zfcp_get_host_port_state(struct Scsi_Host *shost) fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; } -static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) +static void zfcp_scsi_set_rport_dev_loss_tmo(struct fc_rport *rport, + u32 timeout) { rport->dev_loss_tmo = timeout; } @@ -807,10 +809,10 @@ struct fc_function_template zfcp_transport_functions = { .show_host_supported_speeds = 1, .show_host_maxframe_size = 1, .show_host_serial_number = 1, - .get_fc_host_stats = zfcp_get_fc_host_stats, - .reset_fc_host_stats = zfcp_reset_fc_host_stats, - .set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo, - .get_host_port_state = zfcp_get_host_port_state, + .get_fc_host_stats = zfcp_scsi_get_fc_host_stats, + .reset_fc_host_stats = zfcp_scsi_reset_fc_host_stats, + .set_rport_dev_loss_tmo = zfcp_scsi_set_rport_dev_loss_tmo, + .get_host_port_state = zfcp_scsi_get_host_port_state, .terminate_rport_io = zfcp_scsi_terminate_rport_io, .show_host_port_state = 1, .show_host_active_fc4s = 1, -- cgit v1.2.3 From cd4a186aaae9ca1b754e53a4d5c019fb478d0fd0 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:14:59 +0200 Subject: scsi: zfcp: remove unused ERP enum values All constant defines were introduced with v2.6.0 history commit ea127f975424 ("[PATCH] s390 (7/7): zfcp host adapter.") and refactored into enums with commit 287ac01acf22 ("[SCSI] zfcp: Cleanup code in zfcp_erp.c"). ZFCP_STATUS_ERP_DISMISSING and ZFCP_ERP_STEP_FSF_XCONFIG were never used. v2.6.27 commit 287ac01acf22 ("[SCSI] zfcp: Cleanup code in zfcp_erp.c") removed the use of ZFCP_ERP_ACTION_READY on refactoring zfcp_erp_action_exists() to now only check adapter->erp_running_head but no longer adapter->erp_ready_head. The same commit could have changed the function return type from int to "enum zfcp_erp_act_state". ZFCP_ERP_ACTION_READY was never used outside of zfcp_erp_action_exists(). Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 9be629607dc0..b5ca484d5d5f 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -19,7 +19,6 @@ enum zfcp_erp_act_flags { ZFCP_STATUS_ERP_TIMEDOUT = 0x10000000, ZFCP_STATUS_ERP_CLOSE_ONLY = 0x01000000, - ZFCP_STATUS_ERP_DISMISSING = 0x00100000, ZFCP_STATUS_ERP_DISMISSED = 0x00200000, ZFCP_STATUS_ERP_LOWMEM = 0x00400000, ZFCP_STATUS_ERP_NO_REF = 0x00800000, @@ -27,7 +26,6 @@ enum zfcp_erp_act_flags { enum zfcp_erp_steps { ZFCP_ERP_STEP_UNINITIALIZED = 0x0000, - ZFCP_ERP_STEP_FSF_XCONFIG = 0x0001, ZFCP_ERP_STEP_PHYS_PORT_CLOSING = 0x0010, ZFCP_ERP_STEP_PORT_CLOSING = 0x0100, ZFCP_ERP_STEP_PORT_OPENING = 0x0800, @@ -61,7 +59,6 @@ enum zfcp_erp_act_type { enum zfcp_erp_act_state { ZFCP_ERP_ACTION_RUNNING = 1, - ZFCP_ERP_ACTION_READY = 2, }; enum zfcp_erp_act_result { -- cgit v1.2.3 From 013af857d8083df511e3ce0d60039c36fcec40f7 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:15:00 +0200 Subject: scsi: zfcp: zfcp_erp_action_exists() does only check for running Simplify its signature to return boolean and rename it to zfcp_erp_action_is_running() to indicate its actual unmodified semantics. It has always been used like this since v2.6.0 history commit ea127f975424 ("[PATCH] s390 (7/7): zfcp host adapter."). Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index b5ca484d5d5f..245621769c26 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -57,10 +57,6 @@ enum zfcp_erp_act_type { ZFCP_ERP_ACTION_FAILED = 0xe0, }; -enum zfcp_erp_act_state { - ZFCP_ERP_ACTION_RUNNING = 1, -}; - enum zfcp_erp_act_result { ZFCP_ERP_SUCCEEDED = 0, ZFCP_ERP_FAILED = 1, @@ -76,14 +72,14 @@ static void zfcp_erp_adapter_block(struct zfcp_adapter *adapter, int mask) ZFCP_STATUS_COMMON_UNBLOCKED | mask); } -static int zfcp_erp_action_exists(struct zfcp_erp_action *act) +static bool zfcp_erp_action_is_running(struct zfcp_erp_action *act) { struct zfcp_erp_action *curr_act; list_for_each_entry(curr_act, &act->adapter->erp_running_head, list) if (act == curr_act) - return ZFCP_ERP_ACTION_RUNNING; - return 0; + return true; + return false; } static void zfcp_erp_action_ready(struct zfcp_erp_action *act) @@ -99,7 +95,7 @@ static void zfcp_erp_action_ready(struct zfcp_erp_action *act) static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act) { act->status |= ZFCP_STATUS_ERP_DISMISSED; - if (zfcp_erp_action_exists(act) == ZFCP_ERP_ACTION_RUNNING) + if (zfcp_erp_action_is_running(act)) zfcp_erp_action_ready(act); } @@ -622,7 +618,7 @@ void zfcp_erp_notify(struct zfcp_erp_action *erp_action, unsigned long set_mask) unsigned long flags; write_lock_irqsave(&adapter->erp_lock, flags); - if (zfcp_erp_action_exists(erp_action) == ZFCP_ERP_ACTION_RUNNING) { + if (zfcp_erp_action_is_running(erp_action)) { erp_action->status |= set_mask; zfcp_erp_action_ready(erp_action); } -- cgit v1.2.3 From 2fdd45fd20f46d6f36dfa4b3e18dec11af074b5f Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:15:01 +0200 Subject: scsi: zfcp: remove unused return values of ERP trigger functions Since v2.6.27 commit 553448f6c483 ("[SCSI] zfcp: Message cleanup"), none of the callers has been interested any more. Values were not returned consistently in all ERP trigger functions. Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 34 +++++++++++++--------------------- drivers/s390/scsi/zfcp_ext.h | 2 +- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 245621769c26..2968d2f57788 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -294,12 +294,12 @@ static struct zfcp_erp_action *zfcp_erp_setup_act(int need, u32 act_status, return erp_action; } -static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, - struct zfcp_port *port, - struct scsi_device *sdev, - char *id, u32 act_status) +static void zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, + struct zfcp_port *port, + struct scsi_device *sdev, + char *id, u32 act_status) { - int retval = 1, need; + int need; struct zfcp_erp_action *act; need = zfcp_erp_handle_failed(want, adapter, port, sdev); @@ -310,7 +310,6 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, if (!adapter->erp_thread) { need = ZFCP_ERP_ACTION_NONE; /* marker for trace */ - retval = -EIO; goto out; } @@ -327,10 +326,8 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter, ++adapter->erp_total_count; list_add_tail(&act->list, &adapter->erp_ready_head); wake_up(&adapter->erp_ready_wq); - retval = 0; out: zfcp_dbf_rec_trig(id, adapter, port, sdev, want, need); - return retval; } void zfcp_erp_port_forced_no_port_dbf(char *id, struct zfcp_adapter *adapter, @@ -353,14 +350,14 @@ void zfcp_erp_port_forced_no_port_dbf(char *id, struct zfcp_adapter *adapter, write_unlock_irqrestore(&adapter->erp_lock, flags); } -static int _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, +static void _zfcp_erp_adapter_reopen(struct zfcp_adapter *adapter, int clear_mask, char *id) { zfcp_erp_adapter_block(adapter, clear_mask); zfcp_scsi_schedule_rports_block(adapter); - return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, - adapter, NULL, NULL, id, 0); + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_ADAPTER, + adapter, NULL, NULL, id, 0); } /** @@ -439,13 +436,13 @@ void zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear, char *id) write_unlock_irqrestore(&adapter->erp_lock, flags); } -static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) +static void _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) { zfcp_erp_port_block(port, clear); zfcp_scsi_schedule_rport_block(port); - return zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, - port->adapter, port, NULL, id, 0); + zfcp_erp_action_enqueue(ZFCP_ERP_ACTION_REOPEN_PORT, + port->adapter, port, NULL, id, 0); } /** @@ -453,20 +450,15 @@ static int _zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) * @port: port to recover * @clear_mask: flags in port status to be cleared * @id: Id for debug trace event. - * - * Returns 0 if recovery has been triggered, < 0 if not. */ -int zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) +void zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id) { - int retval; unsigned long flags; struct zfcp_adapter *adapter = port->adapter; write_lock_irqsave(&adapter->erp_lock, flags); - retval = _zfcp_erp_port_reopen(port, clear, id); + _zfcp_erp_port_reopen(port, clear, id); write_unlock_irqrestore(&adapter->erp_lock, flags); - - return retval; } static void zfcp_erp_lun_block(struct scsi_device *sdev, int clear_mask) diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index e317c4b513c9..ad7c28ffd49f 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -63,7 +63,7 @@ extern void zfcp_erp_adapter_reopen(struct zfcp_adapter *, int, char *); extern void zfcp_erp_adapter_shutdown(struct zfcp_adapter *, int, char *); extern void zfcp_erp_set_port_status(struct zfcp_port *, u32); extern void zfcp_erp_clear_port_status(struct zfcp_port *, u32); -extern int zfcp_erp_port_reopen(struct zfcp_port *, int, char *); +extern void zfcp_erp_port_reopen(struct zfcp_port *port, int clear, char *id); extern void zfcp_erp_port_shutdown(struct zfcp_port *, int, char *); extern void zfcp_erp_port_forced_reopen(struct zfcp_port *, int, char *); extern void zfcp_erp_set_lun_status(struct scsi_device *, u32); -- cgit v1.2.3 From b24bf22d72c75c4e32e3520041ccdf7c9a2d9e5a Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:15:02 +0200 Subject: scsi: zfcp: explicitly support initiator in scsi_host_template While the default did already correctly print "Initiator" let's make it explicit and convert zfcp to the feature. $ cat /sys/class/scsi_host/host0/supported_mode Initiator $ cat /sys/class/scsi_host/host0/active_mode Initiator The default worked, because not setting the field has it initialized to zero == MODE_UNKNOWN. scsi_host_alloc() sets shost->active_mode = MODE_INITIATOR in this case. The sysfs accessor function show_shost_supported_mode() assumes MODE_INITIATOR in this case. This default behavior was introduced with v2.6.24 commit 7a39ac3f25be ("[SCSI] make supported_mode default to initiator."). The feature flag was introduced with v2.6.24 commit 5dc2b89e1242 ("[SCSI] add supported_mode and active_mode attributes to the host"). So there was no release where zfcp would have shown "unknown". Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_scsi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index b4e1f1b82503..f69ef78ea930 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -401,6 +401,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .shost_attrs = zfcp_sysfs_shost_attrs, .sdev_attrs = zfcp_sysfs_sdev_attrs, .track_queue_depth = 1, + .supported_mode = MODE_INITIATOR, }; /** -- cgit v1.2.3 From 35e9111a1e5d820a0eb6bb8e3d8905d2161e13b2 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:15:03 +0200 Subject: scsi: zfcp: support SCSI_ADAPTER_RESET via scsi_host sysfs attribute host_reset Make use of feature introduced with v3.2 commit 294436914454 ("[SCSI] scsi: Added support for adapter and firmware reset"). The common code interface was introduced for commit 95d31262b3c1 ("[SCSI] qla4xxx: Added support for adapter and firmware reset"). $ echo adapter > /sys/class/scsi_host/host/host_reset Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : REC Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 ZFCP_DBF_REC_TRIG Tag : scshr_y SCSI sysfs host_reset yes LUN : 0xffffffffffffffff none (invalid) WWPN : 0x0000000000000000 none (invalid) D_ID : 0x00000000 none (invalid) Adapter status : 0x4500050b Port status : 0x00000000 none (invalid) LUN status : 0x00000000 none (invalid) Ready count : 0x00000001 Running count : 0x00000000 ERP want : 0x04 ZFCP_ERP_ACTION_REOPEN_ADAPTER ERP need : 0x04 ZFCP_ERP_ACTION_REOPEN_ADAPTER This is the common code equivalent to the zfcp-specific &dev_attr_adapter_failed.attr in zfcp_sysfs_adapter_attrs.attrs[]: $ echo 0 > /sys/bus/ccw/drivers/zfcp//failed The unsupported case returns EOPNOTSUPP: $ echo firmware > /sys/class/scsi_host/host/host_reset -bash: echo: write error: Operation not supported Example trace record formatted with zfcpdbf from s390-tools: Timestamp : ... Area : SCSI Subarea : 00 Level : 1 Exception : - CPU ID : .. Caller : 0x... Record ID : 1 Tag : scshr_n SCSI sysfs host_reset no Request ID : 0x0000000000000000 none (invalid) SCSI ID : 0xffffffff none (invalid) SCSI LUN : 0xffffffff none (invalid) SCSI LUN high : 0xffffffff none (invalid) SCSI result : 0xffffffa1 -EOPNOTSUPP==-95 SCSI retries : 0xff none (invalid) SCSI allowed : 0xff none (invalid) SCSI scribble : 0xffffffffffffffff none (invalid) SCSI opcode : ffffffff ffffffff ffffffff ffffffff none (invalid) FCP rsp inf cod: 0xff none (invalid) FCP rsp IU : 00000000 00000000 00000000 00000000 none (invalid) 00000000 00000000 For any other invalid value, common code returns EINVAL without invoking our callback: $ echo foo > /sys/class/scsi_host/host/host_reset -bash: echo: write error: Invalid argument Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_erp.c | 11 +++++++++++ drivers/s390/scsi/zfcp_ext.h | 1 + drivers/s390/scsi/zfcp_scsi.c | 26 ++++++++++++++++++++++++++ drivers/s390/scsi/zfcp_sysfs.c | 5 +---- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 2968d2f57788..e7e6b63905e2 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -1691,3 +1691,14 @@ void zfcp_erp_clear_lun_status(struct scsi_device *sdev, u32 mask) atomic_set(&zfcp_sdev->erp_counter, 0); } +/** + * zfcp_erp_adapter_reset_sync() - Really reopen adapter and wait. + * @adapter: Pointer to zfcp_adapter to reopen. + * @id: Trace tag string of length %ZFCP_DBF_TAG_LEN. + */ +void zfcp_erp_adapter_reset_sync(struct zfcp_adapter *adapter, char *id) +{ + zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); + zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, id); + zfcp_erp_wait(adapter); +} diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index ad7c28ffd49f..f3b55cce748a 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -76,6 +76,7 @@ extern void zfcp_erp_thread_kill(struct zfcp_adapter *); extern void zfcp_erp_wait(struct zfcp_adapter *); extern void zfcp_erp_notify(struct zfcp_erp_action *, unsigned long); extern void zfcp_erp_timeout_handler(struct timer_list *t); +extern void zfcp_erp_adapter_reset_sync(struct zfcp_adapter *adapter, char *id); /* zfcp_fc.c */ extern struct kmem_cache *zfcp_fc_req_cache; diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index f69ef78ea930..9a01f583e562 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -372,6 +372,31 @@ static int zfcp_scsi_eh_host_reset_handler(struct scsi_cmnd *scpnt) return ret; } +/** + * zfcp_scsi_sysfs_host_reset() - Support scsi_host sysfs attribute host_reset. + * @shost: Pointer to Scsi_Host to perform action on. + * @reset_type: We support %SCSI_ADAPTER_RESET but not %SCSI_FIRMWARE_RESET. + * + * Return: 0 on %SCSI_ADAPTER_RESET, -%EOPNOTSUPP otherwise. + * + * This is similar to zfcp_sysfs_adapter_failed_store(). + */ +static int zfcp_scsi_sysfs_host_reset(struct Scsi_Host *shost, int reset_type) +{ + struct zfcp_adapter *adapter = + (struct zfcp_adapter *)shost->hostdata[0]; + int ret = 0; + + if (reset_type != SCSI_ADAPTER_RESET) { + ret = -EOPNOTSUPP; + zfcp_dbf_scsi_eh("scshr_n", adapter, ~0, ret); + return ret; + } + + zfcp_erp_adapter_reset_sync(adapter, "scshr_y"); + return ret; +} + struct scsi_transport_template *zfcp_scsi_transport_template; static struct scsi_host_template zfcp_scsi_host_template = { @@ -387,6 +412,7 @@ static struct scsi_host_template zfcp_scsi_host_template = { .slave_configure = zfcp_scsi_slave_configure, .slave_destroy = zfcp_scsi_slave_destroy, .change_queue_depth = scsi_change_queue_depth, + .host_reset = zfcp_scsi_sysfs_host_reset, .proc_name = "zfcp", .can_queue = 4096, .this_id = -1, diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 3ac823f2540f..b277be6f7611 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -200,10 +200,7 @@ static ssize_t zfcp_sysfs_adapter_failed_store(struct device *dev, goto out; } - zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); - zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, - "syafai2"); - zfcp_erp_wait(adapter); + zfcp_erp_adapter_reset_sync(adapter, "syafai2"); out: zfcp_ccw_adapter_put(adapter); return retval ? retval : (ssize_t) count; -- cgit v1.2.3 From 6919298c98705c754547f697b0f3792a147aa944 Mon Sep 17 00:00:00 2001 From: Steffen Maier Date: Thu, 17 May 2018 19:15:04 +0200 Subject: scsi: zfcp: cleanup indentation for posting FC events I just happened to see the function header indentation of zfcp_fc_enqueue_event() and I picked some more from checkpatch: $ checkpatch.pl --strict -f drivers/s390/scsi/zfcp_fc.c ... CHECK: Alignment should match open parenthesis #113: FILE: drivers/s390/scsi/zfcp_fc.c:113: + fc_host_post_event(adapter->scsi_host, fc_get_event_number(), + event->code, event->data); CHECK: Blank lines aren't necessary before a close brace '}' #118: FILE: drivers/s390/scsi/zfcp_fc.c:118: + +} ... The change complements v2.6.36 commit 2d1e547f7523 ("[SCSI] zfcp: Post events through FC transport class"). Signed-off-by: Steffen Maier Reviewed-by: Benjamin Block Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c index 54186943896b..f6c415d6ef48 100644 --- a/drivers/s390/scsi/zfcp_fc.c +++ b/drivers/s390/scsi/zfcp_fc.c @@ -111,11 +111,10 @@ void zfcp_fc_post_event(struct work_struct *work) list_for_each_entry_safe(event, tmp, &tmp_lh, list) { fc_host_post_event(adapter->scsi_host, fc_get_event_number(), - event->code, event->data); + event->code, event->data); list_del(&event->list); kfree(event); } - } /** @@ -126,7 +125,7 @@ void zfcp_fc_post_event(struct work_struct *work) * @event_data: The event data (e.g. n_port page in case of els) */ void zfcp_fc_enqueue_event(struct zfcp_adapter *adapter, - enum fc_host_event_code event_code, u32 event_data) + enum fc_host_event_code event_code, u32 event_data) { struct zfcp_fc_event *event; -- cgit v1.2.3 From 9e156c54ace310ce7fb1cd960e62416947f3d47c Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Thu, 17 May 2018 19:15:05 +0200 Subject: scsi: zfcp: assert that the ERP lock is held when tracing a recovery trigger Otherwise iterating with list_for_each() over the adapter->erp_ready_head and adapter->erp_running_head lists can lead to an infinite loop. See commit "zfcp: fix infinite iteration on erp_ready_head list". The run-time check is only performed for debug kernels which have the kernel lock validator enabled. Following is an example of the warning that is reported, if the ERP lock is not held when calling zfcp_dbf_rec_trig(): WARNING: CPU: 0 PID: 604 at drivers/s390/scsi/zfcp_dbf.c:288 zfcp_dbf_rec_trig+0x172/0x188 Modules linked in: ... CPU: 0 PID: 604 Comm: kworker/u128:3 Not tainted 4.16.0-... #1 Hardware name: IBM 2964 N96 702 (z/VM 6.4.0) Workqueue: zfcp_q_0.0.1906 zfcp_scsi_rport_work Krnl PSW : 00000000330fdbf9 00000000367e9728 (zfcp_dbf_rec_trig+0x172/0x188) R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:3 PM:0 RI:0 EA:3 Krnl GPRS: 00000000c57a5d99 3288200000000000 0000000000000000 000000006cc82740 00000000009d09d6 0000000000000000 00000000000000ff 0000000000000000 0000000000000000 0000000000e1b5fe 000000006de01d38 0000000076130958 000000006cc82548 000000006de01a98 00000000009d09d6 000000006a6d3c80 Krnl Code: 00000000009d0ad2: eb7ff0b80004 lmg %r7,%r15,184(%r15) 00000000009d0ad8: c0f4000d7dd0 brcl 15,b80678 #00000000009d0ade: a7f40001 brc 15,9d0ae0 >00000000009d0ae2: a7f4ff7d brc 15,9d09dc 00000000009d0ae6: e340f0f00004 lg %r4,240(%r15) 00000000009d0aec: eb7ff0b80004 lmg %r7,%r15,184(%r15) 00000000009d0af2: 07f4 bcr 15,%r4 00000000009d0af4: 0707 bcr 0,%r7 Call Trace: ([<00000000009d09d6>] zfcp_dbf_rec_trig+0x66/0x188) [<00000000009dd740>] zfcp_scsi_rport_work+0x98/0x190 [<0000000000169b34>] process_one_work+0x3d4/0x6f8 [<000000000016a08a>] worker_thread+0x232/0x418 [<000000000017219e>] kthread+0x166/0x178 [<0000000000b815ea>] kernel_thread_starter+0x6/0xc [<0000000000b815e4>] kernel_thread_starter+0x0/0xc 2 locks held by kworker/u128:3/604: #0: ((wq_completion)name){+.+.}, at: [<0000000082af1024>] process_one_work+0x1dc/0x6f8 #1: ((work_completion)(&port->rport_work)){+.+.}, at: [<0000000082af1024>] process_one_work+0x1dc/0x6f8 Last Breaking-Event-Address: [<00000000009d0ade>] zfcp_dbf_rec_trig+0x16e/0x188 ---[ end trace b2f4020572e2c124 ]--- Suggested-by: Steffen Maier Signed-off-by: Jens Remus Reviewed-by: Benjamin Block Reviewed-by: Steffen Maier Signed-off-by: Steffen Maier Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_dbf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index bb3373260169..781141bf2c28 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -285,6 +285,8 @@ void zfcp_dbf_rec_trig(char *tag, struct zfcp_adapter *adapter, struct list_head *entry; unsigned long flags; + lockdep_assert_held(&adapter->erp_lock); + if (unlikely(!debug_level_enabled(dbf->rec, level))) return; -- cgit v1.2.3 From 6e2e490080c8a7bb2062705f4c78205dbf4b67e9 Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Thu, 17 May 2018 19:15:06 +0200 Subject: scsi: zfcp: add port speed capabilities Add port speed capabilities as defined in FC-LS RPSC ELS that have a counterpart FC_PORTSPEED_* defined in scsi/scsi_transport_fc.h. Suggested-by: Steffen Maier Signed-off-by: Jens Remus Reviewed-by: Steffen Maier Reviewed-by: Fedor Loshakov Acked-by: Hendrik Brueckner Acked-by: Benjamin Block Signed-off-by: Steffen Maier Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 049fdd968130..3c86e27f094d 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -4,7 +4,7 @@ * * Implementation of FSF commands. * - * Copyright IBM Corp. 2002, 2017 + * Copyright IBM Corp. 2002, 2018 */ #define KMSG_COMPONENT "zfcp" @@ -437,6 +437,9 @@ void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter) #define ZFCP_FSF_PORTSPEED_10GBIT (1 << 3) #define ZFCP_FSF_PORTSPEED_8GBIT (1 << 4) #define ZFCP_FSF_PORTSPEED_16GBIT (1 << 5) +#define ZFCP_FSF_PORTSPEED_32GBIT (1 << 6) +#define ZFCP_FSF_PORTSPEED_64GBIT (1 << 7) +#define ZFCP_FSF_PORTSPEED_128GBIT (1 << 8) #define ZFCP_FSF_PORTSPEED_NOT_NEGOTIATED (1 << 15) static u32 zfcp_fsf_convert_portspeed(u32 fsf_speed) @@ -454,6 +457,12 @@ static u32 zfcp_fsf_convert_portspeed(u32 fsf_speed) fdmi_speed |= FC_PORTSPEED_8GBIT; if (fsf_speed & ZFCP_FSF_PORTSPEED_16GBIT) fdmi_speed |= FC_PORTSPEED_16GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_32GBIT) + fdmi_speed |= FC_PORTSPEED_32GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_64GBIT) + fdmi_speed |= FC_PORTSPEED_64GBIT; + if (fsf_speed & ZFCP_FSF_PORTSPEED_128GBIT) + fdmi_speed |= FC_PORTSPEED_128GBIT; if (fsf_speed & ZFCP_FSF_PORTSPEED_NOT_NEGOTIATED) fdmi_speed |= FC_PORTSPEED_NOT_NEGOTIATED; return fdmi_speed; -- cgit v1.2.3 From 16dad2798870c181f3cae976e4e0257d4fdf3a3c Mon Sep 17 00:00:00 2001 From: Jens Remus Date: Thu, 17 May 2018 19:15:07 +0200 Subject: scsi: zfcp: enhance comments on fc_link_speed and supported_speed The comment on fsf_qtcb_bottom_port.supported_speed did read as if the field can only assume one of two possible values (i.e. 0x1 for 1 GBit/s or 0x2 for 2 GBit/s). This is not true for two reasons: first it is a flag field and can thus assume any combination and second there are meanwhile more speeds. Clarify comment on fsf_qtcb_bottom_port.supported_speed and add a comment to fsf_qtcb_bottom_config.fc_link_speed. Signed-off-by: Jens Remus Reviewed-by: Steffen Maier Reviewed-by: Fedor Loshakov Acked-by: Benjamin Block Acked-by: Hendrik Brueckner Signed-off-by: Steffen Maier Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_fsf.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 4baca67aba6d..535628b92f0a 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -4,7 +4,7 @@ * * Interface to the FSF support functions. * - * Copyright IBM Corp. 2002, 2017 + * Copyright IBM Corp. 2002, 2018 */ #ifndef FSF_H @@ -356,7 +356,7 @@ struct fsf_qtcb_bottom_config { u32 adapter_features; u32 connection_features; u32 fc_topology; - u32 fc_link_speed; + u32 fc_link_speed; /* one of ZFCP_FSF_PORTSPEED_* */ u32 adapter_type; u8 res0; u8 peer_d_id[3]; @@ -382,7 +382,7 @@ struct fsf_qtcb_bottom_port { u32 class_of_service; /* should be 0x00000006 for class 2 and 3 */ u8 supported_fc4_types[32]; /* should be 0x00000100 for scsi fcp */ u8 active_fc4_types[32]; - u32 supported_speed; /* 0x0001 for 1 GBit/s or 0x0002 for 2 GBit/s */ + u32 supported_speed; /* any combination of ZFCP_FSF_PORTSPEED_* */ u32 maximum_frame_size; /* fixed value of 2112 */ u64 seconds_since_last_reset; u64 tx_frames; -- cgit v1.2.3 From 1b25a8c4d27170efff11895decf6c1a740be1af5 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Thu, 17 May 2018 14:07:40 -0700 Subject: scsi: storvsc: Avoid allocating memory for temp cpumasks Current code allocates 240 Kbytes (in typical configs) for each synthetic SCSI controller to use as temp cpumask variables. Recode to avoid needing the temp cpumask variables and remove the memory allocation. Signed-off-by: Michael Kelley Acked-by: Stephen Hemminger Signed-off-by: Martin K. Petersen --- drivers/scsi/storvsc_drv.c | 50 ++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index d3897c438448..a66711935912 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -474,13 +474,6 @@ struct storvsc_device { * Mask of CPUs bound to subchannels. */ struct cpumask alloced_cpus; - /* - * Pre-allocated struct cpumask for each hardware queue. - * struct cpumask is used by selecting out-going channels. It is a - * big structure, default to 1024k bytes when CONFIG_MAXSMP=y. - * Pre-allocate it to avoid allocation on the kernel stack. - */ - struct cpumask *cpumask_chns; /* Used for vsc/vsp channel reset process */ struct storvsc_cmd_request init_request; struct storvsc_cmd_request reset_request; @@ -885,13 +878,6 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc) if (stor_device->stor_chns == NULL) return -ENOMEM; - stor_device->cpumask_chns = kcalloc(num_possible_cpus(), - sizeof(struct cpumask), GFP_KERNEL); - if (stor_device->cpumask_chns == NULL) { - kfree(stor_device->stor_chns); - return -ENOMEM; - } - stor_device->stor_chns[device->channel->target_cpu] = device->channel; cpumask_set_cpu(device->channel->target_cpu, &stor_device->alloced_cpus); @@ -1252,7 +1238,6 @@ static int storvsc_dev_remove(struct hv_device *device) vmbus_close(device->channel); kfree(stor_device->stor_chns); - kfree(stor_device->cpumask_chns); kfree(stor_device); return 0; } @@ -1262,7 +1247,7 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device, { u16 slot = 0; u16 hash_qnum; - struct cpumask *alloced_mask = &stor_device->cpumask_chns[q_num]; + const struct cpumask *node_mask; int num_channels, tgt_cpu; if (stor_device->num_sc == 0) @@ -1278,10 +1263,13 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device, * III. Mapping is persistent. */ - cpumask_and(alloced_mask, &stor_device->alloced_cpus, - cpumask_of_node(cpu_to_node(q_num))); + node_mask = cpumask_of_node(cpu_to_node(q_num)); - num_channels = cpumask_weight(alloced_mask); + num_channels = 0; + for_each_cpu(tgt_cpu, &stor_device->alloced_cpus) { + if (cpumask_test_cpu(tgt_cpu, node_mask)) + num_channels++; + } if (num_channels == 0) return stor_device->device->channel; @@ -1289,7 +1277,9 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device, while (hash_qnum >= num_channels) hash_qnum -= num_channels; - for_each_cpu(tgt_cpu, alloced_mask) { + for_each_cpu(tgt_cpu, &stor_device->alloced_cpus) { + if (!cpumask_test_cpu(tgt_cpu, node_mask)) + continue; if (slot == hash_qnum) break; slot++; @@ -1308,7 +1298,7 @@ static int storvsc_do_io(struct hv_device *device, struct vstor_packet *vstor_packet; struct vmbus_channel *outgoing_channel, *channel; int ret = 0; - struct cpumask *alloced_mask; + const struct cpumask *node_mask; int tgt_cpu; vstor_packet = &request->vstor_packet; @@ -1329,11 +1319,11 @@ static int storvsc_do_io(struct hv_device *device, * Ideally, we want to pick a different channel if * available on the same NUMA node. */ - alloced_mask = &stor_device->cpumask_chns[q_num]; - cpumask_and(alloced_mask, &stor_device->alloced_cpus, - cpumask_of_node(cpu_to_node(q_num))); - - for_each_cpu_wrap(tgt_cpu, alloced_mask, q_num + 1) { + node_mask = cpumask_of_node(cpu_to_node(q_num)); + for_each_cpu_wrap(tgt_cpu, + &stor_device->alloced_cpus, q_num + 1) { + if (!cpumask_test_cpu(tgt_cpu, node_mask)) + continue; if (tgt_cpu == q_num) continue; channel = stor_device->stor_chns[tgt_cpu]; @@ -1359,10 +1349,9 @@ static int storvsc_do_io(struct hv_device *device, * NUMA node are busy. Try to find a channel in * other NUMA nodes */ - cpumask_andnot(alloced_mask, &stor_device->alloced_cpus, - cpumask_of_node(cpu_to_node(q_num))); - - for_each_cpu(tgt_cpu, alloced_mask) { + for_each_cpu(tgt_cpu, &stor_device->alloced_cpus) { + if (cpumask_test_cpu(tgt_cpu, node_mask)) + continue; channel = stor_device->stor_chns[tgt_cpu]; if (hv_get_avail_to_write_percent( &channel->outbound) @@ -1911,7 +1900,6 @@ err_out2: err_out1: kfree(stor_device->stor_chns); - kfree(stor_device->cpumask_chns); kfree(stor_device); err_out0: -- cgit v1.2.3 From deac444f4e44c406796a086381db1c09c70ce042 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 17 May 2018 23:26:36 -0700 Subject: scsi: ufs: Extract devfreq registration Failing to register with devfreq leaves hba->devfreq assigned, which causes the error path to dereference the ERR_PTR(). Rather than bolting on more conditionals, move the call of devm_devfreq_add_device() into it's own function and only update hba->devfreq once it's successfully registered. The subsequent patch builds upon this to make UFS actually work again, as it's been broken since f1d981eaecf8 ("PM / devfreq: Use the available min/max frequency") Also switch to use DEVFREQ_GOV_SIMPLE_ONDEMAND constant. Reviewed-by: Chanwoo Choi Signed-off-by: Bjorn Andersson Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 134e99e9f789..c21c9bd18073 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1270,6 +1270,26 @@ static struct devfreq_dev_profile ufs_devfreq_profile = { .get_dev_status = ufshcd_devfreq_get_dev_status, }; +static int ufshcd_devfreq_init(struct ufs_hba *hba) +{ + struct devfreq *devfreq; + int ret; + + devfreq = devm_devfreq_add_device(hba->dev, + &ufs_devfreq_profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, + NULL); + if (IS_ERR(devfreq)) { + ret = PTR_ERR(devfreq); + dev_err(hba->dev, "Unable to register with devfreq %d\n", ret); + return ret; + } + + hba->devfreq = devfreq; + + return 0; +} + static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba) { unsigned long flags; @@ -6505,16 +6525,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) sizeof(struct ufs_pa_layer_attr)); hba->clk_scaling.saved_pwr_info.is_valid = true; if (!hba->devfreq) { - hba->devfreq = devm_devfreq_add_device(hba->dev, - &ufs_devfreq_profile, - "simple_ondemand", - NULL); - if (IS_ERR(hba->devfreq)) { - ret = PTR_ERR(hba->devfreq); - dev_err(hba->dev, "Unable to register with devfreq %d\n", - ret); + ret = ufshcd_devfreq_init(hba); + if (ret) goto out; - } } hba->clk_scaling.is_allowed = true; } -- cgit v1.2.3 From 092b45583c524edac688f0b6cdc62a70c3081e4a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 17 May 2018 23:26:37 -0700 Subject: scsi: ufs: Use freq table with devfreq devfreq requires that the client operates on actual frequencies, not only 0 and UMAX_INT and as such UFS brok with the introduction of f1d981eaecf8 ("PM / devfreq: Use the available min/max frequency"). This patch registers the frequencies of the first clock with devfreq and use these to determine if we're trying to step up or down. Reviewed-by: Chanwoo Choi [for devfreq & OPP part] Reviewed-by: Subhash Jadavani Signed-off-by: Bjorn Andersson Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index c21c9bd18073..892ad43dac9a 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1183,16 +1183,13 @@ static int ufshcd_devfreq_target(struct device *dev, struct ufs_hba *hba = dev_get_drvdata(dev); ktime_t start; bool scale_up, sched_clk_scaling_suspend_work = false; + struct list_head *clk_list = &hba->clk_list_head; + struct ufs_clk_info *clki; unsigned long irq_flags; if (!ufshcd_is_clkscaling_supported(hba)) return -EINVAL; - if ((*freq > 0) && (*freq < UINT_MAX)) { - dev_err(hba->dev, "%s: invalid freq = %lu\n", __func__, *freq); - return -EINVAL; - } - spin_lock_irqsave(hba->host->host_lock, irq_flags); if (ufshcd_eh_in_progress(hba)) { spin_unlock_irqrestore(hba->host->host_lock, irq_flags); @@ -1202,7 +1199,13 @@ static int ufshcd_devfreq_target(struct device *dev, if (!hba->clk_scaling.active_reqs) sched_clk_scaling_suspend_work = true; - scale_up = (*freq == UINT_MAX) ? true : false; + if (list_empty(clk_list)) { + spin_unlock_irqrestore(hba->host->host_lock, irq_flags); + goto out; + } + + clki = list_first_entry(&hba->clk_list_head, struct ufs_clk_info, list); + scale_up = (*freq == clki->max_freq) ? true : false; if (!ufshcd_is_devfreq_scaling_required(hba, scale_up)) { spin_unlock_irqrestore(hba->host->host_lock, irq_flags); ret = 0; @@ -1272,16 +1275,29 @@ static struct devfreq_dev_profile ufs_devfreq_profile = { static int ufshcd_devfreq_init(struct ufs_hba *hba) { + struct list_head *clk_list = &hba->clk_list_head; + struct ufs_clk_info *clki; struct devfreq *devfreq; int ret; - devfreq = devm_devfreq_add_device(hba->dev, + /* Skip devfreq if we don't have any clocks in the list */ + if (list_empty(clk_list)) + return 0; + + clki = list_first_entry(clk_list, struct ufs_clk_info, list); + dev_pm_opp_add(hba->dev, clki->min_freq, 0); + dev_pm_opp_add(hba->dev, clki->max_freq, 0); + + devfreq = devfreq_add_device(hba->dev, &ufs_devfreq_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL); if (IS_ERR(devfreq)) { ret = PTR_ERR(devfreq); dev_err(hba->dev, "Unable to register with devfreq %d\n", ret); + + dev_pm_opp_remove(hba->dev, clki->min_freq); + dev_pm_opp_remove(hba->dev, clki->max_freq); return ret; } @@ -1290,6 +1306,22 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba) return 0; } +static void ufshcd_devfreq_remove(struct ufs_hba *hba) +{ + struct list_head *clk_list = &hba->clk_list_head; + struct ufs_clk_info *clki; + + if (!hba->devfreq) + return; + + devfreq_remove_device(hba->devfreq); + hba->devfreq = NULL; + + clki = list_first_entry(clk_list, struct ufs_clk_info, list); + dev_pm_opp_remove(hba->dev, clki->min_freq); + dev_pm_opp_remove(hba->dev, clki->max_freq); +} + static void __ufshcd_suspend_clkscaling(struct ufs_hba *hba) { unsigned long flags; @@ -7071,6 +7103,7 @@ static void ufshcd_hba_exit(struct ufs_hba *hba) if (hba->devfreq) ufshcd_suspend_clkscaling(hba); destroy_workqueue(hba->clk_scaling.workq); + ufshcd_devfreq_remove(hba); } ufshcd_setup_clocks(hba, false); ufshcd_setup_hba_vreg(hba, false); -- cgit v1.2.3 From 60d6d22d85a79d194e9e1a13b831ffafa741cd6e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 2 May 2018 15:21:38 -0700 Subject: scsi: dpt_i2o: Remove VLA usage On the quest to remove all VLAs from the kernel[1] this moves the sg_list variable off the stack, as already done for other allocated buffers in adpt_i2o_passthru(). Additionally consolidates the error path for kfree(). [1] https://lkml.kernel.org/r/CA+55aFzCG-zNmZwX4A2FQpadafLfEzK6CC=qPXydAacU1RqZWA@mail.gmail.com Signed-off-by: Kees Cook Signed-off-by: Martin K. Petersen --- drivers/scsi/dpt_i2o.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 5ceea8da7bb6..37de8fb186d7 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -1706,7 +1706,7 @@ static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg) u32 reply_size = 0; u32 __user *user_msg = arg; u32 __user * user_reply = NULL; - void *sg_list[pHba->sg_tablesize]; + void **sg_list = NULL; u32 sg_offset = 0; u32 sg_count = 0; int sg_index = 0; @@ -1748,19 +1748,23 @@ static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg) msg[2] = 0x40000000; // IOCTL context msg[3] = adpt_ioctl_to_context(pHba, reply); if (msg[3] == (u32)-1) { - kfree(reply); - return -EBUSY; + rcode = -EBUSY; + goto free; } - memset(sg_list,0, sizeof(sg_list[0])*pHba->sg_tablesize); + sg_list = kcalloc(pHba->sg_tablesize, sizeof(*sg_list), GFP_KERNEL); + if (!sg_list) { + rcode = -ENOMEM; + goto free; + } if(sg_offset) { // TODO add 64 bit API struct sg_simple_element *sg = (struct sg_simple_element*) (msg+sg_offset); sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element); if (sg_count > pHba->sg_tablesize){ printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count); - kfree (reply); - return -EINVAL; + rcode = -EINVAL; + goto free; } for(i = 0; i < sg_count; i++) { @@ -1879,7 +1883,6 @@ cleanup: if (rcode != -ETIME && rcode != -EINTR) { struct sg_simple_element *sg = (struct sg_simple_element*) (msg +sg_offset); - kfree (reply); while(sg_index) { if(sg_list[--sg_index]) { dma_free_coherent(&pHba->pDev->dev, @@ -1889,6 +1892,10 @@ cleanup: } } } + +free: + kfree(sg_list); + kfree(reply); return rcode; } -- cgit v1.2.3 From 2e3611e9546c2ed4def152a51dfd34e8dddae7a5 Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Thu, 3 May 2018 16:37:16 +0530 Subject: scsi: ufs: fix exception event handling The device can set the exception event bit in one of the response UPIU, for example to notify the need for urgent BKOPs operation. In such a case, the host driver calls ufshcd_exception_event_handler to handle this notification. When trying to check the exception event status (for finding the cause for the exception event), the device may be busy with additional SCSI commands handling and may not respond within the 100ms timeout. To prevent that, we need to block SCSI commands during handling of exception events and allow retransmissions of the query requests, in case of timeout. Signed-off-by: Subhash Jadavani Signed-off-by: Maya Erez Signed-off-by: Can Guo Signed-off-by: Asutosh Das Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 892ad43dac9a..4dc48804c17b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5073,6 +5073,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) hba = container_of(work, struct ufs_hba, eeh_work); pm_runtime_get_sync(hba->dev); + scsi_block_requests(hba->host); err = ufshcd_get_ee_status(hba, &status); if (err) { dev_err(hba->dev, "%s: failed to get exception status %d\n", @@ -5086,6 +5087,7 @@ static void ufshcd_exception_event_handler(struct work_struct *work) ufshcd_bkops_exception_event_handler(hba); out: + scsi_unblock_requests(hba->host); pm_runtime_put_sync(hba->dev); return; } -- cgit v1.2.3 From b334456ec2021b1addc19806990115e69ec4ac32 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Thu, 3 May 2018 16:37:17 +0530 Subject: scsi: ufs: ufshcd: fix possible unclocked register access Vendor specific setup_clocks ops may depend on clocks managed by ufshcd driver so if the vendor specific setup_clocks callback is called when the required clocks are turned off, it results into unclocked register access. This change make sure that required clocks are enabled before vendor specific setup_clocks callback is called. Signed-off-by: Subhash Jadavani Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Can Guo Signed-off-by: Asutosh Das Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 4dc48804c17b..79591934ef45 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -6912,9 +6912,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, if (list_empty(head)) goto out; - ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); - if (ret) - return ret; + /* + * vendor specific setup_clocks ops may depend on clocks managed by + * this standard driver hence call the vendor specific setup_clocks + * before disabling the clocks managed here. + */ + if (!on) { + ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE); + if (ret) + return ret; + } list_for_each_entry(clki, head, list) { if (!IS_ERR_OR_NULL(clki->clk)) { @@ -6938,9 +6945,16 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on, } } - ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); - if (ret) - return ret; + /* + * vendor specific setup_clocks ops may depend on clocks managed by + * this standard driver hence call the vendor specific setup_clocks + * after enabling the clocks managed here. + */ + if (on) { + ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE); + if (ret) + return ret; + } out: if (ret) { -- cgit v1.2.3 From 38135535dcc25af856336fda31aeef79d8ad9dab Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Thu, 3 May 2018 16:37:18 +0530 Subject: scsi: ufs: add reference counting for scsi block requests Currently we call the scsi_block_requests()/scsi_unblock_requests() whenever we want to block/unblock scsi requests but as there is no reference counting, nesting of these calls could leave us in undesired state sometime. Consider following call flow sequence: 1. func1() calls scsi_block_requests() but calls func2() before calling scsi_unblock_requests() 2. func2() calls scsi_block_requests() 3. func2() calls scsi_unblock_requests() 4. func1() calls scsi_unblock_requests() As there is no reference counting, we will have scsi requests unblocked after #3 instead of it to be unblocked only after #4. Though we may not have failures seen with this, we might run into some failures in future. Better solution would be to fix this by adding reference counting. Signed-off-by: Subhash Jadavani Signed-off-by: Can Guo Signed-off-by: Asutosh Das Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 28 ++++++++++++++++++++-------- drivers/scsi/ufs/ufshcd.h | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 79591934ef45..2859f6395eea 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -264,6 +264,18 @@ static inline void ufshcd_disable_irq(struct ufs_hba *hba) } } +static void ufshcd_scsi_unblock_requests(struct ufs_hba *hba) +{ + if (atomic_dec_and_test(&hba->scsi_block_reqs_cnt)) + scsi_unblock_requests(hba->host); +} + +static void ufshcd_scsi_block_requests(struct ufs_hba *hba) +{ + if (atomic_inc_return(&hba->scsi_block_reqs_cnt) == 1) + scsi_block_requests(hba->host); +} + /* replace non-printable or non-ASCII characters with spaces */ static inline void ufshcd_remove_non_printable(char *val) { @@ -1074,12 +1086,12 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) * make sure that there are no outstanding requests when * clock scaling is in progress */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); down_write(&hba->clk_scaling_lock); if (ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) { ret = -EBUSY; up_write(&hba->clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } return ret; @@ -1088,7 +1100,7 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba) static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba) { up_write(&hba->clk_scaling_lock); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1460,7 +1472,7 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); } /** @@ -1516,7 +1528,7 @@ start: * work and to enable clocks. */ case CLKS_OFF: - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); @@ -5298,7 +5310,7 @@ skip_err_handling: out: spin_unlock_irqrestore(hba->host->host_lock, flags); - scsi_unblock_requests(hba->host); + ufshcd_scsi_unblock_requests(hba); ufshcd_release(hba); pm_runtime_put_sync(hba->dev); } @@ -5400,7 +5412,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba) /* handle fatal errors only when link is functional */ if (hba->ufshcd_state == UFSHCD_STATE_OPERATIONAL) { /* block commands from scsi mid-layer */ - scsi_block_requests(hba->host); + ufshcd_scsi_block_requests(hba); hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED; @@ -8032,7 +8044,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) /* Hold auto suspend until async scan completes */ pm_runtime_get_sync(dev); - + atomic_set(&hba->scsi_block_reqs_cnt, 0); /* * We are assuming that device wasn't put in sleep/power-down * state exclusively during the boot stage before kernel. diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 53121fd6c287..a44b9f45ddd2 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -499,6 +499,7 @@ struct ufs_stats { * @urgent_bkops_lvl: keeps track of urgent bkops level for device * @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for * device is known or not. + * @scsi_block_reqs_cnt: reference counting for scsi block requests */ struct ufs_hba { void __iomem *mmio_base; @@ -699,6 +700,7 @@ struct ufs_hba { struct rw_semaphore clk_scaling_lock; struct ufs_desc_size desc_size; + atomic_t scsi_block_reqs_cnt; }; /* Returns true if clocks can be gated. Otherwise false */ -- cgit v1.2.3 From 69a6fff068567469c0ef1156ae5ac8d3d71701f0 Mon Sep 17 00:00:00 2001 From: Subhash Jadavani Date: Thu, 3 May 2018 16:37:19 +0530 Subject: scsi: ufs: ufs-qcom: remove broken hci version quirk UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION is only applicable for QCOM UFS host controller version 2.x.y and this has been fixed from version 3.x.y onwards, hence this change removes this quirk for version 3.x.y onwards. [mkp: applied by hand] Signed-off-by: Subhash Jadavani Signed-off-by: Asutosh Das Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufs-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 2b38db2eeafa..221820a7c78b 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1098,7 +1098,7 @@ static void ufs_qcom_advertise_quirks(struct ufs_hba *hba) hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; } - if (host->hw_ver.major >= 0x2) { + if (host->hw_ver.major == 0x2) { hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; if (!ufs_qcom_cap_qunipro(host)) -- cgit v1.2.3 From 7f6ba4f12e6cbfdefbb95cfd8fc67ece6c15d799 Mon Sep 17 00:00:00 2001 From: Venkat Gopalakrishnan Date: Thu, 3 May 2018 16:37:20 +0530 Subject: scsi: ufs: make sure all interrupts are processed As multiple requests are submitted to the ufs host controller in parallel there could be instances where the command completion interrupt arrives later for a request that is already processed earlier as the corresponding doorbell was cleared when handling the previous interrupt. Read the interrupt status in a loop after processing the received interrupt to catch such interrupts and handle it. Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Asutosh Das Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 2859f6395eea..028492179101 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -5489,19 +5489,30 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba) u32 intr_status, enabled_intr_status; irqreturn_t retval = IRQ_NONE; struct ufs_hba *hba = __hba; + int retries = hba->nutrs; spin_lock(hba->host->host_lock); intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = - intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - if (intr_status) - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + /* + * There could be max of hba->nutrs reqs in flight and in worst case + * if the reqs get finished 1 by 1 after the interrupt status is + * read, make sure we handle them by checking the interrupt status + * again in a loop until we process all of the reqs before returning. + */ + do { + enabled_intr_status = + intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); + if (intr_status) + ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); + if (enabled_intr_status) { + ufshcd_sl_intr(hba, enabled_intr_status); + retval = IRQ_HANDLED; + } + + intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); + } while (intr_status && --retries); - if (enabled_intr_status) { - ufshcd_sl_intr(hba, enabled_intr_status); - retval = IRQ_HANDLED; - } spin_unlock(hba->host->host_lock); return retval; } -- cgit v1.2.3 From 10e5e37581fc5817d52ff5f8e2b3362f64eb04f4 Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Thu, 3 May 2018 16:37:22 +0530 Subject: scsi: ufs: Add clock ungating to a separate workqueue UFS driver can receive a request during memory reclaim by kswapd. So when ufs driver puts the ungate work in queue, and if there are no idle workers, kthreadd is invoked to create a new kworker. Since kswapd task holds a mutex which kthreadd also needs, this can cause a deadlock situation. So ungate work must be done in a separate work queue with WQ_MEM_RECLAIM flag enabled. Such a workqueue will have a rescue thread which will be called when the above deadlock condition is possible. Signed-off-by: Vijay Viswanath Signed-off-by: Can Guo Signed-off-by: Asutosh Das Reviewed-by: Subhash Jadavani Signed-off-by: Martin K. Petersen --- drivers/scsi/ufs/ufshcd.c | 11 ++++++++++- drivers/scsi/ufs/ufshcd.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 028492179101..77e2b3e1f8a2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1532,7 +1532,8 @@ start: hba->clk_gating.state = REQ_CLKS_ON; trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state); - schedule_work(&hba->clk_gating.ungate_work); + queue_work(hba->clk_gating.clk_gating_workq, + &hba->clk_gating.ungate_work); /* * fall through to check if we should wait for this * work to be done or not. @@ -1718,6 +1719,8 @@ out: static void ufshcd_init_clk_gating(struct ufs_hba *hba) { + char wq_name[sizeof("ufs_clk_gating_00")]; + if (!ufshcd_is_clkgating_allowed(hba)) return; @@ -1725,6 +1728,11 @@ static void ufshcd_init_clk_gating(struct ufs_hba *hba) INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work); INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work); + snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d", + hba->host->host_no); + hba->clk_gating.clk_gating_workq = alloc_ordered_workqueue(wq_name, + WQ_MEM_RECLAIM); + hba->clk_gating.is_enabled = true; hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show; @@ -1752,6 +1760,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) device_remove_file(hba->dev, &hba->clk_gating.enable_attr); cancel_work_sync(&hba->clk_gating.ungate_work); cancel_delayed_work_sync(&hba->clk_gating.gate_work); + destroy_workqueue(hba->clk_gating.clk_gating_workq); } /* Must be called with host lock acquired */ diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index a44b9f45ddd2..f51758f1e5cc 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -362,6 +362,7 @@ struct ufs_clk_gating { struct device_attribute enable_attr; bool is_enabled; int active_reqs; + struct workqueue_struct *clk_gating_workq; }; struct ufs_saved_pwr_info { -- cgit v1.2.3 From 51b910c3c70986a5a0a84eea11cb8e904e37ba8b Mon Sep 17 00:00:00 2001 From: Christophe Jaillet Date: Thu, 10 May 2018 13:45:58 +0200 Subject: scsi: qlogicpti: Fix an error handling path in 'qpti_sbus_probe()' The 'free_irq()' call is not at the right place in the error handling path. The changed order has been introduced in commit 3d4253d9afab ("[SCSI] qlogicpti: Convert to new SBUS device framework.") Fixes: 3d4253d9afab ("[SCSI] qlogicpti: Convert to new SBUS device framework.") Signed-off-by: Christophe JAILLET Reviewed-by: Dan Carpenter Signed-off-by: Martin K. Petersen --- drivers/scsi/qlogicpti.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index cec9a14982e6..8578e566ab41 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1385,6 +1385,9 @@ fail_unmap_queues: qpti->req_cpu, qpti->req_dvma); #undef QSIZE +fail_free_irq: + free_irq(qpti->irq, qpti); + fail_unmap_regs: of_iounmap(&op->resource[0], qpti->qregs, resource_size(&op->resource[0])); @@ -1392,9 +1395,6 @@ fail_unmap_regs: of_iounmap(&op->resource[0], qpti->sreg, sizeof(unsigned char)); -fail_free_irq: - free_irq(qpti->irq, qpti); - fail_unlink: scsi_host_put(host); -- cgit v1.2.3 From bd81372065fa467e262150c70be885f47f9535df Mon Sep 17 00:00:00 2001 From: Lee Duncan Date: Tue, 15 May 2018 18:25:24 -0700 Subject: scsi: target: transport should handle st FM/EOM/ILI reads When a tape drive is exported via LIO using the pscsi module, a read that requests more bytes per block than the tape can supply returns an empty buffer. This is because the pscsi pass-through target module sees the "ILI" illegal length bit set and thinks there is no reason to return the data. This is a long-standing transport issue, since it assumes that no data need be returned under a check condition, which isn't always the case for tape. Add in a check for tape reads with the ILI, EOM, or FM bits set, with a sense code of NO_SENSE, treating such cases as if the read succeeded. The layered tape driver then "does the right thing" when it gets such a response. Signed-off-by: Bodo Stroesser Signed-off-by: Lee Duncan Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/target/target_core_pscsi.c | 26 ++++++++++++++++++-- drivers/target/target_core_transport.c | 43 +++++++++++++++++++++++++++++----- include/target/target_core_base.h | 1 + 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 0d99b242e82e..f31215b1d009 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -689,8 +689,29 @@ after_mode_sense: } after_mode_select: - if (scsi_status == SAM_STAT_CHECK_CONDITION) + if (scsi_status == SAM_STAT_CHECK_CONDITION) { transport_copy_sense_to_cmd(cmd, req_sense); + + /* + * check for TAPE device reads with + * FM/EOM/ILI set, so that we can get data + * back despite framework assumption that a + * check condition means there is no data + */ + if (sd->type == TYPE_TAPE && + cmd->data_direction == DMA_FROM_DEVICE) { + /* + * is sense data valid, fixed format, + * and have FM, EOM, or ILI set? + */ + if (req_sense[0] == 0xf0 && /* valid, fixed format */ + req_sense[2] & 0xe0 && /* FM, EOM, or ILI */ + (req_sense[2] & 0xf) == 0) { /* key==NO_SENSE */ + pr_debug("Tape FM/EOM/ILI status detected. Treat as normal read.\n"); + cmd->se_cmd_flags |= SCF_TREAT_READ_AS_NORMAL; + } + } + } } enum { @@ -1061,7 +1082,8 @@ static void pscsi_req_done(struct request *req, blk_status_t status) switch (host_byte(result)) { case DID_OK: - target_complete_cmd(cmd, scsi_status); + target_complete_cmd_with_length(cmd, scsi_status, + cmd->data_length - scsi_req(req)->resid_len); break; default: pr_debug("PSCSI Host Byte exception at cmd: %p CDB:" diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 3500aa5927f2..bde14f46ed5b 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -779,7 +779,9 @@ EXPORT_SYMBOL(target_complete_cmd); void target_complete_cmd_with_length(struct se_cmd *cmd, u8 scsi_status, int length) { - if (scsi_status == SAM_STAT_GOOD && length < cmd->data_length) { + if ((scsi_status == SAM_STAT_GOOD || + cmd->se_cmd_flags & SCF_TREAT_READ_AS_NORMAL) && + length < cmd->data_length) { if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { cmd->residual_count += cmd->data_length - length; } else { @@ -2084,12 +2086,24 @@ static void transport_complete_qf(struct se_cmd *cmd) goto queue_status; } - if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) + /* + * Check if we need to send a sense buffer from + * the struct se_cmd in question. We do NOT want + * to take this path of the IO has been marked as + * needing to be treated like a "normal read". This + * is the case if it's a tape read, and either the + * FM, EOM, or ILI bits are set, but there is no + * sense data. + */ + if (!(cmd->se_cmd_flags & SCF_TREAT_READ_AS_NORMAL) && + cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) goto queue_status; switch (cmd->data_direction) { case DMA_FROM_DEVICE: - if (cmd->scsi_status) + /* queue status if not treating this as a normal read */ + if (cmd->scsi_status && + !(cmd->se_cmd_flags & SCF_TREAT_READ_AS_NORMAL)) goto queue_status; trace_target_cmd_complete(cmd); @@ -2194,9 +2208,15 @@ static void target_complete_ok_work(struct work_struct *work) /* * Check if we need to send a sense buffer from - * the struct se_cmd in question. + * the struct se_cmd in question. We do NOT want + * to take this path of the IO has been marked as + * needing to be treated like a "normal read". This + * is the case if it's a tape read, and either the + * FM, EOM, or ILI bits are set, but there is no + * sense data. */ - if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { + if (!(cmd->se_cmd_flags & SCF_TREAT_READ_AS_NORMAL) && + cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { WARN_ON(!cmd->scsi_status); ret = transport_send_check_condition_and_sense( cmd, 0, 1); @@ -2238,7 +2258,18 @@ static void target_complete_ok_work(struct work_struct *work) queue_rsp: switch (cmd->data_direction) { case DMA_FROM_DEVICE: - if (cmd->scsi_status) + /* + * if this is a READ-type IO, but SCSI status + * is set, then skip returning data and just + * return the status -- unless this IO is marked + * as needing to be treated as a normal read, + * in which case we want to go ahead and return + * the data. This happens, for example, for tape + * reads with the FM, EOM, or ILI bits set, with + * no sense data. + */ + if (cmd->scsi_status && + !(cmd->se_cmd_flags & SCF_TREAT_READ_AS_NORMAL)) goto queue_status; atomic_long_add(cmd->data_length, diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 9f9f5902af38..922a39f45abc 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -143,6 +143,7 @@ enum se_cmd_flags_table { SCF_ACK_KREF = 0x00400000, SCF_USE_CPUID = 0x00800000, SCF_TASK_ATTR_SET = 0x01000000, + SCF_TREAT_READ_AS_NORMAL = 0x02000000, }; /* -- cgit v1.2.3 From 017b3f8a10cad93263690251e5c9062df8f39ccd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 17 May 2018 08:57:38 +0100 Subject: scsi: snic: fix a couple of spelling mistakes: "COMPLETE" Trivial fix to spelling mistakes/typos: "SNIC_IOREQ_ABTS_COMPELTE" -> "SNIC_IOREQ_ABTS_COMPLETE" "SNIC_IOREQ_LR_COMPELTE" -> "SNIC_IOREQ_LR_COMPLETE" "SNIC_IOREQ_CMD_COMPELTE" -> "SNIC_IOREQ_CMD_COMPLETE" Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/scsi/snic/snic_scsi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/snic/snic_scsi.c b/drivers/scsi/snic/snic_scsi.c index d8a376b7882d..d9b2e46424aa 100644 --- a/drivers/scsi/snic/snic_scsi.c +++ b/drivers/scsi/snic/snic_scsi.c @@ -47,10 +47,10 @@ static const char * const snic_req_state_str[] = { [SNIC_IOREQ_NOT_INITED] = "SNIC_IOREQ_NOT_INITED", [SNIC_IOREQ_PENDING] = "SNIC_IOREQ_PENDING", [SNIC_IOREQ_ABTS_PENDING] = "SNIC_IOREQ_ABTS_PENDING", - [SNIC_IOREQ_ABTS_COMPLETE] = "SNIC_IOREQ_ABTS_COMPELTE", + [SNIC_IOREQ_ABTS_COMPLETE] = "SNIC_IOREQ_ABTS_COMPLETE", [SNIC_IOREQ_LR_PENDING] = "SNIC_IOREQ_LR_PENDING", - [SNIC_IOREQ_LR_COMPLETE] = "SNIC_IOREQ_LR_COMPELTE", - [SNIC_IOREQ_COMPLETE] = "SNIC_IOREQ_CMD_COMPELTE", + [SNIC_IOREQ_LR_COMPLETE] = "SNIC_IOREQ_LR_COMPLETE", + [SNIC_IOREQ_COMPLETE] = "SNIC_IOREQ_CMD_COMPLETE", }; /* snic cmd status strings */ -- cgit v1.2.3 From e37c7d9a0341a3577a071f4ac55912f38be229e2 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Fri, 18 May 2018 19:25:47 -0400 Subject: scsi: core: sanitize++ in progress Commit 505aa4b6a883 ("scsi: sd: Defer spinning up drive while SANITIZE is in progress") may not be sufficient, especially if the SCSI SANITIZE command is sent via the bsg or sg pass-throughs, since they don't use the sd driver. Add "Sanitize in progress" plus some other recent "... in progress" additional sense codes into the scsi mid-level so they are treated in a similar fashion to "Format in progress". [mkp: checkpatch] Signed-off-by: Douglas Gilbert Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index e9b4f279d29c..9df5fbdbc854 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -985,6 +985,10 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) case 0x08: /* Long write in progress */ case 0x09: /* self test in progress */ case 0x14: /* space allocation in progress */ + case 0x1a: /* start stop unit in progress */ + case 0x1b: /* sanitize in progress */ + case 0x1d: /* configuration in progress */ + case 0x24: /* depopulation in progress */ action = ACTION_DELAYED_RETRY; break; default: -- cgit v1.2.3 From 9af3c479242246e61bd3eb58dba8efd61903a183 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 26 May 2018 15:42:18 +0100 Subject: scsi: pm80xx: fix spelling mistake "UNSORPORTED" -> "SUPPORTED" Trivial fix to spelling mistake in pm8001_printk message text; also I believe NOT_UNSUPPORTED should probably be NOT_SUPPORTED. Also fix the indent of the pm8001_printk statement. Signed-off-by: Colin Ian King Acked-by: Jack Wang Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm8001_hwi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index db88a8e7ee0e..4dd6cad330e8 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -3607,7 +3607,7 @@ int pm8001_mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb) break; default: PM8001_MSG_DBG(pm8001_ha, - pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_UNSORPORTED\n")); + pm8001_printk("DEVREG_FAILURE_DEVICE_TYPE_NOT_SUPPORTED\n")); break; } complete(pm8001_dev->dcompletion); -- cgit v1.2.3 From c16782808865b3b984d530eb535cafa889346989 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 26 May 2018 16:02:29 +0100 Subject: scsi: target: fix spelling mistake "Uknown" -> "Unknown" Trivial fix to spelling mistake in pr_err message text. Signed-off-by: Colin Ian King Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index bde14f46ed5b..f0e8f0f4ccb4 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -3366,7 +3366,7 @@ static void target_tmr_work(struct work_struct *work) tmr->response = TMR_FUNCTION_REJECTED; break; default: - pr_err("Uknown TMR function: 0x%02x.\n", + pr_err("Unknown TMR function: 0x%02x.\n", tmr->function); tmr->response = TMR_FUNCTION_REJECTED; break; -- cgit v1.2.3 From eb217359ebeae7b9ac51045d50f7a516925bc5c2 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 26 May 2018 09:34:22 +0000 Subject: scsi: hisi_sas: fix a typo in hisi_sas_task_prep() Fix a typo in hisi_sas_task_prep(). Fixes: 7eee4b921822 ("scsi: hisi_sas: relocate smp sg map") Signed-off-by: Wei Yongjun Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 39f694eb7b00..6d3796553272 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -390,7 +390,7 @@ static int hisi_sas_task_prep(struct sas_task *task, } n_elem_resp = dma_map_sg(dev, &task->smp_task.smp_resp, 1, DMA_FROM_DEVICE); - if (!n_elem_req) { + if (!n_elem_resp) { rc = -ENOMEM; goto err_out_dma_unmap; } -- cgit v1.2.3 From 757db2dae2c79b1f713043fcc13542683963fa82 Mon Sep 17 00:00:00 2001 From: John Garry Date: Mon, 21 May 2018 18:09:13 +0800 Subject: scsi: hisi_sas: Introduce hisi_sas_phy_set_linkrate() There is much common code and functionality between the HW versions to set the PHY linkrate. As such, this patch factors out the common code into a generic function hisi_sas_phy_set_linkrate(). Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 29 +++++++++++++++++++++++++++- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 35 ++++------------------------------ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 21 ++------------------ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 21 ++------------------ 4 files changed, 36 insertions(+), 70 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 6d3796553272..66388741e73c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -866,6 +866,33 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) return hisi_sas_task_exec(task, gfp_flags, 0, NULL); } +static void hisi_sas_phy_set_linkrate(struct hisi_hba *hisi_hba, int phy_no, + struct sas_phy_linkrates *r) +{ + struct sas_phy_linkrates _r; + + struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; + struct asd_sas_phy *sas_phy = &phy->sas_phy; + enum sas_linkrate min, max; + + if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { + max = sas_phy->phy->maximum_linkrate; + min = r->minimum_linkrate; + } else if (r->minimum_linkrate == SAS_LINK_RATE_UNKNOWN) { + max = r->maximum_linkrate; + min = sas_phy->phy->minimum_linkrate; + } else + return; + + _r.maximum_linkrate = max; + _r.minimum_linkrate = min; + + hisi_hba->hw->phy_disable(hisi_hba, phy_no); + msleep(100); + hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, &_r); + hisi_hba->hw->phy_start(hisi_hba, phy_no); +} + static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata) { @@ -889,7 +916,7 @@ static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, break; case PHY_FUNC_SET_LINK_RATE: - hisi_hba->hw->phy_set_linkrate(hisi_hba, phy_no, funcdata); + hisi_sas_phy_set_linkrate(hisi_hba, phy_no, funcdata); break; case PHY_FUNC_GET_EVENTS: if (hisi_hba->hw->get_events) { diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index abe175fddb86..8fa79d0968a6 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -855,39 +855,12 @@ static enum sas_linkrate phy_get_max_linkrate_v1_hw(void) static void phy_set_linkrate_v1_hw(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { - u32 prog_phy_link_rate = - hisi_sas_phy_read32(hisi_hba, phy_no, PROG_PHY_LINK_RATE); - struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; - struct asd_sas_phy *sas_phy = &phy->sas_phy; - int i; - enum sas_linkrate min, max; - u32 rate_mask = 0; - - if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { - max = sas_phy->phy->maximum_linkrate; - min = r->minimum_linkrate; - } else if (r->minimum_linkrate == SAS_LINK_RATE_UNKNOWN) { - max = r->maximum_linkrate; - min = sas_phy->phy->minimum_linkrate; - } else - return; - - sas_phy->phy->maximum_linkrate = max; - sas_phy->phy->minimum_linkrate = min; - - max -= SAS_LINK_RATE_1_5_GBPS; + enum sas_linkrate max = r->maximum_linkrate; + u32 prog_phy_link_rate = 0x800; - for (i = 0; i <= max; i++) - rate_mask |= 1 << (i * 2); - - prog_phy_link_rate &= ~0xff; - prog_phy_link_rate |= rate_mask; - - disable_phy_v1_hw(hisi_hba, phy_no); - msleep(100); + prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max); hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE, - prog_phy_link_rate); - start_phy_v1_hw(hisi_hba, phy_no); + prog_phy_link_rate); } static int get_wideport_bitmap_v1_hw(struct hisi_hba *hisi_hba, int port_id) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 911bb76a4d0a..fb0e966a44a8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1600,29 +1600,12 @@ static enum sas_linkrate phy_get_max_linkrate_v2_hw(void) static void phy_set_linkrate_v2_hw(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { - struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; - struct asd_sas_phy *sas_phy = &phy->sas_phy; - enum sas_linkrate min, max; + enum sas_linkrate max = r->maximum_linkrate; u32 prog_phy_link_rate = 0x800; - if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { - max = sas_phy->phy->maximum_linkrate; - min = r->minimum_linkrate; - } else if (r->minimum_linkrate == SAS_LINK_RATE_UNKNOWN) { - max = r->maximum_linkrate; - min = sas_phy->phy->minimum_linkrate; - } else - return; - - sas_phy->phy->maximum_linkrate = max; - sas_phy->phy->minimum_linkrate = min; prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max); - - disable_phy_v2_hw(hisi_hba, phy_no); - msleep(100); hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE, - prog_phy_link_rate); - start_phy_v2_hw(hisi_hba, phy_no); + prog_phy_link_rate); } static int get_wideport_bitmap_v2_hw(struct hisi_hba *hisi_hba, int port_id) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index c013673ee8ac..0a80a39eccdd 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1879,29 +1879,12 @@ static int hisi_sas_v3_init(struct hisi_hba *hisi_hba) static void phy_set_linkrate_v3_hw(struct hisi_hba *hisi_hba, int phy_no, struct sas_phy_linkrates *r) { - struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; - struct asd_sas_phy *sas_phy = &phy->sas_phy; - enum sas_linkrate min, max; + enum sas_linkrate max = r->maximum_linkrate; u32 prog_phy_link_rate = 0x800; - if (r->maximum_linkrate == SAS_LINK_RATE_UNKNOWN) { - max = sas_phy->phy->maximum_linkrate; - min = r->minimum_linkrate; - } else if (r->minimum_linkrate == SAS_LINK_RATE_UNKNOWN) { - max = r->maximum_linkrate; - min = sas_phy->phy->minimum_linkrate; - } else - return; - - sas_phy->phy->maximum_linkrate = max; - sas_phy->phy->minimum_linkrate = min; prog_phy_link_rate |= hisi_sas_get_prog_phy_linkrate_mask(max); - - disable_phy_v3_hw(hisi_hba, phy_no); - msleep(100); hisi_sas_phy_write32(hisi_hba, phy_no, PROG_PHY_LINK_RATE, - prog_phy_link_rate); - start_phy_v3_hw(hisi_hba, phy_no); + prog_phy_link_rate); } static void interrupt_disable_v3_hw(struct hisi_hba *hisi_hba) -- cgit v1.2.3 From fa3be0f23139ddc4dffbfdef6bbd118e30dfcafe Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Mon, 21 May 2018 18:09:14 +0800 Subject: scsi: hisi_sas: change slot index allocation mode Currently we find the lowest available empty bit in the IPTT bitmap to allocate the IPTT for a command. To reduce possibility of hitting unknown SoC bugs and also aid in the debugging of those same bugs, change the allocation mode. The next allocation method is to use the next free slot adjacent to the most recently allocated slot, in a round-robin fashion. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 52fc709dd862..3c8840089cd5 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -274,6 +274,7 @@ struct hisi_hba { struct workqueue_struct *wq; int slot_index_count; + int last_slot_index; unsigned long *slot_index_tags; unsigned long reject_stp_links_msk; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 66388741e73c..796fdfc73c75 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -195,11 +195,18 @@ static int hisi_sas_slot_index_alloc(struct hisi_hba *hisi_hba, int *slot_idx) unsigned int index; void *bitmap = hisi_hba->slot_index_tags; - index = find_first_zero_bit(bitmap, hisi_hba->slot_index_count); - if (index >= hisi_hba->slot_index_count) - return -SAS_QUEUE_FULL; + index = find_next_zero_bit(bitmap, hisi_hba->slot_index_count, + hisi_hba->last_slot_index + 1); + if (index >= hisi_hba->slot_index_count) { + index = find_next_zero_bit(bitmap, hisi_hba->slot_index_count, + 0); + if (index >= hisi_hba->slot_index_count) + return -SAS_QUEUE_FULL; + } hisi_sas_slot_index_set(hisi_hba, index); *slot_idx = index; + hisi_hba->last_slot_index = index; + return 0; } -- cgit v1.2.3 From 1b86518581f6111f5996ff8d4304bde2e3b05eb9 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Mon, 21 May 2018 18:09:15 +0800 Subject: scsi: hisi_sas: Change common allocation mode of device id To reduce possibility of hitting unknown SoC bugs and aid debugging and test, change allocation mode of device id from last used device id instead of lowest available index. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 1 + drivers/scsi/hisi_sas/hisi_sas_main.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 3c8840089cd5..b4717bd8af3f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -275,6 +275,7 @@ struct hisi_hba { int slot_index_count; int last_slot_index; + int last_dev_id; unsigned long *slot_index_tags; unsigned long reject_stp_links_msk; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 796fdfc73c75..a7e4c6e77068 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -596,10 +596,12 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct hisi_sas_device *sas_dev = NULL; unsigned long flags; + int last = hisi_hba->last_dev_id; + int first = (hisi_hba->last_dev_id + 1) % HISI_SAS_MAX_DEVICES; int i; spin_lock_irqsave(&hisi_hba->lock, flags); - for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { + for (i = first; i != last; i %= HISI_SAS_MAX_DEVICES) { if (hisi_hba->devices[i].dev_type == SAS_PHY_UNUSED) { int queue = i % hisi_hba->queue_count; struct hisi_sas_dq *dq = &hisi_hba->dq[queue]; @@ -614,7 +616,9 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) INIT_LIST_HEAD(&hisi_hba->devices[i].list); break; } + i++; } + hisi_hba->last_dev_id = i; spin_unlock_irqrestore(&hisi_hba->lock, flags); return sas_dev; -- cgit v1.2.3 From 428f1b3424f4fe750943d8cdd1b0dafad99b0b75 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:16 +0800 Subject: scsi: hisi_sas: Add LED feature for v3 hw This patch implements LED feature of directly attached disk for v3 hw. In fact, this hw has created an SGPIO component for LED feature, and we can control LEDs just by internal registers. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 0a80a39eccdd..a043d9cdbf48 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -107,6 +107,10 @@ #define AWQOS_AWCACHE_CFG 0xc84 #define ARQOS_ARCACHE_CFG 0xc88 #define HILINK_ERR_DFX 0xe04 +#define SAS_GPIO_CFG_0 0x1000 +#define SAS_GPIO_CFG_1 0x1004 +#define SAS_GPIO_TX_0_1 0x1040 +#define SAS_CFG_DRIVE_VLD 0x1070 /* phy registers requiring init */ #define PORT_BASE (0x2000) @@ -549,6 +553,14 @@ static void init_reg_v3_hw(struct hisi_hba *hisi_hba) hisi_sas_write32(hisi_hba, SAS_RAS_INTR1_MASK, 0x0); hisi_sas_write32(hisi_hba, SAS_RAS_INTR2_MASK, 0x0); hisi_sas_write32(hisi_hba, CFG_SAS_RAS_INTR_MASK, 0x0); + + /* LED registers init */ + hisi_sas_write32(hisi_hba, SAS_CFG_DRIVE_VLD, 0x80000ff); + hisi_sas_write32(hisi_hba, SAS_GPIO_TX_0_1, 0x80808080); + hisi_sas_write32(hisi_hba, SAS_GPIO_TX_0_1 + 0x4, 0x80808080); + /* Configure blink generator rate A to 1Hz and B to 4Hz */ + hisi_sas_write32(hisi_hba, SAS_GPIO_CFG_1, 0x121700); + hisi_sas_write32(hisi_hba, SAS_GPIO_CFG_0, 0x800000); } static void config_phy_opt_mode_v3_hw(struct hisi_hba *hisi_hba, int phy_no) @@ -1974,6 +1986,35 @@ static int soft_reset_v3_hw(struct hisi_hba *hisi_hba) return hw_init_v3_hw(hisi_hba); } +static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type, + u8 reg_index, u8 reg_count, u8 *write_data) +{ + struct device *dev = hisi_hba->dev; + u32 *data = (u32 *)write_data; + int i; + + switch (reg_type) { + case SAS_GPIO_REG_TX: + if ((reg_index + reg_count) > ((hisi_hba->n_phy + 3) / 4)) { + dev_err(dev, "write gpio: invalid reg range[%d, %d]\n", + reg_index, reg_index + reg_count - 1); + return -EINVAL; + } + + for (i = 0; i < reg_count; i++) + hisi_sas_write32(hisi_hba, + SAS_GPIO_TX_0_1 + (reg_index + i) * 4, + data[i]); + break; + default: + dev_err(dev, "write gpio: unsupported or bad reg type %d\n", + reg_type); + return -EINVAL; + } + + return 0; +} + static const struct hisi_sas_hw hisi_sas_v3_hw = { .hw_init = hisi_sas_v3_init, .setup_itct = setup_itct_v3_hw, @@ -1999,6 +2040,7 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .soft_reset = soft_reset_v3_hw, .get_phys_state = get_phys_state_v3_hw, .get_events = phy_get_events_v3_hw, + .write_gpio = write_gpio_v3_hw, }; static struct Scsi_Host * -- cgit v1.2.3 From d5a60dfdb364bd1fa59c2c11be54be80f6990a3d Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Mon, 21 May 2018 18:09:17 +0800 Subject: scsi: hisi_sas: Reset disks when discovered When a disk is discovered, it may be in an error state, or there may be residual commands remaining in the disk. To ensure any disk is in good state after discovery, reset via TMF (for SAS disk) or softreset (for a SATA disk). Signed-off-by: Xiang Chen Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 50 ++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index a7e4c6e77068..c8e647a65b30 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -24,6 +24,9 @@ hisi_sas_internal_task_abort(struct hisi_hba *hisi_hba, static int hisi_sas_softreset_ata_disk(struct domain_device *device); static int hisi_sas_control_phy(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata); +static void hisi_sas_release_task(struct hisi_hba *hisi_hba, + struct domain_device *device); +static void hisi_sas_dev_gone(struct domain_device *device); u8 hisi_sas_get_ata_protocol(struct host_to_dev_fis *fis, int direction) { @@ -624,12 +627,49 @@ static struct hisi_sas_device *hisi_sas_alloc_dev(struct domain_device *device) return sas_dev; } +#define HISI_SAS_SRST_ATA_DISK_CNT 3 +static int hisi_sas_init_device(struct domain_device *device) +{ + int rc = TMF_RESP_FUNC_COMPLETE; + struct scsi_lun lun; + struct hisi_sas_tmf_task tmf_task; + int retry = HISI_SAS_SRST_ATA_DISK_CNT; + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + + switch (device->dev_type) { + case SAS_END_DEVICE: + int_to_scsilun(0, &lun); + + tmf_task.tmf = TMF_CLEAR_TASK_SET; + rc = hisi_sas_debug_issue_ssp_tmf(device, lun.scsi_lun, + &tmf_task); + if (rc == TMF_RESP_FUNC_COMPLETE) + hisi_sas_release_task(hisi_hba, device); + break; + case SAS_SATA_DEV: + case SAS_SATA_PM: + case SAS_SATA_PM_PORT: + case SAS_SATA_PENDING: + while (retry-- > 0) { + rc = hisi_sas_softreset_ata_disk(device); + if (!rc) + break; + } + break; + default: + break; + } + + return rc; +} + static int hisi_sas_dev_found(struct domain_device *device) { struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); struct domain_device *parent_dev = device->parent; struct hisi_sas_device *sas_dev; struct device *dev = hisi_hba->dev; + int rc; if (hisi_hba->hw->alloc_dev) sas_dev = hisi_hba->hw->alloc_dev(device); @@ -661,14 +701,22 @@ static int hisi_sas_dev_found(struct domain_device *device) "dev:%016llx at ex:%016llx\n", SAS_ADDR(device->sas_addr), SAS_ADDR(parent_dev->sas_addr)); - return -EINVAL; + rc = -EINVAL; + goto err_out; } } dev_info(dev, "dev[%d:%x] found\n", sas_dev->device_id, sas_dev->dev_type); + rc = hisi_sas_init_device(device); + if (rc) + goto err_out; return 0; + +err_out: + hisi_sas_dev_gone(device); + return rc; } static int hisi_sas_slave_configure(struct scsi_device *sdev) -- cgit v1.2.3 From 235bfc7ff63027e90c25663ed7a976083f5afb47 Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Mon, 21 May 2018 18:09:18 +0800 Subject: scsi: hisi_sas: Create a scsi_host_template per HW module When a SCSI host is registered, the SCSI mid-layer takes a reference to a module in Scsi_host.hostt.module. In doing this, we are prevented from removing the driver module for the host in dangerous scenario, like when a disk is mounted. Currently there is only one scsi_host_template (sht) for all HW versions, and this is the main.c module. So this means that we can possibly remove the HW module in this dangerous scenario, as SCSI mid-layer is only referencing the main.c module. To fix this, create a sht per module, referencing that same module to create the Scsi host. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 8 +++++-- drivers/scsi/hisi_sas/hisi_sas_main.c | 40 +++++++++------------------------- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 23 +++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 24 ++++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 24 +++++++++++++++++++- 5 files changed, 86 insertions(+), 33 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index b4717bd8af3f..37c9a6246dc8 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -248,6 +248,7 @@ struct hisi_sas_hw { u8 reg_index, u8 reg_count, u8 *write_data); int max_command_entries; int complete_hdr_size; + struct scsi_host_template *sht; }; struct hisi_hba { @@ -440,8 +441,6 @@ struct hisi_sas_slot_buf_table { }; extern struct scsi_transport_template *hisi_sas_stt; -extern struct scsi_host_template *hisi_sas_sht; - extern void hisi_sas_stop_phys(struct hisi_hba *hisi_hba); extern int hisi_sas_alloc(struct hisi_hba *hisi_hba, struct Scsi_Host *shost); extern void hisi_sas_free(struct hisi_hba *hisi_hba); @@ -456,6 +455,11 @@ extern int hisi_sas_probe(struct platform_device *pdev, const struct hisi_sas_hw *ops); extern int hisi_sas_remove(struct platform_device *pdev); +extern int hisi_sas_slave_configure(struct scsi_device *sdev); +extern int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time); +extern void hisi_sas_scan_start(struct Scsi_Host *shost); +extern struct device_attribute *host_attrs[]; +extern int hisi_sas_host_reset(struct Scsi_Host *shost, int reset_type); extern void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy); extern void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index c8e647a65b30..664aaf7f5147 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -719,7 +719,7 @@ err_out: return rc; } -static int hisi_sas_slave_configure(struct scsi_device *sdev) +int hisi_sas_slave_configure(struct scsi_device *sdev) { struct domain_device *dev = sdev_to_domain_dev(sdev); int ret = sas_slave_configure(sdev); @@ -731,15 +731,17 @@ static int hisi_sas_slave_configure(struct scsi_device *sdev) return 0; } +EXPORT_SYMBOL_GPL(hisi_sas_slave_configure); -static void hisi_sas_scan_start(struct Scsi_Host *shost) +void hisi_sas_scan_start(struct Scsi_Host *shost) { struct hisi_hba *hisi_hba = shost_priv(shost); hisi_hba->hw->phys_init(hisi_hba); } +EXPORT_SYMBOL_GPL(hisi_sas_scan_start); -static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time) +int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time) { struct hisi_hba *hisi_hba = shost_priv(shost); struct sas_ha_struct *sha = &hisi_hba->sha; @@ -751,6 +753,7 @@ static int hisi_sas_scan_finished(struct Scsi_Host *shost, unsigned long time) sas_drain_work(sha); return 1; } +EXPORT_SYMBOL_GPL(hisi_sas_scan_finished); static void hisi_sas_phyup_work(struct work_struct *work) { @@ -1824,34 +1827,11 @@ EXPORT_SYMBOL_GPL(hisi_sas_kill_tasklets); struct scsi_transport_template *hisi_sas_stt; EXPORT_SYMBOL_GPL(hisi_sas_stt); -static struct device_attribute *host_attrs[] = { +struct device_attribute *host_attrs[] = { &dev_attr_phy_event_threshold, NULL, }; - -static struct scsi_host_template _hisi_sas_sht = { - .module = THIS_MODULE, - .name = DRV_NAME, - .queuecommand = sas_queuecommand, - .target_alloc = sas_target_alloc, - .slave_configure = hisi_sas_slave_configure, - .scan_finished = hisi_sas_scan_finished, - .scan_start = hisi_sas_scan_start, - .change_queue_depth = sas_change_queue_depth, - .bios_param = sas_bios_param, - .can_queue = 1, - .this_id = -1, - .sg_tablesize = SG_ALL, - .max_sectors = SCSI_DEFAULT_MAX_SECTORS, - .use_clustering = ENABLE_CLUSTERING, - .eh_device_reset_handler = sas_eh_device_reset_handler, - .eh_target_reset_handler = sas_eh_target_reset_handler, - .target_destroy = sas_target_destroy, - .ioctl = sas_ioctl, - .shost_attrs = host_attrs, -}; -struct scsi_host_template *hisi_sas_sht = &_hisi_sas_sht; -EXPORT_SYMBOL_GPL(hisi_sas_sht); +EXPORT_SYMBOL_GPL(host_attrs); static struct sas_domain_function_template hisi_sas_transport_ops = { .lldd_dev_found = hisi_sas_dev_found, @@ -2161,7 +2141,7 @@ static struct Scsi_Host *hisi_sas_shost_alloc(struct platform_device *pdev, struct hisi_hba *hisi_hba; struct device *dev = &pdev->dev; - shost = scsi_host_alloc(hisi_sas_sht, sizeof(*hisi_hba)); + shost = scsi_host_alloc(hw->sht, sizeof(*hisi_hba)); if (!shost) { dev_err(dev, "scsi host alloc failed\n"); return NULL; @@ -2211,7 +2191,7 @@ err_out: } int hisi_sas_probe(struct platform_device *pdev, - const struct hisi_sas_hw *hw) + const struct hisi_sas_hw *hw) { struct Scsi_Host *shost; struct hisi_hba *hisi_hba; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 8fa79d0968a6..8d5d8575d939 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1788,6 +1788,28 @@ static int hisi_sas_v1_init(struct hisi_hba *hisi_hba) return 0; } +static struct scsi_host_template sht_v1_hw = { + .name = DRV_NAME, + .module = THIS_MODULE, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = hisi_sas_slave_configure, + .scan_finished = hisi_sas_scan_finished, + .scan_start = hisi_sas_scan_start, + .change_queue_depth = sas_change_queue_depth, + .bios_param = sas_bios_param, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_target_reset_handler = sas_eh_target_reset_handler, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, + .shost_attrs = host_attrs, +}; + static const struct hisi_sas_hw hisi_sas_v1_hw = { .hw_init = hisi_sas_v1_init, .setup_itct = setup_itct_v1_hw, @@ -1807,6 +1829,7 @@ static const struct hisi_sas_hw hisi_sas_v1_hw = { .get_wideport_bitmap = get_wideport_bitmap_v1_hw, .max_command_entries = HISI_SAS_COMMAND_ENTRIES_V1_HW, .complete_hdr_size = sizeof(struct hisi_sas_complete_v1_hdr), + .sht = &sht_v1_hw, }; static int hisi_sas_v1_probe(struct platform_device *pdev) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index fb0e966a44a8..8def327c4f46 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -3501,6 +3501,29 @@ static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type, return 0; } + +static struct scsi_host_template sht_v2_hw = { + .name = DRV_NAME, + .module = THIS_MODULE, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = hisi_sas_slave_configure, + .scan_finished = hisi_sas_scan_finished, + .scan_start = hisi_sas_scan_start, + .change_queue_depth = sas_change_queue_depth, + .bios_param = sas_bios_param, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_target_reset_handler = sas_eh_target_reset_handler, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, + .shost_attrs = host_attrs, +}; + static const struct hisi_sas_hw hisi_sas_v2_hw = { .hw_init = hisi_sas_v2_init, .setup_itct = setup_itct_v2_hw, @@ -3529,6 +3552,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .soft_reset = soft_reset_v2_hw, .get_phys_state = get_phys_state_v2_hw, .write_gpio = write_gpio_v2_hw, + .sht = &sht_v2_hw, }; static int hisi_sas_v2_probe(struct platform_device *pdev) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index a043d9cdbf48..13d21349d1ba 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -2015,6 +2015,28 @@ static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type, return 0; } +static struct scsi_host_template sht_v3_hw = { + .name = DRV_NAME, + .module = THIS_MODULE, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = hisi_sas_slave_configure, + .scan_finished = hisi_sas_scan_finished, + .scan_start = hisi_sas_scan_start, + .change_queue_depth = sas_change_queue_depth, + .bios_param = sas_bios_param, + .can_queue = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_target_reset_handler = sas_eh_target_reset_handler, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, + .shost_attrs = host_attrs, +}; + static const struct hisi_sas_hw hisi_sas_v3_hw = { .hw_init = hisi_sas_v3_init, .setup_itct = setup_itct_v3_hw, @@ -2050,7 +2072,7 @@ hisi_sas_shost_alloc_pci(struct pci_dev *pdev) struct hisi_hba *hisi_hba; struct device *dev = &pdev->dev; - shost = scsi_host_alloc(hisi_sas_sht, sizeof(*hisi_hba)); + shost = scsi_host_alloc(&sht_v3_hw, sizeof(*hisi_hba)); if (!shost) { dev_err(dev, "shost alloc failed\n"); return NULL; -- cgit v1.2.3 From 6175abdeaeaf2602f3e92bd4eca5916e98efe996 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:19 +0800 Subject: scsi: hisi_sas: Init disks after controller reset After the controller is reset, it is possible that the disks attached still have outstanding IO to complete. Thus, when the PHYs come back up after controller reset, it is possible that these IOs complete at some unknown point later. We want to ensure that all IOs are complete after the controller reset so that all associated IPTT and other resources can be recycled safely. To achieve this, re-init the disks by TMF or softreset (in case of ATA devices). If the init fails - maybe because the device was removed or link has not come up - then do not release the device resources, but rather rely on SCSI EH to handle the timeout for these resources later on. This patch also does some cleanup to hisi_sas_init_disk(), including removing superfluous cases in the switch statement. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 664aaf7f5147..dc67bd7c7ab0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1259,6 +1259,23 @@ static void hisi_sas_rescan_topology(struct hisi_hba *hisi_hba, u32 old_state, } } +static void hisi_sas_reset_init_all_devices(struct hisi_hba *hisi_hba) +{ + struct hisi_sas_device *sas_dev; + struct domain_device *device; + int i; + + for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { + sas_dev = &hisi_hba->devices[i]; + device = sas_dev->sas_device; + + if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device) + continue; + + hisi_sas_init_device(device); + } +} + static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; @@ -1287,7 +1304,6 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) scsi_unblock_requests(shost); goto out; } - hisi_sas_release_tasks(hisi_hba); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); @@ -1295,6 +1311,7 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) hisi_hba->hw->phys_init(hisi_hba); msleep(1000); hisi_sas_refresh_port_id(hisi_hba); + hisi_sas_reset_init_all_devices(hisi_hba); scsi_unblock_requests(shost); state = hisi_hba->hw->get_phys_state(hisi_hba); -- cgit v1.2.3 From a865ae14ff62797f14b760b2063b90c81d27d178 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:20 +0800 Subject: scsi: hisi_sas: Try wait commands before before controller reset We may reset the controller in many scenarios, such as SCSI EH and HW errors. There should be no IO which returns from target when SCSI EH is active. But for other scenarios, there may be. It is not necessary to make such IOs fail. This patch adds an function of trying to wait for any commands, or IO, to complete before host reset. If no more CQ returned from host controller in 100ms, we assume no more IO can return, and then stop waiting. We wait 5s at most. The HW has a register CQE_SEND_CNT to indicate the total number of CQs that has been reported to driver. We can use this register and it is reliable to resd this register in such scenarios that require host reset. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 ++ drivers/scsi/hisi_sas/hisi_sas_main.c | 2 ++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 19 +++++++++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 37c9a6246dc8..8ce6abb48867 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -246,6 +246,8 @@ struct hisi_sas_hw { u32 (*get_phys_state)(struct hisi_hba *hisi_hba); int (*write_gpio)(struct hisi_hba *hisi_hba, u8 reg_type, u8 reg_index, u8 reg_count, u8 *write_data); + void (*wait_cmds_complete_timeout)(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms); int max_command_entries; int complete_hdr_size; struct scsi_host_template *sht; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index dc67bd7c7ab0..5bc522c35690 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1293,6 +1293,8 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) old_state = hisi_hba->hw->get_phys_state(hisi_hba); scsi_block_requests(shost); + hisi_hba->hw->wait_cmds_complete_timeout(hisi_hba, 100, 5000); + if (timer_pending(&hisi_hba->timer)) del_timer_sync(&hisi_hba->timer); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 8def327c4f46..18dbaffc9988 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -144,6 +144,7 @@ #define SAS_ECC_INTR_NCQ_MEM3_ECC_1B_OFF 19 #define SAS_ECC_INTR_MSK 0x1ec #define HGC_ERR_STAT_EN 0x238 +#define CQE_SEND_CNT 0x248 #define DLVRY_Q_0_BASE_ADDR_LO 0x260 #define DLVRY_Q_0_BASE_ADDR_HI 0x264 #define DLVRY_Q_0_DEPTH 0x268 @@ -3501,6 +3502,23 @@ static int write_gpio_v2_hw(struct hisi_hba *hisi_hba, u8 reg_type, return 0; } +static void wait_cmds_complete_timeout_v2_hw(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms) +{ + struct device *dev = hisi_hba->dev; + int entries, entries_old = 0, time; + + for (time = 0; time < timeout_ms; time += delay_ms) { + entries = hisi_sas_read32(hisi_hba, CQE_SEND_CNT); + if (entries == entries_old) + break; + + entries_old = entries; + msleep(delay_ms); + } + + dev_dbg(dev, "wait commands complete %dms\n", time); +} static struct scsi_host_template sht_v2_hw = { .name = DRV_NAME, @@ -3552,6 +3570,7 @@ static const struct hisi_sas_hw hisi_sas_v2_hw = { .soft_reset = soft_reset_v2_hw, .get_phys_state = get_phys_state_v2_hw, .write_gpio = write_gpio_v2_hw, + .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v2_hw, .sht = &sht_v2_hw, }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 13d21349d1ba..dd5f542bbc45 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -92,6 +92,7 @@ #define SAS_ECC_INTR 0x1e8 #define SAS_ECC_INTR_MSK 0x1ec #define HGC_ERR_STAT_EN 0x238 +#define CQE_SEND_CNT 0x248 #define DLVRY_Q_0_BASE_ADDR_LO 0x260 #define DLVRY_Q_0_BASE_ADDR_HI 0x264 #define DLVRY_Q_0_DEPTH 0x268 @@ -2015,6 +2016,24 @@ static int write_gpio_v3_hw(struct hisi_hba *hisi_hba, u8 reg_type, return 0; } +static void wait_cmds_complete_timeout_v3_hw(struct hisi_hba *hisi_hba, + int delay_ms, int timeout_ms) +{ + struct device *dev = hisi_hba->dev; + int entries, entries_old = 0, time; + + for (time = 0; time < timeout_ms; time += delay_ms) { + entries = hisi_sas_read32(hisi_hba, CQE_SEND_CNT); + if (entries == entries_old) + break; + + entries_old = entries; + msleep(delay_ms); + } + + dev_dbg(dev, "wait commands complete %dms\n", time); +} + static struct scsi_host_template sht_v3_hw = { .name = DRV_NAME, .module = THIS_MODULE, @@ -2063,6 +2082,7 @@ static const struct hisi_sas_hw hisi_sas_v3_hw = { .get_phys_state = get_phys_state_v3_hw, .get_events = phy_get_events_v3_hw, .write_gpio = write_gpio_v3_hw, + .wait_cmds_complete_timeout = wait_cmds_complete_timeout_v3_hw, }; static struct Scsi_Host * -- cgit v1.2.3 From 78bd2b4f6e7c0522cc8bc8ad651f20813ae06f6c Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:21 +0800 Subject: scsi: hisi_sas: Include TMF elements in struct hisi_sas_slot In future scenarios we will want to use the TMF struct for more task types than SSP. As such, we can add struct hisi_sas_tmf_task directly into struct hisi_sas_slot, and this will mean we can remove the TMF parameters from the task prep functions. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 14 +++++++------- drivers/scsi/hisi_sas/hisi_sas_main.c | 15 +++++++-------- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 12 ++++++------ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 10 +++++----- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +++++----- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 8ce6abb48867..60bd652534fe 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -179,6 +179,11 @@ struct hisi_sas_device { u8 dev_status; }; +struct hisi_sas_tmf_task { + u8 tmf; + u16 tag_of_task_to_be_managed; +}; + struct hisi_sas_slot { struct list_head entry; struct list_head delivery; @@ -199,11 +204,7 @@ struct hisi_sas_slot { struct work_struct abort_slot; struct timer_list internal_abort_timer; bool is_internal; -}; - -struct hisi_sas_tmf_task { - u8 tmf; - u16 tag_of_task_to_be_managed; + struct hisi_sas_tmf_task *tmf; }; struct hisi_sas_hw { @@ -217,8 +218,7 @@ struct hisi_sas_hw { int (*get_free_slot)(struct hisi_hba *hisi_hba, struct hisi_sas_dq *dq); void (*start_delivery)(struct hisi_sas_dq *dq); void (*prep_ssp)(struct hisi_hba *hisi_hba, - struct hisi_sas_slot *slot, int is_tmf, - struct hisi_sas_tmf_task *tmf); + struct hisi_sas_slot *slot); void (*prep_smp)(struct hisi_hba *hisi_hba, struct hisi_sas_slot *slot); void (*prep_stp)(struct hisi_hba *hisi_hba, diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 5bc522c35690..4e8046ecf477 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -266,10 +266,9 @@ static void hisi_sas_task_prep_smp(struct hisi_hba *hisi_hba, } static void hisi_sas_task_prep_ssp(struct hisi_hba *hisi_hba, - struct hisi_sas_slot *slot, int is_tmf, - struct hisi_sas_tmf_task *tmf) + struct hisi_sas_slot *slot) { - hisi_hba->hw->prep_ssp(hisi_hba, slot, is_tmf, tmf); + hisi_hba->hw->prep_ssp(hisi_hba, slot); } static void hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba, @@ -322,7 +321,7 @@ out: static int hisi_sas_task_prep(struct sas_task *task, struct hisi_sas_dq **dq_pointer, - int is_tmf, struct hisi_sas_tmf_task *tmf, + bool is_tmf, struct hisi_sas_tmf_task *tmf, int *pass) { struct domain_device *device = task->dev; @@ -461,8 +460,8 @@ static int hisi_sas_task_prep(struct sas_task *task, slot->cmd_hdr = &cmd_hdr_base[dlvry_queue_slot]; slot->task = task; slot->port = port; - if (is_tmf) - slot->is_internal = true; + slot->tmf = tmf; + slot->is_internal = is_tmf; task->lldd_task = slot; INIT_WORK(&slot->abort_slot, hisi_sas_slot_abort); @@ -475,7 +474,7 @@ static int hisi_sas_task_prep(struct sas_task *task, hisi_sas_task_prep_smp(hisi_hba, slot); break; case SAS_PROTOCOL_SSP: - hisi_sas_task_prep_ssp(hisi_hba, slot, is_tmf, tmf); + hisi_sas_task_prep_ssp(hisi_hba, slot); break; case SAS_PROTOCOL_SATA: case SAS_PROTOCOL_STP: @@ -527,7 +526,7 @@ prep_out: } static int hisi_sas_task_exec(struct sas_task *task, gfp_t gfp_flags, - int is_tmf, struct hisi_sas_tmf_task *tmf) + bool is_tmf, struct hisi_sas_tmf_task *tmf) { u32 rc; u32 pass = 0; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 8d5d8575d939..05609ac1b31d 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -986,8 +986,7 @@ static void prep_smp_v1_hw(struct hisi_hba *hisi_hba, } static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, - struct hisi_sas_slot *slot, int is_tmf, - struct hisi_sas_tmf_task *tmf) + struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; @@ -996,7 +995,8 @@ static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, struct hisi_sas_port *port = slot->port; struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; - int has_data = 0, priority = is_tmf; + struct hisi_sas_tmf_task *tmf = slot->tmf; + int has_data = 0, priority = !!tmf; u8 *buf_cmd, fburst = 0; u32 dw1, dw2; @@ -1010,7 +1010,7 @@ static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, dw1 = 1 << CMD_HDR_VERIFY_DTL_OFF; - if (is_tmf) { + if (tmf) { dw1 |= 3 << CMD_HDR_SSP_FRAME_TYPE_OFF; } else { switch (scsi_cmnd->sc_data_direction) { @@ -1031,7 +1031,7 @@ static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, dw1 |= sas_dev->device_id << CMD_HDR_DEVICE_ID_OFF; hdr->dw1 = cpu_to_le32(dw1); - if (is_tmf) { + if (tmf) { dw2 = ((sizeof(struct ssp_tmf_iu) + sizeof(struct ssp_frame_hdr)+3)/4) << CMD_HDR_CFL_OFF; @@ -1062,7 +1062,7 @@ static void prep_ssp_v1_hw(struct hisi_hba *hisi_hba, hdr->dw2 = cpu_to_le32(dw2); memcpy(buf_cmd, &task->ssp_task.LUN, 8); - if (!is_tmf) { + if (!tmf) { buf_cmd[9] = fburst | task->ssp_task.task_attr | (task->ssp_task.task_prio << 3); memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 18dbaffc9988..fafb3f1835be 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1745,8 +1745,7 @@ static void prep_smp_v2_hw(struct hisi_hba *hisi_hba, } static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba, - struct hisi_sas_slot *slot, int is_tmf, - struct hisi_sas_tmf_task *tmf) + struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; @@ -1755,7 +1754,8 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_port *port = slot->port; struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; - int has_data = 0, priority = is_tmf; + struct hisi_sas_tmf_task *tmf = slot->tmf; + int has_data = 0, priority = !!tmf; u8 *buf_cmd; u32 dw1 = 0, dw2 = 0; @@ -1766,7 +1766,7 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba, (1 << CMD_HDR_CMD_OFF)); /* ssp */ dw1 = 1 << CMD_HDR_VDTL_OFF; - if (is_tmf) { + if (tmf) { dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF; dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF; } else { @@ -1809,7 +1809,7 @@ static void prep_ssp_v2_hw(struct hisi_hba *hisi_hba, sizeof(struct ssp_frame_hdr); memcpy(buf_cmd, &task->ssp_task.LUN, 8); - if (!is_tmf) { + if (!tmf) { buf_cmd[9] = task->ssp_task.task_attr | (task->ssp_task.task_prio << 3); memcpy(buf_cmd + 12, task->ssp_task.cmd->cmnd, diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index dd5f542bbc45..8c996aa42528 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -911,8 +911,7 @@ static void prep_prd_sge_v3_hw(struct hisi_hba *hisi_hba, } static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, - struct hisi_sas_slot *slot, int is_tmf, - struct hisi_sas_tmf_task *tmf) + struct hisi_sas_slot *slot) { struct sas_task *task = slot->task; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; @@ -921,7 +920,8 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, struct hisi_sas_port *port = slot->port; struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; - int has_data = 0, priority = is_tmf; + struct hisi_sas_tmf_task *tmf = slot->tmf; + int has_data = 0, priority = !!tmf; u8 *buf_cmd; u32 dw1 = 0, dw2 = 0; @@ -932,7 +932,7 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, (1 << CMD_HDR_CMD_OFF)); /* ssp */ dw1 = 1 << CMD_HDR_VDTL_OFF; - if (is_tmf) { + if (tmf) { dw1 |= 2 << CMD_HDR_FRAME_TYPE_OFF; dw1 |= DIR_NO_DATA << CMD_HDR_DIR_OFF; } else { @@ -974,7 +974,7 @@ static void prep_ssp_v3_hw(struct hisi_hba *hisi_hba, sizeof(struct ssp_frame_hdr); memcpy(buf_cmd, &task->ssp_task.LUN, 8); - if (!is_tmf) { + if (!tmf) { buf_cmd[9] = ssp_task->task_attr | (ssp_task->task_prio << 3); memcpy(buf_cmd + 12, scsi_cmnd->cmnd, scsi_cmnd->cmd_len); } else { -- cgit v1.2.3 From b09fcd09e9767f81187aa4036fb16d14e2f2fc79 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:22 +0800 Subject: scsi: hisi_sas: Add v2 hw force PHY function for internal ATA command This patch adds a force PHY function for internal ATA command for v2 hw. Because there is an SoC bug in v2 hw, and need send an IO through each PHY of a port to work around a bug which occurs after a controller reset. This force PHY function will be used in the later patch. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 2 ++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 60bd652534fe..9400824f23ad 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -180,6 +180,8 @@ struct hisi_sas_device { }; struct hisi_sas_tmf_task { + int force_phy; + int phy_id; u8 tmf; u16 tag_of_task_to_be_managed; }; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index fafb3f1835be..369ef7ebb1e0 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -296,6 +296,10 @@ #define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF) #define CMD_HDR_TLR_CTRL_OFF 6 #define CMD_HDR_TLR_CTRL_MSK (0x3 << CMD_HDR_TLR_CTRL_OFF) +#define CMD_HDR_PHY_ID_OFF 8 +#define CMD_HDR_PHY_ID_MSK (0x1ff << CMD_HDR_PHY_ID_OFF) +#define CMD_HDR_FORCE_PHY_OFF 17 +#define CMD_HDR_FORCE_PHY_MSK (0x1 << CMD_HDR_FORCE_PHY_OFF) #define CMD_HDR_PORT_OFF 18 #define CMD_HDR_PORT_MSK (0xf << CMD_HDR_PORT_OFF) #define CMD_HDR_PRIORITY_OFF 27 @@ -2512,6 +2516,7 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba, struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; struct asd_sas_port *sas_port = device->port; struct hisi_sas_port *port = to_hisi_sas_port(sas_port); + struct hisi_sas_tmf_task *tmf = slot->tmf; u8 *buf_cmd; int has_data = 0, hdr_tag = 0; u32 dw1 = 0, dw2 = 0; @@ -2524,6 +2529,12 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba, else hdr->dw0 |= cpu_to_le32(4 << CMD_HDR_CMD_OFF); + if (tmf && tmf->force_phy) { + hdr->dw0 |= CMD_HDR_FORCE_PHY_MSK; + hdr->dw0 |= cpu_to_le32((1 << tmf->phy_id) + << CMD_HDR_PHY_ID_OFF); + } + /* dw1 */ switch (task->data_dir) { case DMA_TO_DEVICE: -- cgit v1.2.3 From 31709548d2aca9861a72e2890d62fc87c52199de Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:23 +0800 Subject: scsi: hisi_sas: Terminate STP reject quickly for v2 hw For v2 hw, STP link from target is rejected after host reset because of a SoC bug. The STP reject will be terminated after we have sent IO from each PHY of a port. This is not an problem before, as we don't need to setup STP link from target immediately after host reset. But now, it is. Because we want to send soft-reset immediately after host reset. In order to terminate STP reject quickly, this patch send ATA reset command through each PHY of a port. Notes: ATA reset command don't need target's response. Besides, we do abort dev for each device before terminating STP reject. This is a quirk of v2 hw. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 4e8046ecf477..cd55849bcd07 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1275,6 +1275,81 @@ static void hisi_sas_reset_init_all_devices(struct hisi_hba *hisi_hba) } } +static void hisi_sas_send_ata_reset_each_phy(struct hisi_hba *hisi_hba, + struct asd_sas_port *sas_port, + struct domain_device *device) +{ + struct hisi_sas_tmf_task tmf_task = { .force_phy = 1 }; + struct ata_port *ap = device->sata_dev.ap; + struct device *dev = hisi_hba->dev; + int s = sizeof(struct host_to_dev_fis); + int rc = TMF_RESP_FUNC_FAILED; + struct asd_sas_phy *sas_phy; + struct ata_link *link; + u8 fis[20] = {0}; + u32 state; + + state = hisi_hba->hw->get_phys_state(hisi_hba); + list_for_each_entry(sas_phy, &sas_port->phy_list, port_phy_el) { + if (!(state & BIT(sas_phy->id))) + continue; + + ata_for_each_link(link, ap, EDGE) { + int pmp = sata_srst_pmp(link); + + tmf_task.phy_id = sas_phy->id; + hisi_sas_fill_ata_reset_cmd(link->device, 1, pmp, fis); + rc = hisi_sas_exec_internal_tmf_task(device, fis, s, + &tmf_task); + if (rc != TMF_RESP_FUNC_COMPLETE) { + dev_err(dev, "phy%d ata reset failed rc=%d\n", + sas_phy->id, rc); + break; + } + } + } +} + +static void hisi_sas_terminate_stp_reject(struct hisi_hba *hisi_hba) +{ + struct device *dev = hisi_hba->dev; + int port_no, rc, i; + + for (i = 0; i < HISI_SAS_MAX_DEVICES; i++) { + struct hisi_sas_device *sas_dev = &hisi_hba->devices[i]; + struct domain_device *device = sas_dev->sas_device; + + if ((sas_dev->dev_type == SAS_PHY_UNUSED) || !device) + continue; + + rc = hisi_sas_internal_task_abort(hisi_hba, device, + HISI_SAS_INT_ABT_DEV, 0); + if (rc < 0) + dev_err(dev, "STP reject: abort dev failed %d\n", rc); + } + + for (port_no = 0; port_no < hisi_hba->n_phy; port_no++) { + struct hisi_sas_port *port = &hisi_hba->port[port_no]; + struct asd_sas_port *sas_port = &port->sas_port; + struct domain_device *port_dev = sas_port->port_dev; + struct domain_device *device; + + if (!port_dev || !DEV_IS_EXPANDER(port_dev->dev_type)) + continue; + + /* Try to find a SATA device */ + list_for_each_entry(device, &sas_port->dev_list, + dev_list_node) { + if (dev_is_sata(device)) { + hisi_sas_send_ata_reset_each_phy(hisi_hba, + sas_port, + device); + break; + } + } + } +} + static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) { struct device *dev = hisi_hba->dev; @@ -1312,6 +1387,9 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) hisi_hba->hw->phys_init(hisi_hba); msleep(1000); hisi_sas_refresh_port_id(hisi_hba); + + if (hisi_hba->reject_stp_links_msk) + hisi_sas_terminate_stp_reject(hisi_hba); hisi_sas_reset_init_all_devices(hisi_hba); scsi_unblock_requests(shost); -- cgit v1.2.3 From d87e72fb4fda6c48909e870811ae5252d0520f99 Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Mon, 21 May 2018 18:09:24 +0800 Subject: scsi: hisi_sas: Fix return value when get_free_slot() failed It is an step of executing task to get free slot. If the step fails, we will cleanup LLDD resources and should return failure to upper layer or internal caller to abort task execution of this time. But in the current code, the caller of get_free_slot() doesn't return failure when get_free_slot() failed. This patch is to fix it. Signed-off-by: Xiaofei Tan Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index cd55849bcd07..3028024cb437 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -443,6 +443,7 @@ static int hisi_sas_task_prep(struct sas_task *task, wr_q_index = hisi_hba->hw->get_free_slot(hisi_hba, dq); if (wr_q_index < 0) { spin_unlock_irqrestore(&dq->lock, flags_dq); + rc = -EAGAIN; goto err_out_buf; } @@ -1708,6 +1709,7 @@ hisi_sas_internal_abort_task_exec(struct hisi_hba *hisi_hba, int device_id, wr_q_index = hisi_hba->hw->get_free_slot(hisi_hba, dq); if (wr_q_index < 0) { spin_unlock_irqrestore(&dq->lock, flags_dq); + rc = -EAGAIN; goto err_out_buf; } list_add_tail(&slot->delivery, &dq->list); -- cgit v1.2.3 From 3e1fb1b8abf0c862a7f5d39cb3354a1fd5e9f96a Mon Sep 17 00:00:00 2001 From: Xiang Chen Date: Mon, 21 May 2018 18:09:25 +0800 Subject: scsi: hisi_sas: Mark PHY as in reset for nexus reset When issuing a nexus reset for directly attached device, we want to ignore the PHY down events so libsas will not deform and reform the port. In the case that the attached SAS changes for the reset, libsas will deform and form a port. For scenario that the PHY does not come up after a timeout period, then report the PHY down to libsas. Signed-off-by: Xiang Chen Signed-off-by: John Garry Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas.h | 5 ++++- drivers/scsi/hisi_sas/hisi_sas_main.c | 40 ++++++++++++++++++++++++++++++---- drivers/scsi/hisi_sas/hisi_sas_v1_hw.c | 8 +++++++ drivers/scsi/hisi_sas/hisi_sas_v2_hw.c | 14 ++++++++++++ drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 7 ++++++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas.h b/drivers/scsi/hisi_sas/hisi_sas.h index 9400824f23ad..7052a5d45f7f 100644 --- a/drivers/scsi/hisi_sas/hisi_sas.h +++ b/drivers/scsi/hisi_sas/hisi_sas.h @@ -136,11 +136,14 @@ struct hisi_sas_phy { struct hisi_sas_port *port; struct asd_sas_phy sas_phy; struct sas_identify identify; + struct completion *reset_completion; + spinlock_t lock; u64 port_id; /* from hw */ u64 frame_rcvd_size; u8 frame_rcvd[32]; u8 phy_attached; - u8 reserved[3]; + u8 in_reset; + u8 reserved[2]; u32 phy_type; enum sas_linkrate minimum_linkrate; enum sas_linkrate maximum_linkrate; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 3028024cb437..6f562974f8f6 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1529,12 +1529,39 @@ static int hisi_sas_clear_aca(struct domain_device *device, u8 *lun) static int hisi_sas_debug_I_T_nexus_reset(struct domain_device *device) { - struct sas_phy *phy = sas_get_local_phy(device); + struct sas_phy *local_phy = sas_get_local_phy(device); int rc, reset_type = (device->dev_type == SAS_SATA_DEV || (device->tproto & SAS_PROTOCOL_STP)) ? 0 : 1; - rc = sas_phy_reset(phy, reset_type); - sas_put_local_phy(phy); - msleep(2000); + struct hisi_hba *hisi_hba = dev_to_hisi_hba(device); + struct sas_ha_struct *sas_ha = &hisi_hba->sha; + struct asd_sas_phy *sas_phy = sas_ha->sas_phy[local_phy->number]; + struct hisi_sas_phy *phy = container_of(sas_phy, + struct hisi_sas_phy, sas_phy); + DECLARE_COMPLETION_ONSTACK(phyreset); + + if (scsi_is_sas_phy_local(local_phy)) { + phy->in_reset = 1; + phy->reset_completion = &phyreset; + } + + rc = sas_phy_reset(local_phy, reset_type); + sas_put_local_phy(local_phy); + + if (scsi_is_sas_phy_local(local_phy)) { + int ret = wait_for_completion_timeout(&phyreset, 2 * HZ); + unsigned long flags; + + spin_lock_irqsave(&phy->lock, flags); + phy->reset_completion = NULL; + phy->in_reset = 0; + spin_unlock_irqrestore(&phy->lock, flags); + + /* report PHY down if timed out */ + if (!ret) + hisi_sas_phy_down(hisi_hba, sas_phy->id, 0); + } else + msleep(2000); + return rc; } @@ -1883,6 +1910,7 @@ void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy) struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; struct sas_ha_struct *sas_ha = &hisi_hba->sha; + struct device *dev = hisi_hba->dev; if (rdy) { /* Phy down but ready */ @@ -1891,6 +1919,10 @@ void hisi_sas_phy_down(struct hisi_hba *hisi_hba, int phy_no, int rdy) } else { struct hisi_sas_port *port = phy->port; + if (phy->in_reset) { + dev_info(dev, "ignore flutter phy%d down\n", phy_no); + return; + } /* Phy down and not ready */ sas_ha->notify_phy_event(sas_phy, PHYE_LOSS_OF_SIGNAL); sas_phy_disconnected(sas_phy); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 05609ac1b31d..89ab18c1959c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1373,6 +1373,7 @@ static irqreturn_t int_phyup_v1_hw(int irq_no, void *p) u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd; struct sas_identify_frame *id = (struct sas_identify_frame *)frame_rcvd; irqreturn_t res = IRQ_HANDLED; + unsigned long flags; irq_value = hisi_sas_phy_read32(hisi_hba, phy_no, CHL_INT2); if (!(irq_value & CHL_INT2_SL_PHY_ENA_MSK)) { @@ -1426,6 +1427,13 @@ static irqreturn_t int_phyup_v1_hw(int irq_no, void *p) SAS_PROTOCOL_SMP; hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); + spin_lock_irqsave(&phy->lock, flags); + if (phy->reset_completion) { + phy->in_reset = 0; + complete(phy->reset_completion); + } + spin_unlock_irqrestore(&phy->lock, flags); + end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT2, CHL_INT2_SL_PHY_ENA_MSK); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 369ef7ebb1e0..213c530e63f2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -2661,6 +2661,7 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) struct device *dev = hisi_hba->dev; u32 *frame_rcvd = (u32 *)sas_phy->frame_rcvd; struct sas_identify_frame *id = (struct sas_identify_frame *)frame_rcvd; + unsigned long flags; hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1); @@ -2713,6 +2714,12 @@ static int phy_up_v2_hw(int phy_no, struct hisi_hba *hisi_hba) set_link_timer_quirk(hisi_hba); } hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); + spin_lock_irqsave(&phy->lock, flags); + if (phy->reset_completion) { + phy->in_reset = 0; + complete(phy->reset_completion); + } + spin_unlock_irqrestore(&phy->lock, flags); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, @@ -3201,6 +3208,7 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p) u32 ent_tmp, ent_msk, ent_int, port_id, link_rate, hard_phy_linkrate; irqreturn_t res = IRQ_HANDLED; u8 attached_sas_addr[SAS_ADDR_SIZE] = {0}; + unsigned long flags; int phy_no, offset; phy_no = sas_phy->id; @@ -3275,6 +3283,12 @@ static irqreturn_t sata_int_v2_hw(int irq_no, void *p) phy->identify.target_port_protocols = SAS_PROTOCOL_SATA; hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); + spin_lock_irqsave(&phy->lock, flags); + if (phy->reset_completion) { + phy->in_reset = 0; + complete(phy->reset_completion); + } + spin_unlock_irqrestore(&phy->lock, flags); end: hisi_sas_write32(hisi_hba, ENT_INT_SRC1 + offset, ent_tmp); hisi_sas_write32(hisi_hba, ENT_INT_SRC_MSK1 + offset, ent_msk); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 8c996aa42528..9f1e2d03f914 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -1143,6 +1143,7 @@ static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no]; struct asd_sas_phy *sas_phy = &phy->sas_phy; struct device *dev = hisi_hba->dev; + unsigned long flags; hisi_sas_phy_write32(hisi_hba, phy_no, PHYCTRL_PHY_ENA_MSK, 1); @@ -1211,6 +1212,12 @@ static irqreturn_t phy_up_v3_hw(int phy_no, struct hisi_hba *hisi_hba) phy->phy_attached = 1; hisi_sas_notify_phy_event(phy, HISI_PHYE_PHY_UP); res = IRQ_HANDLED; + spin_lock_irqsave(&phy->lock, flags); + if (phy->reset_completion) { + phy->in_reset = 0; + complete(phy->reset_completion); + } + spin_unlock_irqrestore(&phy->lock, flags); end: hisi_sas_phy_write32(hisi_hba, phy_no, CHL_INT0, CHL_INT0_SL_PHY_ENABLE_MSK); -- cgit v1.2.3 From dc19e3b4a80e0bb1e5f080473fffa0ac8c0694a6 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 24 May 2018 21:08:57 -0700 Subject: scsi: lpfc: Fix MDS diagnostics failure (Rx < Tx) MDS diagnostics fail because of frame count mismatch. Unavailability of SGL is the trigger for this issue. If ELS SGL is not available to process MDS frame, IOCB is put in FCP txq but not attempted to post afterwards. So, driver stops processing incoming frames as it runs out of IOCB. lpfc_drain_txq attempts to submit IOCBS that are queued in ELS txq but MDS frames are posted to FCP WQ. Attempt to submit IOCBs that are present in FCP txq when MDS loopback is running. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hbadisc.c | 3 +-- drivers/scsi/lpfc/lpfc_sli.c | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index cf2cbaa241b9..2fef54fab86d 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -708,8 +708,7 @@ lpfc_work_done(struct lpfc_hba *phba) HA_RXMASK)); } } - if ((phba->sli_rev == LPFC_SLI_REV4) && - (!list_empty(&pring->txq))) + if (phba->sli_rev == LPFC_SLI_REV4) lpfc_drain_txq(phba); /* * Turn on Ring interrupts diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 6b709cd4140b..4b70d53acb72 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -19069,9 +19069,22 @@ lpfc_drain_txq(struct lpfc_hba *phba) struct lpfc_sglq *sglq; union lpfc_wqe128 wqe; uint32_t txq_cnt = 0; + struct lpfc_queue *wq; - pring = lpfc_phba_elsring(phba); - if (unlikely(!pring)) + if (phba->link_flag & LS_MDS_LOOPBACK) { + /* MDS WQE are posted only to first WQ*/ + wq = phba->sli4_hba.fcp_wq[0]; + if (unlikely(!wq)) + return 0; + pring = wq->pring; + } else { + wq = phba->sli4_hba.els_wq; + if (unlikely(!wq)) + return 0; + pring = lpfc_phba_elsring(phba); + } + + if (unlikely(!pring) || list_empty(&pring->txq)) return 0; spin_lock_irqsave(&pring->ring_lock, iflags); @@ -19112,7 +19125,7 @@ lpfc_drain_txq(struct lpfc_hba *phba) fail_msg = "to convert bpl to sgl"; else if (lpfc_sli4_iocb2wqe(phba, piocbq, &wqe)) fail_msg = "to convert iocb to wqe"; - else if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe)) + else if (lpfc_sli4_wq_put(wq, &wqe)) fail_msg = " - Wq is full"; else lpfc_sli_ringtxcmpl_put(phba, pring, piocbq); -- cgit v1.2.3 From 4d5e789a2eb111d7f9e032d0ebaecb465a2eca8f Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 24 May 2018 21:08:58 -0700 Subject: scsi: lpfc: correct oversubscription of nvme io requests for an adapter Under large configurations, the driver would start to log message 6065 - NVME out of buffers (exchanges). The driver is using the ndlp cmd_qdepth value when determining the max outstanding ios for an adapter. This value, by default, is set to 65536, which exceeds the maximum exchange counts supported on an adapter. The ndlp cmd_qdepth has no relevance and outstanding io count should be capped at the max exchange count with IO requests beyond that level getting bounced back with an EBUSY status so that they are retried by the block layer. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_attr.c | 7 +++++++ drivers/scsi/lpfc/lpfc_nportdisc.c | 6 ++++++ drivers/scsi/lpfc/lpfc_nvme.c | 23 +++++++++++++++++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index a66c4fbc7690..729d343861f4 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -297,6 +297,13 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, len = snprintf(buf, PAGE_SIZE, "NVME Initiator Enabled\n"); spin_lock_irq(shost->host_lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "XRI Dist lpfc%d Total %d NVME %d SCSI %d ELS %d\n", + phba->brd_no, + phba->sli4_hba.max_cfg_param.max_xri, + phba->sli4_hba.nvme_xri_max, + phba->sli4_hba.scsi_xri_max, + lpfc_sli4_get_els_iocb_cnt(phba)); /* Port state is only one of two values for now. */ if (localport->port_id) diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index e790c0bc64fc..1a803975bcbc 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1982,6 +1982,12 @@ lpfc_cmpl_prli_prli_issue(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, if (bf_get_be32(prli_disc, nvpr)) ndlp->nlp_type |= NLP_NVME_DISCOVERY; + /* This node is an NVME target. Adjust the command + * queue depth on this node to not exceed the available + * xris. + */ + ndlp->cmd_qdepth = phba->sli4_hba.nvme_xri_max; + /* * If prli_fba is set, the Target supports FirstBurst. * If prli_fb_sz is 0, the FirstBurst size is unlimited, diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index f5f90d19b215..288dd3caff8a 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -973,9 +973,22 @@ lpfc_nvme_io_cmd_wqe_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, /* Sanity check on return of outstanding command */ if (!lpfc_ncmd || !lpfc_ncmd->nvmeCmd || !lpfc_ncmd->nrport) { + if (!lpfc_ncmd) { + lpfc_printf_vlog(vport, KERN_ERR, + LOG_NODE | LOG_NVME_IOERR, + "6071 Null lpfc_ncmd pointer. No " + "release, skip completion\n"); + return; + } + lpfc_printf_vlog(vport, KERN_ERR, LOG_NODE | LOG_NVME_IOERR, - "6071 Completion pointers bad on wqe %p.\n", - wcqe); + "6066 Missing cmpl ptrs: lpfc_ncmd %p, " + "nvmeCmd %p nrport %p\n", + lpfc_ncmd, lpfc_ncmd->nvmeCmd, + lpfc_ncmd->nrport); + + /* Release the lpfc_ncmd regardless of the missing elements. */ + lpfc_release_nvme_buf(phba, lpfc_ncmd); return; } nCmd = lpfc_ncmd->nvmeCmd; @@ -1537,8 +1550,10 @@ lpfc_nvme_fcp_io_submit(struct nvme_fc_local_port *pnvme_lport, !expedite) { lpfc_printf_vlog(vport, KERN_INFO, LOG_NVME_IOERR, "6174 Fail IO, ndlp qdepth exceeded: " - "idx %d DID %x\n", - lpfc_queue_info->index, ndlp->nlp_DID); + "idx %d DID %x pend %d qdepth %d\n", + lpfc_queue_info->index, ndlp->nlp_DID, + atomic_read(&ndlp->cmd_pending), + ndlp->cmd_qdepth); atomic_inc(&lport->xmt_fcp_qdepth); ret = -EBUSY; goto out_fail; -- cgit v1.2.3 From 7438273fa23bea6d1e647e66c451570b86e2758b Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 24 May 2018 21:08:59 -0700 Subject: scsi: lpfc: Fix crash in blk_mq layer when executing modprobe -r lpfc modprobe -r lpfc produces the following: Call Trace: __blk_mq_run_hw_queue+0xa2/0xb0 __blk_mq_delay_run_hw_queue+0x9d/0xb0 ? blk_mq_hctx_has_pending+0x32/0x80 blk_mq_run_hw_queue+0x50/0xd0 blk_mq_sched_insert_request+0x110/0x1b0 blk_execute_rq_nowait+0x76/0x180 nvme_keep_alive_work+0x8a/0xd0 [nvme_core] process_one_work+0x17f/0x440 worker_thread+0x126/0x3c0 ? manage_workers.isra.24+0x2a0/0x2a0 kthread+0xd1/0xe0 ? insert_kthread_work+0x40/0x40 ret_from_fork_nospec_begin+0x21/0x21 ? insert_kthread_work+0x40/0x40 However, rmmod lpfc would run correctly. When an nvme remoteport is unregistered with the host nvme transport, it needs to set the remoteport->dev_loss_tmo value 0 to indicate an immediate termination of device loss and prevent any further keep alives to that rport. The driver was never setting dev_loss_tmo causing the nvme transport to continue to send the keep alive. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_nvme.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 288dd3caff8a..76a5a99605aa 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -2862,6 +2862,15 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * The transport will update it. */ ndlp->upcall_flags |= NLP_WAIT_FOR_UNREG; + + /* Don't let the host nvme transport keep sending keep-alives + * on this remoteport. Vport is unloading, no recovery. The + * return values is ignored. The upcall is a courtesy to the + * transport. + */ + if (vport->load_flag & FC_UNLOADING) + (void)nvme_fc_set_remoteport_devloss(remoteport, 0); + ret = nvme_fc_unregister_remoteport(remoteport); if (ret != 0) { lpfc_nlp_put(ndlp); -- cgit v1.2.3 From c221768bd49a7423be57c00a56985c0e9c4122cd Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 24 May 2018 21:09:00 -0700 Subject: scsi: lpfc: Fix 16gb hbas failing cq create. The lancer G5 chip family fails the CQ create with 16k page size. The hardware incorrectly reports it supports large page sizes when it is actually limited to 4k pages. A prior patch resolved this for the A0 chip revision only. This patch excludes all revisions of the G5 asic from using large page sizes. As knowing the actual chip revision is unnecessary, the now unused definitions are removed Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hw4.h | 11 ----------- drivers/scsi/lpfc/lpfc_init.c | 9 +-------- drivers/scsi/lpfc/lpfc_sli4.h | 1 - 3 files changed, 1 insertion(+), 20 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 807901af9bbe..f43f0bacb77a 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -104,17 +104,6 @@ struct lpfc_sli_intf { #define LPFC_SLI_INTF_IF_TYPE_VIRT 1 }; -struct lpfc_sli_asic_rev { - u32 word0; -#define LPFC_SLI_ASIC_VER_A 0x0 -#define LPFC_SLI_ASIC_VER_B 0x1 -#define LPFC_SLI_ASIC_VER_C 0x2 -#define LPFC_SLI_ASIC_VER_D 0x3 -#define lpfc_sli_asic_ver_SHIFT 4 -#define lpfc_sli_asic_ver_MASK 0x0000000F -#define lpfc_sli_asic_ver_WORD word0 -}; - #define LPFC_SLI4_MBX_EMBED true #define LPFC_SLI4_MBX_NEMBED false diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 83bc8d849a0d..48a5f067cec6 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -9545,11 +9545,6 @@ lpfc_sli4_pci_mem_setup(struct lpfc_hba *phba) return error; } - if (pci_read_config_dword(pdev, LPFC_SLI_ASIC_VER, - &phba->sli4_hba.sli_asic_ver.word0)) { - return error; - } - /* There is no SLI3 failback for SLI4 devices. */ if (bf_get(lpfc_sli_intf_valid, &phba->sli4_hba.sli_intf) != LPFC_SLI_INTF_VALID) { @@ -10711,9 +10706,7 @@ lpfc_get_sli4_parameters(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) if ((bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == LPFC_SLI_INTF_IF_TYPE_2) && (bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf) == - LPFC_SLI_INTF_FAMILY_LNCR_A0) && - (bf_get(lpfc_sli_asic_ver, &phba->sli4_hba.sli_asic_ver) == - LPFC_SLI_ASIC_VER_A)) + LPFC_SLI_INTF_FAMILY_LNCR_A0)) exp_wqcq_pages = false; if ((bf_get(cfg_cqpsize, mbx_sli4_parameters) & LPFC_CQ_16K_PAGE_SZ) && diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index 179e870a00b4..cf64aca82bd0 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -592,7 +592,6 @@ struct lpfc_sli4_hba { uint32_t ue_to_sr; uint32_t ue_to_rp; struct lpfc_register sli_intf; - struct lpfc_register sli_asic_ver; struct lpfc_pc_sli4_params pc_sli4_params; struct lpfc_bbscn_params bbscn_params; struct lpfc_hba_eq_hdl *hba_eq_hdl; /* HBA per-WQ handle */ -- cgit v1.2.3 From b92dc72df3c6b46d2d085b56dcc764f798323e7c Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 24 May 2018 21:09:01 -0700 Subject: scsi: lpfc: Fix port initialization failure. The driver exits port setup after failing the lpfc_sli4_get_parameters command (messages 0356, 2541, & 1412). The older CNA adapters do not support the MBX command. In the past the code was allowed to fail and continue on with initialization. However a nvme change moved a closing bracket and now makes all failures terminal. Revise the logic so that terminal failure only occurs if the command failed on the newer adapters. Additionally, if parameters are set that require information from the command and the command failed, the parameters are erroneous and port set up should fail even on the older adapters. Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_init.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 48a5f067cec6..7ae343b14630 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -5850,6 +5850,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) int fof_vectors = 0; int extra; uint64_t wwn; + u32 if_type; + u32 if_fam; phba->sli4_hba.num_online_cpu = num_online_cpus(); phba->sli4_hba.num_present_cpu = lpfc_present_cpu; @@ -6171,15 +6173,28 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) */ rc = lpfc_get_sli4_parameters(phba, mboxq); if (rc) { + if_type = bf_get(lpfc_sli_intf_if_type, + &phba->sli4_hba.sli_intf); + if_fam = bf_get(lpfc_sli_intf_sli_family, + &phba->sli4_hba.sli_intf); if (phba->sli4_hba.extents_in_use && phba->sli4_hba.rpi_hdrs_in_use) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2999 Unsupported SLI4 Parameters " "Extents and RPI headers enabled.\n"); + if (if_type == LPFC_SLI_INTF_IF_TYPE_0 && + if_fam == LPFC_SLI_INTF_FAMILY_BE2) { + mempool_free(mboxq, phba->mbox_mem_pool); + rc = -EIO; + goto out_free_bsmbx; + } + } + if (!(if_type == LPFC_SLI_INTF_IF_TYPE_0 && + if_fam == LPFC_SLI_INTF_FAMILY_BE2)) { + mempool_free(mboxq, phba->mbox_mem_pool); + rc = -EIO; + goto out_free_bsmbx; } - mempool_free(mboxq, phba->mbox_mem_pool); - rc = -EIO; - goto out_free_bsmbx; } mempool_free(mboxq, phba->mbox_mem_pool); -- cgit v1.2.3 From 1b5c2cb196684f1418fe82257a1b0a8cb0aabc9d Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 24 May 2018 21:09:02 -0700 Subject: scsi: lpfc: update driver version to 12.0.0.4 Update the driver version to 12.0.0.4 Signed-off-by: Dick Kennedy Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 9fca71d7c297..18c23afcf46b 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "12.0.0.3" +#define LPFC_DRIVER_VERSION "12.0.0.4" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ -- cgit v1.2.3