diff --git a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml index db165a235cb6..66d73e9025d6 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml @@ -18,7 +18,14 @@ properties: - const: qcom,sa8255p-ufshc reg: - maxItems: 1 + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: std + - const: mcq interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml index b441f0d26081..d03295806657 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml @@ -18,6 +18,7 @@ select: - qcom,eliza-ufshc - qcom,hawi-ufshc - qcom,kaanapali-ufshc + - qcom,maili-ufshc - qcom,nord-ufshc - qcom,sm8650-ufshc - qcom,sm8750-ufshc @@ -31,6 +32,7 @@ properties: - qcom,eliza-ufshc - qcom,hawi-ufshc - qcom,kaanapali-ufshc + - qcom,maili-ufshc - qcom,nord-ufshc - qcom,sm8650-ufshc - qcom,sm8750-ufshc @@ -78,6 +80,7 @@ allOf: contains: enum: - qcom,eliza-ufshc + - qcom,maili-ufshc - qcom,nord-ufshc then: properties: diff --git a/Documentation/devicetree/bindings/ufs/ufs-common.yaml b/Documentation/devicetree/bindings/ufs/ufs-common.yaml index ed97f5682509..cc32e1189d50 100644 --- a/Documentation/devicetree/bindings/ufs/ufs-common.yaml +++ b/Documentation/devicetree/bindings/ufs/ufs-common.yaml @@ -105,6 +105,64 @@ properties: Restricts the UFS controller to rate-a or rate-b for both TX and RX directions. + tx-precode-enable-g6: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 1 + items: + - items: + - description: Host_Lane0 precode + enum: [0, 1] + - description: Device_Lane0 precode + enum: [0, 1] + - items: + - description: Host_Lane1 precode + enum: [0, 1] + - description: Device_Lane1 precode + enum: [0, 1] + description: + Static TX Precode enable values for HS-G6 only. + +patternProperties: + "^txeq-preshoot-g[1-6]$": + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 1 + items: + - items: + - description: Host_Lane0 Preshoot value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + - description: Device_Lane0 Preshoot value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + - items: + - description: Host_Lane1 Preshoot value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + - description: Device_Lane1 Preshoot value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + description: | + Static TX Equalization PreShoot settings for High Speed Gears. These + values are programmed to the corresponding UniPro PA layer attribute + PA_TxEQG[1-6]Setting. Each value selects a Pre-Shoot level as defined + by the MIPI M-PHY specification (TX_HS_PreShoot_Setting). + + "^txeq-deemphasis-g[1-6]$": + $ref: /schemas/types.yaml#/definitions/uint32-matrix + minItems: 1 + items: + - items: + - description: Host_Lane0 DeEmphasis value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + - description: Device_Lane0 DeEmphasis value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + - items: + - description: Host_Lane1 DeEmphasis value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + - description: Device_Lane1 DeEmphasis value + enum: [0, 1, 2, 3, 4, 5, 6, 7] + description: | + Static TX Equalization DeEmphasis settings for High Speed Gears. These + values are programmed to the corresponding UniPro PA layer attribute + PA_TxEQG[1-6]Setting. Each value selects a De-Emphasis level as defined + by the MIPI M-PHY specification (TX_HS_DeEmphasis_Setting). + dependencies: freq-table-hz: [ clocks ] operating-points-v2: [ clocks, clock-names ] diff --git a/Documentation/scsi/index.rst b/Documentation/scsi/index.rst index f15a0f348ae4..52970f0159ca 100644 --- a/Documentation/scsi/index.rst +++ b/Documentation/scsi/index.rst @@ -56,6 +56,7 @@ SCSI host adapter drivers g_NCR5380 hpsa hptiop + leapraid libsas lpfc megaraid diff --git a/Documentation/scsi/leapraid.rst b/Documentation/scsi/leapraid.rst new file mode 100644 index 000000000000..99930ce2b8d0 --- /dev/null +++ b/Documentation/scsi/leapraid.rst @@ -0,0 +1,110 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================= +LeapRAID Driver for Linux +========================= + +Introduction +============ + +LeapRAID is a storage RAID controller driver developed by LeapIO Tech Inc. The +controller targets enterprise storage, cloud infrastructure, high performance +computing (HPC), and AI workloads. + +It provides high-performance storage virtualization over PCI Express Gen4 +and supports both SAS and SATA HDDs and SSDs. It offers both Host Bus Adapter +and RAID modes to meet diverse deployment requirements. + +Supported devices +================= + +- LeapHBA-8200C + +Features +======== +- PCIe Gen4 x8 host interface +- Support for SAS and SATA devices +- RAID levels: 0, 1, 10, 5, 50, 6, 60 +- Advanced error handling and end-to-end data integrity + +LeapRAID specific host attributes +================================= + +:: + + /sys/class/scsi_host/host*/fw_queue_depth + /sys/class/scsi_host/host*/host_sas_address + /sys/class/scsi_host/host*/board_name + +The host "fw_queue_depth" read-only attribute shows the firmware queue +depth of the host. + +The host "host_sas_address" read-only attribute shows the SAS address +of the host. + +The host "board_name" read-only attribute shows the board name reported +by manufacturing page 0. + +LeapRAID specific disk attributes +================================= + +:: + + /sys/class/scsi_disk/host:bus:target:lun/device/sas_device_handle + /sys/class/scsi_disk/host:bus:target:lun/device/ncq_cmd_prio_enable + +The read-only attribute "sas_device_handle" represents the disk's device +handle, which is a unique identifier maintained by the firmware. + +This attribute "ncq_cmd_prio_enable" controls NCQ command priority. A value +of 0 disables NCQ priority handling for RT-priority I/O. Writing 1 enables +NCQ priority handling when the device reports support for the feature through +VPD page 0x89. Unsupported devices keep the effective state at 0. + +LeapRAID module parameters +========================== + +The following module parameters can be configured at driver load time to +control driver behavior and tuning options. + +1. open_pcie_trace +------------------ + +This parameter controls whether PCIe transaction tracing is enabled in the +driver. When set to 1, PCIe trace collection is enabled by default, allowing +detailed tracing of PCIe operations for debugging and performance analysis. +Setting it to 0 disables the trace functionality to reduce overhead in +production environments. + +2. enable_mp +------------ + +This parameter enables or disables multipath support for target devices. +When set to 1, multipath functionality is enabled (default), allowing +multiple paths to be established. Setting it to 0 disables multipath +handling. + +3. max_msix_vectors +------------------- + +This parameter sets the upper limit on the number of MSI-X interrupt +vectors that the driver will request during initialization. The default +value of -1 allows the driver to use all available vectors as provided +by the device. Setting a positive integer restricts the number of vectors. + +4. poll_queues +-------------- + +This parameter specifies the number of I/O queues to be used when operating +in io_uring poll mode. The default value is 0. + +File Location +============= +The driver source is located at: + +``drivers/scsi/leapraid/`` + +.. note:: + + This document is intended for kernel developers and system + integrators who need to build, test, and deploy the LeapRAID driver. diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..a8a58f059c49 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11788,7 +11788,7 @@ F: Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt F: drivers/infiniband/hw/hns/ HISILICON SAS Controller -M: Yihang Li +M: Xingui Yang S: Supported W: http://www.hisilicon.com F: Documentation/devicetree/bindings/scsi/hisilicon-sas.txt @@ -14650,6 +14650,13 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening F: scripts/leaking_addresses.pl +LEAPIO SCSI RAID DRIVER +M: Dongdong Hao +L: linux-scsi@vger.kernel.org +S: Supported +F: Documentation/scsi/leapraid.rst +F: drivers/scsi/leapraid/ + LED SUBSYSTEM M: Lee Jones M: Pavel Machek @@ -24300,9 +24307,8 @@ F: include/uapi/scsi/ F: include/ufs/ SCSI TAPE DRIVER -M: Kai Mäkisara L: linux-scsi@vger.kernel.org -S: Maintained +S: Orphan F: Documentation/scsi/st.rst F: drivers/scsi/st.* F: drivers/scsi/st_*.h diff --git a/drivers/ata/pata_buddha.c b/drivers/ata/pata_buddha.c index c36ee991d5e5..3b1f0ee2f875 100644 --- a/drivers/ata/pata_buddha.c +++ b/drivers/ata/pata_buddha.c @@ -253,9 +253,9 @@ static void pata_buddha_remove(struct zorro_dev *z) } static const struct zorro_device_id pata_buddha_zorro_tbl[] = { - { ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, BOARD_BUDDHA}, - { ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, BOARD_CATWEASEL}, - { 0 } + { .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, .driver_data = BOARD_BUDDHA }, + { .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, .driver_data = BOARD_CATWEASEL }, + { } }; MODULE_DEVICE_TABLE(zorro, pata_buddha_zorro_tbl); @@ -282,7 +282,7 @@ static int __init pata_buddha_late_init(void) /* Manually bind to all X-Surf boards */ while ((z = zorro_find_device(ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, z))) { static struct zorro_device_id xsurf_ent = { - ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, BOARD_XSURF + .id = ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF, .driver_data = BOARD_XSURF }; pata_buddha_probe(z, &xsurf_ent); diff --git a/drivers/s390/scsi/Makefile b/drivers/s390/scsi/Makefile index 352056eb0dd1..01b41f7c6860 100644 --- a/drivers/s390/scsi/Makefile +++ b/drivers/s390/scsi/Makefile @@ -3,6 +3,8 @@ # Makefile for the S/390 specific device drivers # +CONTEXT_ANALYSIS := y + zfcp-objs := zfcp_aux.o zfcp_ccw.o zfcp_dbf.o zfcp_erp.o \ zfcp_fc.o zfcp_fsf.o zfcp_qdio.o zfcp_scsi.o zfcp_sysfs.o \ zfcp_unit.o zfcp_diag.o diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8ff7db7921b5..fea573e00f9d 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { mempool_destroy(adapter->pool.erp_req); + mempool_destroy(adapter->pool.gid_pn_req); mempool_destroy(adapter->pool.scsi_req); mempool_destroy(adapter->pool.scsi_abort); mempool_destroy(adapter->pool.qtcb_pool); diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 71f625926ae1..81fb8af408e9 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -4,7 +4,7 @@ * * Debug traces for zfcp. * - * Copyright IBM Corp. 2002, 2023 + * Copyright IBM Corp. 2002, 2026 */ #define pr_fmt(fmt) "zfcp: " fmt @@ -35,13 +35,18 @@ static inline unsigned int zfcp_dbf_plen(unsigned int offset) return sizeof(struct zfcp_dbf_pay) + offset - ZFCP_DBF_PAY_MAX_REC; } +#define ZFCP_DBF_PAY_LEVEL 1 + static inline void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area, - u64 req_id) + u64 req_id, int level) { struct zfcp_dbf_pay *pl = &dbf->pay_buf; u16 offset = 0, rec_length; + if (unlikely(!debug_level_enabled(dbf->pay, level))) + return; + spin_lock(&dbf->pay_lock); memset(pl, 0, sizeof(*pl)); pl->fsf_req_id = req_id; @@ -51,7 +56,7 @@ void zfcp_dbf_pl_write(struct zfcp_dbf *dbf, void *data, u16 length, char *area, rec_length = min((u16) ZFCP_DBF_PAY_MAX_REC, (u16) (length - offset)); memcpy(pl->data, data + offset, rec_length); - debug_event(dbf->pay, 1, pl, zfcp_dbf_plen(rec_length)); + debug_event(dbf->pay, level, pl, zfcp_dbf_plen(rec_length)); offset += rec_length; pl->counter++; @@ -96,7 +101,27 @@ void zfcp_dbf_hba_fsf_res(char *tag, int level, struct zfcp_fsf_req *req) rec->pl_len = q_head->log_length; zfcp_dbf_pl_write(dbf, (char *)q_pref + q_head->log_start, - rec->pl_len, "fsf_res", req->req_id); + rec->pl_len, "fsf_res", req->req_id, + ZFCP_DBF_PAY_LEVEL); + + if (q_head->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) { + struct fsf_qtcb_bottom_support *q_bott = + &req->qtcb->bottom.support; + u32 plogi_len = 0, prli_len = 0; + + if (q_bott->els1_length) { + rec->u.res.plogi_len = q_bott->els1_length; + plogi_len = min_t(u32, q_bott->els1_length, + sizeof(q_bott->els)); + } + if (q_bott->els2_length) { + rec->u.res.prli_len = q_bott->els2_length; + prli_len = min_t(u32, q_bott->els2_length, + sizeof(q_bott->els) - plogi_len); + } + zfcp_dbf_pl_write(dbf, q_bott->els, plogi_len + prli_len, + "fsf_els", req->req_id, 4); + } debug_event(dbf->hba, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); @@ -220,6 +245,13 @@ void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req) rec->u.uss.lun = srb->fcp_lun; memcpy(&rec->u.uss.queue_designator, &srb->queue_designator, sizeof(rec->u.uss.queue_designator)); + rec->u.uss.length = srb->length; + rec->u.uss.res1 = srb->res1; + rec->u.uss.res2 = srb->res2; + rec->u.uss.class = srb->class; + rec->u.uss.res3 = srb->res3; + rec->u.uss.s_id = ntoh24(srb->s_id); + memcpy(&rec->u.uss.res4, &srb->res4, sizeof(rec->u.uss.res4)); /* status read buffer payload length */ rec->pl_len = (!srb->length) ? 0 : srb->length - @@ -227,12 +259,48 @@ void zfcp_dbf_hba_fsf_uss(char *tag, struct zfcp_fsf_req *req) if (rec->pl_len) zfcp_dbf_pl_write(dbf, srb->payload.data, rec->pl_len, - "fsf_uss", req->req_id); + "fsf_uss", req->req_id, ZFCP_DBF_PAY_LEVEL); log: debug_event(dbf->hba, level, rec, sizeof(*rec)); spin_unlock_irqrestore(&dbf->hba_lock, flags); } +/** + * zfcp_dbf_hba_uas - trace event for sysfs unit add store + * @tag: tag indicating which kind of unit add store condition occurred + * @level: debug trace level + * @adapter: pointer to struct zfcp_adapter + * @wwpn: remote port wwn + * @fcp_lun: FCP LUN + * @ret: return value + */ +void zfcp_dbf_hba_uas(char *tag, int level, struct zfcp_adapter *adapter, + u64 wwpn, u64 fcp_lun, int ret) +{ + struct zfcp_dbf *dbf = adapter->dbf; + struct zfcp_dbf_hba *rec = &dbf->hba_buf; + unsigned long flags; + + if (unlikely(!debug_level_enabled(dbf->hba, level))) + return; + + spin_lock_irqsave(&dbf->hba_lock, flags); + memset(rec, 0, sizeof(*rec)); + + memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN); + rec->id = ZFCP_DBF_HBA_UAS; + rec->fsf_req_id = ~0u; + rec->fsf_req_status = ~0u; + rec->fsf_cmd = ~0u; + rec->fsf_seq_no = ~0u; + rec->u.uas.wwpn = wwpn; + rec->u.uas.fcp_lun = fcp_lun; + rec->u.uas.ret = ret; + + debug_event(dbf->hba, level, rec, sizeof(*rec)); + spin_unlock_irqrestore(&dbf->hba_lock, flags); +} + /** * zfcp_dbf_hba_bit_err - trace event for bit error conditions * @tag: tag indicating which kind of bit error unsolicited status was received @@ -732,7 +800,7 @@ void zfcp_dbf_scsi_common(char *tag, int level, struct scsi_device *sdev, min_t(u16, max_t(u16, rec->pl_len, ZFCP_DBF_PAY_MAX_REC), FSF_FCP_RSP_SIZE), - "fcp_riu", fsf->req_id); + "fcp_riu", fsf->req_id, ZFCP_DBF_PAY_LEVEL); } debug_event(dbf->scsi, level, rec, sizeof(*rec)); diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index 4d1435c573bc..79973fb24b1c 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -3,7 +3,7 @@ * zfcp device driver * debug feature declarations * - * Copyright IBM Corp. 2008, 2020 + * Copyright IBM Corp. 2008, 2026 */ #ifndef ZFCP_DBF_H @@ -140,6 +140,8 @@ struct zfcp_dbf_hba_res { u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE]; u32 port_handle; u32 lun_handle; + u32 plogi_len; + u32 prli_len; } __packed; /** @@ -149,6 +151,13 @@ struct zfcp_dbf_hba_res { * @d_id: destination ID * @lun: logical unit number * @queue_designator: queue designator + * @length: buffer length + * @res1: reserved field 1 + * @res2: reserved field 2 + * @class: class of service + * @res3: reserved field 3 + * @s_id: source ID + * @res4: reserved field 4 */ struct zfcp_dbf_hba_uss { u32 status_type; @@ -156,6 +165,25 @@ struct zfcp_dbf_hba_uss { u32 d_id; u64 lun; u64 queue_designator; + u32 length; + u32 res1; + u8 res2; + u32 class; + u8 res3; + u32 s_id; + u8 res4[20]; +} __packed; + +/** + * struct zfcp_dbf_hba_uas - trace record for sysfs unit add store + * @wwpn: remote port wwn + * @fcp_lun: FCP LUN + * @ret: return value + */ +struct zfcp_dbf_hba_uas { + u64 wwpn; + u64 fcp_lun; + u32 ret; } __packed; /** @@ -184,6 +212,7 @@ struct zfcp_dbf_hba_fces { * @ZFCP_DBF_HBA_BIT: bit error trace record * @ZFCP_DBF_HBA_BASIC: basic adapter event, only trace tag, no other data * @ZFCP_DBF_HBA_FCES: FC Endpoint Security trace record + * @ZFCP_DBF_HBA_UAS: unit add store trace record */ enum zfcp_dbf_hba_id { ZFCP_DBF_HBA_RES = 1, @@ -191,6 +220,7 @@ enum zfcp_dbf_hba_id { ZFCP_DBF_HBA_BIT = 3, ZFCP_DBF_HBA_BASIC = 4, ZFCP_DBF_HBA_FCES = 5, + ZFCP_DBF_HBA_UAS = 6, }; /** @@ -207,6 +237,7 @@ enum zfcp_dbf_hba_id { * @u.uss: data for unsolicited status buffer * @u.be: data for bit error unsolicited status buffer * @u.fces: data for FC Endpoint Security + * @u.uas: data for unit add store */ struct zfcp_dbf_hba { u8 id; @@ -221,6 +252,7 @@ struct zfcp_dbf_hba { struct zfcp_dbf_hba_uss uss; struct fsf_bit_error_payload be; struct zfcp_dbf_hba_fces fces; + struct zfcp_dbf_hba_uas uas; } u; } __packed; diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h index 9f5152b42b0e..0eb9bc9d03e4 100644 --- a/drivers/s390/scsi/zfcp_ext.h +++ b/drivers/s390/scsi/zfcp_ext.h @@ -4,7 +4,7 @@ * * External function declarations. * - * Copyright IBM Corp. 2002, 2023 + * Copyright IBM Corp. 2002, 2026 */ #ifndef ZFCP_EXT_H @@ -49,6 +49,8 @@ extern void zfcp_dbf_hba_fsf_fces(char *tag, const struct zfcp_fsf_req *req, extern void zfcp_dbf_hba_fsf_reqid(const char *const tag, const int level, struct zfcp_adapter *const adapter, const u64 req_id); +extern void zfcp_dbf_hba_uas(char *tag, int level, struct zfcp_adapter *adapter, + u64 wwpn, u64 fcp_lun, int ret); extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **); extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); @@ -152,7 +154,8 @@ extern ssize_t zfcp_fsf_scnprint_fc_security(char *buf, size_t size, /* zfcp_qdio.c */ extern int zfcp_qdio_setup(struct zfcp_adapter *); extern void zfcp_qdio_destroy(struct zfcp_qdio *); -extern int zfcp_qdio_sbal_get(struct zfcp_qdio *); +extern int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) + __must_hold(qdio->req_q_lock); extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_qdio_req *); extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *, struct zfcp_qdio_req *, struct scatterlist *); diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h index 5e6b601af980..4b92e85ec71d 100644 --- a/drivers/s390/scsi/zfcp_fsf.h +++ b/drivers/s390/scsi/zfcp_fsf.h @@ -246,7 +246,9 @@ struct fsf_status_read_buffer { u8 d_id[3]; u32 class; u64 fcp_lun; - u8 res3[24]; + u8 res3; + u8 s_id[3]; + u8 res4[20]; union { u8 data[FSF_STATUS_READ_PAYLOAD_SIZE]; u32 word[FSF_STATUS_READ_PAYLOAD_SIZE/sizeof(u32)]; diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index ce1af72d9b61..bac8385e069f 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -281,6 +281,7 @@ static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio) * Returns: 0 on success, -EIO if there is no free sbal after waiting. */ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio) + __must_hold(qdio->req_q_lock) { long ret; diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index 42423549e511..4f23d585d062 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -4,7 +4,7 @@ * * sysfs attributes. * - * Copyright IBM Corp. 2008, 2020 + * Copyright IBM Corp. 2008, 2026 */ #define pr_fmt(fmt) "zfcp: " fmt @@ -442,17 +442,24 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev, const char *buf, size_t count) { struct zfcp_port *port = container_of(dev, struct zfcp_port, dev); - u64 fcp_lun; - int retval; + struct zfcp_adapter *adapter = port->adapter; + u64 fcp_lun = 0; + int retval = -EINVAL; - if (kstrtoull(buf, 0, (unsigned long long *) &fcp_lun)) - return -EINVAL; + if (kstrtoull(buf, 0, (unsigned long long *)&fcp_lun)) { + zfcp_dbf_hba_uas("syuast1", 3, adapter, port->wwpn, + fcp_lun, retval); + return retval; + } flush_work(&port->rport_work); retval = zfcp_unit_add(port, fcp_lun); - if (retval) + if (retval) { + zfcp_dbf_hba_uas("syuast2", 3, adapter, port->wwpn, + fcp_lun, retval); return retval; + } return count; } diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c3042393af23..4a2af0f702e1 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -499,6 +499,7 @@ source "drivers/scsi/esas2r/Kconfig" source "drivers/scsi/megaraid/Kconfig.megaraid" source "drivers/scsi/mpt3sas/Kconfig" source "drivers/scsi/mpi3mr/Kconfig" +source "drivers/scsi/leapraid/Kconfig" source "drivers/scsi/smartpqi/Kconfig" config SCSI_HPTIOP diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 842c254bb226..098f35219e7d 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -91,6 +91,7 @@ obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/ obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o obj-$(CONFIG_SCSI_HPSA) += hpsa.o +obj-$(CONFIG_SCSI_LEAPRAID) += leapraid/ obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/ obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/ obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index 204448bfd04b..f81e53b53e20 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -275,9 +275,9 @@ static void a2091_remove(struct zorro_dev *z) } static struct zorro_device_id a2091_zorro_tbl[] = { - { ZORRO_PROD_CBM_A590_A2091_1 }, - { ZORRO_PROD_CBM_A590_A2091_2 }, - { 0 } + { .id = ZORRO_PROD_CBM_A590_A2091_1 }, + { .id = ZORRO_PROD_CBM_A590_A2091_2 }, + { } }; MODULE_DEVICE_TABLE(zorro, a2091_zorro_tbl); diff --git a/drivers/scsi/aha1542.c b/drivers/scsi/aha1542.c index fd766282d4a4..93dab19c1cb9 100644 --- a/drivers/scsi/aha1542.c +++ b/drivers/scsi/aha1542.c @@ -1083,7 +1083,7 @@ static int isa_registered; #ifdef CONFIG_PNP static const struct pnp_device_id aha1542_pnp_ids[] = { { .id = "ADP1542" }, - { .id = "" } + { } }; MODULE_DEVICE_TABLE(pnp, aha1542_pnp_ids); diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 2df399c537c1..8c9d423129c0 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -2627,7 +2627,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi, } -static void +static noinline_for_stack void bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi, struct bfa_fcs_fdmi_port_attr_s *port_attr) { diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index 6183ce05d8cf..ba490174a417 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -2182,7 +2182,6 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb) { struct DeviceCtlBlk *dcb = srb->dcb; u8 bval; - int fact; if (srb->msgin_buf[4] > 15) srb->msgin_buf[4] = 15; @@ -2205,11 +2204,6 @@ static void msgin_set_sync(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb) dcb->sync_period |= ALT_SYNC | bval; dcb->min_nego_period = srb->msgin_buf[3]; - if (dcb->sync_period & WIDE_SYNC) - fact = 500; - else - fact = 250; - if (!(srb->state & SRB_DO_SYNC_NEGO)) { /* Reply with corrected SDTR Message */ diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 80ab0ff921d4..d81ec47a7140 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -5,6 +5,7 @@ * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH. * All rights reserved. */ +#include #include #include #include @@ -693,8 +694,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg) !(tmp_pg->flags & ALUA_PG_RUNNING)) { struct alua_dh_data *h; - tmp_pg->state = desc[0] & 0x0f; - tmp_pg->pref = desc[0] >> 7; + tmp_pg->state = FIELD_GET(SCSI_ACCESS_STATE_MASK, desc[0]); + tmp_pg->pref = FIELD_GET(SCSI_ACCESS_STATE_PREFERRED, desc[0]); rcu_read_lock(); list_for_each_entry_rcu(h, &tmp_pg->dh_list, node) { diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 1838032f6486..b79c6a7ea791 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -1997,6 +1997,8 @@ efct_hw_io_abort(struct efct_hw *hw, struct efct_hw_io *io_to_abort, wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_abort, io_to_abort); if (!wqcb) { efc_log_err(hw->os, "can't allocate request tag\n"); + io_to_abort->abort_in_progress = false; + kref_put(&io_to_abort->ref, io_to_abort->release); return -ENOSPC; } diff --git a/drivers/scsi/elx/efct/efct_unsol.c b/drivers/scsi/elx/efct/efct_unsol.c index e6addab66a60..6a871a59c909 100644 --- a/drivers/scsi/elx/efct/efct_unsol.c +++ b/drivers/scsi/elx/efct/efct_unsol.c @@ -385,6 +385,7 @@ efct_dispatch_fcp_cmd(struct efct_node *node, struct efc_hw_sequence *seq) if (cmnd->fc_flags & FCP_CFL_LEN_MASK) { efc_log_err(efct, "Additional CDB not supported\n"); + efct_scsi_io_free(io); return -EIO; } /* diff --git a/drivers/scsi/fnic/Makefile b/drivers/scsi/fnic/Makefile index c025e875009e..48c5e0cb8ca6 100644 --- a/drivers/scsi/fnic/Makefile +++ b/drivers/scsi/fnic/Makefile @@ -6,6 +6,7 @@ fnic-y := \ fnic_attrs.o \ fnic_isr.o \ fnic_main.o \ + fnic_nvme.o \ fnic_res.o \ fnic_fcs.o \ fdls_disc.o \ diff --git a/drivers/scsi/fnic/fcpio.h b/drivers/scsi/fnic/fcpio.h index 54a0b2ba8f6f..a98a2a3bfe15 100644 --- a/drivers/scsi/fnic/fcpio.h +++ b/drivers/scsi/fnic/fcpio.h @@ -21,6 +21,18 @@ #define FCPIO_HOST_SEQ_ID_RANGE_START 0x80 #define FCPIO_HOST_SEQ_ID_RANGE_END 0xff +struct fnic_nvme_io_event { + struct list_head links; + struct work_struct io_work; + void *arg1; +}; + +struct fnic_io_event_s { + struct list_head links; + struct work_struct io_work; + void *arg1; +}; + /* * Command entry type */ @@ -33,6 +45,9 @@ enum fcpio_type { FCPIO_ICMND_CMPL, FCPIO_ITMF, FCPIO_ITMF_CMPL, + FCPIO_NVME_CMD, + FCPIO_NVME_ERSP_HW_CMPL, + FCPIO_NVME_ERSP_FW_CMPL, /* * Target request types @@ -179,10 +194,22 @@ fcpio_header_dec(struct fcpio_header *hdr, *tag = hdr->tag; } +#define NVME_CMD_SZ 96 #define CDB_16 16 #define CDB_32 32 #define LUN_ADDRESS 8 +struct fcpio_nvme_cmnd { + uint8_t d_id[3]; + u_int8_t flags; /* command flags */ + u_int32_t sgl_cnt; /* scatter-gather list count */ + u_int64_t sgl_addr; /* scatter-gather list addr */ + u_int16_t cmd_len; + u_int16_t _resvd1; /* reserved: should be 0 */ + u_int32_t data_len; /* length of data expected */ + u_int8_t nvme_cmnd[NVME_CMD_SZ]; /* NVME command */ +}; + /* * fcpio_icmnd_16: host -> firmware request * @@ -458,6 +485,7 @@ struct fcpio_host_req { /* * Initiator host requests */ + struct fcpio_nvme_cmnd nvcmnd; struct fcpio_icmnd_16 icmnd_16; struct fcpio_icmnd_32 icmnd_32; struct fcpio_itmf itmf; @@ -482,6 +510,12 @@ struct fcpio_host_req { } u; }; +struct fcpio_nvme_cmpl { + uint8_t resp_size; + uint8_t resvd[3]; + uint8_t resp_bytes[32]; +}; + /* * fcpio_icmnd_cmpl: firmware -> host response * @@ -682,6 +716,7 @@ struct fcpio_fw_req { * Initiator firmware responses */ struct fcpio_icmnd_cmpl icmnd_cmpl; + struct fcpio_nvme_cmpl nvme_cmpl; struct fcpio_itmf_cmpl itmf_cmpl; /* diff --git a/drivers/scsi/fnic/fdls_disc.c b/drivers/scsi/fnic/fdls_disc.c index 554dea767885..8cb40466872f 100644 --- a/drivers/scsi/fnic/fdls_disc.c +++ b/drivers/scsi/fnic/fdls_disc.c @@ -13,6 +13,10 @@ #include #define FC_FC4_TYPE_SCSI 0x08 +#define FNIC_NVME_FEAT_TARG (1 << 0) +#define FNIC_NVME_FEAT_INIT (1 << 1) +#define FNIC_NVME_FEAT_DISC (1 << 2) + #define PORT_SPEED_BIT_8 8 #define PORT_SPEED_BIT_9 9 #define PORT_SPEED_BIT_14 14 @@ -104,7 +108,7 @@ uint8_t *fdls_alloc_frame(struct fnic_iport_s *iport) frame = mempool_alloc(fnic->frame_pool, GFP_ATOMIC); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame"); return NULL; } @@ -136,7 +140,7 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type, */ idx = find_next_zero_bit(oxid_pool->bitmap, FNIC_OXID_POOL_SZ, oxid_pool->next_idx); if (idx == FNIC_OXID_POOL_SZ) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Alloc oxid: all oxid slots are busy iport state:%d\n", iport->state); return FNIC_UNASSIGNED_OXID; @@ -148,7 +152,7 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type, oxid = FNIC_OXID_ENCODE(idx, oxid_frame_type); *active_oxid = oxid; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "alloc oxid: 0x%x, iport state: %d\n", oxid, iport->state); return oxid; @@ -169,7 +173,7 @@ static void fdls_free_oxid_idx(struct fnic_iport_s *iport, uint16_t oxid_idx) lockdep_assert_held(&fnic->fnic_lock); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "free oxid idx: 0x%x\n", oxid_idx); WARN_ON(!test_and_clear_bit(oxid_idx, oxid_pool->bitmap)); @@ -194,7 +198,7 @@ void fdls_reclaim_oxid_handler(struct work_struct *work) struct reclaim_entry_s *reclaim_entry, *next; unsigned long delay_j, cur_jiffies; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Reclaim oxid callback\n"); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); @@ -223,7 +227,7 @@ void fdls_reclaim_oxid_handler(struct work_struct *work) delay_j = reclaim_entry->expires - cur_jiffies; schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Scheduling next callback at:%ld jiffies\n", delay_j); } @@ -266,7 +270,7 @@ void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid) lockdep_assert_held(&fnic->fnic_lock); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Schedule oxid free. oxid: 0x%x\n", *active_oxid); *active_oxid = FNIC_UNASSIGNED_OXID; @@ -275,7 +279,7 @@ void fdls_schedule_oxid_free(struct fnic_iport_s *iport, uint16_t *active_oxid) kzalloc_obj(struct reclaim_entry_s, GFP_ATOMIC); if (!reclaim_entry) { - FNIC_FCS_DBG(KERN_WARNING, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_WARNING, fnic, "Failed to allocate memory for reclaim struct for oxid idx: %d\n", oxid_idx); @@ -313,7 +317,7 @@ void fdls_schedule_oxid_free_retry_work(struct work_struct *work) for_each_set_bit(idx, oxid_pool->pending_schedule_free, FNIC_OXID_POOL_SZ) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Schedule oxid free. oxid idx: %d\n", idx); reclaim_entry = kzalloc_obj(*reclaim_entry); @@ -323,12 +327,19 @@ void fdls_schedule_oxid_free_retry_work(struct work_struct *work) return; } - clear_bit(idx, oxid_pool->pending_schedule_free); reclaim_entry->oxid_idx = idx; reclaim_entry->expires = round_jiffies(jiffies + delay_j); + spin_lock_irqsave(&fnic->fnic_lock, flags); + /* Reset may have cleared this bit while this worker allocated. */ + if (!test_and_clear_bit(idx, oxid_pool->pending_schedule_free)) { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + kfree(reclaim_entry); + continue; + } list_add_tail(&reclaim_entry->links, &oxid_pool->oxid_reclaim_list); spin_unlock_irqrestore(&fnic->fnic_lock, flags); + schedule_delayed_work(&oxid_pool->oxid_reclaim_work, delay_j); } } @@ -383,10 +394,25 @@ static bool fdls_is_oxid_tgt_req(uint16_t oxid) return true; } +static inline bool fdls_is_oxid_nvme_req(uint16_t oxid) +{ + return FNIC_FRAME_TYPE(oxid) == FNIC_FRAME_TYPE_NVME_LS; +} + static void fdls_reset_oxid_pool(struct fnic_iport_s *iport) { struct fnic_oxid_pool_s *oxid_pool = &iport->oxid_pool; + struct reclaim_entry_s *reclaim_entry, *next; + cancel_delayed_work(&oxid_pool->oxid_reclaim_work); + cancel_delayed_work(&oxid_pool->schedule_oxid_free_retry); + list_for_each_entry_safe(reclaim_entry, next, + &oxid_pool->oxid_reclaim_list, links) { + list_del(&reclaim_entry->links); + kfree(reclaim_entry); + } + bitmap_clear(oxid_pool->pending_schedule_free, 0, FNIC_OXID_POOL_SZ); + bitmap_clear(oxid_pool->bitmap, 0, FNIC_OXID_POOL_SZ); oxid_pool->next_idx = 0; } @@ -416,7 +442,7 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout) struct fnic *fnic = iport->fnic; if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport fcid: 0x%x: Canceling fabric disc timer\n", iport->fcid); fnic_del_fabric_timer_sync(fnic); @@ -429,7 +455,7 @@ fdls_start_fabric_timer(struct fnic_iport_s *iport, int timeout) fabric_tov = jiffies + msecs_to_jiffies(timeout); mod_timer(&iport->fabric.retry_timer, round_jiffies(fabric_tov)); iport->fabric.timer_pending = 1; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fabric timer is %d ", timeout); } @@ -441,7 +467,7 @@ fdls_start_tport_timer(struct fnic_iport_s *iport, struct fnic *fnic = iport->fnic; if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid 0x%x: Canceling disc timer\n", tport->fcid); fnic_del_tport_timer_sync(fnic, tport); @@ -562,6 +588,22 @@ static void fdls_init_fabric_abts_frame(uint8_t *frame, }; } +static void fdls_set_frame_type(struct fnic *fnic, uint8_t *frame_type) +{ + if (IS_FNIC_FCP_INITIATOR(fnic)) + *frame_type = FC_TYPE_FCP; + else if (IS_FNIC_NVME_INITIATOR(fnic)) + *frame_type = FC_TYPE_NVME; +} + +static void fdls_set_feature_type(struct fnic *fnic, uint8_t *feature_type) +{ + if (IS_FNIC_FCP_INITIATOR(fnic)) + *feature_type = FCP_FEAT_INIT; + else if (IS_FNIC_NVME_INITIATOR(fnic)) + *feature_type = FNIC_NVME_FEAT_INIT; +} + static void fdls_send_rscn_resp(struct fnic_iport_s *iport, struct fc_frame_header *rscn_fchdr) @@ -575,7 +617,7 @@ fdls_send_rscn_resp(struct fnic_iport_s *iport, frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send RSCN response"); return; } @@ -588,7 +630,7 @@ fdls_send_rscn_resp(struct fnic_iport_s *iport, oxid = FNIC_STD_GET_OX_ID(rscn_fchdr); FNIC_STD_SET_OX_ID(pels_acc->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send RSCN response with oxid: 0x%x", iport->fcid, oxid); @@ -608,7 +650,7 @@ fdls_send_logo_resp(struct fnic_iport_s *iport, frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send LOGO response"); return; } @@ -621,13 +663,57 @@ fdls_send_logo_resp(struct fnic_iport_s *iport, oxid = FNIC_STD_GET_OX_ID(req_fchdr); FNIC_STD_SET_OX_ID(plogo_resp->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send LOGO response with oxid: 0x%x", iport->fcid, oxid); fnic_send_fcoe_frame(iport, frame, frame_size); } +int fdls_send_ls_req_abts(struct fnic_iport_s *iport, + struct fnic_tport_s *tport, unsigned int oxid) +{ + uint8_t *frame; + uint8_t s_id[3]; + uint8_t d_id[3]; + struct fnic *fnic = iport->fnic; + struct fc_frame_header *pls_req_abts; + uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET + + sizeof(struct fc_frame_header); + int ret; + + frame = fdls_alloc_frame(iport); + if (frame == NULL) { + FNIC_FCS_DBG(KERN_ERR, fnic, + "Failed to allocate frame to send ls req ABTS"); + return -ENOMEM; + } + + pls_req_abts = (struct fc_frame_header *) (frame + + FNIC_ETH_FCOE_HDRS_OFFSET); + fdls_init_fabric_abts_frame(frame, iport); + + hton24(s_id, iport->fcid); + hton24(d_id, tport->fcid); + FNIC_STD_SET_S_ID(*pls_req_abts, s_id); + FNIC_STD_SET_D_ID(*pls_req_abts, d_id); + + FNIC_STD_SET_OX_ID(*pls_req_abts, oxid); + + FNIC_FCS_DBG(KERN_INFO, fnic, + "iport 0x%x: tport: 0x%x FDLS sending ls req abts with oxid: 0x%x", + iport->fcid, tport->fcid, oxid); + + ret = fnic_send_fcoe_frame(iport, frame, frame_size); + if (ret) { + mempool_free(frame, fnic->frame_pool); + return ret; + } + + return 0; +} + + void fdls_send_tport_abts(struct fnic_iport_s *iport, struct fnic_tport_s *tport) @@ -642,7 +728,7 @@ fdls_send_tport_abts(struct fnic_iport_s *iport, frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send tport ABTS"); return; } @@ -665,7 +751,7 @@ fdls_send_tport_abts(struct fnic_iport_s *iport, FNIC_STD_SET_OX_ID(*ptport_abts, tport->active_oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send tport abts: tport->state: %d ", iport->fcid, tport->state); @@ -687,7 +773,7 @@ static void fdls_send_fabric_abts(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send fabric ABTS"); return; } @@ -750,7 +836,7 @@ static void fdls_send_fabric_abts(struct fnic_iport_s *iport) oxid = iport->active_oxid_fabric_req; FNIC_STD_SET_OX_ID(*pfabric_abts, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send fabric abts. iport->fabric.state: %d oxid: 0x%x", iport->fcid, iport->fabric.state, oxid); @@ -773,7 +859,7 @@ static uint8_t *fdls_alloc_init_fdmi_abts_frame(struct fnic_iport_s *iport, frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send FDMI ABTS"); return NULL; } @@ -802,7 +888,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport) if (frame == NULL) return; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send FDMI PLOGI abts. iport->fabric.state: %d oxid: 0x%x", iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_plogi); fnic_send_fcoe_frame(iport, frame, frame_size); @@ -813,7 +899,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport) if (frame == NULL) return; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send FDMI RHBA abts. iport->fabric.state: %d oxid: 0x%x", iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rhba); fnic_send_fcoe_frame(iport, frame, frame_size); @@ -828,7 +914,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport) return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send FDMI RPA abts. iport->fabric.state: %d oxid: 0x%x", iport->fcid, iport->fabric.state, iport->active_oxid_fdmi_rpa); fnic_send_fcoe_frame(iport, frame, frame_size); @@ -840,7 +926,7 @@ static void fdls_send_fdmi_abts(struct fnic_iport_s *iport) mod_timer(&iport->fabric.fdmi_timer, round_jiffies(fdmi_tov)); iport->fabric.fdmi_pending |= FDLS_FDMI_ABORT_PENDING; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); } @@ -856,7 +942,7 @@ static void fdls_send_fabric_flogi(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send FLOGI"); iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -885,7 +971,7 @@ static void fdls_send_fabric_flogi(struct fnic_iport_s *iport) &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send FLOGI", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -894,7 +980,7 @@ static void fdls_send_fabric_flogi(struct fnic_iport_s *iport) } FNIC_STD_SET_OX_ID(pflogi->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send fabric FLOGI with oxid: 0x%x", iport->fcid, oxid); @@ -916,7 +1002,7 @@ static void fdls_send_fabric_plogi(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send PLOGI"); iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -928,7 +1014,7 @@ static void fdls_send_fabric_plogi(struct fnic_iport_s *iport) oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_PLOGI, &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send fabric PLOGI", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -937,7 +1023,7 @@ static void fdls_send_fabric_plogi(struct fnic_iport_s *iport) } FNIC_STD_SET_OX_ID(pplogi->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send fabric PLOGI with oxid: 0x%x", iport->fcid, oxid); @@ -962,7 +1048,7 @@ static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send FDMI PLOGI"); goto err_out; } @@ -974,7 +1060,7 @@ static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport) &iport->active_oxid_fdmi_plogi); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send FDMI PLOGI", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -985,7 +1071,7 @@ static void fdls_send_fdmi_plogi(struct fnic_iport_s *iport) hton24(d_id, FC_FID_MGMT_SERV); FNIC_STD_SET_D_ID(pplogi->fchdr, d_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send FDMI PLOGI with oxid: 0x%x", iport->fcid, oxid); @@ -1009,7 +1095,7 @@ static void fdls_send_rpn_id(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send RPN_ID"); iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -1036,7 +1122,7 @@ static void fdls_send_rpn_id(struct fnic_iport_s *iport) &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send RPN_ID", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1045,7 +1131,7 @@ static void fdls_send_rpn_id(struct fnic_iport_s *iport) } FNIC_STD_SET_OX_ID(prpn_id->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send RPN ID with oxid: 0x%x", iport->fcid, oxid); @@ -1068,7 +1154,7 @@ static void fdls_send_scr(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send SCR"); iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -1090,7 +1176,7 @@ static void fdls_send_scr(struct fnic_iport_s *iport) oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_SCR, &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send SCR", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1099,7 +1185,7 @@ static void fdls_send_scr(struct fnic_iport_s *iport) } FNIC_STD_SET_OX_ID(pscr->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send SCR with oxid: 0x%x", iport->fcid, oxid); @@ -1123,7 +1209,7 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send GPN FT"); iport->fabric.flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -1137,10 +1223,10 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state) .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)}, .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR, .ct_fs_subtype = FC_NS_SUBTYPE, - .ct_cmd = cpu_to_be16(FC_NS_GPN_FT)}, - .gpn_ft.fn_fc4_type = 0x08 + .ct_cmd = cpu_to_be16(FC_NS_GPN_FT)} }; + fdls_set_frame_type(fnic, &pgpn_ft->gpn_ft.fn_fc4_type); hton24(fcid, iport->fcid); FNIC_STD_SET_S_ID(pgpn_ft->fchdr, fcid); @@ -1148,7 +1234,7 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state) &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send GPN FT", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1157,7 +1243,7 @@ static void fdls_send_gpn_ft(struct fnic_iport_s *iport, int fdls_state) } FNIC_STD_SET_OX_ID(pgpn_ft->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send GPN FT with oxid: 0x%x", iport->fcid, oxid); @@ -1183,7 +1269,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send TGT ADISC"); tport->flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -1203,7 +1289,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport) oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_ADISC, &tport->active_oxid); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send TGT ADISC", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1222,7 +1308,7 @@ fdls_send_tgt_adisc(struct fnic_iport_s *iport, struct fnic_tport_s *tport) padisc->els.adisc_cmd = ELS_ADISC; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send ADISC to tgt fcid: 0x%x", iport->fcid, tport->fcid); @@ -1242,7 +1328,7 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport) if ((tport->state == FDLS_TGT_STATE_OFFLINING) || (tport->state == FDLS_TGT_STATE_OFFLINE)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid 0x%x: tport state is offlining/offline\n", tport->fcid); return false; @@ -1257,24 +1343,31 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport) tport->flags |= FNIC_FDLS_TPORT_TERMINATING; if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid 0x%x: Canceling disc timer\n", tport->fcid); fnic_del_tport_timer_sync(fnic, tport); tport->timer_pending = 0; } - spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - fnic_rport_exch_reset(iport->fnic, tport->fcid); - spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + if (IS_FNIC_FCP_INITIATOR(fnic)) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + fnic_rport_exch_reset(iport->fnic, tport->fcid); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + } else if (IS_FNIC_NVME_INITIATOR(fnic)) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + nvfnic_exch_reset(iport, tport); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + } - if (tport->flags & FNIC_FDLS_SCSI_REGISTERED) { + if ((tport->flags & FNIC_FDLS_SCSI_REGISTERED) || + (tport->flags & FNIC_FDLS_NVME_REGISTERED)) { tport_del_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC); if (!tport_del_evt) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "Failed to allocate memory for tport fcid: 0x%0x\n", - tport->fcid); + FNIC_FCS_DBG(KERN_INFO, fnic, + "iport: 0x%x tport 0x%x: Failed to allocate memory\n", + iport->fcid, tport->fcid); return false; } tport_del_evt->event = TGT_EV_RPORT_DEL; @@ -1282,12 +1375,13 @@ bool fdls_delete_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport) list_add_tail(&tport_del_evt->links, &fnic->tport_event_list); queue_work(fnic_event_queue, &fnic->tport_work); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "tport 0x%x not reg with scsi_transport. Freeing locally", - tport->fcid); + FNIC_FCS_DBG(KERN_INFO, fnic, + "tport 0x%x not registered, freeing locally\n", + tport->fcid); list_del(&tport->links); kfree(tport); } + return true; } @@ -1305,7 +1399,7 @@ fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send TGT PLOGI"); tport->flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -1316,7 +1410,7 @@ fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport) oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PLOGI, &tport->active_oxid); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate oxid to send PLOGI to fcid: 0x%x", iport->fcid, tport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1330,7 +1424,7 @@ fdls_send_tgt_plogi(struct fnic_iport_s *iport, struct fnic_tport_s *tport) hton24(d_id, tport->fcid); FNIC_STD_SET_D_ID(pplogi->fchdr, d_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send tgt PLOGI to tgt: 0x%x with oxid: 0x%x", iport->fcid, tport->fcid, oxid); @@ -1353,7 +1447,7 @@ fnic_fc_plogi_rsp_rdf(struct fnic_iport_s *iport, be16_to_cpu(plogi_rsp->els.fl_cssp[2].cp_rdfs) & FNIC_FC_C3_RDF; struct fnic *fnic = iport->fnic; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "MFS: b2b_rdf_size: 0x%x spc3_rdf_size: 0x%x", b2b_rdf_size, spc3_rdf_size); @@ -1372,7 +1466,7 @@ static void fdls_send_register_fc4_types(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send RFT"); return; } @@ -1396,7 +1490,7 @@ static void fdls_send_register_fc4_types(struct fnic_iport_s *iport) &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send RFT", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1404,15 +1498,22 @@ static void fdls_send_register_fc4_types(struct fnic_iport_s *iport) } FNIC_STD_SET_OX_ID(prft_id->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "0x%x: FDLS send RFT with oxid: 0x%x", iport->fcid, - oxid); + if (IS_FNIC_NVME_INITIATOR(fnic)) + prft_id->rft_id.fr_fts.ff_type_map[FC_TYPE_NVME / FC_NS_BPW] = + cpu_to_be32(1 << (FC_TYPE_NVME % FC_NS_BPW)); + else if (IS_FNIC_FCP_INITIATOR(fnic)) + prft_id->rft_id.fr_fts.ff_type_map[FC_TYPE_FCP / FC_NS_BPW] = + cpu_to_be32(1 << (FC_TYPE_FCP % FC_NS_BPW)); - prft_id->rft_id.fr_fts.ff_type_map[0] = - cpu_to_be32(1 << FC_TYPE_FCP); + prft_id->rft_id.fr_fts.ff_type_map[FC_TYPE_CT / FC_NS_BPW] |= + cpu_to_be32(1 << (FC_TYPE_CT % FC_NS_BPW)); - prft_id->rft_id.fr_fts.ff_type_map[1] = - cpu_to_be32(1 << (FC_TYPE_CT % FC_NS_BPW)); + FNIC_FCS_DBG(KERN_INFO, fnic, + "0x%x: FDLS send RFT 0x%08x 0x%08x 0x%08x with oxid: 0x%x", + iport->fcid, prft_id->rft_id.fr_fts.ff_type_map[0], + prft_id->rft_id.fr_fts.ff_type_map[1], + prft_id->rft_id.fr_fts.ff_type_map[2], + oxid); fnic_send_fcoe_frame(iport, frame, frame_size); @@ -1432,7 +1533,7 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send RFF"); return; } @@ -1446,10 +1547,11 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport) .fc_std_ct_hdr = {.ct_rev = FC_CT_REV, .ct_fs_type = FC_FST_DIR, .ct_fs_subtype = FC_NS_SUBTYPE, .ct_cmd = cpu_to_be16(FC_NS_RFF_ID)}, - .rff_id.fr_feat = 0x2, - .rff_id.fr_type = FC_TYPE_FCP }; + fdls_set_frame_type(fnic, &prff_id->rff_id.fr_type); + fdls_set_feature_type(fnic, &prff_id->rff_id.fr_feat); + hton24(fcid, iport->fcid); FNIC_STD_SET_S_ID(prff_id->fchdr, fcid); FNIC_STD_SET_PORT_ID(prff_id->rff_id, fcid); @@ -1458,7 +1560,7 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport) &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send RFF", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1466,11 +1568,10 @@ static void fdls_send_register_fc4_features(struct fnic_iport_s *iport) } FNIC_STD_SET_OX_ID(prff_id->fchdr, oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "0x%x: FDLS send RFF with oxid: 0x%x", iport->fcid, - oxid); - - prff_id->rff_id.fr_type = FC_TYPE_FCP; + FNIC_FCS_DBG(KERN_INFO, fnic, + "0x%x: FDLS send RFF with oxid: 0x%x type 0%x feat 0%x", + iport->fcid, oxid, prff_id->rff_id.fr_type, + prff_id->rff_id.fr_feat); fnic_send_fcoe_frame(iport, frame, frame_size); @@ -1493,7 +1594,7 @@ fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send TGT PRLI"); tport->flags |= FNIC_FDLS_RETRY_FRAME; goto err_out; @@ -1504,16 +1605,24 @@ fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport) .fchdr = {.fh_r_ctl = FC_RCTL_ELS_REQ, .fh_type = FC_TYPE_ELS, .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}, .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID)}, - .els_prli = {.prli_cmd = ELS_PRLI, - .prli_spp_len = 16, - .prli_len = cpu_to_be16(0x14)}, - .sp = {.spp_type = 0x08, .spp_flags = 0x0020, - .spp_params = cpu_to_be32(0xA2)} + .els_prli = {.prli_cmd = ELS_PRLI}, + .sp = {.spp_params = cpu_to_be32(iport->service_params)} }; + fdls_set_frame_type(fnic, &pprli->sp.spp_type); + if (IS_FNIC_FCP_INITIATOR(fnic)) { + pprli->els_prli.prli_spp_len = 16; + pprli->els_prli.prli_len = cpu_to_be16(0x14); + pprli->sp.spp_flags = FC_SPP_EST_IMG_PAIR; + } else if (IS_FNIC_NVME_INITIATOR(fnic)) { + /* Per FC-NVMe, Establish Image Pair must not be set for NVMe PRLI. */ + pprli->els_prli.prli_spp_len = 20; + pprli->els_prli.prli_len = cpu_to_be16(0x18); + } + oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_PRLI, &tport->active_oxid); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "0x%x: Failed to allocate OXID to send TGT PRLI to 0x%x", iport->fcid, tport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1530,7 +1639,7 @@ fdls_send_tgt_prli(struct fnic_iport_s *iport, struct fnic_tport_s *tport) FNIC_STD_SET_S_ID(pprli->fchdr, s_id); FNIC_STD_SET_D_ID(pprli->fchdr, d_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send PRLI to tgt: 0x%x with oxid: 0x%x", iport->fcid, tport->fcid, oxid); @@ -1564,7 +1673,7 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send fabric LOGO"); return; } @@ -1576,7 +1685,7 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport) &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send fabric LOGO", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1589,7 +1698,7 @@ void fdls_send_fabric_logo(struct fnic_iport_s *iport) iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send fabric LOGO with oxid: 0x%x", iport->fcid, oxid); @@ -1621,7 +1730,7 @@ void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send fabric LOGO"); return; } @@ -1631,7 +1740,7 @@ void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport) oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_TGT_LOGO, &tport->active_oxid); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send tgt LOGO", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1642,7 +1751,7 @@ void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport) hton24(d_id, tport->fcid); FNIC_STD_SET_D_ID(plogo->fchdr, d_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send tgt LOGO with oxid: 0x%x", iport->fcid, oxid); @@ -1657,7 +1766,7 @@ static void fdls_tgt_discovery_start(struct fnic_iport_s *iport) u32 old_link_down_cnt = iport->fnic->link_down_cnt; struct fnic *fnic = iport->fnic; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Starting FDLS target discovery", iport->fcid); list_for_each_entry_safe(tport, next, &iport->tport_list, links) { @@ -1711,7 +1820,7 @@ static void fdls_target_restart_nexus(struct fnic_tport_s *tport) struct fnic *fnic = iport->fnic; bool retval = true; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid: 0x%x state: %d restart_count: %d", tport->fcid, tport->state, tport->nexus_restart_count); @@ -1721,13 +1830,13 @@ static void fdls_target_restart_nexus(struct fnic_tport_s *tport) retval = fdls_delete_tport(iport, tport); if (retval != true) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Error deleting tport: 0x%x", fcid); return; } if (nexus_restart_count >= FNIC_TPORT_MAX_NEXUS_RESTART) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Exceeded nexus restart retries tport: 0x%x", fcid); return; @@ -1744,7 +1853,7 @@ static void fdls_target_restart_nexus(struct fnic_tport_s *tport) */ new_tport = fdls_create_tport(iport, fcid, wwpn); if (!new_tport) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Error creating new tport: 0x%x", fcid); return; } @@ -1773,12 +1882,12 @@ static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport; struct fnic *fnic = iport->fnic; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS create tport: fcid: 0x%x wwpn: 0x%llx", fcid, wwpn); tport = kzalloc_obj(struct fnic_tport_s, GFP_ATOMIC); if (!tport) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Memory allocation failure while creating tport: 0x%x\n", fcid); return NULL; @@ -1790,13 +1899,14 @@ static struct fnic_tport_s *fdls_create_tport(struct fnic_iport_s *iport, tport->fcid = fcid; tport->wwpn = wwpn; tport->iport = iport; + INIT_LIST_HEAD(&tport->ls_req_list); - FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_DEBUG, fnic, "Need to setup tport timer callback"); timer_setup(&tport->retry_timer, fdls_tport_timer_callback, 0); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Added tport 0x%x", tport->fcid); fdls_set_tport_state(tport, FDLS_TGT_STATE_INIT); list_add_tail(&tport->links, &iport->tport_list); @@ -1847,7 +1957,7 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send FDMI RHBA"); return; } @@ -1875,7 +1985,7 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) &iport->active_oxid_fdmi_rhba); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send FDMI RHBA", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -1896,14 +2006,14 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_NODE_NAME, FNIC_FDMI_NN_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "NN set, off=%d", attr_off_bytes); strscpy_pad(data, FNIC_FDMI_MANUFACTURER, FNIC_FDMI_MANU_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MANUFACTURER, FNIC_FDMI_MANU_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "MFG set <%s>, off=%d", data, attr_off_bytes); err = vnic_dev_fw_info(fnic->vdev, &fw_info); @@ -1912,7 +2022,7 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) FNIC_FDMI_SERIAL_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_SERIAL_NUMBER, FNIC_FDMI_SERIAL_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "SERIAL set <%s>, off=%d", data, attr_off_bytes); } @@ -1923,21 +2033,21 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL, FNIC_FDMI_MODEL_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "MODEL set <%s>, off=%d", data, attr_off_bytes); strscpy_pad(data, FNIC_FDMI_MODEL_DESCRIPTION, FNIC_FDMI_MODEL_DES_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MODEL_DES, FNIC_FDMI_MODEL_DES_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "MODEL_DESC set <%s>, off=%d", data, attr_off_bytes); if (!err) { strscpy_pad(data, fw_info->hw_version, FNIC_FDMI_HW_VER_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HARDWARE_VERSION, FNIC_FDMI_HW_VER_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "HW_VER set <%s>, off=%d", data, attr_off_bytes); } @@ -1946,14 +2056,14 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_DRIVER_VERSION, FNIC_FDMI_DR_VER_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "DRV_VER set <%s>, off=%d", data, attr_off_bytes); strscpy_pad(data, "N/A", FNIC_FDMI_ROM_VER_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_ROM_VERSION, FNIC_FDMI_ROM_VER_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ROM_VER set <%s>, off=%d", data, attr_off_bytes); if (!err) { @@ -1961,14 +2071,14 @@ static void fdls_fdmi_register_hba(struct fnic_iport_s *iport) fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FIRMWARE_VERSION, FNIC_FDMI_FW_VER_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FW_VER set <%s>, off=%d", data, attr_off_bytes); } len = sizeof(struct fc_std_fdmi_rhba) + attr_off_bytes; frame_size += len; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send FDMI RHBA with oxid: 0x%x fs: %d", iport->fcid, oxid, frame_size); @@ -1992,7 +2102,7 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send FDMI RPA"); return; } @@ -2020,7 +2130,7 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport) &iport->active_oxid_fdmi_rpa); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Failed to allocate OXID to send FDMI RPA", iport->fcid); mempool_free(frame, fnic->frame_pool); @@ -2063,7 +2173,10 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport) put_unaligned_be64(iport->wwnn, data); memset(data, 0, FNIC_FDMI_FC4_LEN); - data[2] = 1; + if (IS_FNIC_FCP_INITIATOR(fnic)) + data[2] = 1; + else if (IS_FNIC_NVME_INITIATOR(fnic)) + data[6] = 1; fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_FC4_TYPES, FNIC_FDMI_FC4_LEN, data, &attr_off_bytes); @@ -2079,28 +2192,34 @@ static void fdls_fdmi_register_pa(struct fnic_iport_s *iport) fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_MAX_FRAME_SIZE, FNIC_FDMI_MFS_LEN, data, &attr_off_bytes); - snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "host%d", - fnic->host->host_no); + if (IS_FNIC_FCP_INITIATOR(fnic)) + snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "host%d", + fnic->host->host_no); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + snprintf(tmp_data, FNIC_FDMI_OS_NAME_LEN - 1, "nvfnic%d", + fnic->fnic_num); strscpy_pad(data, tmp_data, FNIC_FDMI_OS_NAME_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_OS_NAME, FNIC_FDMI_OS_NAME_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "OS name set <%s>, off=%d", data, attr_off_bytes); - sprintf(fc_host_system_hostname(fnic->host), "%s", utsname()->nodename); - strscpy_pad(data, fc_host_system_hostname(fnic->host), - FNIC_FDMI_HN_LEN); + if (IS_FNIC_FCP_INITIATOR(fnic)) + sprintf(fc_host_system_hostname(fnic->host), "%s", + utsname()->nodename); + + strscpy_pad(data, utsname()->nodename, FNIC_FDMI_HN_LEN); fnic_fdmi_attr_set(fdmi_attr, FNIC_FDMI_TYPE_HOST_NAME, FNIC_FDMI_HN_LEN, data, &attr_off_bytes); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Host name set <%s>, off=%d", data, attr_off_bytes); len = sizeof(struct fc_std_fdmi_rpa) + attr_off_bytes; frame_size += len; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send FDMI RPA with oxid: 0x%x fs: %d", iport->fcid, oxid, frame_size); @@ -2117,7 +2236,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) struct fnic *fnic = iport->fnic; unsigned long flags; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tp: %d fab state: %d fab retry counter: %d max_flogi_retries: %d", iport->fabric.timer_pending, iport->fabric.state, iport->fabric.retry_counter, iport->max_flogi_retries); @@ -2132,7 +2251,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) if (iport->fabric.del_timer_inprogress) { iport->fabric.del_timer_inprogress = 0; spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fabric_del_timer inprogress(%d). Skip timer cb", iport->fabric.del_timer_inprogress); return; @@ -2160,7 +2279,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED; fdls_send_fabric_flogi(iport); } else - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Exceeded max FLOGI retries"); } break; @@ -2182,7 +2301,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED; fdls_send_fabric_plogi(iport); } else - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Exceeded max PLOGI retries"); } break; @@ -2213,7 +2332,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) else { /* ABTS has timed out */ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ABTS timed out. Starting PLOGI: %p", iport); fnic_fdls_start_plogi(iport); } @@ -2230,7 +2349,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) } else { /* ABTS has timed out */ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ABTS timed out. Starting PLOGI: %p", iport); fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */ } @@ -2247,7 +2366,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) else { /* ABTS has timed out */ fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ABTS timed out. Starting PLOGI %p", iport); fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */ } @@ -2269,7 +2388,7 @@ void fdls_fabric_timer_callback(struct timer_list *t) if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) { fdls_send_gpn_ft(iport, iport->fabric.state); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ABTS timeout for fabric GPN_FT. Check name server: %p", iport); } @@ -2289,7 +2408,7 @@ void fdls_fdmi_retry_plogi(struct fnic_iport_s *iport) /* If max retries not exhausted, start over from fdmi plogi */ if (iport->fabric.fdmi_retry < FDLS_FDMI_MAX_RETRY) { iport->fabric.fdmi_retry++; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Retry FDMI PLOGI. FDMI retry: %d", iport->fabric.fdmi_retry); fdls_send_fdmi_plogi(iport); @@ -2307,7 +2426,7 @@ void fdls_fdmi_timer_callback(struct timer_list *t) spin_lock_irqsave(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending); if (!iport->fabric.fdmi_pending) { @@ -2315,7 +2434,7 @@ void fdls_fdmi_timer_callback(struct timer_list *t) spin_unlock_irqrestore(&fnic->fnic_lock, flags); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending); /* if not abort pending, send an abort */ @@ -2324,7 +2443,7 @@ void fdls_fdmi_timer_callback(struct timer_list *t) spin_unlock_irqrestore(&fnic->fnic_lock, flags); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending); /* ABTS pending for an active fdmi request that is pending. @@ -2332,29 +2451,36 @@ void fdls_fdmi_timer_callback(struct timer_list *t) * Schedule to free the OXID after 2*r_a_tov and proceed */ if (iport->fabric.fdmi_pending & FDLS_FDMI_PLOGI_PENDING) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDMI PLOGI ABTS timed out. Schedule oxid free: 0x%x\n", iport->active_oxid_fdmi_plogi); fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_plogi); } else { if (iport->fabric.fdmi_pending & FDLS_FDMI_REG_HBA_PENDING) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDMI RHBA ABTS timed out. Schedule oxid free: 0x%x\n", iport->active_oxid_fdmi_rhba); fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rhba); } if (iport->fabric.fdmi_pending & FDLS_FDMI_RPA_PENDING) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDMI RPA ABTS timed out. Schedule oxid free: 0x%x\n", iport->active_oxid_fdmi_rpa); fdls_schedule_oxid_free(iport, &iport->active_oxid_fdmi_rpa); } } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending); - fdls_fdmi_retry_plogi(iport); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + iport->fabric.fdmi_pending = 0; + /* If max retries not exhaused, start over from fdmi plogi */ + if (iport->fabric.fdmi_retry < FDLS_FDMI_MAX_RETRY) { + iport->fabric.fdmi_retry++; + FNIC_FCS_DBG(KERN_INFO, fnic, + "retry fdmi timer %d", iport->fabric.fdmi_retry); + fdls_send_fdmi_plogi(iport); + } + FNIC_FCS_DBG(KERN_INFO, fnic, "iport->fabric.fdmi_pending: 0x%x\n", iport->fabric.fdmi_pending); spin_unlock_irqrestore(&fnic->fnic_lock, flags); } @@ -2367,7 +2493,7 @@ static void fdls_send_delete_tport_msg(struct fnic_tport_s *tport) tport_del_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC); if (!tport_del_evt) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Failed to allocate memory for tport event fcid: 0x%x", tport->fcid); return; @@ -2385,6 +2511,8 @@ static void fdls_tport_timer_callback(struct timer_list *t) struct fnic *fnic = iport->fnic; uint16_t oxid; unsigned long flags; + struct fc_frame_header fchdr = {0}; + uint8_t fcid[3]; spin_lock_irqsave(&fnic->fnic_lock, flags); if (!tport->timer_pending) { @@ -2400,13 +2528,13 @@ static void fdls_tport_timer_callback(struct timer_list *t) if (tport->del_timer_inprogress) { tport->del_timer_inprogress = 0; spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport_del_timer inprogress. Skip timer cb tport fcid: 0x%x\n", tport->fcid); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid: 0x%x timer pending: %d state: %d retry counter: %d", tport->fcid, tport->timer_pending, tport->state, tport->retry_counter); @@ -2467,15 +2595,22 @@ static void fdls_tport_timer_callback(struct timer_list *t) } else { /* exceeded retry count */ fdls_schedule_oxid_free(iport, &tport->active_oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ADISC not responding. Deleting target port: 0x%x", tport->fcid); fdls_send_delete_tport_msg(tport); } break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "oxid: 0x%x Unknown tport state: 0x%x", oxid, tport->state); + FNIC_FCS_DBG(KERN_INFO, fnic, + "0x%x timeout tport 0x%x oxid 0x%x state %d\n", + iport->fcid, tport->fcid, oxid, tport->state); + if (IS_FNIC_NVME_INITIATOR(fnic)) { + hton24(fcid, tport->fcid); + FNIC_STD_SET_S_ID(fchdr, fcid); + FNIC_STD_SET_OX_ID(fchdr, oxid); + nvfnic_process_ls_abts_rsp(iport, &fchdr); + } break; } spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -2524,26 +2659,26 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport, tport = fnic_find_tport_by_fcid(iport, tgt_fcid); if (!tport) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Tgt ADISC response tport not found: 0x%x", tgt_fcid); return; } if ((iport->state != FNIC_IPORT_STATE_READY) || (tport->state != FDLS_TGT_STATE_ADISC) || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping this ADISC response"); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport state: %d tport state: %d Is abort issued on PRLI? %d", iport->state, tport->state, (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)); return; } if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping frame from target: 0x%x", tgt_fcid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Reason: Stale ADISC/Aborted ADISC/OOO frame delivery"); return; } @@ -2555,7 +2690,7 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport, case ELS_LS_ACC: atomic64_inc(&iport->iport_stats.tport_adisc_ls_accepts); if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport 0x%p Canceling fabric disc timer\n", tport); fnic_del_tport_timer_sync(fnic, tport); @@ -2565,12 +2700,12 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport, frame_wwnn = get_unaligned_be64(&adisc_rsp->els.adisc_wwnn); frame_wwpn = get_unaligned_be64(&adisc_rsp->els.adisc_wwpn); if ((frame_wwnn == tport->wwnn) && (frame_wwpn == tport->wwpn)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ADISC accepted from target: 0x%x. Target logged in", tgt_fcid); fdls_set_tport_state(tport, FDLS_TGT_STATE_READY); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Error mismatch frame: ADISC"); } break; @@ -2580,14 +2715,14 @@ fdls_process_tgt_adisc_rsp(struct fnic_iport_s *iport, if (((els_rjt->rej.er_reason == ELS_RJT_BUSY) || (els_rjt->rej.er_reason == ELS_RJT_UNAB)) && (tport->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ADISC ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x", tgt_fcid); /* Retry ADISC again from the timer routine. */ tport->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "ADISC returned ELS_LS_RJT from target: 0x%x", tgt_fcid); fdls_delete_tport(iport, tport); @@ -2611,33 +2746,33 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport, fcid = FNIC_STD_GET_S_ID(fchdr); tgt_fcid = ntoh24(fcid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS processing target PLOGI response: tgt_fcid: 0x%x", tgt_fcid); tport = fnic_find_tport_by_fcid(iport, tgt_fcid); if (!tport) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport not found: 0x%x", tgt_fcid); return; } if ((iport->state != FNIC_IPORT_STATE_READY) || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping frame! iport state: %d tport state: %d", iport->state, tport->state); return; } if (tport->state != FDLS_TGT_STATE_PLOGI) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI rsp recvd in wrong state. Drop the frame and restart nexus"); fdls_target_restart_nexus(tport); return; } if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI response from target: 0x%x. Dropping frame", tgt_fcid); return; @@ -2649,7 +2784,7 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport, switch (plogi_rsp->els.fl_cmd) { case ELS_LS_ACC: atomic64_inc(&iport->iport_stats.tport_plogi_ls_accepts); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI accepted by target: 0x%x", tgt_fcid); break; @@ -2658,14 +2793,14 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport, if (((els_rjt->rej.er_reason == ELS_RJT_BUSY) || (els_rjt->rej.er_reason == ELS_RJT_UNAB)) && (tport->retry_counter < iport->max_plogi_retries)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x", tgt_fcid); /* Retry plogi again from the timer routine. */ tport->flags |= FNIC_FDLS_RETRY_FRAME; return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI returned ELS_LS_RJT from target: 0x%x", tgt_fcid); fdls_delete_tport(iport, tport); @@ -2673,18 +2808,18 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport, default: atomic64_inc(&iport->iport_stats.tport_plogi_misc_rejects); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI not accepted from target fcid: 0x%x", tgt_fcid); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Found the PLOGI target: 0x%x and state: %d", (unsigned int) tgt_fcid, tport->state); if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid 0x%x: Canceling disc timer\n", tport->fcid); fnic_del_tport_timer_sync(fnic, tport); @@ -2702,13 +2837,13 @@ fdls_process_tgt_plogi_rsp(struct fnic_iport_s *iport, min(max_payload_size, iport->max_payload_size); if (tport->max_payload_size < FNIC_MIN_DATA_FIELD_SIZE) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "MFS: tport max frame size below spec bounds: %d", tport->max_payload_size); tport->max_payload_size = FNIC_MIN_DATA_FIELD_SIZE; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "MAX frame size: %u iport max_payload_size: %d tport mfs: %d", max_payload_size, iport->max_payload_size, tport->max_payload_size); @@ -2736,12 +2871,12 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, fcid = FNIC_STD_GET_S_ID(fchdr); tgt_fcid = ntoh24(fcid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS process tgt PRLI response: 0x%x", tgt_fcid); tport = fnic_find_tport_by_fcid(iport, tgt_fcid); if (!tport) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport not found: 0x%x", tgt_fcid); /* Handle or just drop? */ return; @@ -2749,24 +2884,24 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, if ((iport->state != FNIC_IPORT_STATE_READY) || (tport->flags & FNIC_FDLS_TGT_ABORT_ISSUED)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping frame! iport st: %d tport st: %d tport fcid: 0x%x", iport->state, tport->state, tport->fcid); return; } if (tport->state != FDLS_TGT_STATE_PRLI) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PRLI rsp recvd in wrong state. Drop frame. Restarting nexus"); fdls_target_restart_nexus(tport); return; } if (FNIC_STD_GET_OX_ID(fchdr) != tport->active_oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping PRLI response from target: 0x%x ", tgt_fcid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Reason: Stale PRLI response/Aborted PDISC/OOO frame delivery"); return; } @@ -2777,14 +2912,21 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, switch (prli_rsp->els_prli.prli_cmd) { case ELS_LS_ACC: atomic64_inc(&iport->iport_stats.tport_prli_ls_accepts); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PRLI accepted from target: 0x%x", tgt_fcid); - if (prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + if (IS_FNIC_FCP_INITIATOR(fnic) && + prli_rsp->sp.spp_type != FC_FC4_TYPE_SCSI) { + FNIC_FCS_DBG(KERN_INFO, fnic, "mismatched target zoned with FC SCSI initiator: 0x%x", tgt_fcid); mismatched_tgt = true; + } else if (IS_FNIC_NVME_INITIATOR(fnic) && + prli_rsp->sp.spp_type != FC_TYPE_NVME) { + FNIC_FCS_DBG(KERN_ERR, fnic, + "mismatched target zoned with NVME initiator: 0x%x", + tgt_fcid); + mismatched_tgt = true; } if (mismatched_tgt) { fdls_tgt_logout(iport, tport); @@ -2798,7 +2940,7 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, || (els_rjt->rej.er_reason == ELS_RJT_UNAB)) && (tport->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PRLI ret ELS_LS_RJT BUSY. Retry from timer routine: 0x%x", tgt_fcid); @@ -2806,7 +2948,7 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, tport->flags |= FNIC_FDLS_RETRY_FRAME; return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PRLI returned ELS_LS_RJT from target: 0x%x", tgt_fcid); @@ -2815,17 +2957,17 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, return; default: atomic64_inc(&iport->iport_stats.tport_prli_misc_rejects); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PRLI not accepted from target: 0x%x", tgt_fcid); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Found the PRLI target: 0x%x and state: %d", (unsigned int) tgt_fcid, tport->state); if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport fcid 0x%x: Canceling disc timer\n", tport->fcid); fnic_del_tport_timer_sync(fnic, tport); @@ -2836,14 +2978,22 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, tport->fcp_csp = be32_to_cpu(prli_rsp->sp.spp_params); tport->retry_counter = 0; - if (tport->fcp_csp & FCP_SPPF_RETRY) - tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY; + if (IS_FNIC_FCP_INITIATOR(fnic)) { + if (tport->fcp_csp & FCP_SPPF_RETRY) + tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY; + } else if (IS_FNIC_NVME_INITIATOR(fnic)) { + if (tport->fcp_csp & FNIC_NVME_SP_SLER) + tport->tgt_flags |= FNIC_FC_RP_FLAGS_RETRY; + } /* Check if the device plays Target Mode Function */ - if (!(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "Remote port(0x%x): no target support. Deleting it\n", - tgt_fcid); + if ((IS_FNIC_FCP_INITIATOR(fnic) && + !(tport->fcp_csp & FCP_PRLI_FUNC_TARGET)) || + (IS_FNIC_NVME_INITIATOR(fnic) && + !(tport->fcp_csp & FCP_PRLI_FUNC_TARGET))) { + FNIC_FCS_DBG(KERN_INFO, fnic, + "Remote port(0x%x): no target support. Deleting it\n", + tgt_fcid); fdls_tgt_logout(iport, tport); fdls_delete_tport(iport, tport); return; @@ -2852,20 +3002,23 @@ fdls_process_tgt_prli_rsp(struct fnic_iport_s *iport, fdls_set_tport_state(tport, FDLS_TGT_STATE_READY); /* Inform the driver about new target added */ - tport_add_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC); - if (!tport_add_evt) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "tport event memory allocation failure: 0x%0x\n", - tport->fcid); - return; + if (IS_FNIC_FCP_INITIATOR(fnic) || + IS_FNIC_NVME_INITIATOR(fnic)) { + tport_add_evt = kzalloc_obj(struct fnic_tport_event_s, GFP_ATOMIC); + if (!tport_add_evt) { + FNIC_FCS_DBG(KERN_INFO, fnic, + "iport fcid: 0x%x tport event memory allocation failure: 0x%0x\n", + iport->fcid, tport->fcid); + return; + } + tport_add_evt->event = TGT_EV_RPORT_ADD; + tport_add_evt->arg1 = (void *)tport; + FNIC_FCS_DBG(KERN_INFO, fnic, + "iport fcid: 0x%x add tport event fcid: 0x%x\n", + tport->fcid, iport->fcid); + list_add_tail(&tport_add_evt->links, &fnic->tport_event_list); + queue_work(fnic_event_queue, &fnic->tport_work); } - tport_add_evt->event = TGT_EV_RPORT_ADD; - tport_add_evt->arg1 = (void *) tport; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "iport fcid: 0x%x add tport event fcid: 0x%x\n", - tport->fcid, iport->fcid); - list_add_tail(&tport_add_evt->links, &fnic->tport_event_list); - queue_work(fnic_event_queue, &fnic->tport_work); } @@ -2881,21 +3034,21 @@ fdls_process_rff_id_rsp(struct fnic_iport_s *iport, uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); if (fdls_get_state(fdls) != FDLS_STATE_REGISTER_FC4_FEATURES) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RFF_ID resp recvd in state(%d). Dropping.", fdls_get_state(fdls)); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); return; } rsp = FNIC_STD_GET_FC_CT_CMD((&rff_rsp->fc_std_ct_hdr)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS process RFF ID response: 0x%04x", iport->fcid, (uint32_t) rsp); @@ -2904,7 +3057,7 @@ fdls_process_rff_id_rsp(struct fnic_iport_s *iport, switch (rsp) { case FC_FS_ACC: if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -2918,18 +3071,18 @@ fdls_process_rff_id_rsp(struct fnic_iport_s *iport, if (((reason_code == FC_FS_RJT_BSY) || (reason_code == FC_FS_RJT_UNABL)) && (fdls->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RFF_ID ret ELS_LS_RJT BUSY. Retry from timer routine %p", iport); /* Retry again from the timer routine */ fdls->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RFF_ID returned ELS_LS_RJT. Halting discovery %p", iport); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -2954,14 +3107,14 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport, uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); if (fdls_get_state(fdls) != FDLS_STATE_REGISTER_FC4_TYPES) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RFT_ID resp recvd in state(%d). Dropping.", fdls_get_state(fdls)); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); return; @@ -2969,7 +3122,7 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport, rsp = FNIC_STD_GET_FC_CT_CMD((&rft_rsp->fc_std_ct_hdr)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS process RFT ID response: 0x%04x", iport->fcid, (uint32_t) rsp); @@ -2978,7 +3131,7 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport, switch (rsp) { case FC_FS_ACC: if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -2992,19 +3145,19 @@ fdls_process_rft_id_rsp(struct fnic_iport_s *iport, if (((reason_code == FC_FS_RJT_BSY) || (reason_code == FC_FS_RJT_UNABL)) && (fdls->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: RFT_ID ret ELS_LS_RJT BUSY. Retry from timer routine", iport->fcid); /* Retry again from the timer routine */ fdls->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: RFT_ID REJ. Halting discovery reason %d expl %d", iport->fcid, reason_code, rft_rsp->fc_std_ct_hdr.ct_explan); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -3029,20 +3182,20 @@ fdls_process_rpn_id_rsp(struct fnic_iport_s *iport, uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); if (fdls_get_state(fdls) != FDLS_STATE_RPN_ID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RPN_ID resp recvd in state(%d). Dropping.", fdls_get_state(fdls)); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); return; } rsp = FNIC_STD_GET_FC_CT_CMD((&rpn_rsp->fc_std_ct_hdr)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS process RPN ID response: 0x%04x", iport->fcid, (uint32_t) rsp); fdls_free_oxid(iport, oxid, &iport->active_oxid_fabric_req); @@ -3050,7 +3203,7 @@ fdls_process_rpn_id_rsp(struct fnic_iport_s *iport, switch (rsp) { case FC_FS_ACC: if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -3064,17 +3217,17 @@ fdls_process_rpn_id_rsp(struct fnic_iport_s *iport, if (((reason_code == FC_FS_RJT_BSY) || (reason_code == FC_FS_RJT_UNABL)) && (fdls->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RPN_ID returned REJ BUSY. Retry from timer routine %p", iport); /* Retry again from the timer routine */ fdls->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RPN_ID ELS_LS_RJT. Halting discovery %p", iport); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -3097,18 +3250,18 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, struct fnic *fnic = iport->fnic; uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS process SCR response: 0x%04x", (uint32_t) scr_rsp->scr.scr_cmd); if (fdls_get_state(fdls) != FDLS_STATE_SCR) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "SCR resp recvd in state(%d). Dropping.", fdls_get_state(fdls)); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); } @@ -3119,7 +3272,7 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, case ELS_LS_ACC: atomic64_inc(&iport->iport_stats.fabric_scr_ls_accepts); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -3133,17 +3286,17 @@ fdls_process_scr_rsp(struct fnic_iport_s *iport, if (((els_rjt->rej.er_reason == ELS_RJT_BUSY) || (els_rjt->rej.er_reason == ELS_RJT_UNAB)) && (fdls->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "SCR ELS_LS_RJT BUSY. Retry from timer routine %p", iport); /* Retry again from the timer routine */ fdls->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "SCR returned ELS_LS_RJT. Halting discovery %p", iport); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); @@ -3171,7 +3324,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport, u32 old_link_down_cnt = iport->fnic->link_down_cnt; struct fnic *fnic = iport->fnic; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS process GPN_FT tgt list", iport->fcid); gpn_ft_tgt = @@ -3185,7 +3338,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport, fcid = ntoh24(gpn_ft_tgt->fcid); wwpn = be64_to_cpu(gpn_ft_tgt->wwpn); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "tport: 0x%x: ctrl:0x%x", fcid, gpn_ft_tgt->ctrl); if (fcid == iport->fcid) { @@ -3232,7 +3385,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport, rem_len -= sizeof(struct fc_gpn_ft_rsp_iu); } if (rem_len <= 0) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "GPN_FT response: malformed/corrupt frame rxlen: %d remlen: %d", len, rem_len); } @@ -3242,7 +3395,7 @@ fdls_process_gpn_ft_tgt_list(struct fnic_iport_s *iport, list_for_each_entry_safe(tport, next, &iport->tport_list, links) { if (!(tport->flags & FNIC_FDLS_TPORT_IN_GPN_FT_LIST)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Remove port: 0x%x not found in GPN_FT list", tport->fcid); fdls_delete_tport(iport, tport); @@ -3271,7 +3424,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, struct fnic *fnic = iport->fnic; uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS process GPN_FT response: iport state: %d len: %d", iport->state, len); @@ -3291,14 +3444,14 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, && ((fdls_get_state(fdls) == FDLS_STATE_RSCN_GPN_FT) || (fdls_get_state(fdls) == FDLS_STATE_SEND_GPNFT) || (fdls_get_state(fdls) == FDLS_STATE_TGT_DISCOVERY))))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "GPNFT resp recvd in fab state(%d) iport_state(%d). Dropping.", fdls_get_state(fdls), iport->state); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); } @@ -3311,10 +3464,10 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, switch (rsp) { case FC_FS_ACC: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: GPNFT_RSP accept", iport->fcid); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Canceling fabric disc timer\n", iport->fcid); fnic_del_fabric_timer_sync(fnic); @@ -3329,7 +3482,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, * that will be taken care in next link up event */ if (iport->state != FNIC_IPORT_STATE_READY) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Halting target discovery: fab st: %d iport st: %d ", fdls_get_state(fdls), iport->state); break; @@ -3339,22 +3492,22 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, case FC_FS_RJT: reason_code = gpn_ft_rsp->fc_std_ct_hdr.ct_reason; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: GPNFT_RSP Reject reason: %d", iport->fcid, reason_code); if (((reason_code == FC_FS_RJT_BSY) || (reason_code == FC_FS_RJT_UNABL)) && (fdls->retry_counter < FDLS_RETRY_COUNT)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: GPNFT_RSP ret REJ/BSY. Retry from timer routine", iport->fcid); /* Retry again from the timer routine */ fdls->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: GPNFT_RSP reject", iport->fcid); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Canceling fabric disc timer\n", iport->fcid); fnic_del_fabric_timer_sync(fnic); @@ -3368,7 +3521,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, count = 0; list_for_each_entry_safe(tport, next, &iport->tport_list, links) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "GPN_FT_REJECT: Remove port: 0x%x", tport->fcid); fdls_delete_tport(iport, tport); @@ -3378,7 +3531,7 @@ fdls_process_gpn_ft_rsp(struct fnic_iport_s *iport, } count++; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "GPN_FT_REJECT: Removed (0x%x) ports", count); } break; @@ -3403,7 +3556,7 @@ fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport, uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); } @@ -3412,7 +3565,7 @@ fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport, switch (flogo_rsp->els.fl_cmd) { case ELS_LS_ACC: if (iport->fabric.state != FDLS_STATE_FABRIC_LOGO) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Flogo response. Fabric not in LOGO state. Dropping! %p", iport); return; @@ -3422,25 +3575,25 @@ fdls_process_fabric_logo_rsp(struct fnic_iport_s *iport, iport->state = FNIC_IPORT_STATE_LINK_WAIT; if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport 0x%p Canceling fabric disc timer\n", iport); fnic_del_fabric_timer_sync(fnic); } iport->fabric.timer_pending = 0; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Flogo response from Fabric for did: 0x%x", ntoh24(fchdr->fh_d_id)); return; case ELS_LS_RJT: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Flogo response from Fabric for did: 0x%x returned ELS_LS_RJT", ntoh24(fchdr->fh_d_id)); return; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGO response not accepted or rejected: 0x%x", flogo_rsp->els.fl_cmd); } @@ -3458,17 +3611,17 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, struct fnic *fnic = iport->fnic; uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS processing FLOGI response", iport->fcid); if (fdls_get_state(fabric) != FDLS_STATE_FABRIC_FLOGI) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI response received in state (%d). Dropping frame", fdls_get_state(fabric)); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fabric), oxid, iport->active_oxid_fabric_req); return; @@ -3480,7 +3633,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, case ELS_LS_ACC: atomic64_inc(&iport->iport_stats.fabric_flogi_ls_accepts); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport fcid: 0x%x Canceling fabric disc timer\n", iport->fcid); fnic_del_fabric_timer_sync(fnic); @@ -3490,7 +3643,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, iport->fabric.retry_counter = 0; fcid = FNIC_STD_GET_D_ID(fchdr); iport->fcid = ntoh24(fcid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FLOGI response accepted", iport->fcid); /* Learn the Service Params */ @@ -3500,7 +3653,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, iport->max_payload_size = min(rdf_size, iport->max_payload_size); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "max_payload_size from fabric: %u set: %d", rdf_size, iport->max_payload_size); @@ -3510,24 +3663,26 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, if (FNIC_LOGI_FEATURES(flogi_rsp->els) & FNIC_FC_EDTOV_NSEC) iport->e_d_tov = iport->e_d_tov / FNIC_NSEC_TO_MSEC; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "From fabric: R_A_TOV: %d E_D_TOV: %d", iport->r_a_tov, iport->e_d_tov); - fc_host_fabric_name(iport->fnic->host) = - get_unaligned_be64(&FNIC_LOGI_NODE_NAME(flogi_rsp->els)); - fc_host_port_id(iport->fnic->host) = iport->fcid; + if (IS_FNIC_FCP_INITIATOR(fnic)) { + fc_host_fabric_name(iport->fnic->host) = + get_unaligned_be64(&FNIC_LOGI_NODE_NAME(flogi_rsp->els)); + fc_host_port_id(iport->fnic->host) = iport->fcid; + } fnic_fdls_learn_fcoe_macs(iport, rx_frame, fcid); if (fnic_fdls_register_portid(iport, iport->fcid, rx_frame) != 0) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FLOGI registration failed", iport->fcid); break; } memcpy(&fcmac[3], fcid, 3); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Adding vNIC device MAC addr: %02x:%02x:%02x:%02x:%02x:%02x", fcmac[0], fcmac[1], fcmac[2], fcmac[3], fcmac[4], fcmac[5]); @@ -3535,7 +3690,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, if (fdls_get_state(fabric) == FDLS_STATE_FABRIC_FLOGI) { fnic_fdls_start_plogi(iport); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI response received. Starting PLOGI"); } else { /* From FDLS_STATE_FABRIC_FLOGI state fabric can only go to @@ -3543,7 +3698,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, * state, hence we don't have to worry about undoing: * the fnic_fdls_register_portid and vnic_dev_add_addr */ - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI response received in state (%d). Dropping frame", fdls_get_state(fabric)); } @@ -3552,7 +3707,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, case ELS_LS_RJT: atomic64_inc(&iport->iport_stats.fabric_flogi_ls_rejects); if (fabric->retry_counter < iport->max_flogi_retries) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI returned ELS_LS_RJT BUSY. Retry from timer routine %p", iport); @@ -3560,11 +3715,11 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, fabric->flags |= FNIC_FDLS_RETRY_FRAME; } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI returned ELS_LS_RJT. Halting discovery %p", iport); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport 0x%p Canceling fabric disc timer\n", iport); fnic_del_fabric_timer_sync(fnic); @@ -3575,7 +3730,7 @@ fdls_process_flogi_rsp(struct fnic_iport_s *iport, break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI response not accepted: 0x%x", flogi_rsp->els.fl_cmd); atomic64_inc(&iport->iport_stats.fabric_flogi_misc_rejects); @@ -3594,13 +3749,13 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport, uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); if (fdls_get_state((&iport->fabric)) != FDLS_STATE_FABRIC_PLOGI) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Fabric PLOGI response received in state (%d). Dropping frame", fdls_get_state(&iport->fabric)); return; } if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fabric_req); return; @@ -3611,7 +3766,7 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport, case ELS_LS_ACC: atomic64_inc(&iport->iport_stats.fabric_plogi_ls_accepts); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport fcid: 0x%x fabric PLOGI response: Accepted\n", iport->fcid); fnic_del_fabric_timer_sync(fnic); @@ -3626,15 +3781,15 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport, if (((els_rjt->rej.er_reason == ELS_RJT_BUSY) || (els_rjt->rej.er_reason == ELS_RJT_UNAB)) && (iport->fabric.retry_counter < iport->max_plogi_retries)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Fabric PLOGI ELS_LS_RJT BUSY. Retry from timer routine", iport->fcid); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Fabric PLOGI ELS_LS_RJT. Halting discovery", iport->fcid); if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport fcid: 0x%x Canceling fabric disc timer\n", iport->fcid); fnic_del_fabric_timer_sync(fnic); @@ -3645,7 +3800,7 @@ fdls_process_fabric_plogi_rsp(struct fnic_iport_s *iport, } break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI response not accepted: 0x%x", plogi_rsp->els.fl_cmd); atomic64_inc(&iport->iport_stats.fabric_plogi_misc_rejects); @@ -3664,7 +3819,7 @@ static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport, uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); if (iport->active_oxid_fdmi_plogi != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. state: %d, oxid recvd: 0x%x, active oxid: 0x%x\n", fdls_get_state(fdls), oxid, iport->active_oxid_fdmi_plogi); return; @@ -3678,9 +3833,9 @@ static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport, iport->fabric.fdmi_pending = 0; switch (plogi_rsp->els.fl_cmd) { case ELS_LS_ACC: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS process fdmi PLOGI response status: ELS_LS_ACC\n"); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Sending fdmi registration for port 0x%x\n", iport->fcid); @@ -3691,7 +3846,7 @@ static void fdls_process_fdmi_plogi_rsp(struct fnic_iport_s *iport, round_jiffies(fdmi_tov)); break; case ELS_LS_RJT: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Fabric FDMI PLOGI returned ELS_LS_RJT reason: 0x%x", els_rjt->rej.er_reason); @@ -3715,7 +3870,7 @@ static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport, uint16_t oxid; if (!iport->fabric.fdmi_pending) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Received FDMI ack while not waiting: 0x%x\n", FNIC_STD_GET_OX_ID(fchdr)); return; @@ -3725,7 +3880,7 @@ static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport, if ((iport->active_oxid_fdmi_rhba != oxid) && (iport->active_oxid_fdmi_rpa != oxid)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Incorrect OXID in response. oxid recvd: 0x%x, active oxids(rhba,rpa): 0x%x, 0x%x\n", oxid, iport->active_oxid_fdmi_rhba, iport->active_oxid_fdmi_rpa); return; @@ -3738,13 +3893,13 @@ static void fdls_process_fdmi_reg_ack(struct fnic_iport_s *iport, fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa); } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport fcid: 0x%x: Received FDMI registration ack\n", iport->fcid); if (!iport->fabric.fdmi_pending) { timer_delete_sync(&iport->fabric.fdmi_timer); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport fcid: 0x%x: Canceling FDMI timer\n", iport->fcid); } @@ -3760,7 +3915,7 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport, s_id = ntoh24(FNIC_STD_GET_S_ID(fchdr)); if (!(s_id != FC_FID_MGMT_SERV)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received abts rsp with invalid SID: 0x%x. Dropping frame", s_id); return; @@ -3770,23 +3925,23 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport, switch (FNIC_FRAME_TYPE(oxid)) { case FNIC_FRAME_TYPE_FDMI_PLOGI: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received FDMI PLOGI ABTS rsp with oxid: 0x%x", oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_plogi); iport->fabric.fdmi_pending &= ~FDLS_FDMI_PLOGI_PENDING; iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); break; case FNIC_FRAME_TYPE_FDMI_RHBA: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received FDMI RHBA ABTS rsp with oxid: 0x%x", oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); @@ -3800,14 +3955,14 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport, iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING; fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rhba); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); break; case FNIC_FRAME_TYPE_FDMI_RPA: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received FDMI RPA ABTS rsp with oxid: 0x%x", oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); @@ -3821,12 +3976,12 @@ static void fdls_process_fdmi_abts_rsp(struct fnic_iport_s *iport, iport->fabric.fdmi_pending &= ~FDLS_FDMI_ABORT_PENDING; fdls_free_oxid(iport, oxid, &iport->active_oxid_fdmi_rpa); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: iport->fabric.fdmi_pending: 0x%x", iport->fcid, iport->fabric.fdmi_pending); break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received abts rsp with invalid oxid: 0x%x. Dropping frame", oxid); break; @@ -3861,7 +4016,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (!((s_id == FC_FID_DIR_SERV) || (s_id == FC_FID_FLOGI) || (s_id == FC_FID_FCTRL))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received abts rsp with invalid SID: 0x%x. Dropping frame", s_id); return; @@ -3869,14 +4024,14 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, oxid = FNIC_STD_GET_OX_ID(fchdr); if (iport->active_oxid_fabric_req != oxid) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received abts rsp with invalid oxid: 0x%x. Dropping frame", oxid); return; } if (iport->fabric.timer_pending) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Canceling fabric disc timer %p\n", iport); fnic_del_fabric_timer_sync(fnic); } @@ -3884,11 +4039,11 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, iport->fabric.flags &= ~FNIC_FDLS_FABRIC_ABORT_ISSUED; if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received abts rsp BA_ACC for fabric_state: %d OX_ID: 0x%x", fabric_state, be16_to_cpu(ba_acc->acc.ba_ox_id)); } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "BA_RJT fs: %d OX_ID: 0x%x rc: 0x%x rce: 0x%x", fabric_state, FNIC_STD_GET_OX_ID(&ba_rjt->fchdr), ba_rjt->rjt.br_reason, ba_rjt->rjt.br_explan); @@ -3903,7 +4058,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (iport->fabric.retry_counter < iport->max_flogi_retries) fdls_send_fabric_flogi(iport); else - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Exceeded max FLOGI retries"); break; case FNIC_FRAME_TYPE_FABRIC_LOGO: @@ -3914,7 +4069,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (iport->fabric.retry_counter < iport->max_plogi_retries) fdls_send_fabric_plogi(iport); else - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Exceeded max PLOGI retries"); break; case FNIC_FRAME_TYPE_FABRIC_RPN: @@ -3928,7 +4083,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) fdls_send_scr(iport); else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "SCR exhausted retries. Start fabric PLOGI %p", iport); fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */ @@ -3938,7 +4093,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) fdls_send_register_fc4_types(iport); else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RFT exhausted retries. Start fabric PLOGI %p", iport); fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */ @@ -3948,7 +4103,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (iport->fabric.retry_counter < FDLS_RETRY_COUNT) fdls_send_register_fc4_features(iport); else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RFF exhausted retries. Start fabric PLOGI %p", iport); fnic_fdls_start_plogi(iport); /* go back to fabric Plogi */ @@ -3958,7 +4113,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, if (iport->fabric.retry_counter <= FDLS_RETRY_COUNT) fdls_send_gpn_ft(iport, fabric_state); else - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "GPN FT exhausted retries. Start fabric PLOGI %p", iport); break; @@ -3967,7 +4122,7 @@ fdls_process_fabric_abts_rsp(struct fnic_iport_s *iport, * We should not be here since we already validated rx oxid with * our active_oxid_fabric_req */ - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Invalid OXID/active oxid 0x%x\n", oxid); WARN_ON(true); return; @@ -3987,7 +4142,7 @@ fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) sizeof(struct fc_std_abts_ba_acc); nport_id = ntoh24(fchdr->fh_s_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received abort from SID 0x%8x", nport_id); tport = fnic_find_tport_by_fcid(iport, nport_id); @@ -4000,7 +4155,7 @@ fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "0x%x: Failed to allocate frame to send response for ABTS req", iport->fcid); return; @@ -4021,7 +4176,7 @@ fdls_process_abts_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) pba_acc->acc.ba_rx_id = cpu_to_be16(FNIC_STD_GET_RX_ID(fchdr)); pba_acc->acc.ba_ox_id = cpu_to_be16(FNIC_STD_GET_OX_ID(fchdr)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS send BA ACC with oxid: 0x%x", iport->fcid, oxid); @@ -4041,7 +4196,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport, sizeof(struct fc_std_els_rjt_rsp); if (iport->fcid != d_id) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping unsupported ELS with illegal frame bits 0x%x\n", d_id); atomic64_inc(&iport->iport_stats.unsupported_frames_dropped); @@ -4050,7 +4205,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport, if ((iport->state != FNIC_IPORT_STATE_READY) && (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping unsupported ELS request in iport state: %d", iport->state); atomic64_inc(&iport->iport_stats.unsupported_frames_dropped); @@ -4059,7 +4214,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport, frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send response to unsupported ELS request"); return; } @@ -4067,7 +4222,7 @@ fdls_process_unsupported_els_req(struct fnic_iport_s *iport, pls_rsp = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET); fdls_init_els_rjt_frame(frame, iport); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Process unsupported ELS request from SID: 0x%x", iport->fcid, ntoh24(fchdr->fh_s_id)); @@ -4094,12 +4249,12 @@ fdls_process_rls_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET + sizeof(struct fc_std_rls_acc); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Process RLS request %d", iport->fnic->fnic_num); if ((iport->state != FNIC_IPORT_STATE_READY) && (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received RLS req in iport state: %d. Dropping the frame.", iport->state); return; @@ -4107,7 +4262,7 @@ fdls_process_rls_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send RLS accept"); return; } @@ -4148,33 +4303,33 @@ fdls_process_els_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr, if ((iport->state != FNIC_IPORT_STATE_READY) && (iport->state != FNIC_IPORT_STATE_FABRIC_DISC)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping ELS frame type: 0x%x in iport state: %d", type, iport->state); return; } switch (type) { case ELS_ECHO: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "sending LS_ACC for ECHO request %d\n", iport->fnic->fnic_num); break; case ELS_RRQ: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "sending LS_ACC for RRQ request %d\n", iport->fnic->fnic_num); break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "sending LS_ACC for 0x%x ELS frame\n", type); break; } frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send ELS response for 0x%x", type); return; @@ -4220,17 +4375,17 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, tport = fnic_find_tport_by_fcid(iport, s_id); if (!tport) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Received tgt abts rsp with invalid SID: 0x%x", s_id); return; } if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "tport 0x%p Canceling fabric disc timer\n", tport); fnic_del_tport_timer_sync(fnic, tport); } if (iport->state != FNIC_IPORT_STATE_READY) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Received tgt abts rsp in iport state(%d). Dropping.", iport->state); return; @@ -4245,15 +4400,15 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, switch (frame_type) { case FNIC_FRAME_TYPE_TGT_ADISC: if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "OX_ID: 0x%x tgt_fcid: 0x%x rcvd tgt adisc abts resp BA_ACC", be16_to_cpu(ba_acc->acc.ba_ox_id), tport->fcid); } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "ADISC BA_RJT rcvd tport_fcid: 0x%x tport_state: %d ", tport->fcid, tport_state); - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "reason code: 0x%x reason code explanation:0x%x ", ba_rjt->rjt.br_reason, ba_rjt->rjt.br_explan); @@ -4265,7 +4420,7 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, return; } fdls_free_oxid(iport, oxid, &tport->active_oxid); - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "ADISC not responding. Deleting target port: 0x%x", tport->fcid); fdls_delete_tport(iport, tport); @@ -4278,14 +4433,14 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, break; case FNIC_FRAME_TYPE_TGT_PLOGI: if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Received tgt PLOGI abts response BA_ACC tgt_fcid: 0x%x", tport->fcid); } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PLOGI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x", tport->fcid, FNIC_STD_GET_OX_ID(fchdr)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "reason code: 0x%x reason code explanation: 0x%x", ba_rjt->rjt.br_reason, ba_rjt->rjt.br_explan); @@ -4308,14 +4463,14 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, break; case FNIC_FRAME_TYPE_TGT_PRLI: if (fchdr->fh_r_ctl == FC_RCTL_BA_ACC) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Received tgt PRLI abts response BA_ACC", tport->fcid); } else if (fchdr->fh_r_ctl == FC_RCTL_BA_RJT) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PRLI BA_RJT received for tport_fcid: 0x%x OX_ID: 0x%x ", tport->fcid, FNIC_STD_GET_OX_ID(fchdr)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "reason code: 0x%x reason code explanation: 0x%x", ba_rjt->rjt.br_reason, ba_rjt->rjt.br_explan); @@ -4331,7 +4486,7 @@ fdls_process_tgt_abts_rsp(struct fnic_iport_s *iport, fdls_set_tport_state(tport, FDLS_TGT_STATE_PLOGI); break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received ABTS response for unknown frame %p", iport); break; } @@ -4351,14 +4506,14 @@ fdls_process_plogi_req(struct fnic_iport_s *iport, sizeof(struct fc_std_els_rjt_rsp); if (iport->fcid != d_id) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received PLOGI with illegal frame bits. Dropping frame from 0x%x", d_id); return; } if (iport->state != FNIC_IPORT_STATE_READY) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received PLOGI request in iport state: %d Dropping frame", iport->state); return; @@ -4366,7 +4521,7 @@ fdls_process_plogi_req(struct fnic_iport_s *iport, frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send response to PLOGI request"); return; } @@ -4374,7 +4529,7 @@ fdls_process_plogi_req(struct fnic_iport_s *iport, pplogi_rsp = (struct fc_std_els_rjt_rsp *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET); fdls_init_els_rjt_frame(frame, iport); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: Process PLOGI request from SID: 0x%x", iport->fcid, ntoh24(fchdr->fh_s_id)); @@ -4404,11 +4559,11 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) nport_id = ntoh24(logo->els.fl_n_port_id); nport_name = be64_to_cpu(logo->els.fl_n_port_wwn); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Process LOGO request from fcid: 0x%x", nport_id); if (iport->state != FNIC_IPORT_STATE_READY) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Dropping LOGO req from 0x%x in iport state: %d", nport_id, iport->state); return; @@ -4418,19 +4573,19 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) if (!tport) { /* We are not logged in with the nport, log and drop... */ - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Received LOGO from an nport not logged in: 0x%x(0x%llx)", nport_id, nport_name); return; } if (tport->fcid != nport_id) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Received LOGO with invalid target port fcid: 0x%x(0x%llx)", nport_id, nport_name); return; } if (tport->timer_pending) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "tport fcid 0x%x: Canceling disc timer\n", tport->fcid); fnic_del_tport_timer_sync(fnic, tport); @@ -4447,7 +4602,7 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) if ((iport->state == FNIC_IPORT_STATE_READY) && (fdls_get_state(&iport->fabric) != FDLS_STATE_SEND_GPNFT) && (fdls_get_state(&iport->fabric) != FDLS_STATE_RSCN_GPN_FT)) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Sending GPNFT in response to LOGO from Target:0x%x", nport_id); fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT); @@ -4460,7 +4615,7 @@ fdls_process_logo_req(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) fdls_send_logo_resp(iport, &logo->fchdr); if ((fdls_get_state(&iport->fabric) != FDLS_STATE_SEND_GPNFT) && (fdls_get_state(&iport->fabric) != FDLS_STATE_RSCN_GPN_FT)) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Sending GPNFT in response to LOGO from Target:0x%x", nport_id); fdls_send_gpn_ft(iport, FDLS_STATE_SEND_GPNFT); @@ -4487,11 +4642,11 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) atomic64_inc(&iport->iport_stats.num_rscns); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS process RSCN %p", iport); if (iport->state != FNIC_IPORT_STATE_READY) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS RSCN received in state(%d). Dropping", fdls_get_state(fdls)); return; @@ -4508,18 +4663,18 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) if ((rscn_payload_len == 0xFFFF) && (sid == FC_FID_FCTRL)) { rscn_type = PC_RSCN; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "pcrscn: PCRSCN received. sid: 0x%x payload len: 0x%x", sid, rscn_payload_len); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RSCN payload_len: 0x%x page_len: 0x%x", rscn_payload_len, rscn->els.rscn_page_len); /* if this happens then we need to send ADISC to all the tports. */ list_for_each_entry_safe(tport, next, &iport->tport_list, links) { if (tport->state == FDLS_TGT_STATE_READY) tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RSCN for port id: 0x%x", tport->fcid); } } /* end else */ @@ -4527,7 +4682,7 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) num_ports = (rscn_payload_len - 4) / rscn->els.rscn_page_len; rscn_port = (struct fc_els_rscn_page *)(rscn + 1); } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RSCN received for num_ports: %d payload_len: %d page_len: %d ", num_ports, rscn_payload_len, rscn->els.rscn_page_len); @@ -4551,14 +4706,14 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) if (tport->state == FDLS_TGT_STATE_READY) tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RSCN for port id: 0x%x", tport->fcid); } break; } tport = fnic_find_tport_by_fcid(iport, nport_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "RSCN port id list: 0x%x", nport_id); if (!tport) { @@ -4573,13 +4728,13 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) rscn_type == PC_RSCN && fnic->role == FNIC_ROLE_FCP_INITIATOR) { if (fnic->pc_rscn_handling_status == PC_RSCN_HANDLING_IN_PROGRESS) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PCRSCN handling already in progress. Skip host reset: %d", iport->fnic->fnic_num); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Processing PCRSCN. Queuing fnic for host reset: %d", iport->fnic->fnic_num); fnic->pc_rscn_handling_status = PC_RSCN_HANDLING_IN_PROGRESS; @@ -4595,7 +4750,7 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr) queue_work(reset_fnic_work_queue, &reset_fnic_work); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FDLS process RSCN sending GPN_FT: newports: %d", newports); fdls_send_gpn_ft(iport, FDLS_STATE_RSCN_GPN_FT); fdls_send_rscn_resp(iport, fchdr); @@ -4606,9 +4761,11 @@ void fnic_fdls_disc_start(struct fnic_iport_s *iport) { struct fnic *fnic = iport->fnic; - fc_host_fabric_name(iport->fnic->host) = 0; - fc_host_post_event(iport->fnic->host, fc_get_event_number(), - FCH_EVT_LIPRESET, 0); + if (IS_FNIC_FCP_INITIATOR(fnic)) { + fc_host_fabric_name(iport->fnic->host) = 0; + fc_host_post_event(iport->fnic->host, fc_get_event_number(), + FCH_EVT_LIPRESET, 0); + } if (!iport->usefip) { if (iport->flags & FNIC_FIRST_LINK_UP) { @@ -4644,20 +4801,20 @@ fdls_process_adisc_req(struct fnic_iport_s *iport, uint16_t acc_frame_size = FNIC_ETH_FCOE_HDRS_OFFSET + sizeof(struct fc_std_els_adisc); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Process ADISC request %d", iport->fnic->fnic_num); fcid = FNIC_STD_GET_S_ID(fchdr); tgt_fcid = ntoh24(fcid); tport = fnic_find_tport_by_fcid(iport, tgt_fcid); if (!tport) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "tport for fcid: 0x%x not found. Dropping ADISC req.", tgt_fcid); return; } if (iport->state != FNIC_IPORT_STATE_READY) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Dropping ADISC req from fcid: 0x%x in iport state: %d", tgt_fcid, iport->state); return; @@ -4668,16 +4825,16 @@ fdls_process_adisc_req(struct fnic_iport_s *iport, if ((frame_wwnn != tport->wwnn) || (frame_wwpn != tport->wwpn)) { /* send reject */ - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "ADISC req from fcid: 0x%x mismatch wwpn: 0x%llx wwnn: 0x%llx", tgt_fcid, frame_wwpn, frame_wwnn); - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "local tport wwpn: 0x%llx wwnn: 0x%llx. Sending RJT", tport->wwpn, tport->wwnn); rjt_frame = fdls_alloc_frame(iport); if (rjt_frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate rjt_frame to send response to ADISC request"); return; } @@ -4700,7 +4857,7 @@ fdls_process_adisc_req(struct fnic_iport_s *iport, acc_frame = fdls_alloc_frame(iport); if (acc_frame == NULL) { - FNIC_FCS_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_ERR, fnic, "Failed to allocate frame to send ADISC accept"); return; } @@ -4754,7 +4911,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, /* some common validation */ if (fdls_get_state(fabric) > FDLS_STATE_FABRIC_FLOGI) { if (iport->fcid != d_id || (!FNIC_FC_FRAME_CS_CTL(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "invalid frame received. Dropping frame"); return -1; } @@ -4764,14 +4921,14 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, if ((fchdr->fh_r_ctl == FC_RCTL_BA_ACC) || (fchdr->fh_r_ctl == FC_RCTL_BA_RJT)) { if (!(FNIC_FC_FRAME_TYPE_BLS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received ABTS invalid frame. Dropping frame"); return -1; } if (fdls_is_oxid_fabric_req(oxid)) { if (!(iport->fabric.flags & FNIC_FDLS_FABRIC_ABORT_ISSUED)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unexpected ABTS RSP(oxid:0x%x) from 0x%x. Dropping frame", oxid, s_id); return -1; @@ -4781,8 +4938,10 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, return FNIC_FDMI_BLS_ABTS_RSP; } else if (fdls_is_oxid_tgt_req(oxid)) { return FNIC_TPORT_BLS_ABTS_RSP; + } else if (fdls_is_oxid_nvme_req(oxid)) { + return FNIC_LS_REQ_ABTS_RSP; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received ABTS rsp with unknown oxid(0x%x) from 0x%x. Dropping frame", oxid, s_id); return -1; @@ -4791,7 +4950,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, /* BLS ABTS Req */ if ((fchdr->fh_r_ctl == FC_RCTL_BA_ABTS) && (FNIC_FC_FRAME_TYPE_BLS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Receiving Abort Request from s_id: 0x%x", s_id); return FNIC_BLS_ABTS_REQ; } @@ -4803,7 +4962,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, if ((!FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(fchdr)) || (!FNIC_FC_FRAME_UNSOLICITED(fchdr)) || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received LOGO invalid frame. Dropping frame"); return -1; } @@ -4812,12 +4971,12 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, if ((!FNIC_FC_FRAME_FCTL_FIRST_LAST_SEQINIT(fchdr)) || (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) || (!FNIC_FC_FRAME_UNSOLICITED(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received RSCN invalid FCTL. Dropping frame"); return -1; } if (s_id != FC_FID_FCTRL) - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received RSCN from target FCTL: 0x%x type: 0x%x s_id: 0x%x.", fchdr->fh_f_ctl[0], fchdr->fh_type, s_id); return FNIC_ELS_RSCN_REQ; @@ -4832,7 +4991,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, case ELS_RRQ: return FNIC_ELS_RRQ; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Unsupported frame (type:0x%02x) from fcid: 0x%x", type, s_id); return FNIC_ELS_UNSUPPORTED_REQ; @@ -4841,14 +5000,14 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, /* solicited response from fabric or target */ oxid_frame_type = FNIC_FRAME_TYPE(oxid); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "oxid frame code: 0x%x, oxid: 0x%x\n", oxid_frame_type, oxid); switch (oxid_frame_type) { case FNIC_FRAME_TYPE_FABRIC_FLOGI: if (type == ELS_LS_ACC) { if ((s_id != FC_FID_FLOGI) || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4859,7 +5018,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, if (type == ELS_LS_ACC) { if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4870,7 +5029,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, if (type == ELS_LS_ACC) { if ((s_id != FC_FID_FCTRL) || (!FNIC_FC_FRAME_TYPE_ELS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4879,7 +5038,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, case FNIC_FRAME_TYPE_FABRIC_RPN: if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4887,7 +5046,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, case FNIC_FRAME_TYPE_FABRIC_RFT: if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4895,7 +5054,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, case FNIC_FRAME_TYPE_FABRIC_RFF: if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4903,7 +5062,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, case FNIC_FRAME_TYPE_FABRIC_GPN_FT: if ((s_id != FC_FID_DIR_SERV) || (!FNIC_FC_FRAME_TYPE_FC_GS(fchdr))) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown frame. Dropping frame"); return -1; } @@ -4925,7 +5084,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, return FNIC_TPORT_ADISC_RSP; case FNIC_FRAME_TYPE_TGT_LOGO: if (!FNIC_FC_FRAME_TYPE_ELS(fchdr)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping Unknown frame in tport solicited exchange range type: 0x%x.", fchdr->fh_type); return -1; @@ -4933,7 +5092,7 @@ fnic_fdls_validate_and_get_frame_type(struct fnic_iport_s *iport, return FNIC_TPORT_LOGO_RSP; default: /* Drop the Rx frame and log/stats it */ - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Solicited response: unknown OXID: 0x%x", oxid); return -1; } @@ -5003,7 +5162,7 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame, break; case FNIC_TPORT_LOGO_RSP: /* Logo response from tgt which we have deleted */ - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Logo response from tgt: 0x%x", ntoh24(fchdr->fh_s_id)); break; @@ -5013,6 +5172,9 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame, case FNIC_FABRIC_BLS_ABTS_RSP: fdls_process_fabric_abts_rsp(iport, fchdr); break; + case FNIC_LS_REQ_ABTS_RSP: + nvfnic_process_ls_abts_rsp(iport, fchdr); + break; case FNIC_FDMI_BLS_ABTS_RSP: fdls_process_fdmi_abts_rsp(iport, fchdr); break; @@ -5046,9 +5208,9 @@ void fnic_fdls_recv_frame(struct fnic_iport_s *iport, void *rx_frame, fdls_process_fdmi_reg_ack(iport, fchdr, frame_type); break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "s_id: 0x%x d_did: 0x%x", s_id, d_id); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Received unknown FCoE frame of len: %d. Dropping frame", len); break; } @@ -5065,20 +5227,24 @@ void fnic_fdls_link_down(struct fnic_iport_s *iport) struct fnic_tport_s *tport, *next; struct fnic *fnic = iport->fnic; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS processing link down", iport->fcid); fdls_set_state((&iport->fabric), FDLS_STATE_LINKDOWN); iport->fabric.flags = 0; - spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - fnic_fcpio_reset(iport->fnic); - spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); - list_for_each_entry_safe(tport, next, &iport->tport_list, links) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + if (IS_FNIC_FCP_INITIATOR(fnic) || + IS_FNIC_NVME_INITIATOR(fnic)) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + fnic_fcpio_reset(iport->fnic); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + list_for_each_entry_safe(tport, next, &iport->tport_list, links) { + FNIC_FCS_DBG(KERN_INFO, fnic, "removing rport: 0x%x", tport->fcid); - fdls_delete_tport(iport, tport); + fdls_delete_tport(iport, tport); + } } + fdls_reset_oxid_pool(iport); if (fnic_fdmi_support == 1) { if (iport->fabric.fdmi_pending > 0) { @@ -5088,6 +5254,6 @@ void fnic_fdls_link_down(struct fnic_iport_s *iport) iport->flags &= ~FNIC_FDMI_ACTIVE; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "0x%x: FDLS finish processing link down", iport->fcid); } diff --git a/drivers/scsi/fnic/fdls_fc.h b/drivers/scsi/fnic/fdls_fc.h index 012f43afd083..cdf84462dd37 100644 --- a/drivers/scsi/fnic/fdls_fc.h +++ b/drivers/scsi/fnic/fdls_fc.h @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #define FDLS_MIN_FRAMES (32) #define FDLS_MIN_FRAME_ELEM (4) @@ -39,6 +42,9 @@ #define FNIC_FCP_SP_CONF_CMPL 0x00000080 #define FNIC_FCP_SP_RETRY 0x00000100 +#define FNIC_NVME_SP_INITIATOR 0x00000020 +#define FNIC_NVME_SP_SLER 0x00000100 + #define FNIC_FC_CONCUR_SEQS (0xFF) #define FNIC_FC_RO_INFO (0x1F) @@ -50,6 +56,7 @@ #define FNIC_FCP_RSP_FCTL (0x000099) #define FNIC_REQ_ABTS_FCTL (0x000009) +#define FNIC_NVME_LS_REQ_FCTL (0x000029) #define FNIC_FC_PH_VER_HI (0x20) #define FNIC_FC_PH_VER_LO (0x20) diff --git a/drivers/scsi/fnic/fip.c b/drivers/scsi/fnic/fip.c index 132f00512ee1..231dec180675 100644 --- a/drivers/scsi/fnic/fip.c +++ b/drivers/scsi/fnic/fip.c @@ -27,7 +27,7 @@ void fnic_fcoe_reset_vlans(struct fnic *fnic) } spin_unlock_irqrestore(&fnic->vlans_lock, flags); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Reset vlan complete\n"); } @@ -46,7 +46,7 @@ void fnic_fcoe_send_vlan_req(struct fnic *fnic) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_ERR, fnic, "Failed to allocate frame to send VLAN req"); return; } @@ -54,10 +54,10 @@ void fnic_fcoe_send_vlan_req(struct fnic *fnic) fnic_fcoe_reset_vlans(fnic); fnic->set_vlan(fnic, 0); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "set vlan done\n"); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "got MAC 0x%x:%x:%x:%x:%x:%x\n", iport->hwmac[0], iport->hwmac[1], iport->hwmac[2], iport->hwmac[3], iport->hwmac[4], iport->hwmac[5]); @@ -81,13 +81,13 @@ void fnic_fcoe_send_vlan_req(struct fnic *fnic) iport->fip.state = FDLS_FIP_VLAN_DISCOVERY_STARTED; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Send VLAN req\n"); fnic_send_fip_frame(iport, frame, frame_size); vlan_tov = jiffies + msecs_to_jiffies(FCOE_CTLR_FIPVLAN_TOV); mod_timer(&fnic->retry_fip_timer, round_jiffies(vlan_tov)); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fip timer set\n"); } @@ -111,11 +111,11 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph) struct fip_vlan_desc *vlan_desc; unsigned long flags; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p got vlan resp\n", fnic); desc_len = be16_to_cpu(vlan_notif->fip.fip_dl_len); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "desc_len %d\n", desc_len); spin_lock_irqsave(&fnic->vlans_lock, flags); @@ -128,23 +128,20 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph) if (vlan_desc->fd_desc.fip_dtype == FIP_DT_VLAN) { if (vlan_desc->fd_desc.fip_dlen != 1) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Invalid descriptor length(%x) in VLan response\n", vlan_desc->fd_desc.fip_dlen); } num_vlan++; vid = be16_to_cpu(vlan_desc->fd_vlan); - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "process_vlan_resp: FIP VLAN %d\n", vid); - vlan = kzalloc_obj(*vlan); + vlan = kzalloc_obj(*vlan, GFP_ATOMIC); if (!vlan) { /* retry from timer */ - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Mem Alloc failure\n"); spin_unlock_irqrestore(&fnic->vlans_lock, flags); @@ -155,14 +152,14 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph) list_add_tail(&vlan->list, &fnic->vlan_list); break; } - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, - "Invalid descriptor type(%x) in VLan response\n", - vlan_desc->fd_desc.fip_dtype); /* - * Note : received a type=2 descriptor here i.e. FIP - * MAC Address Descriptor + * Note : skip any type=2 descriptor here + * (i.e. FIP MAC Address Descriptor) */ + if (vlan_desc->fd_desc.fip_dtype != FIP_DT_MAC) + FNIC_FIP_DBG(KERN_INFO, fnic, + "Invalid descriptor type(0x%x) in vlan response\n", + vlan_desc->fd_desc.fip_dtype); cur_desc += vlan_desc->fd_desc.fip_dlen; desc_len -= vlan_desc->fd_desc.fip_dlen; } @@ -170,7 +167,7 @@ void fnic_fcoe_process_vlan_resp(struct fnic *fnic, struct fip_header *fiph) /* any VLAN descriptors present ? */ if (num_vlan == 0) { atomic64_inc(&fnic_stats->vlan_stats.resp_withno_vlanID); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p No VLAN descriptors in FIP VLAN response\n", fnic); } @@ -195,7 +192,7 @@ void fnic_fcoe_start_fcf_discovery(struct fnic *fnic) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_ERR, fnic, "Failed to allocate frame to start FCF discovery"); return; } @@ -222,7 +219,7 @@ void fnic_fcoe_start_fcf_discovery(struct fnic *fnic) FNIC_STD_SET_NODE_NAME(&pdisc_sol->name_desc.fd_wwn, iport->wwnn); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Start FCF discovery\n"); fnic_send_fip_frame(iport, frame, frame_size); @@ -257,16 +254,14 @@ void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph) switch (iport->fip.state) { case FDLS_FIP_FCF_DISCOVERY_STARTED: if (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_SOL) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p Solicited adv\n", fnic); if ((disc_adv->prio_desc.fd_pri < iport->selected_fcf.fcf_priority) && (be16_to_cpu(disc_adv->fip.fip_flags) & FIP_FL_AVAIL)) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p FCF Available\n", fnic); memcpy(iport->selected_fcf.fcf_mac, disc_adv->mac_desc.fd_mac, ETH_ALEN); @@ -274,8 +269,8 @@ void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph) disc_adv->prio_desc.fd_pri; iport->selected_fcf.fka_adv_period = be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period); - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, "adv time %d", + FNIC_FIP_DBG(KERN_INFO, fnic, + "adv time %d", iport->selected_fcf.fka_adv_period); iport->selected_fcf.ka_disabled = (disc_adv->fka_adv_desc.fd_flags & 1); @@ -294,8 +289,7 @@ void fnic_fcoe_fip_discovery_resp(struct fnic *fnic, struct fip_header *fiph) iport->selected_fcf.fka_adv_period = be32_to_cpu(disc_adv->fka_adv_desc.fd_fka_period); FNIC_FIP_DBG(KERN_INFO, - fnic->host, - fnic->fnic_num, + fnic, "change fka to %d", iport->selected_fcf.fka_adv_period); } @@ -362,7 +356,7 @@ void fnic_fcoe_start_flogi(struct fnic *fnic) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_ERR, fnic, "Failed to allocate frame to start FIP FLOGI"); return; } @@ -415,7 +409,7 @@ void fnic_fcoe_start_flogi(struct fnic *fnic) oxid = fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_FABRIC_FLOGI, &iport->active_oxid_fabric_req); if (oxid == FNIC_UNASSIGNED_OXID) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Failed to allocate OXID to send FIP FLOGI"); mempool_free(frame, fnic->frame_pool); return; @@ -427,7 +421,7 @@ void fnic_fcoe_start_flogi(struct fnic *fnic) FNIC_STD_SET_NODE_NAME(&pflogi_req->flogi_desc.flogi.els.fl_wwnn, iport->wwnn); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "FIP start FLOGI\n"); fnic_send_fip_frame(iport, frame, frame_size); iport->fip.flogi_retry++; @@ -457,11 +451,11 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) struct fnic_stats *fnic_stats = &fnic->fnic_stats; struct fc_frame_header *fchdr = &flogi_rsp->rsp_desc.flogi.fchdr; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p FIP FLOGI rsp\n", fnic); desc_len = be16_to_cpu(flogi_rsp->fip.fip_dl_len); if (desc_len != 38) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Invalid Descriptor List len (%x). Dropping frame\n", desc_len); return; @@ -471,7 +465,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) && (flogi_rsp->rsp_desc.fd_desc.fip_dlen == 36)) || !((flogi_rsp->mac_desc.fd_desc.fip_dtype == 2) && (flogi_rsp->mac_desc.fd_desc.fip_dlen == 2))) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Dropping frame invalid type and len mix\n"); return; } @@ -484,7 +478,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) || (s_id != FC_FID_FLOGI) || (frame_type != FNIC_FABRIC_FLOGI_RSP) || (fchdr->fh_type != 0x01)) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Dropping invalid frame: s_id %x F %x R %x t %x OX_ID %x\n", s_id, fchdr->fh_f_ctl[0], fchdr->fh_r_ctl, fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr)); @@ -492,7 +486,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) } if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p rsp for pending FLOGI\n", fnic); oxid = FNIC_STD_GET_OX_ID(fchdr); @@ -502,8 +496,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) if ((be16_to_cpu(flogi_rsp->fip.fip_dl_len) == FIP_FLOGI_LEN) && (flogi_rsp->rsp_desc.flogi.els.fl_cmd == ELS_LS_ACC)) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p FLOGI success\n", fnic); memcpy(iport->fpma, flogi_rsp->mac_desc.fd_mac, ETH_ALEN); iport->fcid = @@ -519,8 +512,7 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) if (fnic_fdls_register_portid(iport, iport->fcid, NULL) != 0) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p flogi registration failed\n", fnic); return; @@ -528,8 +520,8 @@ void fnic_fcoe_process_flogi_resp(struct fnic *fnic, struct fip_header *fiph) iport->fip.state = FDLS_FIP_FLOGI_COMPLETE; iport->state = FNIC_IPORT_STATE_FABRIC_DISC; - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, "iport->state:%d\n", + FNIC_FIP_DBG(KERN_INFO, fnic, + "iport->state:%d\n", iport->state); fnic_fdls_disc_start(iport); if (!((iport->selected_fcf.ka_disabled) @@ -575,7 +567,7 @@ void fnic_common_fip_cleanup(struct fnic *fnic) if (!iport->usefip) return; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p fip cleanup\n", fnic); iport->fip.state = FDLS_FIP_INIT; @@ -617,7 +609,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph) int found = false; int max_count = 0; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p clear virtual link handler\n", fnic); if (!((cvl_msg->fcf_mac_desc.fd_desc.fip_dtype == 2) @@ -625,7 +617,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph) || !((cvl_msg->name_desc.fd_desc.fip_dtype == 4) && (cvl_msg->name_desc.fd_desc.fip_dlen == 3))) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "invalid mix: ft %x fl %x ndt %x ndl %x", cvl_msg->fcf_mac_desc.fd_desc.fip_dtype, cvl_msg->fcf_mac_desc.fd_desc.fip_dlen, @@ -640,8 +632,7 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph) if (!((cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype == 11) && (cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen == 5))) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Invalid type and len mix type: %d len: %d\n", cvl_msg->vn_ports_desc[i].fd_desc.fip_dtype, cvl_msg->vn_ports_desc[i].fd_desc.fip_dlen); @@ -664,12 +655,12 @@ void fnic_fcoe_process_cvl(struct fnic *fnic, struct fip_header *fiph) spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); max_count++; if (max_count >= FIP_FNIC_RESET_WAIT_COUNT) { - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "Rthr waited too long. Skipping handle link event %p\n", fnic); return; } - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic reset in progress. Link event needs to wait %p", fnic); } @@ -714,7 +705,7 @@ int fdls_fip_recv_frame(struct fnic *fnic, void *frame) return true; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Not a FIP Frame"); return false; } @@ -724,7 +715,7 @@ void fnic_work_on_fip_timer(struct work_struct *work) struct fnic *fnic = container_of(work, struct fnic, fip_timer_work); struct fnic_iport_s *iport = &fnic->iport; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "FIP timeout\n"); if (iport->fip.state == FDLS_FIP_VLAN_DISCOVERY_STARTED) { @@ -732,7 +723,7 @@ void fnic_work_on_fip_timer(struct work_struct *work) } else if (iport->fip.state == FDLS_FIP_FCF_DISCOVERY_STARTED) { u8 zmac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "FCF Discovery timeout\n"); if (memcmp(iport->selected_fcf.fcf_mac, zmac, ETH_ALEN) != 0) { @@ -754,13 +745,13 @@ void fnic_work_on_fip_timer(struct work_struct *work) round_jiffies(fcf_tov)); } } else { - FNIC_FIP_DBG(KERN_INFO, fnic->host, - fnic->fnic_num, "FCF Discovery timeout\n"); + FNIC_FIP_DBG(KERN_INFO, fnic, + "FCF Discovery timeout\n"); fnic_vlan_discovery_timeout(fnic); } } else if (iport->fip.state == FDLS_FIP_FLOGI_STARTED) { fdls_schedule_oxid_free(iport, &iport->active_oxid_fabric_req); - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "FLOGI timeout\n"); if (iport->fip.flogi_retry < fnic->config.flogi_retries) fnic_fcoe_start_flogi(fnic); @@ -807,7 +798,7 @@ void fnic_handle_enode_ka_timer(struct timer_list *t) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_ERR, fnic, "Failed to allocate frame to send enode ka"); return; } @@ -828,7 +819,7 @@ void fnic_handle_enode_ka_timer(struct timer_list *t) memcpy(penode_ka->eth.h_dest, iport->selected_fcf.fcf_mac, ETH_ALEN); memcpy(penode_ka->mac_desc.fd_mac, iport->hwmac, ETH_ALEN); - FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_DEBUG, fnic, "Handle enode KA timer\n"); fnic_send_fip_frame(iport, frame, frame_size); enode_ka_tov = jiffies @@ -861,7 +852,7 @@ void fnic_handle_vn_ka_timer(struct timer_list *t) frame = fdls_alloc_frame(iport); if (frame == NULL) { - FNIC_FIP_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_ERR, fnic, "Failed to allocate frame to send vn ka"); return; } @@ -887,7 +878,7 @@ void fnic_handle_vn_ka_timer(struct timer_list *t) memcpy(pvn_port_ka->vn_port_desc.fd_fc_id, fcid, 3); FNIC_STD_SET_NPORT_NAME(&pvn_port_ka->vn_port_desc.fd_wwpn, iport->wwpn); - FNIC_FIP_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_DEBUG, fnic, "Handle vnport KA timer\n"); fnic_send_fip_frame(iport, frame, frame_size); vn_ka_tov = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); @@ -977,7 +968,7 @@ void fnic_work_on_fcs_ka_timer(struct work_struct *work) *fnic = container_of(work, struct fnic, fip_timer_work); struct fnic_iport_s *iport = &fnic->iport; - FNIC_FIP_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FIP_DBG(KERN_INFO, fnic, "fnic 0x%p fcs ka timeout\n", fnic); fnic_common_fip_cleanup(fnic); diff --git a/drivers/scsi/fnic/fip.h b/drivers/scsi/fnic/fip.h index 79fee7628870..4866152a32df 100644 --- a/drivers/scsi/fnic/fip.h +++ b/drivers/scsi/fnic/fip.h @@ -142,7 +142,7 @@ fnic_debug_dump_fip_frame(struct fnic *fnic, struct ethhdr *eth, u16 op = be16_to_cpu(fiph->fip_op); u8 sub = fiph->fip_subcode; - FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_DEBUG, fnic, "FIP %s packet contents: op: 0x%x sub: 0x%x (len = %d)", pfx, op, sub, len); diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 8724d64f2525..c576a7f5083e 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -27,10 +27,11 @@ #include "vnic_stats.h" #include "vnic_scsi.h" #include "fnic_fdls.h" +#include "fnic_nvme.h" #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.8.0.3" +#define DRV_VERSION "1.9.0.0" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " @@ -43,6 +44,7 @@ #define FNIC_DFLT_QUEUE_DEPTH 256 #define FNIC_STATS_RATE_LIMIT 4 /* limit rate at which stats are pulled up */ #define LUN0_DELAY_TIME 9 +#define FNIC_ROLE_CONFIG_MASK (0xFF0) /* * Tag bits used for special requests. @@ -79,6 +81,8 @@ #define FNIC_DEV_RST_ABTS_DONE BIT(19) #define FNIC_DEV_RST_TERM_DONE BIT(20) #define FNIC_DEV_RST_ABTS_PENDING BIT(21) +#define FNIC_NVME_ADMIN_IO_TIMER_PENDING BIT(22) +#define FNIC_NVME_ADMIN_IO BIT(23) #define FNIC_FW_RESET_TIMEOUT 60000 /* mSec */ #define FNIC_FCOE_MAX_CMD_LEN 16 @@ -144,6 +148,9 @@ #define PCI_SUBDEVICE_ID_CISCO_HELSINKI 0x02e4 /* VIC 15235 */ #define PCI_SUBDEVICE_ID_CISCO_GOTHENBURG 0x02f2 /* VIC 15425 */ +#define IS_FNIC_FCP_INITIATOR(fnic) (fnic->role == FNIC_ROLE_FCP_INITIATOR) +#define IS_FNIC_NVME_INITIATOR(fnic) (fnic->role == FNIC_ROLE_NVME_INITIATOR) + struct fnic_pcie_device { u32 device; u8 *desc; @@ -234,12 +241,15 @@ extern spinlock_t reset_fnic_list_lock; extern struct list_head reset_fnic_list; extern struct workqueue_struct *reset_fnic_work_queue; extern struct work_struct reset_fnic_work; - +extern struct workqueue_struct *fnic_cmpl_queue; #define FNIC_MAIN_LOGGING 0x01 #define FNIC_FCS_LOGGING 0x02 #define FNIC_SCSI_LOGGING 0x04 #define FNIC_ISR_LOGGING 0x08 +#define FNIC_FDLS_LOGGING 0x10 +#define FNIC_NVME_LOGGING 0x20 +#define FNIC_FIP_LOGGING 0x40 #define FNIC_CHECK_LOGGING(LEVEL, CMD) \ do { \ @@ -249,38 +259,39 @@ do { \ } while (0); \ } while (0) -#define FNIC_MAIN_DBG(kern_level, host, fnic_num, fmt, args...) \ - FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING, \ - shost_printk(kern_level, host, \ - "fnic<%d>: %s: %d: " fmt, fnic_num,\ - __func__, __LINE__, ##args);) - -#define FNIC_FCS_DBG(kern_level, host, fnic_num, fmt, args...) \ - FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \ - shost_printk(kern_level, host, \ - "fnic<%d>: %s: %d: " fmt, fnic_num,\ - __func__, __LINE__, ##args);) - -#define FNIC_FIP_DBG(kern_level, host, fnic_num, fmt, args...) \ - FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \ - shost_printk(kern_level, host, \ - "fnic<%d>: %s: %d: " fmt, fnic_num,\ - __func__, __LINE__, ##args);) - -#define FNIC_SCSI_DBG(kern_level, host, fnic_num, fmt, args...) \ - FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING, \ - shost_printk(kern_level, host, \ - "fnic<%d>: %s: %d: " fmt, fnic_num,\ - __func__, __LINE__, ##args);) - -#define FNIC_ISR_DBG(kern_level, host, fnic_num, fmt, args...) \ - FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING, \ - shost_printk(kern_level, host, \ - "fnic<%d>: %s: %d: " fmt, fnic_num,\ - __func__, __LINE__, ##args);) - -#define FNIC_MAIN_NOTE(kern_level, host, fmt, args...) \ - shost_printk(kern_level, host, fmt, ##args) +#define fnic_printk(kern_level, fnic, fmt, ...) \ + (IS_FNIC_FCP_INITIATOR(fnic) ? \ + shost_printk(kern_level, fnic->host, "fnic<%d>: %s: %d: " fmt, \ + fnic->fnic_num, __func__, __LINE__, ##__VA_ARGS__) : \ + printk(kern_level "fnic<%d>: %s: %d: " fmt, fnic->fnic_num, \ + __func__, __LINE__, ##__VA_ARGS__)) + +#define FNIC_MAIN_DBG(kern_level, fnic, fmt, args...) \ + FNIC_CHECK_LOGGING(FNIC_MAIN_LOGGING, \ + fnic_printk(kern_level, fnic, fmt, ##args);) + +#define FNIC_FCS_DBG(kern_level, fnic, fmt, args...) \ + FNIC_CHECK_LOGGING(FNIC_FCS_LOGGING, \ + fnic_printk(kern_level, fnic, fmt, ##args);) + +#define FNIC_FIP_DBG(kern_level, fnic, fmt, args...) \ + FNIC_CHECK_LOGGING(FNIC_FIP_LOGGING, \ + fnic_printk(kern_level, fnic, fmt, ##args);) + +#define FNIC_SCSI_DBG(kern_level, fnic, fmt, args...) \ + FNIC_CHECK_LOGGING(FNIC_SCSI_LOGGING, \ + fnic_printk(kern_level, fnic, fmt, ##args);) + +#define FNIC_ISR_DBG(kern_level, fnic, fmt, args...) \ + FNIC_CHECK_LOGGING(FNIC_ISR_LOGGING, \ + fnic_printk(kern_level, fnic, fmt, ##args);) + +#define FNIC_NVME_DBG(kern_level, fnic, fmt, args...) \ + FNIC_CHECK_LOGGING(FNIC_NVME_LOGGING, \ + fnic_printk(kern_level, fnic, fmt, ##args);) + +#define FNIC_MAIN_NOTE(kern_level, fnic, fmt, args...) \ + fnic_printk(kern_level, fnic, fmt, ##args) #define FNIC_WQ_COPY_MAX 64 #define FNIC_WQ_MAX 1 @@ -325,6 +336,7 @@ enum fnic_state { enum fnic_role_e { FNIC_ROLE_FCP_INITIATOR = 0, + FNIC_ROLE_NVME_INITIATOR, }; enum fnic_evt { @@ -343,6 +355,11 @@ struct fnic_frame_list { int rx_ethhdr_stripped; }; +struct fnic_tag_t { + struct list_head free_list; + int tag_id; +}; + struct fnic_event { struct list_head list; struct fnic *fnic; @@ -460,6 +477,15 @@ struct fnic { struct list_head vlan_list; /*** FIP related data members -- end ***/ + /* NVME data members */ + struct dentry *fnic_nvmef_debugfs_host; + struct dentry *fnic_nvmef_debugfs_file; + struct sbitmap nvfnic_tag_map; + struct work_struct nvme_io_cmpl_work; + atomic_t nvme_io_event_queued; + struct llist_head nvme_io_event_llist; + struct completion *nvme_lport_unreg_done; + /* copy work queue cache line section */ ____cacheline_aligned struct vnic_wq_copy hw_copy_wq[FNIC_WQ_COPY_MAX]; ____cacheline_aligned struct fnic_cpy_wq sw_copy_wq[FNIC_WQ_COPY_MAX]; @@ -513,6 +539,7 @@ int fnic_host_reset(struct Scsi_Host *shost); void fnic_reset(struct Scsi_Host *shost); int fnic_issue_fc_host_lip(struct Scsi_Host *shost); void fnic_get_host_port_state(struct Scsi_Host *shost); +void fnic_fcpio_reset(struct fnic *fnic); int fnic_wq_copy_cmpl_handler(struct fnic *fnic, int copy_work_to_do, unsigned int cq_index); int fnic_wq_cmpl_handler(struct fnic *fnic, int); int fnic_flogi_reg_handler(struct fnic *fnic, u32); @@ -526,6 +553,9 @@ void fnic_log_q_error(struct fnic *fnic); void fnic_handle_link_event(struct fnic *fnic); int fnic_stats_debugfs_init(struct fnic *fnic); void fnic_stats_debugfs_remove(struct fnic *fnic); +void fnic_nvmef_debugfs_init(struct fnic *fnic); +void fnic_nvmef_debugfs_remove(struct fnic *fnic); +int nvfnic_get_nvmef_info(struct fnic *fnic, struct fnic_nvmef_info *info); int fnic_is_abts_pending(struct fnic *, struct scsi_cmnd *); void fnic_handle_fip_frame(struct work_struct *work); @@ -559,6 +589,8 @@ void fnic_scsi_unload(struct fnic *fnic); void fnic_scsi_unload_cleanup(struct fnic *fnic); int fnic_get_debug_info(struct stats_debug_info *info, struct fnic *fnic); +int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq, + unsigned int hwq); struct fnic_scsi_iter_data { struct fnic *fnic; @@ -598,7 +630,7 @@ fnic_debug_dump(struct fnic *fnic, uint8_t *u8arr, int len) int i; for (i = 0; i < len; i = i+8) { - FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "%d: %02x %02x %02x %02x %02x %02x %02x %02x", i / 8, u8arr[i + 0], u8arr[i + 1], u8arr[i + 2], u8arr[i + 3], u8arr[i + 4], u8arr[i + 5], u8arr[i + 6], u8arr[i + 7]); @@ -613,7 +645,7 @@ fnic_debug_dump_fc_frame(struct fnic *fnic, struct fc_frame_header *fchdr, s_id = ntoh24(fchdr->fh_s_id); d_id = ntoh24(fchdr->fh_d_id); - FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "%s packet contents: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x (len = %d)\n", pfx, s_id, d_id, fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr), len); diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c index ba86964fb45e..61f167e20574 100644 --- a/drivers/scsi/fnic/fnic_debugfs.c +++ b/drivers/scsi/fnic/fnic_debugfs.c @@ -10,10 +10,19 @@ extern int fnic_get_debug_info(struct stats_debug_info *debug_buffer, struct fnic *fnic); +static int fnic_nvmef_debugfs_open(struct inode *inode, + struct file *file); +static ssize_t fnic_nvmef_debugfs_read(struct file *file, + char __user *ubuf, + size_t nbytes, loff_t *pos); +static int fnic_nvmef_debugfs_release(struct inode *inode, + struct file *file); + static struct dentry *fnic_trace_debugfs_root; static struct dentry *fnic_trace_debugfs_file; static struct dentry *fnic_trace_enable; static struct dentry *fnic_stats_debugfs_root; +static struct dentry *fnic_nvmef_debugfs_root; static struct dentry *fnic_fc_trace_debugfs_file; static struct dentry *fnic_fc_rdata_trace_debugfs_file; @@ -46,6 +55,9 @@ int fnic_debugfs_init(void) fnic_stats_debugfs_root = debugfs_create_dir("statistics", fnic_trace_debugfs_root); + fnic_nvmef_debugfs_root = debugfs_create_dir("nvme_info", + fnic_trace_debugfs_root); + /* Allocate memory to structure */ fc_trc_flag = vmalloc(sizeof(struct fc_trace_flag_type)); @@ -70,6 +82,9 @@ int fnic_debugfs_init(void) */ void fnic_debugfs_terminate(void) { + debugfs_remove(fnic_nvmef_debugfs_root); + fnic_nvmef_debugfs_root = NULL; + debugfs_remove(fnic_stats_debugfs_root); fnic_stats_debugfs_root = NULL; @@ -669,6 +684,13 @@ static const struct file_operations fnic_reset_debugfs_fops = { .release = fnic_reset_stats_release, }; +static const struct file_operations fnic_nvmef_debugfs_fops = { + .owner = THIS_MODULE, + .open = fnic_nvmef_debugfs_open, + .read = fnic_nvmef_debugfs_read, + .release = fnic_nvmef_debugfs_release, +}; + /* * fnic_stats_init - Initialize stats struct and create stats file per fnic * @@ -681,7 +703,10 @@ int fnic_stats_debugfs_init(struct fnic *fnic) { char name[16]; - snprintf(name, sizeof(name), "host%d", fnic->host->host_no); + if (IS_FNIC_FCP_INITIATOR(fnic)) + snprintf(name, sizeof(name), "host%d", fnic->host->host_no); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + snprintf(name, sizeof(name), "nvfnic%d", fnic->fnic_num); fnic->fnic_stats_debugfs_host = debugfs_create_dir(name, fnic_stats_debugfs_root); @@ -719,3 +744,75 @@ void fnic_stats_debugfs_remove(struct fnic *fnic) debugfs_remove(fnic->fnic_stats_debugfs_host); fnic->fnic_stats_debugfs_host = NULL; } + +void fnic_nvmef_debugfs_init(struct fnic *fnic) +{ + char name[16]; + + snprintf(name, sizeof(name), "host%d", fnic->fnic_num); + + fnic->fnic_nvmef_debugfs_host = debugfs_create_dir(name, + fnic_nvmef_debugfs_root); + fnic->fnic_nvmef_debugfs_file = debugfs_create_file("nvmef_info", + S_IFREG | 0444, + fnic->fnic_nvmef_debugfs_host, + fnic, + &fnic_nvmef_debugfs_fops); +} + +static int fnic_nvmef_debugfs_open(struct inode *inode, struct file *file) +{ + + struct fnic *fnic = inode->i_private; + struct fnic_nvmef_info *info; + int buf_size = 2 * PAGE_SIZE; + + info = kzalloc_obj(struct fnic_nvmef_info, GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->info_buffer = vmalloc(buf_size); + if (!info->info_buffer) { + kfree(info); + return -ENOMEM; + } + + info->buf_size = buf_size; + memset((void *)info->info_buffer, 0, buf_size); + info->buffer_len = nvfnic_get_nvmef_info(fnic, info); + + file->private_data = info; + + return 0; +} + +static ssize_t fnic_nvmef_debugfs_read(struct file *file, + char __user *ubuf, + size_t nbytes, loff_t *pos) +{ + struct fnic_nvmef_info *info = file->private_data; + + return simple_read_from_buffer(ubuf, nbytes, pos, + info->info_buffer, info->buffer_len); +} + +static int fnic_nvmef_debugfs_release(struct inode *inode, struct file *file) +{ + struct fnic_nvmef_info *info = file->private_data; + + vfree(info->info_buffer); + kfree(info); + return 0; +} + +void fnic_nvmef_debugfs_remove(struct fnic *fnic) +{ + if (!fnic) + return; + + debugfs_remove(fnic->fnic_nvmef_debugfs_file); + fnic->fnic_nvmef_debugfs_file = NULL; + + debugfs_remove(fnic->fnic_nvmef_debugfs_host); + fnic->fnic_nvmef_debugfs_host = NULL; +} diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 063eb864a5cd..b00672ef8b00 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -31,6 +31,11 @@ struct workqueue_struct *fnic_event_queue; static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC; +static inline bool fnic_is_nvme_frame(struct fc_frame_header *fchdr) +{ + return (fchdr->fh_type == FC_TYPE_NVME); +} + /* * Internal Functions * This function will initialize the src_mac address to be @@ -39,7 +44,7 @@ static uint8_t FCOE_ALL_FCF_MAC[6] = FC_FCOE_FLOGI_MAC; static inline void fnic_fdls_set_fcoe_srcmac(struct fnic *fnic, uint8_t *src_mac) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Setting src mac: %02x:%02x:%02x:%02x:%02x:%02x", src_mac[0], src_mac[1], src_mac[2], src_mac[3], src_mac[4], src_mac[5]); @@ -54,7 +59,7 @@ static inline void fnic_fdls_set_fcoe_srcmac(struct fnic *fnic, static inline void fnic_fdls_set_fcoe_dstmac(struct fnic *fnic, uint8_t *dst_mac) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Setting dst mac: %02x:%02x:%02x:%02x:%02x:%02x", dst_mac[0], dst_mac[1], dst_mac[2], dst_mac[3], dst_mac[4], dst_mac[5]); @@ -82,7 +87,7 @@ void fnic_fdls_link_status_change(struct fnic *fnic, int linkup) { struct fnic_iport_s *iport = &fnic->iport; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "link up: %d, usefip: %d", linkup, iport->usefip); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); @@ -90,12 +95,12 @@ void fnic_fdls_link_status_change(struct fnic *fnic, int linkup) if (linkup) { if (iport->usefip) { iport->state = FNIC_IPORT_STATE_FIP; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "link up: %d, usefip: %d", linkup, iport->usefip); fnic_fcoe_send_vlan_req(fnic); } else { iport->state = FNIC_IPORT_STATE_FABRIC_DISC; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iport->state: %d", iport->state); fnic_fdls_disc_start(iport); } @@ -125,13 +130,13 @@ void fnic_fdls_learn_fcoe_macs(struct fnic_iport_s *iport, void *rx_frame, memcpy(&fcmac[3], fcid, 3); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "learn fcoe: dst_mac: %02x:%02x:%02x:%02x:%02x:%02x", ethhdr->h_dest[0], ethhdr->h_dest[1], ethhdr->h_dest[2], ethhdr->h_dest[3], ethhdr->h_dest[4], ethhdr->h_dest[5]); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "learn fcoe: fc_mac: %02x:%02x:%02x:%02x:%02x:%02x", fcmac[0], fcmac[1], fcmac[2], fcmac[3], fcmac[4], fcmac[5]); @@ -149,13 +154,12 @@ void fnic_fdls_init(struct fnic *fnic, int usefip) iport->fnic = fnic; iport->usefip = usefip; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "iportsrcmac: %02x:%02x:%02x:%02x:%02x:%02x", iport->hwmac[0], iport->hwmac[1], iport->hwmac[2], iport->hwmac[3], iport->hwmac[4], iport->hwmac[5]); INIT_LIST_HEAD(&iport->tport_list); - INIT_LIST_HEAD(&iport->tport_list_pending_del); fnic_fdls_disc_init(iport); } @@ -168,14 +172,14 @@ void fnic_handle_link(struct work_struct *work) int max_count = 0; if (vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI) - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Interrupt mode is not MSI\n"); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Stop link rx events\n"); return; } @@ -184,10 +188,10 @@ void fnic_handle_link(struct work_struct *work) if ((fnic->state != FNIC_IN_ETH_MODE) && (fnic->state != FNIC_IN_FC_MODE)) { spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic in transitional state: %d. link up: %d ignored", fnic->state, vnic_dev_link_status(fnic->vdev)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Current link status: %d iport state: %d\n", fnic->link_status, fnic->iport.state); return; @@ -199,36 +203,36 @@ void fnic_handle_link(struct work_struct *work) fnic->link_down_cnt = vnic_dev_link_down_cnt(fnic->vdev); while (fnic->reset_in_progress == IN_PROGRESS) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic reset in progress. Link event needs to wait\n"); spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "waiting for reset completion\n"); wait_for_completion_timeout(&fnic->reset_completion_wait, msecs_to_jiffies(5000)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "woken up from reset completion wait\n"); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); max_count++; if (max_count >= MAX_RESET_WAIT_COUNT) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Rstth waited for too long. Skipping handle link event\n"); spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); return; } } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Marking fnic reset in progress\n"); fnic->reset_in_progress = IN_PROGRESS; if ((vnic_dev_get_intr_mode(fnic->vdev) != VNIC_DEV_INTR_MODE_MSI) || (fnic->link_status != old_link_status)) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "old link status: %d link status: %d\n", old_link_status, (int) fnic->link_status); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "old down count %d down count: %d\n", old_link_down_cnt, (int) fnic->link_down_cnt); } @@ -237,36 +241,36 @@ void fnic_handle_link(struct work_struct *work) if (!fnic->link_status) { /* DOWN -> DOWN */ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "down->down\n"); } else { if (old_link_down_cnt != fnic->link_down_cnt) { /* UP -> DOWN -> UP */ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "up->down. Link down\n"); fnic_fdls_link_status_change(fnic, 0); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "down->up. Link up\n"); fnic_fdls_link_status_change(fnic, 1); } else { /* UP -> UP */ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "up->up\n"); } } } else if (fnic->link_status) { /* DOWN -> UP */ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "down->up. Link up\n"); fnic_fdls_link_status_change(fnic, 1); } else { /* UP -> DOWN */ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "up->down. Link down\n"); fnic_fdls_link_status_change(fnic, 0); } @@ -275,7 +279,7 @@ void fnic_handle_link(struct work_struct *work) fnic->reset_in_progress = NOT_IN_PROGRESS; complete(&fnic->reset_completion_wait); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Marking fnic reset completion\n"); spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); } @@ -285,6 +289,7 @@ void fnic_handle_frame(struct work_struct *work) struct fnic *fnic = container_of(work, struct fnic, frame_work); struct fnic_frame_list *cur_frame, *next; int fchdr_offset = 0; + struct fc_frame_header *fchdr; spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); list_for_each_entry_safe(cur_frame, next, &fnic->frame_queue, links) { @@ -302,7 +307,7 @@ void fnic_handle_frame(struct work_struct *work) */ if (fnic->state != FNIC_IN_FC_MODE && fnic->state != FNIC_IN_ETH_MODE) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Cannot process frame in transitional state\n"); spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); return; @@ -314,8 +319,14 @@ void fnic_handle_frame(struct work_struct *work) fchdr_offset = (cur_frame->rx_ethhdr_stripped) ? 0 : FNIC_ETH_FCOE_HDRS_OFFSET; - fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp, - cur_frame->frame_len, fchdr_offset); + fchdr = (struct fc_frame_header *)((u8 *)cur_frame->fp + fchdr_offset); + if (IS_FNIC_NVME_INITIATOR(fnic) && fnic_is_nvme_frame(fchdr)) { + nvfnic_ls_rsp_recv(&fnic->iport, fchdr, + cur_frame->frame_len - fchdr_offset); + } else { + fnic_fdls_recv_frame(&fnic->iport, cur_frame->fp, + cur_frame->frame_len, fchdr_offset); + } mempool_free(cur_frame->fp, fnic->frame_recv_pool); mempool_free(cur_frame, fnic->frame_elem_pool); @@ -328,7 +339,7 @@ void fnic_handle_fip_frame(struct work_struct *work) struct fnic_frame_list *cur_frame, *next; struct fnic *fnic = container_of(work, struct fnic, fip_frame_work); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Processing FIP frame\n"); spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); @@ -407,7 +418,7 @@ void fnic_update_mac_locked(struct fnic *fnic, u8 *new) if (ether_addr_equal(data, new)) return; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Update MAC: %u\n", *new); if (!is_zero_ether_addr(data) && !ether_addr_equal(data, ctl)) @@ -477,7 +488,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc if (!fcs_ok) { atomic64_inc(&fnic_stats->misc_stats.frame_errors); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic 0x%p fcs error. Dropping packet.\n", fnic); goto drop; } @@ -487,21 +498,21 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc if (fnic_import_rq_eth_pkt(fnic, fp)) return; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Dropping h_proto 0x%x", be16_to_cpu(eh->h_proto)); goto drop; } } else { /* wrong CQ type */ - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic rq_cmpl wrong cq type x%x\n", type); goto drop; } if (!fcs_ok || packet_error || !fcoe_fnic_crc_ok || fcoe_enc_error) { atomic64_inc(&fnic_stats->misc_stats.frame_errors); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fcoe %x fcsok %x pkterr %x ffco %x fee %x\n", fcoe, fcs_ok, packet_error, fcoe_fnic_crc_ok, fcoe_enc_error); @@ -511,7 +522,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->stop_rx_link_events) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic->stop_rx_link_events: %d\n", fnic->stop_rx_link_events); goto drop; @@ -521,7 +532,7 @@ static void fnic_rq_cmpl_frame_recv(struct vnic_rq *rq, struct cq_desc frame_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC); if (!frame_elem) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Failed to allocate memory for frame elem"); goto drop; } @@ -567,7 +578,7 @@ int fnic_rq_cmpl_handler(struct fnic *fnic, int rq_work_to_do) if (cur_work_done && fnic->stop_rx_link_events != 1) { err = vnic_rq_fill(&fnic->rq[i], fnic_alloc_rq_frame); if (err) - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "fnic_alloc_rq_frame can't alloc" " frame\n"); } @@ -593,7 +604,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) len = FNIC_FRAME_HT_ROOM; buf = mempool_alloc(fnic->frame_recv_pool, GFP_ATOMIC); if (!buf) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Unable to allocate RQ buffer of size: %d\n", len); return -ENOMEM; } @@ -601,7 +612,7 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) pa = dma_map_single(&fnic->pdev->dev, buf, len, DMA_FROM_DEVICE); if (dma_mapping_error(&fnic->pdev->dev, pa)) { ret = -ENOMEM; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "PCI mapping failed with error %d\n", ret); goto free_buf; } @@ -615,9 +626,13 @@ int fnic_alloc_rq_frame(struct vnic_rq *rq) void fnic_free_rq_buf(struct vnic_rq *rq, struct vnic_rq_buf *buf) { - void *rq_buf = buf->os_buf; + void *rq_buf; struct fnic *fnic = vnic_dev_priv(rq->vdev); + if (WARN_ON(!buf)) + return; + + rq_buf = buf->os_buf; dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_FROM_DEVICE); @@ -642,7 +657,7 @@ static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len) if ((fnic_fc_trace_set_data(fnic->fnic_num, FNIC_FC_SEND | 0x80, (char *) frame, frame_len)) != 0) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic ctlr frame trace error"); } @@ -650,9 +665,9 @@ static int fnic_send_frame(struct fnic *fnic, void *frame, int frame_len) if (!vnic_wq_desc_avail(wq)) { dma_unmap_single(&fnic->pdev->dev, pa, frame_len, DMA_TO_DEVICE); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "vnic work queue descriptor is not available"); - ret = -1; + ret = -ENXIO; goto fnic_send_frame_end; } @@ -686,7 +701,6 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size, struct fcoe_hdr *pfcoe_hdr; struct fnic_frame_list *frame_elem; int len = frame_size; - int ret; struct fc_frame_header *fchdr = (struct fc_frame_header *) (frame + FNIC_ETH_FCOE_HDRS_OFFSET); @@ -706,13 +720,13 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size, && (fnic->state != FNIC_IN_ETH_MODE)) { frame_elem = mempool_alloc(fnic->frame_elem_pool, GFP_ATOMIC); if (!frame_elem) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Failed to allocate memory for frame elem"); return -ENOMEM; } memset(frame_elem, 0, sizeof(struct fnic_frame_list)); - FNIC_FCS_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Queueing FC frame: sid/did/type/oxid = 0x%x/0x%x/0x%x/0x%x\n", ntoh24(fchdr->fh_s_id), ntoh24(fchdr->fh_d_id), fchdr->fh_type, FNIC_STD_GET_OX_ID(fchdr)); @@ -724,11 +738,10 @@ fdls_send_fcoe_frame(struct fnic *fnic, void *frame, int frame_size, fnic_debug_dump_fc_frame(fnic, fchdr, frame_size, "Outgoing"); - ret = fnic_send_frame(fnic, frame, len); - return ret; + return fnic_send_frame(fnic, frame, len); } -void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, +int fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, int frame_size) { struct fnic *fnic = iport->fnic; @@ -736,7 +749,7 @@ void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, /* If module unload is in-progress, don't send */ if (fnic->in_remove) - return; + return -ESHUTDOWN; if (iport->fabric.flags & FNIC_FDLS_FPMA_LEARNT) { srcmac = iport->fpma; @@ -746,7 +759,7 @@ void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, dstmac = FCOE_ALL_FCF_MAC; } - fdls_send_fcoe_frame(fnic, frame, frame_size, srcmac, dstmac); + return fdls_send_fcoe_frame(fnic, frame, frame_size, srcmac, dstmac); } int @@ -778,7 +791,7 @@ void fnic_flush_tx(struct work_struct *work) struct fc_frame *fp; struct fnic_frame_list *cur_frame, *next; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Flush queued frames"); list_for_each_entry_safe(cur_frame, next, &fnic->tx_queue, links) { @@ -797,7 +810,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id, struct ethhdr *ethhdr; int ret; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Setting port id: 0x%x fp: 0x%p fnic state: %d", port_id, fp, fnic->state); @@ -810,7 +823,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id, if (fnic->state == FNIC_IN_ETH_MODE || fnic->state == FNIC_IN_FC_MODE) fnic->state = FNIC_IN_ETH_TRANS_FC_MODE; else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Unexpected fnic state while processing FLOGI response\n"); return -1; } @@ -821,7 +834,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id, */ ret = fnic_flogi_reg_handler(fnic, port_id); if (ret < 0) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI registration error ret: %d fnic state: %d\n", ret, fnic->state); if (fnic->state == FNIC_IN_ETH_TRANS_FC_MODE) @@ -831,7 +844,7 @@ fnic_fdls_register_portid(struct fnic_iport_s *iport, u32 port_id, } iport->fabric.flags |= FNIC_FDLS_FPMA_LEARNT; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FLOGI registration success\n"); return 0; } @@ -873,6 +886,9 @@ static void fnic_wq_complete_frame_send(struct vnic_wq *wq, { struct fnic *fnic = vnic_dev_priv(wq->vdev); + if (WARN_ON(!buf)) + return; + dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_TO_DEVICE); mempool_free(buf->os_buf, fnic->frame_pool); @@ -915,6 +931,9 @@ void fnic_free_wq_buf(struct vnic_wq *wq, struct vnic_wq_buf *buf) { struct fnic *fnic = vnic_dev_priv(wq->vdev); + if (WARN_ON(!buf)) + return; + dma_unmap_single(&fnic->pdev->dev, buf->dma_addr, buf->len, DMA_TO_DEVICE); @@ -931,7 +950,7 @@ fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport, struct fc_rport_identifiers ids; struct rport_dd_data_s *rdd_data; - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Adding rport fcid: 0x%x", tport->fcid); ids.node_name = tport->wwnn; @@ -943,12 +962,12 @@ fnic_fdls_add_tport(struct fnic_iport_s *iport, struct fnic_tport_s *tport, rport = fc_remote_port_add(fnic->host, 0, &ids); spin_lock_irqsave(&fnic->fnic_lock, flags); if (!rport) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Failed to add rport for tport: 0x%x", tport->fcid); return; } - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Added rport fcid: 0x%x", tport->fcid); /* Mimic these assignments in queuecommand to avoid timing issues */ @@ -987,7 +1006,7 @@ fnic_fdls_remove_tport(struct fnic_iport_s *iport, fc_remote_port_delete(rport); spin_lock_irqsave(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Deregistered and freed tport fcid: 0x%x from scsi transport fc", tport->fcid); @@ -1014,11 +1033,14 @@ void fnic_delete_fcp_tports(struct fnic *fnic) spin_lock_irqsave(&fnic->fnic_lock, flags); list_for_each_entry_safe(tport, next, &fnic->iport.tport_list, links) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "removing fcp rport fcid: 0x%x", tport->fcid); fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINING); fnic_del_tport_timer_sync(fnic, tport); - fnic_fdls_remove_tport(&fnic->iport, tport, flags); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_fdls_remove_tport(&fnic->iport, tport, flags); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_delete_tport(&fnic->iport, tport, flags); } spin_unlock_irqrestore(&fnic->fnic_lock, flags); } @@ -1041,36 +1063,40 @@ void fnic_tport_event_handler(struct work_struct *work) tport = cur_evt->arg1; switch (cur_evt->event) { case TGT_EV_RPORT_ADD: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Add rport event"); if (tport->state == FDLS_TGT_STATE_READY) { - fnic_fdls_add_tport(&fnic->iport, - (struct fnic_tport_s *) cur_evt->arg1, flags); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_fdls_add_tport(&fnic->iport, tport, flags); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_add_tport(fnic, tport, flags); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Target not ready. Add rport event dropped: 0x%x", tport->fcid); } break; case TGT_EV_RPORT_DEL: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Remove rport event"); if (tport->state == FDLS_TGT_STATE_OFFLINING) { - fnic_fdls_remove_tport(&fnic->iport, - (struct fnic_tport_s *) cur_evt->arg1, flags); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_fdls_remove_tport(&fnic->iport, tport, flags); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_delete_tport(&fnic->iport, tport, flags); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "remove rport event dropped tport fcid: 0x%x", tport->fcid); } break; case TGT_EV_TPORT_DELETE: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Delete tport event"); fdls_delete_tport(tport->iport, tport); break; default: - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Unknown tport event"); break; } @@ -1110,12 +1136,15 @@ void fnic_reset_work_handler(struct work_struct *work) spin_unlock_irqrestore(&reset_fnic_list_lock, reset_fnic_list_lock_flags); - dev_err(&cur_fnic->pdev->dev, "fnic: <%d>: issuing a host reset\n", - cur_fnic->fnic_num); - host_reset_ret_code = fnic_host_reset(cur_fnic->host); - dev_err(&cur_fnic->pdev->dev, - "fnic: <%d>: returned from host reset with status: %d\n", - cur_fnic->fnic_num, host_reset_ret_code); + if (IS_FNIC_FCP_INITIATOR(cur_fnic)) { + dev_err(&cur_fnic->pdev->dev, + "fnic: <%d>: issuing a host reset\n", + cur_fnic->fnic_num); + host_reset_ret_code = fnic_host_reset(cur_fnic->host); + dev_err(&cur_fnic->pdev->dev, + "fnic: <%d>: returned from host reset with status: %d\n", + cur_fnic->fnic_num, host_reset_ret_code); + } spin_lock_irqsave(&cur_fnic->fnic_lock, cur_fnic->lock_flags); cur_fnic->pc_rscn_handling_status = @@ -1142,7 +1171,7 @@ void fnic_fcpio_reset(struct fnic *fnic) if (unlikely(fnic->state == FNIC_IN_FC_TRANS_ETH_MODE)) { /* fw reset is in progress, poll for its completion */ spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "fnic is in unexpected state: %d for fw_reset\n", fnic->state); return; @@ -1155,7 +1184,7 @@ void fnic_fcpio_reset(struct fnic *fnic) fnic->fw_reset_done = &fw_reset_done; spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Issuing fw reset\n"); if (fnic_fw_reset_handler(fnic)) { spin_lock_irqsave(&fnic->fnic_lock, flags); @@ -1163,14 +1192,14 @@ void fnic_fcpio_reset(struct fnic *fnic) fnic->state = old_state; spin_unlock_irqrestore(&fnic->fnic_lock, flags); } else { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Waiting for fw completion\n"); time_remain = wait_for_completion_timeout(&fw_reset_done, msecs_to_jiffies(FNIC_FW_RESET_TIMEOUT)); - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "Woken up after fw completion timeout\n"); if (time_remain == 0) { - FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_FCS_DBG(KERN_INFO, fnic, "FW reset completion timed out after %d ms\n", FNIC_FW_RESET_TIMEOUT); } diff --git a/drivers/scsi/fnic/fnic_fdls.h b/drivers/scsi/fnic/fnic_fdls.h index e2959120c4f9..ce4b3aae77e3 100644 --- a/drivers/scsi/fnic/fnic_fdls.h +++ b/drivers/scsi/fnic/fnic_fdls.h @@ -78,14 +78,17 @@ #define FNIC_FDLS_FPMA_LEARNT 0x2 /* tport flags */ -#define FNIC_FDLS_TPORT_IN_GPN_FT_LIST 0x1 -#define FNIC_FDLS_TGT_ABORT_ISSUED 0x2 -#define FNIC_FDLS_TPORT_SEND_ADISC 0x4 -#define FNIC_FDLS_RETRY_FRAME 0x8 -#define FNIC_FDLS_TPORT_BUSY 0x10 -#define FNIC_FDLS_TPORT_TERMINATING 0x20 -#define FNIC_FDLS_TPORT_DELETED 0x40 -#define FNIC_FDLS_SCSI_REGISTERED 0x200 +#define FNIC_FDLS_TPORT_IN_GPN_FT_LIST 0x1 +#define FNIC_FDLS_TGT_ABORT_ISSUED 0x2 +#define FNIC_FDLS_TPORT_SEND_ADISC 0x4 +#define FNIC_FDLS_RETRY_FRAME 0x8 +#define FNIC_FDLS_TPORT_BUSY 0x10 +#define FNIC_FDLS_TPORT_TERMINATING 0x20 +#define FNIC_FDLS_TPORT_DELETED 0x40 +#define FNIC_FDLS_NVME_REGISTERED 0x80 +#define FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING 0x100 +#define FNIC_FDLS_SCSI_REGISTERED 0x200 +#define FNIC_TPORT_CAN_BE_FREED 0x400 /* Retry supported by rport(returned by prli service parameters) */ #define FDLS_FC_RP_FLAGS_RETRY 0x1 @@ -94,6 +97,7 @@ #define fdls_get_state(_fdls_fabric) ((_fdls_fabric)->state) #define FNIC_FDMI_ACTIVE 0x8 +#define FNIC_LPORT_NVME_REGISTERED 0x4 #define FNIC_FIRST_LINK_UP 0x2 #define fdls_set_tport_state(_tport, _state) (_tport->state = _state) @@ -119,6 +123,13 @@ #define FNIC_FRAME_TYPE_TGT_PRLI 0x2800 #define FNIC_FRAME_TYPE_TGT_ADISC 0x2A00 #define FNIC_FRAME_TYPE_TGT_LOGO 0x2C00 +#define FNIC_FRAME_TYPE_NVME_LS 0x3000 + +#define NVFNIC_FCPIO_TAG_POOL_SZ (2048) +#define NVFNIC_LS_REQ_OXID_POOL_SZ (64) +#define NVFNIC_LS_REQ_OXID_BASE (0x2500) +#define FNIC_LPORT_NVME_REGISTERED 0x4 + struct fnic_fip_fcf_s { uint16_t vlan_id; @@ -220,6 +231,8 @@ struct fnic_tport_s { struct fc_rport *rport; char str_wwpn[20]; char str_wwnn[20]; + struct list_head ls_req_list; + struct nvme_fc_remote_port *nv_rport; }; /* OXID pool related structures */ @@ -288,7 +301,6 @@ struct fnic_iport_s { struct fnic_fdls_fip_s fip; struct fnic_fdls_fabric_s fabric; struct list_head tport_list; - struct list_head tport_list_pending_del; /* list of tports for which we are yet to send PLOGO */ struct list_head inprocess_tport_list; struct list_head deleted_tport_list; @@ -308,6 +320,13 @@ struct fnic_iport_s { struct fnic_iport_stats iport_stats; char str_wwpn[20]; char str_wwnn[20]; + + /* nvme */; + void *nvfnic_fcpio_tag[NVFNIC_FCPIO_TAG_POOL_SZ]; + struct nvme_fc_local_port *nv_lport; + struct nvme_fc_port_template *nv_tmpl; + struct fnic_oxid_pool_s ls_req_oxid_pool; + struct list_head fcpio_list; }; struct rport_dd_data_s { @@ -336,6 +355,7 @@ enum fnic_recv_frame_type_e { FNIC_TPORT_ADISC_RSP, FNIC_TPORT_BLS_ABTS_RSP, FNIC_TPORT_LOGO_RSP, + FNIC_LS_REQ_ABTS_RSP, /* unsolicited requests */ FNIC_BLS_ABTS_REQ, @@ -368,6 +388,12 @@ enum fnic_port_speeds { DCEM_PORTSPEED_128G = 128000, }; +static inline bool fdls_tport_is_offline(struct fnic_tport_s *tport) +{ + return (tport->state == FDLS_TGT_STATE_OFFLINING || + tport->state == FDLS_TGT_STATE_OFFLINE); +} + /* Function Declarations */ /* fdls_disc.c */ void fnic_fdls_disc_init(struct fnic_iport_s *iport); @@ -381,6 +407,8 @@ uint16_t fdls_alloc_oxid(struct fnic_iport_s *iport, int oxid_frame_type, uint16_t *active_oxid); void fdls_free_oxid(struct fnic_iport_s *iport, uint16_t oxid, uint16_t *active_oxid); +int fdls_send_ls_req_abts(struct fnic_iport_s *iport, + struct fnic_tport_s *tport, unsigned int oxid); void fdls_tgt_logout(struct fnic_iport_s *iport, struct fnic_tport_s *tport); void fnic_del_fabric_timer_sync(struct fnic *fnic); @@ -398,7 +426,7 @@ void fdls_fdmi_retry_plogi(struct fnic_iport_s *iport); /* fnic_fcs.c */ void fnic_fdls_init(struct fnic *fnic, int usefip); -void fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, +int fnic_send_fcoe_frame(struct fnic_iport_s *iport, void *frame, int frame_size); void fnic_fcoe_send_vlan_req(struct fnic *fnic); int fnic_send_fip_frame(struct fnic_iport_s *iport, diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h index 0d974e040ab7..93a24efba26c 100644 --- a/drivers/scsi/fnic/fnic_io.h +++ b/drivers/scsi/fnic/fnic_io.h @@ -6,13 +6,19 @@ #ifndef _FNIC_IO_H_ #define _FNIC_IO_H_ +#include #include #include "fnic_fdls.h" +#include "fcpio.h" +#include #define FNIC_DFLT_SG_DESC_CNT 32 #define FNIC_MAX_SG_DESC_CNT 256 /* Maximum descriptors per sgl */ #define FNIC_SG_DESC_ALIGN 16 /* Descriptor address alignment */ +#define NVME_STAT_ERROR 0x2 +#define NVME_STAT_TASK_SET_FULL 0x3 + struct host_sg_desc { __le64 addr; __le32 len; @@ -39,6 +45,7 @@ enum fnic_ioreq_state { FNIC_IOREQ_ABTS_PENDING, FNIC_IOREQ_ABTS_COMPLETE, FNIC_IOREQ_CMD_COMPLETE, + FNIC_IOREQ_RESET_TERM, }; struct fnic_io_req { @@ -50,12 +57,27 @@ struct fnic_io_req { dma_addr_t sgl_list_pa; /* dma address for sgl list */ u16 sgl_cnt; u8 sgl_type; /* device DMA descriptor list type */ + u8 sgl_mapped:1; /* set when sgl_list_pa is a valid DMA mapping */ u8 io_completed:1; /* set to 1 when fw completes IO */ u32 port_id; /* remote port DID */ unsigned long start_time; /* in jiffies */ + u32 tag; + enum fnic_ioreq_state cmd_state; + u32 cmd_flags; + u32 abts_state; struct completion *abts_done; /* completion for abts */ struct completion *dr_done; /* completion for device reset */ - unsigned int tag; struct scsi_cmnd *sc; /* midlayer's cmd pointer */ + + struct vnic_wq_copy *wq; + struct llist_node nvfnic_io_cmpl; + struct nvmefc_fcp_req *fcp_req; + void (*done)(struct fnic_io_req *io_req); + unsigned long waitq_start_time; /* in jiffies */ + + struct timer_list admin_io_timer; + uint32_t status; + struct fnic_tag_t *tag_data; + struct fnic_io_event_s io_evt; }; #endif /* _FNIC_IO_H_ */ diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c index 7ed50b11afa6..02856745580f 100644 --- a/drivers/scsi/fnic/fnic_isr.c +++ b/drivers/scsi/fnic/fnic_isr.c @@ -222,7 +222,7 @@ int fnic_request_intr(struct fnic *fnic) fnic->msix[i].devname, fnic->msix[i].devid); if (err) { - FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_ERR, fnic, "request_irq failed with error: %d\n", err); fnic_free_intr(fnic); @@ -250,10 +250,10 @@ int fnic_set_intr_mode_msix(struct fnic *fnic) * We need n RQs, m WQs, o Copy WQs, n+m+o CQs, and n+m+o+1 INTRs * (last INTR is used for WQ/RQ errors and notification area) */ - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_INFO, fnic, "rq-array size: %d wq-array size: %d copy-wq array size: %d\n", n, m, o); - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_INFO, fnic, "rq_count: %d raw_wq_count: %d wq_copy_count: %d cq_count: %d\n", fnic->rq_count, fnic->raw_wq_count, fnic->wq_copy_count, fnic->cq_count); @@ -265,17 +265,17 @@ int fnic_set_intr_mode_msix(struct fnic *fnic) vec_count = pci_alloc_irq_vectors(fnic->pdev, min_irqs, vecs, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_INFO, fnic, "allocated %d MSI-X vectors\n", vec_count); if (vec_count > 0) { if (vec_count < vecs) { - FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_ERR, fnic, "interrupts number mismatch: vec_count: %d vecs: %d\n", vec_count, vecs); if (vec_count < min_irqs) { - FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_ERR, fnic, "no interrupts for copy wq\n"); return 1; } @@ -287,7 +287,7 @@ int fnic_set_intr_mode_msix(struct fnic *fnic) fnic->wq_copy_count = vec_count - n - m - 1; fnic->wq_count = fnic->raw_wq_count + fnic->wq_copy_count; if (fnic->cq_count != vec_count - 1) { - FNIC_ISR_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_ERR, fnic, "CQ count: %d does not match MSI-X vector count: %d\n", fnic->cq_count, vec_count); fnic->cq_count = vec_count - 1; @@ -295,23 +295,23 @@ int fnic_set_intr_mode_msix(struct fnic *fnic) fnic->intr_count = vec_count; fnic->err_intr_offset = fnic->rq_count + fnic->wq_count; - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_INFO, fnic, "rq_count: %d raw_wq_count: %d copy_wq_base: %d\n", fnic->rq_count, fnic->raw_wq_count, fnic->copy_wq_base); - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_INFO, fnic, "wq_copy_count: %d wq_count: %d cq_count: %d\n", fnic->wq_copy_count, fnic->wq_count, fnic->cq_count); - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, - "intr_count: %d err_intr_offset: %u", + FNIC_ISR_DBG(KERN_INFO, fnic, + "intr_count: %d err_intr_offset: %u\n", fnic->intr_count, fnic->err_intr_offset); vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSIX); - FNIC_ISR_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_INFO, fnic, "fnic using MSI-X\n"); return 0; } @@ -351,7 +351,7 @@ int fnic_set_intr_mode(struct fnic *fnic) fnic->intr_count = 1; fnic->err_intr_offset = 0; - FNIC_ISR_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_DEBUG, fnic, "Using MSI Interrupts\n"); vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_MSI); @@ -377,7 +377,7 @@ int fnic_set_intr_mode(struct fnic *fnic) fnic->cq_count = 3; fnic->intr_count = 3; - FNIC_ISR_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_ISR_DBG(KERN_DEBUG, fnic, "Using Legacy Interrupts\n"); vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 24d62c0874ac..9b3025007075 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -42,12 +42,12 @@ static struct kmem_cache *fdls_frame_cache; static struct kmem_cache *fdls_frame_elem_cache; static struct kmem_cache *fdls_frame_recv_cache; static LIST_HEAD(fnic_list); -static DEFINE_SPINLOCK(fnic_list_lock); static DEFINE_IDA(fnic_ida); struct work_struct reset_fnic_work; LIST_HEAD(reset_fnic_list); DEFINE_SPINLOCK(reset_fnic_list_lock); +DEFINE_SPINLOCK(fnic_list_lock); /* Supported devices by fnic module */ static const struct pci_device_id fnic_id_table[] = { @@ -89,6 +89,15 @@ module_param(fnic_fc_trace_max_pages, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(fnic_fc_trace_max_pages, "Total allocated memory pages for fc trace buffer"); +unsigned int nvme_dev_loss_tmo = 30; +module_param_named(nvme_dev_loss_tmo, nvme_dev_loss_tmo, uint, 0644); +MODULE_PARM_DESC(nvme_dev_loss_tmo, "configurable NVME dev loss timeout"); + +unsigned int nvme_max_ios_to_process = 16; +module_param(nvme_max_ios_to_process, uint, 0644); +MODULE_PARM_DESC(nvme_max_ios_to_process, + "Maximum number of NVME IOs to process per work queue"); + static unsigned int fnic_max_qdepth = FNIC_DFLT_QUEUE_DEPTH; module_param(fnic_max_qdepth, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(fnic_max_qdepth, "Queue depth to report for each LUN"); @@ -100,6 +109,7 @@ MODULE_PARM_DESC(pc_rscn_handling_feature_flag, struct workqueue_struct *reset_fnic_work_queue; struct workqueue_struct *fnic_fip_queue; +struct workqueue_struct *fnic_cmpl_queue; static int fnic_sdev_init(struct scsi_device *sdev) { @@ -185,7 +195,7 @@ static void fnic_get_host_speed(struct Scsi_Host *shost) u32 port_speed = vnic_dev_port_speed(fnic->vdev); struct fnic_stats *fnic_stats = &fnic->fnic_stats; - FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_INFO, fnic, "port_speed: %d Mbps", port_speed); atomic64_set(&fnic_stats->misc_stats.port_speed_in_mbps, port_speed); @@ -235,7 +245,7 @@ static void fnic_get_host_speed(struct Scsi_Host *shost) fc_host_speed(shost) = FC_PORTSPEED_128GBIT; break; default: - FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_INFO, fnic, "Unknown FC speed: %d Mbps", port_speed); fc_host_speed(shost) = FC_PORTSPEED_UNKNOWN; break; @@ -261,7 +271,7 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host) spin_unlock_irqrestore(&fnic->fnic_lock, flags); if (ret) { - FNIC_MAIN_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_DEBUG, fnic, "fnic: Get vnic stats failed: 0x%x", ret); return stats; } @@ -287,64 +297,66 @@ static struct fc_host_statistics *fnic_get_stats(struct Scsi_Host *host) void fnic_dump_fchost_stats(struct Scsi_Host *host, struct fc_host_statistics *stats) { - FNIC_MAIN_NOTE(KERN_NOTICE, host, + struct fnic *fnic = *((struct fnic **) shost_priv(host)); + + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: seconds since last reset = %llu\n", stats->seconds_since_last_reset); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: tx frames = %llu\n", stats->tx_frames); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: tx words = %llu\n", stats->tx_words); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: rx frames = %llu\n", stats->rx_frames); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: rx words = %llu\n", stats->rx_words); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: lip count = %llu\n", stats->lip_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: nos count = %llu\n", stats->nos_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: error frames = %llu\n", stats->error_frames); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: dumped frames = %llu\n", stats->dumped_frames); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: link failure count = %llu\n", stats->link_failure_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: loss of sync count = %llu\n", stats->loss_of_sync_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: loss of signal count = %llu\n", stats->loss_of_signal_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: prim seq protocol err count = %llu\n", stats->prim_seq_protocol_err_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: invalid tx word count= %llu\n", stats->invalid_tx_word_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: invalid crc count = %llu\n", stats->invalid_crc_count); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: fcp input requests = %llu\n", stats->fcp_input_requests); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: fcp output requests = %llu\n", stats->fcp_output_requests); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: fcp control requests = %llu\n", stats->fcp_control_requests); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: fcp input megabytes = %llu\n", stats->fcp_input_megabytes); - FNIC_MAIN_NOTE(KERN_NOTICE, host, + FNIC_MAIN_NOTE(KERN_NOTICE, fnic, "fnic: fcp output megabytes = %llu\n", stats->fcp_output_megabytes); return; @@ -370,7 +382,7 @@ static void fnic_reset_host_stats(struct Scsi_Host *host) spin_unlock_irqrestore(&fnic->fnic_lock, flags); if (ret) { - FNIC_MAIN_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_DEBUG, fnic, "fnic: Reset vnic stats failed" " 0x%x", ret); return; @@ -547,6 +559,9 @@ static int fnic_cleanup(struct fnic *fnic) vnic_wq_copy_clean(&fnic->hw_copy_wq[i], fnic_wq_copy_cleanup_handler); + if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_flush_nvme_io_list(fnic); + for (i = 0; i < fnic->cq_count; i++) vnic_cq_clean(&fnic->cq[i]); for (i = 0; i < fnic->intr_count; i++) @@ -676,6 +691,59 @@ static int fnic_scsi_drv_init(struct fnic *fnic) return 0; } +static int fnic_nvme_drv_init(struct fnic *fnic) +{ + int ret; + int hwq; + + fnic->fnic_max_tag_id = NVFNIC_FCPIO_TAG_POOL_SZ; + + for (hwq = 0; hwq < fnic->wq_copy_count; hwq++) { + fnic->sw_copy_wq[hwq].ioreq_table_size = fnic->fnic_max_tag_id; + fnic->sw_copy_wq[hwq].io_req_table = + kzalloc((fnic->sw_copy_wq[hwq].ioreq_table_size + 1) * + sizeof(struct fnic_io_req *), GFP_KERNEL); + + if (!fnic->sw_copy_wq[hwq].io_req_table) { + fnic_free_ioreq_tables_mq(fnic); + return -ENOMEM; + } + } + + dev_info(&fnic->pdev->dev, "fnic copy wqs: %d, Q0 ioreq table size: %d\n", + fnic->wq_copy_count, fnic->sw_copy_wq[0].ioreq_table_size); + + if (sbitmap_init_node(&fnic->nvfnic_tag_map, NVFNIC_FCPIO_TAG_POOL_SZ, + -1, GFP_KERNEL, NUMA_NO_NODE, false, true)) { + dev_err(&fnic->pdev->dev, + "Unable to allocate tag pool\n"); + ret = -ENOMEM; + goto out_free_ioreq_tables; + } + + fnic->io_req_pool = mempool_create_slab_pool(2, fnic_io_req_cache); + if (!fnic->io_req_pool) { + ret = -ENOMEM; + goto out_free_sbitmap; + } + + init_llist_head(&fnic->nvme_io_event_llist); + INIT_WORK(&fnic->nvme_io_cmpl_work, nvfnic_nvme_iodone_work); + + ret = nvfnic_add_lport(fnic); + if (ret) + goto out_free_req_pool; + + return 0; +out_free_req_pool: + mempool_destroy(fnic->io_req_pool); +out_free_sbitmap: + sbitmap_free(&fnic->nvfnic_tag_map); +out_free_ioreq_tables: + fnic_free_ioreq_tables_mq(fnic); + return ret; +} + void fnic_mq_map_queues_cpus(struct Scsi_Host *host) { struct fnic *fnic = *((struct fnic **) shost_priv(host)); @@ -684,16 +752,16 @@ void fnic_mq_map_queues_cpus(struct Scsi_Host *host) struct blk_mq_queue_map *qmap = &host->tag_set.map[HCTX_TYPE_DEFAULT]; if (intr_mode == VNIC_DEV_INTR_MODE_MSI || intr_mode == VNIC_DEV_INTR_MODE_INTX) { - FNIC_MAIN_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_ERR, fnic, "intr_mode is not msix\n"); return; } - FNIC_MAIN_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_INFO, fnic, "qmap->nr_queues: %d\n", qmap->nr_queues); if (l_pdev == NULL) { - FNIC_MAIN_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_MAIN_DBG(KERN_ERR, fnic, "l_pdev is null\n"); return; } @@ -842,7 +910,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_fnic_get_config; } - switch (fnic->config.flags & 0xff0) { + switch (fnic->config.flags & FNIC_ROLE_CONFIG_MASK) { case VFCF_FC_INITIATOR: { host = @@ -861,8 +929,28 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) fnic->fnic_num); } break; + case VFCF_FC_TARGET: + dev_info(&fnic->pdev->dev, + "fnic: %d is scsi target\n", + fnic->fnic_num); + err = -EOPNOTSUPP; + goto err_out_fnic_role; + case VFCF_FC_NVME_INITIATOR: + fnic_nvmef_debugfs_init(fnic); + fnic->role = FNIC_ROLE_NVME_INITIATOR; + dev_info(&fnic->pdev->dev, "fnic: %d is NVME initiator\n", + fnic->fnic_num); + break; + case VFCF_FC_NVME_TARGET: + dev_info(&fnic->pdev->dev, + "fnic: %d is NVME target\n", + fnic->fnic_num); + err = -EOPNOTSUPP; + goto err_out_fnic_role; default: - dev_info(&fnic->pdev->dev, "fnic: %d has no role defined\n", fnic->fnic_num); + dev_info(&fnic->pdev->dev, + "fnic: %d has no role defined (0x%x)\n", + fnic->fnic_num, fnic->config.flags & FNIC_ROLE_CONFIG_MASK); err = -EINVAL; goto err_out_fnic_role; } @@ -995,11 +1083,16 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iport->max_flogi_retries = fnic->config.flogi_retries; iport->max_plogi_retries = fnic->config.plogi_retries; iport->plogi_timeout = fnic->config.plogi_timeout; - iport->service_params = - (FNIC_FCP_SP_INITIATOR | FNIC_FCP_SP_RD_XRDY_DIS | - FNIC_FCP_SP_CONF_CMPL); - if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR) - iport->service_params |= FNIC_FCP_SP_RETRY; + if (IS_FNIC_FCP_INITIATOR(fnic)) { + iport->service_params = (FNIC_FCP_SP_INITIATOR | + FNIC_FCP_SP_RD_XRDY_DIS | FNIC_FCP_SP_CONF_CMPL); + if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR) + iport->service_params |= FNIC_FCP_SP_RETRY; + } else if (IS_FNIC_NVME_INITIATOR(fnic)) { + iport->service_params = (FNIC_NVME_SP_INITIATOR); + if (fnic->config.flags & VFCF_FCP_SEQ_LVL_ERR) + iport->service_params |= FNIC_NVME_SP_SLER; + } iport->boot_time = jiffies; iport->e_d_tov = fnic->config.ed_tov; @@ -1031,6 +1124,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) INIT_LIST_HEAD(&fnic->frame_queue); INIT_LIST_HEAD(&fnic->tx_queue); INIT_LIST_HEAD(&fnic->tport_event_list); + init_llist_head(&fnic->nvme_io_event_llist); INIT_DELAYED_WORK(&iport->oxid_pool.schedule_oxid_free_retry, fdls_schedule_oxid_free_retry_work); @@ -1061,9 +1155,15 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) fnic_fdls_init(fnic, (fnic->config.flags & VFCF_FIP_CAPABLE)); - err = fnic_scsi_drv_init(fnic); - if (err) - goto err_out_scsi_drv_init; + if (IS_FNIC_FCP_INITIATOR(fnic)) { + err = fnic_scsi_drv_init(fnic); + if (err) + goto err_out_scsi_drv_init; + } else if (IS_FNIC_NVME_INITIATOR(fnic)) { + err = fnic_nvme_drv_init(fnic); + if (err) + goto err_out_nvme_drv_init; + } err = fnic_stats_debugfs_init(fnic); if (err) { @@ -1083,7 +1183,9 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_out_free_stats_debugfs: fnic_stats_debugfs_remove(fnic); fnic_free_ioreq_tables_mq(fnic); - scsi_remove_host(fnic->host); + if (IS_FNIC_FCP_INITIATOR(fnic)) + scsi_remove_host(fnic->host); +err_out_nvme_drv_init: err_out_scsi_drv_init: fnic_free_intr(fnic); err_out_fnic_request_intr: @@ -1109,7 +1211,10 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_out_fnic_alloc_vnic_res: fnic_clear_intr_mode(fnic); err_out_fnic_set_intr_mode: - scsi_host_put(fnic->host); + if (IS_FNIC_NVME_INITIATOR(fnic)) + fnic_nvmef_debugfs_remove(fnic); + if (IS_FNIC_FCP_INITIATOR(fnic)) + scsi_host_put(fnic->host); err_out_fnic_role: err_out_scsi_host_alloc: err_out_fnic_get_config: @@ -1158,7 +1263,10 @@ static void fnic_remove(struct pci_dev *pdev) */ flush_workqueue(fnic_event_queue); - fnic_scsi_unload(fnic); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_scsi_unload(fnic); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_nvme_unload(fnic); if (vnic_dev_get_intr_mode(fnic->vdev) == VNIC_DEV_INTR_MODE_MSI) timer_delete_sync(&fnic->notify_timer); @@ -1175,6 +1283,7 @@ static void fnic_remove(struct pci_dev *pdev) if ((fnic_fdmi_support == 1) && (fnic->iport.fabric.fdmi_pending > 0)) timer_delete_sync(&fnic->iport.fabric.fdmi_timer); + fnic_nvmef_debugfs_remove(fnic); fnic_stats_debugfs_remove(fnic); /* @@ -1184,6 +1293,11 @@ static void fnic_remove(struct pci_dev *pdev) */ fnic_cleanup(fnic); + if (IS_FNIC_NVME_INITIATOR(fnic)) { + sbitmap_free(&fnic->nvfnic_tag_map); + fnic_free_ioreq_tables_mq(fnic); + } + spin_lock_irqsave(&fnic_list_lock, flags); list_del(&fnic->list); spin_unlock_irqrestore(&fnic_list_lock, flags); @@ -1202,8 +1316,10 @@ static void fnic_remove(struct pci_dev *pdev) pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); ida_free(&fnic_ida, fnic->fnic_num); - fnic_scsi_unload_cleanup(fnic); - scsi_host_put(fnic->host); + if (IS_FNIC_FCP_INITIATOR(fnic)) { + fnic_scsi_unload_cleanup(fnic); + scsi_host_put(fnic->host); + } kfree(fnic); } @@ -1323,6 +1439,15 @@ static int __init fnic_init_module(void) goto err_create_fip_workq; } + fnic_cmpl_queue = + alloc_workqueue("fnic_cmpl_wq", + WQ_PERCPU | WQ_HIGHPRI | WQ_MEM_RECLAIM, 0); + if (!fnic_cmpl_queue) { + pr_err("fnic completion work queue create failed\n"); + err = -ENOMEM; + goto err_create_cmpl_workq; + } + if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) { reset_fnic_work_queue = create_singlethread_workqueue("reset_fnic_work_queue"); @@ -1354,6 +1479,8 @@ static int __init fnic_init_module(void) err_pci_register: fc_release_transport(fnic_fc_transport); err_fc_transport: + destroy_workqueue(fnic_cmpl_queue); +err_create_cmpl_workq: destroy_workqueue(fnic_fip_queue); err_create_fip_workq: if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) @@ -1382,6 +1509,7 @@ static int __init fnic_init_module(void) static void __exit fnic_cleanup_module(void) { pci_unregister_driver(&fnic_driver); + destroy_workqueue(fnic_cmpl_queue); destroy_workqueue(fnic_event_queue); if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) diff --git a/drivers/scsi/fnic/fnic_nvme.c b/drivers/scsi/fnic/fnic_nvme.c new file mode 100644 index 000000000000..b237948dcafd --- /dev/null +++ b/drivers/scsi/fnic/fnic_nvme.c @@ -0,0 +1,2268 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fnic.h" +#include "fnic_trace.h" +#include "fdls_fc.h" + +#if IS_REACHABLE(CONFIG_NVME_FC) + +static bool nvfnic_ls_req_cleanup(struct fnic_iport_s *iport, + struct nvmefc_ls_req *lsreq, + uint16_t oxid); + +int nvfnic_get_sg_count(struct fnic_io_req *io_req) +{ + return io_req->fcp_req->sg_cnt; +} + +int nvfnic_dma_map_sgl(struct fnic *fnic, struct fnic_io_req *io_req, + int sg_count) +{ + io_req->sgl_list_pa = dma_map_single(&fnic->pdev->dev, + io_req->sgl_list, + sizeof(io_req->sgl_list[0]) * + sg_count, DMA_TO_DEVICE); + if (dma_mapping_error(&fnic->pdev->dev, io_req->sgl_list_pa)) { + dev_err(&fnic->pdev->dev, "DMA mapping failed\n"); + io_req->sgl_list_pa = 0; + io_req->sgl_mapped = 0; + return -EBUSY; + } + + io_req->sgl_mapped = 1; + return 0; +} + +void nvfnic_dma_unmap_sgl(struct fnic *fnic, struct fnic_io_req *io_req) +{ + if (io_req->sgl_mapped) { + dma_unmap_single(&fnic->pdev->dev, io_req->sgl_list_pa, + sizeof(io_req->sgl_list[0]) * io_req->sgl_cnt, + DMA_TO_DEVICE); + io_req->sgl_mapped = 0; + io_req->sgl_list_pa = 0; + } +} + +static void nvfnic_update_io_bytes(struct fnic *fnic, + struct fnic_io_req *io_req, u8 opcode) +{ + if (opcode == nvme_cmd_read) + fnic->fcp_input_bytes += io_req->fcp_req->transferred_length; + else if (opcode == nvme_cmd_write) + fnic->fcp_output_bytes += io_req->fcp_req->transferred_length; +} + +static void nvfnic_update_io_stats(struct fnic *fnic, u8 opcode) +{ + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + + switch (opcode) { + case nvme_cmd_read: + atomic64_inc(&fnic_stats->nvme_stats.nvme_input_requests); + break; + case nvme_cmd_write: + atomic64_inc(&fnic_stats->nvme_stats.nvme_output_requests); + break; + default: + atomic64_inc(&fnic_stats->nvme_stats.nvme_control_requests); + break; + } +} + +static void nvfnic_update_cmpl_stats(struct fnic *fnic, + struct fnic_io_req *io_req) +{ + struct io_path_stats *io_stats = &fnic->fnic_stats.io_stats; + atomic64_t *duration_stat; + unsigned long io_duration_time; + + atomic64_dec(&io_stats->active_ios); + if (atomic64_read(&fnic->io_cmpl_skip)) + atomic64_dec(&fnic->io_cmpl_skip); + else + atomic64_inc(&io_stats->io_completions); + + io_duration_time = jiffies_to_msecs(jiffies - io_req->start_time); + + if (io_duration_time <= 10) + duration_stat = &io_stats->io_btw_0_to_10_msec; + else if (io_duration_time <= 100) + duration_stat = &io_stats->io_btw_10_to_100_msec; + else if (io_duration_time <= 500) + duration_stat = &io_stats->io_btw_100_to_500_msec; + else if (io_duration_time <= 5000) + duration_stat = &io_stats->io_btw_500_to_5000_msec; + else if (io_duration_time <= 10000) + duration_stat = &io_stats->io_btw_5000_to_10000_msec; + else if (io_duration_time <= 30000) + duration_stat = &io_stats->io_btw_10000_to_30000_msec; + else { + duration_stat = &io_stats->io_greater_than_30000_msec; + if (io_duration_time > + atomic64_read(&io_stats->current_max_io_time)) + atomic64_set(&io_stats->current_max_io_time, + io_duration_time); + } + + atomic64_inc(duration_stat); +} + +int +nvfnic_alloc_fcpio_tag(struct fnic_iport_s *iport, struct fnic_io_req *io_req) +{ + struct fnic *fnic = iport->fnic; + int tag; + + tag = sbitmap_get(&fnic->nvfnic_tag_map); + if (tag >= 0) { + WRITE_ONCE(io_req->tag, tag); + fnic->sw_copy_wq[0].io_req_table[tag] = io_req; + return tag; + } + return FNIC_NVME_NO_FREE_TAG; +} + +void +nvfnic_free_fcpio_tag(struct fnic_iport_s *iport, struct fnic_io_req *io_req) +{ + struct fnic *fnic = iport->fnic; + uint16_t tag = io_req->tag; + + if (tag == FNIC_NVME_NO_FREE_TAG) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Attempting to free invalid tag: 0x%x\n", tag); + return; + } + fnic->sw_copy_wq[0].io_req_table[tag] = NULL; + WRITE_ONCE(io_req->tag, FNIC_NVME_NO_FREE_TAG); + sbitmap_clear_bit(&fnic->nvfnic_tag_map, tag); +} + +void +nvfnic_reset_fcpio_tag_pool(struct fnic_iport_s *iport) +{ + WARN_ON(sbitmap_weight(&iport->fnic->nvfnic_tag_map)); +} + +struct fnic_io_req * +nvfnic_find_io_req_by_tag(struct fnic *fnic, uint16_t tag) +{ + if (tag == FNIC_NVME_NO_FREE_TAG || + !sbitmap_test_bit(&fnic->nvfnic_tag_map, tag)) + return NULL; + return fnic->sw_copy_wq[0].io_req_table[tag]; +} + +/* + * Unmap the data buffer and sense buffer for an io_req, + * also unmap and free the device-private scatter/gather list. + */ +void nvfnic_release_nvme_ioreq_buf(struct fnic_iport_s *iport, + struct fnic_io_req *io_req) +{ + struct fnic *fnic = iport->fnic; + + nvfnic_dma_unmap_sgl(fnic, io_req); + + if (io_req->sgl_cnt) + mempool_free(io_req->sgl_list_alloc, + fnic->io_sgl_pool[io_req->sgl_type]); +} + +int nvfnic_get_nvmef_info(struct fnic *fnic, struct fnic_nvmef_info *info) +{ + int len = 0; + struct fnic_iport_s *iport = &fnic->iport; + int buf_size = info->buf_size; + struct fnic_tport_s *tport; + struct fnic_tport_s *next; + unsigned long flags; + + if (buf_size <= 0) + return 0; + + len += scnprintf(info->info_buffer + len, buf_size - len, + "lport wwpn 0x%llx wwnn 0x%llx fcid 0x%06x\n", + iport->wwpn, iport->wwnn, iport->fcid); + + spin_lock_irqsave(&fnic->fnic_lock, flags); + list_for_each_entry_safe(tport, next, &iport->tport_list, links) { + if (len >= buf_size - 1) + break; + + len += scnprintf(info->info_buffer + len, buf_size - len, + "tport wwpn 0x%llx wwnn 0x%llx fcid 0x%06x\n", + tport->wwpn, tport->wwnn, tport->fcid); + } + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + return len; +} + +inline int nvfnic_queue_wq_nvme_copy_desc(struct fnic *fnic, + struct vnic_wq_copy *wq, + struct fnic_io_req *io_req, + int sg_count) +{ + struct scatterlist *sg; + struct fnic_tport_s *tport = io_req->tport; + struct host_sg_desc *desc; + struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats; + unsigned int i; + unsigned long intr_flags; + int flags; + u8 exch_flags; + struct scatterlist *sgl; + int idx; + int ret = 0; + + if (sg_count) { + /* For each SGE, create a device desc entry */ + desc = io_req->sgl_list; + sgl = io_req->fcp_req->first_sgl; + for_each_sg(sgl, sg, sg_count, i) { + desc->addr = cpu_to_le64(sg_dma_address(sg)); + desc->len = cpu_to_le32(sg_dma_len(sg)); + desc->_resvd = 0; + desc++; + } + + ret = nvfnic_dma_map_sgl(fnic, io_req, sg_count); + if (ret) + return ret; + } + + idx = (struct vnic_wq_copy *)wq - &fnic->hw_copy_wq[0]; + + /* Enqueue the descriptor in the Copy WQ */ + spin_lock_irqsave(&fnic->wq_copy_lock[idx], intr_flags); + + if (vnic_wq_copy_desc_avail(wq) <= fnic->wq_copy_desc_low[idx]) + free_wq_copy_descs(fnic, wq, idx); + + if (unlikely(!vnic_wq_copy_desc_avail(wq))) { + spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], intr_flags); + FNIC_NVME_DBG(KERN_ERR, fnic, + "Enqueue failure: No descriptors\n"); + atomic64_inc(&misc_stats->io_cpwq_alloc_failures); + return -EBUSY; + } + + flags = 0; + if (io_req->fcp_req->io_dir == NVMEFC_FCP_READ) + flags = FCPIO_ICMND_RDDATA; + else if (io_req->fcp_req->io_dir == NVMEFC_FCP_WRITE) + flags = FCPIO_ICMND_WRDATA; + + exch_flags = 0; + + fnic_queue_wq_copy_desc_nvme_io(wq, io_req->tag, + exch_flags, io_req->sgl_cnt, + io_req->sgl_list_pa, flags, + io_req->fcp_req->cmdaddr, + io_req->fcp_req->cmdlen, + io_req->fcp_req->payload_length, + io_req->port_id, + tport->max_payload_size, tport->r_a_tov, + tport->e_d_tov); + + atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs); + if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) > + atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs)) + atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs, + atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs)); + + spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], intr_flags); + return 0; +} + +bool +nvfnic_transport_ready(struct fnic_iport_s *iport, struct fnic_tport_s *tport) +{ + struct fnic *fnic = iport->fnic; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + + if (tport == NULL) + return false; + + if (fdls_get_state(&iport->fabric) == FDLS_STATE_LINKDOWN || + iport->state != FNIC_IPORT_STATE_READY) { + atomic64_inc(&fnic_stats->misc_stats.iport_not_ready); + return false; + } + + if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) + return false; + + if (fdls_tport_is_offline(tport)) { + atomic64_inc(&fnic_stats->misc_stats.tport_not_ready); + return false; + } + + return true; +} + +int nvfnic_queuecommand(struct fnic_io_req *io_req) +{ + struct fnic_iport_s *iport = io_req->iport; + struct fnic *fnic = iport->fnic; + struct fnic_tport_s *tport = io_req->tport; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + struct vnic_wq_copy *wq = io_req->wq; + int ret = 0; + int sg_count = 0; + unsigned long ptr; + unsigned char *lba; + u64 cmd_trace; + int idx; + struct nvme_fc_cmd_iu *cmdiu = io_req->fcp_req->cmdaddr; + + io_req->cmd_state = FNIC_IOREQ_NOT_INITED; + io_req->cmd_flags = FNIC_NO_FLAGS; + /* Map the data buffer */ + sg_count = nvfnic_get_sg_count(io_req); + if (sg_count < 0) { + FNIC_TRACE(nvfnic_queuecommand, fnic->fnic_num, + io_req->tag, io_req, 0, io_req->fcp_req->io_dir, + sg_count, io_req->cmd_state); + FNIC_NVME_DBG(KERN_INFO, fnic, "sg count is less-than-zero\n"); + ret = -1; + goto out; + } + + /* Determine the type of scatter/gather list we need */ + io_req->sgl_cnt = sg_count; + io_req->sgl_type = FNIC_SGL_CACHE_DFLT; + if (sg_count > FNIC_DFLT_SG_DESC_CNT) + io_req->sgl_type = FNIC_SGL_CACHE_MAX; + + if (sg_count) { + io_req->sgl_list = + mempool_alloc(fnic->io_sgl_pool[io_req->sgl_type], + GFP_ATOMIC); + if (!io_req->sgl_list) { + atomic64_inc(&fnic_stats->io_stats.alloc_failures); + FNIC_NVME_DBG(KERN_INFO, fnic, + "Unable to alloc SGLs\n"); + ret = -ENOMEM; + goto out; + } + + /* Cache sgl list allocated address before alignment */ + io_req->sgl_list_alloc = io_req->sgl_list; + ptr = (unsigned long)io_req->sgl_list; + if (ptr % FNIC_SG_DESC_ALIGN) { + io_req->sgl_list = (struct host_sg_desc *) + (((unsigned long)ptr + FNIC_SG_DESC_ALIGN - 1) + & ~(FNIC_SG_DESC_ALIGN - 1)); + } + } + + io_req->port_id = tport->fcid; + io_req->start_time = jiffies; + io_req->cmd_state = FNIC_IOREQ_CMD_PENDING; + io_req->cmd_flags = FNIC_IO_INITIALIZED; + + /* create copy wq desc and enqueue it */ + idx = wq - &fnic->hw_copy_wq[0]; + atomic64_inc(&fnic_stats->io_stats.ios[idx]); + ret = nvfnic_queue_wq_nvme_copy_desc(fnic, io_req->wq, io_req, sg_count); + if (ret) { + FNIC_NVME_DBG(KERN_ERR, fnic, "Unable to queue frame\n"); + /* + * In case another thread cancelled the request, + * refetch the pointer under the lock. + */ + nvfnic_release_nvme_ioreq_buf(iport, io_req); + FNIC_TRACE(nvfnic_queuecommand, fnic->fnic_num, + io_req->tag, io_req->fcp_req, 0, 0, 0, + (((u64)io_req->cmd_flags << 32) | io_req->cmd_state)); + return ret; + } + + atomic64_inc(&fnic_stats->io_stats.active_ios); + atomic64_inc(&fnic_stats->io_stats.num_ios); + if (atomic64_read(&fnic_stats->io_stats.active_ios) > + atomic64_read(&fnic_stats->io_stats.max_active_ios)) + atomic64_set(&fnic_stats->io_stats.max_active_ios, + atomic64_read(&fnic_stats->io_stats.active_ios)); + io_req->cmd_flags |= FNIC_IO_ISSUED; + out: + lba = (char *)&cmdiu->sqe.rw.slba; + cmd_trace = ((u64) cmdiu->sqe.rw.opcode << 56 | (u64) lba[4] << 40 | + (u64) lba[5] << 32 | (u64) lba[0] << 24 | + (u64) lba[1] << 16 | (u64) lba[2] << 8 | lba[3]); + + FNIC_TRACE(nvfnic_queuecommand, fnic->fnic_num, + io_req->tag, 0, io_req, + sg_count, cmd_trace, + (((u64)io_req->cmd_flags >> 32) | + io_req->cmd_state)); + + return ret; +} + +int nvfnic_fcpio_send(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, void *hw_queue_handle, + struct nvmefc_fcp_req *fcp_req) +{ + struct fnic_iport_s *iport = lport->private; + struct fnic_io_req *io_req; + int ret; + struct fnic *fnic = iport->fnic; + unsigned long flags = 0; + struct fnic_tport_s *tport; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + + spin_lock_irqsave(&fnic->fnic_lock, flags); + + tport = (struct fnic_tport_s *)rport->private; + atomic64_inc(&fnic_stats->io_stats.nvme_io_reqs_rcvd); + if (!nvfnic_transport_ready(iport, tport)) { + atomic64_inc(&fnic_stats->io_stats.nvme_io_rsps_sent); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + if (tport != NULL) + FNIC_NVME_DBG(KERN_INFO, fnic, + "iport: 0x%x tport: 0x%x not ready\n", + iport->fcid, tport->fcid); + else + FNIC_NVME_DBG(KERN_INFO, fnic, + "iport: 0x%x tport not ready\n", + iport->fcid); + return -ENODEV; + } + atomic_inc(&fnic->in_flight); + + io_req = (struct fnic_io_req *) fcp_req->private; + io_req->iport = iport; + io_req->tport = (struct fnic_tport_s *)rport->private; + io_req->fcp_req = fcp_req; + io_req->done = nvfnic_fcpio_cmpl; + io_req->sgl_list = NULL; + io_req->sgl_list_alloc = NULL; + io_req->sgl_list_pa = 0; + io_req->sgl_cnt = 0; + io_req->sgl_mapped = 0; + io_req->wq = hw_queue_handle; + init_llist_node(&io_req->nvfnic_io_cmpl); + + io_req->tag = nvfnic_alloc_fcpio_tag(iport, io_req); + if (io_req->tag == FNIC_NVME_NO_FREE_TAG) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "No free tag available. Failing IO\n"); + atomic64_inc(&fnic_stats->io_stats.alloc_failures); + atomic_dec(&fnic->in_flight); + atomic64_inc(&fnic_stats->io_stats.nvme_io_rsps_sent); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return -EBUSY; + } + + ret = nvfnic_queuecommand(io_req); + if (ret) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Queuecommand failed tag: 0x%x\n", + io_req->tag); + nvfnic_free_fcpio_tag(iport, io_req); + } + + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + atomic_dec(&fnic->in_flight); + return ret; +} + +void nvfnic_fcpio_nvme_fast_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc) +{ + u8 type; + u8 hdr_status; + struct fcpio_tag ftag; + u32 id; + struct fnic_io_req *io_req; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + unsigned long start_time; + u64 cmd_trace; + char *lba; + struct nvme_fc_cmd_iu *cmdiu; + struct fnic_iport_s *iport; + unsigned int tag; + + /* Decode the cmpl description to get the io_req id */ + fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag); + fcpio_tag_id_dec(&ftag, &id); + tag = id & FNIC_TAG_MASK; + + if (tag >= fnic->fnic_max_tag_id) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Tag out of range tag: 0x%x hdr status: %s\n", tag, + fnic_fcpio_status_to_str(hdr_status)); + return; + } + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + + io_req = nvfnic_find_io_req_by_tag(fnic, tag); + + WARN_ON_ONCE(!io_req); + if (!io_req) { + atomic64_inc(&fnic_stats->io_stats.ioreq_null); + FNIC_NVME_DBG(KERN_ERR, fnic, + "IO req null hdr: %s tag: 0x%x desc: 0x%p\n", + fnic_fcpio_status_to_str(hdr_status), id, desc); + FNIC_NVME_DBG(KERN_ERR, fnic, + "type: 0x%x status: 0x%x rsvd: 0x%x tag: 0x%x\n", + desc->hdr.type, desc->hdr.status, desc->hdr._resvd, + id); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + cmdiu = io_req->fcp_req->cmdaddr; + if (io_req->tag != tag) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "Tag mismatch tag:%d io:0x%x id:0x%x csn:%08x\n", + tag, io_req->tag, id, be32_to_cpu(cmdiu->csn)); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + iport = io_req->iport; + + start_time = io_req->start_time; + + /* firmware completed the io */ + io_req->io_completed = 1; + if (io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) { + /* + * set the FNIC_IO_DONE so that this doesn't get + * flagged as 'out of order' if it was not aborted + */ + io_req->cmd_flags |= FNIC_IO_DONE; + io_req->cmd_flags |= FNIC_IO_ABTS_PENDING; + if (hdr_status == FCPIO_ABORTED) + io_req->cmd_flags |= FNIC_IO_ABORTED; + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + + FNIC_NVME_DBG(KERN_INFO, fnic, + "icmnd abts hdr:%d %s tag:0x%x io:%p", + hdr_status, fnic_fcpio_status_to_str(hdr_status), + id, io_req); + return; + } + + if (io_req->cmd_state != FNIC_IOREQ_CMD_PENDING) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "IO freed id:%d tag:0x%x st:0x%x csn:%08x\n", + id, io_req->tag, io_req->cmd_state, + be32_to_cpu(cmdiu->csn)); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + /* Mark the IO as complete */ + io_req->cmd_state = FNIC_IOREQ_CMD_COMPLETE; + switch (hdr_status) { + case FCPIO_SUCCESS: + io_req->fcp_req->status = 0; + io_req->fcp_req->transferred_length = + io_req->fcp_req->payload_length; + io_req->fcp_req->rcv_rsplen = 12; + break; + default: + FNIC_NVME_DBG(KERN_ERR, fnic, "HDR status is non-zero\n"); + io_req->fcp_req->status = NVME_SC_INTERNAL; + break; + } + + if (hdr_status != FCPIO_SUCCESS) { + atomic64_inc(&fnic_stats->io_stats.io_failures); + FNIC_NVME_DBG(KERN_INFO, fnic, "hdr status: %s\n", + fnic_fcpio_status_to_str(hdr_status)); + } + + io_req->cmd_flags |= FNIC_IO_DONE; + + cmdiu = io_req->fcp_req->cmdaddr; + lba = (char *)&cmdiu->sqe.rw.slba; + cmd_trace = ((u64) hdr_status << 56) | + (u64) cmdiu->sqe.rw.opcode << 32 | + (u64) lba[0] << 24 | (u64) lba[1] << 16 | + (u64) lba[2] << 8 | lba[3]; + + FNIC_TRACE(nvfnic_fcpio_nvme_fast_cmpl_handler, fnic->fnic_num, + tag, io_req, + jiffies_to_msecs(jiffies - start_time), + desc, cmd_trace, + (((u64) io_req->cmd_flags << 32) | + io_req->cmd_state)); + + nvfnic_update_io_stats(fnic, cmdiu->sqe.rw.opcode); + nvfnic_update_io_bytes(fnic, io_req, cmdiu->sqe.rw.opcode); + nvfnic_update_cmpl_stats(fnic, io_req); + + nvfnic_release_nvme_ioreq_buf(iport, io_req); + if (io_req->done) + io_req->done(io_req); + + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); +} + +void nvfnic_fcpio_ersp_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc, int sw_flag) +{ + u8 type; + u8 hdr_status; + struct fcpio_tag ftag; + u32 id; + struct fcpio_nvme_cmpl *nvme_cmpl; + struct fnic_io_req *io_req; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + unsigned long start_time; + uint32_t rsplen; + struct nvme_fc_ersp_iu *ersp; + struct nvme_fc_ersp_iu *nrsp; + struct nvme_fc_cmd_iu *cmdiu; + struct nvme_command *sqe; + struct nvme_completion *cqe; + u64 cmd_trace; + struct fnic_iport_s *iport; + unsigned int tag; + char *lba; + uint64_t tport_wwpn = 0; + + /* Decode the cmpl description to get the io_req id */ + fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag); + fcpio_tag_id_dec(&ftag, &id); + nvme_cmpl = &desc->u.nvme_cmpl; + ersp = (struct nvme_fc_ersp_iu *) nvme_cmpl->resp_bytes; + tag = id & FNIC_TAG_MASK; + + if (tag >= fnic->fnic_max_tag_id) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Tag out of range tag:0x%x hdr:%s\n", tag, + fnic_fcpio_status_to_str(hdr_status)); + return; + } + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + + io_req = nvfnic_find_io_req_by_tag(fnic, tag); + if (!io_req) { + atomic64_inc(&fnic_stats->io_stats.ioreq_null); + FNIC_NVME_DBG(KERN_ERR, fnic, + "IOREQ is null hdr status: %s tag: 0x%x desc: %p\n", + fnic_fcpio_status_to_str(hdr_status), tag, desc); + FNIC_NVME_DBG(KERN_ERR, fnic, + "type: 0x%x status: 0x%x rsvd: 0x%x\n", + desc->hdr.type, desc->hdr.status, desc->hdr._resvd); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + iport = io_req->iport; + if (io_req->tport != NULL) + tport_wwpn = io_req->tport->wwpn; + cmdiu = io_req->fcp_req->cmdaddr; + + if (io_req->tag != tag) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Tag mismatch io:0x%x tag:0x%x id:0x%x\n", + io_req->tag, tag, id); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + nrsp = (struct nvme_fc_ersp_iu *)io_req->fcp_req->rspaddr; + cmdiu = (struct nvme_fc_cmd_iu *)io_req->fcp_req->cmdaddr; + sqe = &cmdiu->sqe; + cqe = &nrsp->cqe; + start_time = io_req->start_time; + + /* firmware completed the io */ + io_req->io_completed = 1; + + if (io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) { + /* + * set the FNIC_IO_DONE so that this doesn't get + * flagged as 'out of order' if it was not aborted + */ + io_req->cmd_flags |= FNIC_IO_DONE; + io_req->cmd_flags |= FNIC_IO_ABTS_PENDING; + if (hdr_status == FCPIO_ABORTED) + io_req->cmd_flags |= FNIC_IO_ABORTED; + + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + FNIC_NVME_DBG(KERN_INFO, fnic, + "ABTS pending hdr status: %s tag: 0x%x", + fnic_fcpio_status_to_str(hdr_status), tag); + return; + } + + if (io_req->cmd_state != FNIC_IOREQ_CMD_PENDING) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "IO already freed by abort. tag: 0x%x id: 0x%x\n", + io_req->tag, id); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + /* Mark the IO as complete */ + io_req->cmd_state = FNIC_IOREQ_CMD_COMPLETE; + + switch (hdr_status) { + case FCPIO_SUCCESS: + io_req->fcp_req->status = 0; + if (!sw_flag) { + io_req->fcp_req->transferred_length = + io_req->fcp_req->payload_length; + rsplen = 32; + nrsp->iu_len = + cpu_to_be16(sizeof(struct nvme_fc_ersp_iu) / 4); + nrsp->xfrd_len = + cpu_to_be32(io_req->fcp_req->payload_length); + + nrsp->ersp_result = 0; + cqe->command_id = sqe->common.command_id; + cqe->status = 0; + cqe->result.u64 = 0; + } else { + io_req->fcp_req->transferred_length = + be32_to_cpu(ersp->xfrd_len); + rsplen = be16_to_cpu(ersp->iu_len) * 4; + if (rsplen > sizeof(nvme_cmpl->resp_bytes) || + rsplen > io_req->fcp_req->rsplen) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport wwpn 0x%llx ERSP len %u desc %u req %u\n", + tport_wwpn, rsplen, + (u32)sizeof(nvme_cmpl->resp_bytes), + io_req->fcp_req->rsplen); + io_req->fcp_req->status = NVME_SC_INTERNAL; + io_req->fcp_req->rcv_rsplen = 0; + break; + } + memcpy(io_req->fcp_req->rspaddr, ersp, rsplen); + } + atomic64_inc(&fnic_stats->nvme_stats.nvme_ersps); + io_req->fcp_req->rcv_rsplen = rsplen; + break; + + default: + FNIC_NVME_DBG(KERN_ERR, fnic, + "Unexpected header status: %d\n", hdr_status); + io_req->fcp_req->status = NVME_SC_INTERNAL; + break; + } + + if (hdr_status != FCPIO_SUCCESS) { + atomic64_inc(&fnic_stats->io_stats.io_failures); + FNIC_NVME_DBG(KERN_ERR, fnic, "hdr status: %s tag: 0x%x\n", + fnic_fcpio_status_to_str(hdr_status), tag); + } + + io_req->cmd_flags |= FNIC_IO_DONE; + + lba = (char *) &cmdiu->sqe.rw.slba; + cmd_trace = ((u64) hdr_status << 56) | + (u64) ersp->ersp_result << 48 | + (u64) cmdiu->sqe.rw.opcode << 32 | + (u64) lba[0] << 24 | (u64) lba[1] << 16 | + (u64) lba[2] << 8 | lba[3]; + + FNIC_TRACE(nvfnic_fcpio_ersp_cmpl_handler, fnic->fnic_num, + tag, io_req, + ((u64) nvme_cmpl->resvd[1] << 56 | + (u64) nvme_cmpl->resvd[0] << 48 | + jiffies_to_msecs(jiffies - start_time)), + desc, cmd_trace, + (((u64) io_req->cmd_flags << 32) | + io_req->cmd_state)); + + nvfnic_update_io_stats(fnic, cmdiu->sqe.rw.opcode); + nvfnic_update_io_bytes(fnic, io_req, cmdiu->sqe.rw.opcode); + nvfnic_update_cmpl_stats(fnic, io_req); + + nvfnic_release_nvme_ioreq_buf(iport, io_req); + + /* Call NVME completion function to complete the IO */ + if (io_req->done) + io_req->done(io_req); + + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); +} + +void nvfnic_fcpio_nvme_itmf_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc) +{ + u8 type; + u8 hdr_status; + struct fcpio_tag ftag; + u32 id; + unsigned int tag; + struct fnic_io_req *io_req; + struct nvme_fc_cmd_iu *cmd_iu; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + struct abort_stats *abts_stats = &fnic->fnic_stats.abts_stats; + struct terminate_stats *term_stats = &fnic->fnic_stats.term_stats; + struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats; + struct fnic_iport_s *iport; + + fcpio_header_dec(&desc->hdr, &type, &hdr_status, &ftag); + fcpio_tag_id_dec(&ftag, &id); + tag = id & FNIC_TAG_MASK; + + if (tag >= fnic->fnic_max_tag_id) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Tag out of range id:0x%x tag:0x%x hdr:%s\n", + id, tag, fnic_fcpio_status_to_str(hdr_status)); + return; + } + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + + io_req = nvfnic_find_io_req_by_tag(fnic, tag); + WARN_ON_ONCE(!io_req); + if (!io_req) { + atomic64_inc(&fnic_stats->io_stats.ioreq_null); + FNIC_NVME_DBG(KERN_ERR, fnic, + "IOREQ null hdr:%s tag:0x%x desc:%p\n", + fnic_fcpio_status_to_str(hdr_status), tag, desc); + FNIC_NVME_DBG(KERN_ERR, fnic, + "type: 0x%x status: 0x%x rsvd: 0x%x\n", + desc->hdr.type, desc->hdr.status, desc->hdr._resvd); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + cmd_iu = io_req->fcp_req->cmdaddr; + FNIC_NVME_DBG(KERN_INFO, fnic, + "Received ITMF completion tag: 0x%x hdr_status: %d csn: 0x%08x\n", + tag, hdr_status, be32_to_cpu(cmd_iu->csn)); + + iport = io_req->iport; + + /* Completion of abort cmd */ + switch (hdr_status) { + case FCPIO_SUCCESS: + FNIC_NVME_DBG(KERN_DEBUG, fnic, + "Abort success received tag: 0x%x id: 0x%x\n", + tag, id); + break; + case FCPIO_TIMEOUT: + FNIC_NVME_DBG(KERN_ERR, fnic, + "Abort timeout received tag: 0x%x id: 0x%x\n", + tag, id); + if (io_req->cmd_flags & FNIC_IO_ABTS_ISSUED) + atomic64_inc(&abts_stats->abort_fw_timeouts); + else + atomic64_inc(&term_stats->terminate_fw_timeouts); + break; + case FCPIO_ITMF_REJECTED: + FNIC_NVME_DBG(KERN_ERR, fnic, + "Abort reject received tag: 0x%x id: 0x%x\n", + tag, id); + break; + + case FCPIO_IO_NOT_FOUND: + FNIC_NVME_DBG(KERN_ERR, fnic, + "Abort IO not found tag:0x%x id:0x%x\n", + tag, id); + if (io_req->cmd_flags & FNIC_IO_ABTS_ISSUED) + atomic64_inc(&abts_stats->abort_io_not_found); + else + atomic64_inc(&term_stats->terminate_io_not_found); + break; + default: + FNIC_NVME_DBG(KERN_ERR, fnic, + "Abort unknown received tag: 0x%x id: 0x%x\n", + tag, id); + if (io_req->cmd_flags & FNIC_IO_ABTS_ISSUED) + atomic64_inc(&abts_stats->abort_failures); + else + atomic64_inc(&term_stats->terminate_failures); + break; + } + + if (io_req->cmd_state != FNIC_IOREQ_ABTS_PENDING) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Abort late completion tag:0x%x id:0x%x\n", + tag, id); + /* This is a late completion. Ignore it */ + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + io_req->abts_state = hdr_status; + + /* If the status is IO not found consider it as success. + * NVME sends abort even if rport is down in which case + * we will get FCPIO_TIMEOUT. Consider this as success. + */ + if ((hdr_status == FCPIO_IO_NOT_FOUND) || + (hdr_status == FCPIO_TIMEOUT) || + (hdr_status == FCPIO_ITMF_REJECTED)) + io_req->abts_state = FCPIO_SUCCESS; + + io_req->cmd_flags |= FNIC_IO_ABT_TERM_DONE; + + if (!(io_req->cmd_flags & (FNIC_IO_ABORTED | FNIC_IO_DONE))) + atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls); + + io_req->fcp_req->transferred_length = 0; + io_req->fcp_req->rcv_rsplen = 0; + if (io_req->abts_state == FCPIO_SUCCESS) + io_req->fcp_req->status = NVME_SC_ABORT_REQ; + else + io_req->fcp_req->status = NVME_SC_INTERNAL; + + atomic64_dec(&fnic_stats->io_stats.active_ios); + if (atomic64_read(&fnic->io_cmpl_skip)) + atomic64_dec(&fnic->io_cmpl_skip); + else + atomic64_inc(&fnic_stats->io_stats.io_completions); + + nvfnic_release_nvme_ioreq_buf(iport, io_req); + if (io_req->done) + io_req->done(io_req); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); +} + +bool _cleanup_tport_io(struct sbitmap *map, unsigned int tag, + void *data) +{ + struct fnic_tport_s *tport = data; + struct fnic_iport_s *iport = tport->iport; + struct fnic *fnic = iport->fnic; + struct fnic_io_req *io_req; + enum fnic_ioreq_state old_ioreq_state; + unsigned long flags; + + spin_lock_irqsave(&fnic->fnic_lock, flags); + io_req = nvfnic_find_io_req_by_tag(fnic, tag); + if (!io_req || io_req->tport != tport) { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return true; + } + + if ((io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) || + (io_req->cmd_state == FNIC_DEV_RST_TERM_ISSUED)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "Abort already pending 0x%x\n", io_req->tag); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return true; + } + + FNIC_NVME_DBG(KERN_ERR, fnic, + "io_req tag: 0x%x abort after unregister timeout\n", + io_req->tag); + + old_ioreq_state = io_req->cmd_state; + io_req->cmd_state = FNIC_IOREQ_ABTS_PENDING; + io_req->abts_state = FCPIO_INVALID_CODE; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + if (!nvfnic_queue_abort_io_req(fnic, io_req->tag, + FCPIO_ITMF_ABT_TASK_TERM, + io_req)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Failed to enqueue abort for ioreq tag: 0x%x\n", + io_req->tag); + spin_lock_irqsave(&fnic->fnic_lock, flags); + io_req->cmd_state = old_ioreq_state; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + } + return true; +} + +void +nvfnic_cleanup_tport_io(struct fnic *fnic, struct fnic_tport_s *tport) +{ + unsigned long flags; + struct nvfnic_ls_req *nvfnic_ls_req, *next; + struct nvmefc_ls_req *lsreq; + uint16_t oxid; + LIST_HEAD(done_reqs); + + spin_lock_irqsave(&fnic->fnic_lock, flags); + list_for_each_entry_safe(nvfnic_ls_req, next, + &(tport->ls_req_list), list) { + lsreq = nvfnic_ls_req->ls_req; + if (!lsreq || (lsreq->private == NULL)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "fnic_cleanup_tport_io lsreq NULL\n"); + continue; + } + if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "fnic_cleanup_tport_io lsreq abort started\n"); + continue; + } + list_del_init(&nvfnic_ls_req->list); + lsreq->private = NULL; + oxid = nvfnic_ls_req->oxid; + fdls_free_oxid(&fnic->iport, oxid, &nvfnic_ls_req->oxid); + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE; + list_add_tail(&nvfnic_ls_req->list, &done_reqs); + } + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + list_for_each_entry_safe(nvfnic_ls_req, next, &done_reqs, list) { + list_del_init(&nvfnic_ls_req->list); + lsreq = nvfnic_ls_req->ls_req; + timer_delete_sync(&nvfnic_ls_req->ls_req_timer); + lsreq->done(lsreq, -ENXIO); + } + + /* For link-down, IOs are freed by firmware reset completion */ + if (fdls_get_state(&fnic->iport.fabric) == FDLS_STATE_LINKDOWN) + return; + + sbitmap_for_each_set(&fnic->nvfnic_tag_map, _cleanup_tport_io, tport); +} + +void +nvfnic_terminate_tport_ls_reqs(struct fnic *fnic, struct fnic_tport_s *tport) +{ + struct nvmefc_ls_req *lsreq; + struct nvfnic_ls_req *nvfnic_ls_req, *next; + int count = 0; + uint16_t oxid; + LIST_HEAD(done_reqs); + + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + list_for_each_entry_safe(nvfnic_ls_req, next, + &(tport->ls_req_list), list) { + + lsreq = nvfnic_ls_req->ls_req; + if (!lsreq || (lsreq->private == NULL)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "lsreq is NULL\n"); + continue; + } + if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "lsreq abort started\n"); + continue; + } + oxid = nvfnic_ls_req->oxid; + list_del_init(&nvfnic_ls_req->list); + lsreq->private = NULL; + fdls_free_oxid(&fnic->iport, oxid, &nvfnic_ls_req->oxid); + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE; + list_add_tail(&nvfnic_ls_req->list, &done_reqs); + } + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + + list_for_each_entry_safe(nvfnic_ls_req, next, &done_reqs, list) { + list_del_init(&nvfnic_ls_req->list); + lsreq = nvfnic_ls_req->ls_req; + timer_delete_sync(&nvfnic_ls_req->ls_req_timer); + lsreq->done(lsreq, -ENXIO); + count++; + } + + FNIC_NVME_DBG(KERN_INFO, fnic, + "fnic_terminate_tport_lsreqs tport: 0x%x: freed lsreq: %d\n", + tport->fcid, count); +} + +bool _terminate_tport_ios(struct sbitmap *map, unsigned int tag, + void *data) +{ + struct fnic_tport_s *tport = data; + struct fnic_iport_s *iport = tport->iport; + struct fnic *fnic = iport->fnic; + struct fnic_io_req *io_req; + enum fnic_ioreq_state old_ioreq_state; + unsigned long flags; + + spin_lock_irqsave(&fnic->fnic_lock, flags); + io_req = fnic->sw_copy_wq[0].io_req_table[tag]; + if (!io_req) { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return true; + } + + if (io_req->tport != tport) { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return true; + } + + if ((io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) || + (io_req->cmd_state == FNIC_DEV_RST_TERM_ISSUED)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "Abort already pending 0x%x\n", io_req->tag); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return true; + } + + FNIC_NVME_DBG(KERN_INFO, fnic, + "Terminate tag: 0x%x (tport fcid 0x%x)\n", + io_req->tag, io_req->tport->fcid); + + old_ioreq_state = io_req->cmd_state; + io_req->cmd_state = FNIC_IOREQ_ABTS_PENDING; + io_req->abts_state = FCPIO_INVALID_CODE; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + if (!nvfnic_queue_abort_io_req(fnic, io_req->tag, + FCPIO_ITMF_ABT_TASK_TERM, io_req)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Failed to enqueue abort for ioreq tag: 0x%x\n", + io_req->tag); + spin_lock_irqsave(&fnic->fnic_lock, flags); + io_req->cmd_state = old_ioreq_state; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + } + return true; +} + +void nvfnic_terminate_tport_ios(struct fnic *fnic, + struct fnic_tport_s *tport) +{ + struct abort_stats *abts_stats = &fnic->fnic_stats.abts_stats; + + sbitmap_for_each_set(&fnic->nvfnic_tag_map, _terminate_tport_ios, tport); + + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport: 0x%x aborted %lld in_flight %d\n", + tport->fcid, atomic64_read(&abts_stats->aborts), + atomic_read(&fnic->in_flight)); +} + +bool _cleanup_all_nvme_io(struct sbitmap *map, unsigned int tag, + void *data) +{ + struct fnic_iport_s *iport = data; + struct fnic_io_req *io_req; + + io_req = iport->fnic->sw_copy_wq[0].io_req_table[tag]; + if (!io_req) + return true; + + io_req->cmd_state = FNIC_DEV_RST_TERM_ISSUED; + io_req->fcp_req->status = NVME_SC_INTERNAL; + io_req->fcp_req->transferred_length = 0; + io_req->fcp_req->rcv_rsplen = 0; + nvfnic_release_nvme_ioreq_buf(iport, io_req); + io_req->done(io_req); + return true; +} + +void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic) +{ + struct fnic_iport_s *iport = &fnic->iport; + + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + sbitmap_for_each_set(&fnic->nvfnic_tag_map, _cleanup_all_nvme_io, + iport); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); +} + +void nvfnic_nvme_zero_devloss_tports(struct fnic *fnic) +{ + struct fnic_tport_s *tport, *next; + + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + list_for_each_entry_safe(tport, next, &fnic->iport.tport_list, links) { + if (tport->flags & FNIC_FDLS_NVME_REGISTERED) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + nvme_fc_set_remoteport_devloss(tport->nv_rport, 0); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + } + } + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); +} + +void nvfnic_nvme_unload(struct fnic *fnic) +{ + int ret = 0; + struct fnic_iport_s *iport = &fnic->iport; + unsigned long flags; + unsigned int time_wait = FNIC_NVME_LPORT_REMOVE_WAIT; + unsigned int time_remain; + DECLARE_COMPLETION_ONSTACK(nvme_lport_unreg_done); + + /* Mark iport state as INIT so that no IOs can be issued from this point */ + spin_lock_irqsave(&fnic->fnic_lock, flags); + fnic->in_remove = 1; + fnic->iport.state = FNIC_IPORT_STATE_LINK_WAIT; + fnic->nvme_lport_unreg_done = &nvme_lport_unreg_done; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + /* + * If fnic is already processing link-down or fnic is held + * in disabled state following a reboot we dont need to issue + * firmware reset and unregister remote ports as it is already + * done as part of link down handling. + */ + if (fdls_get_state(&iport->fabric) == FDLS_STATE_LINKDOWN) { + while (fnic->reset_in_progress == IN_PROGRESS) { + wait_for_completion_timeout(&fnic->reset_completion_wait, + msecs_to_jiffies(5000)); + FNIC_NVME_DBG(KERN_INFO, fnic, + "rmmod waiting for reset %p\n", fnic); + } + } else if (fdls_get_state(&iport->fabric) != FDLS_STATE_INIT) + fnic_fcpio_reset(fnic); + + /* + * Mark state so that the workqueue thread stops forwarding + * received frames and link events to the local port. ISR and + * other threads that can queue work items will also stop + * creating work items on the fnic workqueue + */ + nvfnic_nvme_zero_devloss_tports(fnic); + fnic_flush_tport_event_list(fnic); + fnic_delete_fcp_tports(fnic); + + if (iport->flags & FNIC_LPORT_NVME_REGISTERED) { + ret = nvme_fc_unregister_localport(fnic->iport.nv_lport); + if (ret) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Unregister nvme localport failed: %d\n", ret); + } else { + time_remain = wait_for_completion_timeout( + fnic->nvme_lport_unreg_done, + msecs_to_jiffies(time_wait)); + if (!time_remain) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Local port removal timed out\n"); + WARN_ON(1); + } + iport->flags &= ~FNIC_LPORT_NVME_REGISTERED; + kfree(iport->nv_tmpl); + } + } + + spin_lock_irqsave(&fnic->fnic_lock, flags); + fnic->nvme_lport_unreg_done = NULL; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + nvfnic_flush_nvme_io_list(fnic); +} + +struct nvfnic_ls_req* +nvfnic_find_ls_req(struct fnic_tport_s *tport, uint16_t oxid) +{ + struct nvfnic_ls_req *nvfnic_ls_req, *next; + + list_for_each_entry_safe(nvfnic_ls_req, next, &(tport->ls_req_list), list) { + if (nvfnic_ls_req->oxid == oxid) + return nvfnic_ls_req; + } + return NULL; +} + +void nvfnic_fcpio_cmpl(struct fnic_io_req *io_req) +{ + struct fnic *fnic = io_req->iport->fnic; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + + nvfnic_free_fcpio_tag(io_req->iport, io_req); + atomic64_inc(&fnic_stats->io_stats.nvme_ios_queued_for_rsp); + io_req->waitq_start_time = jiffies; + + llist_add(&io_req->nvfnic_io_cmpl, &fnic->nvme_io_event_llist); + atomic_inc(&fnic->nvme_io_event_queued); + atomic64_inc(&fnic_stats->io_stats.nvme_num_ios_in_waitq); + + queue_work(fnic_cmpl_queue, &fnic->nvme_io_cmpl_work); +} + +void nvfnic_process_ls_abts_rsp(struct fnic_iport_s *iport, + struct fc_frame_header *fchdr) +{ + uint32_t tport_fcid; + struct fnic_tport_s *tport; + struct nvfnic_ls_req *nvfnic_ls_req; + struct nvmefc_ls_req *lsreq; + uint8_t *fcid; + uint16_t oxid = FNIC_STD_GET_OX_ID(fchdr); + struct fnic *fnic = iport->fnic; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + + fcid = FNIC_STD_GET_S_ID(fchdr); + tport_fcid = ntoh24(fcid); + + tport = fnic_find_tport_by_fcid(iport, tport_fcid); + if (tport == NULL) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x not found\n", tport_fcid); + return; + } + + nvfnic_ls_req = nvfnic_find_ls_req(tport, oxid); + if (nvfnic_ls_req == NULL) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x lsreq oxid: 0x%x not found\n", + tport_fcid, oxid); + return; + } + + lsreq = nvfnic_ls_req->ls_req; + if ((lsreq == NULL) || (lsreq->private == NULL)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport: 0x%x lsreq oxid: 0x%x already aborted\n", + tport_fcid, oxid); + return; + } + + atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_abort_responses); + nvfnic_ls_req->state = FNIC_LS_REQ_ABTS_COMPLETE; + + FNIC_NVME_DBG(KERN_DEBUG, fnic, "nvme_ls_requests: %lld\n", + (u64) atomic64_read(&fnic_stats->nvme_stats.nvme_ls_requests)); + + list_del(&nvfnic_ls_req->list); + fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid); + lsreq->private = NULL; + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + timer_delete_sync(&nvfnic_ls_req->ls_req_timer); + lsreq->done(lsreq, NVME_SC_HOST_ABORTED_CMD); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); +} + +/** + * nvfnic_ls_rsp_recv - Handle received NVMe FC link service (LS) response + * @iport: Pointer to the local FNIC port structure + * @fchdr: Pointer to the Fibre Channel frame header for the + * received response + * @len: Length of the received frame + * + * This function processes link service (LS) responses received from + * NVMe Discovery Controllers or regular NVMe subsystems during + * association. + */ +void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport, + struct fc_frame_header *fchdr, int len) +{ + uint8_t *fcid; + uint32_t tport_fcid; + struct fnic_tport_s *tport; + struct nvfnic_ls_req *nvfnic_ls_req; + struct nvmefc_ls_req *lsreq; + uint16_t oxid; + uint32_t rsp_len; + int sid_len = offsetof(struct fc_frame_header, fh_s_id) + + sizeof(fchdr->fh_s_id); + int status = 0; + struct fnic *fnic = iport->fnic; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + + if (len < (int)sizeof(*fchdr)) { + if (len >= sid_len) { + fcid = FNIC_STD_GET_S_ID(fchdr); + tport_fcid = ntoh24(fcid); + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x LS rsp len %d too short\n", + tport_fcid, len); + } else { + FNIC_NVME_DBG(KERN_ERR, fnic, + "LS response len %d too short\n", len); + } + return; + } + rsp_len = len - sizeof(*fchdr); + + fcid = FNIC_STD_GET_S_ID(fchdr); + tport_fcid = ntoh24(fcid); + + tport = fnic_find_tport_by_fcid(iport, tport_fcid); + if (!tport) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x not found\n", tport_fcid); + return; + } + + oxid = FNIC_STD_GET_OX_ID(fchdr); + nvfnic_ls_req = nvfnic_find_ls_req(tport, oxid); + if (!nvfnic_ls_req) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x no nvfnic_lsreq for oxid: 0x%x\n", + tport_fcid, oxid); + return; + } + + lsreq = nvfnic_ls_req->ls_req; + if (!lsreq || (lsreq->private == NULL)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport:0x%x lsreq:0x%x already done\n", + tport_fcid, oxid); + return; + } + if (!lsreq->rspaddr) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport 0x%x lsreq 0x%x rspaddr NULL\n", + tport_fcid, oxid); + return; + } + + if ((nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING) || + (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport 0x%x lsreq oxid: 0x%x abts pending\n", + tport_fcid, oxid); + return; + } + + if (rsp_len > lsreq->rsplen) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport:0x%x lsreq:0x%x rsp %u > %u\n", + tport_fcid, oxid, rsp_len, lsreq->rsplen); + status = -EOVERFLOW; + } + + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE; + atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_responses); + + list_del_init(&nvfnic_ls_req->list); + lsreq->private = NULL; + fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid); + + if (status == 0) { + FNIC_NVME_DBG(KERN_DEBUG, fnic, + "tport:0x%x lsreq:0x%x completed\n", + tport_fcid, oxid); + + /* Copy the Response */ + memcpy(lsreq->rspaddr, (uint8_t *)fchdr + sizeof(*fchdr), + rsp_len); + } + + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + timer_delete_sync(&nvfnic_ls_req->ls_req_timer); + lsreq->done(lsreq, status); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); +} + +static bool nvfnic_ls_req_cleanup(struct fnic_iport_s *iport, + struct nvmefc_ls_req *lsreq, + uint16_t oxid) +{ + struct nvfnic_ls_req *nvfnic_ls_req = lsreq->private; + + if (!nvfnic_ls_req) + return false; + + lsreq->private = NULL; + list_del(&nvfnic_ls_req->list); + fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid); + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_COMPLETE; + + return true; +} + +void nvfnic_ls_req_timeout(struct timer_list *t) +{ + struct nvfnic_ls_req *nvfnic_ls_req = timer_container_of(nvfnic_ls_req, + t, ls_req_timer); + struct fnic *fnic = nvfnic_ls_req->fnic; + struct nvmefc_ls_req *ls_req = nvfnic_ls_req->ls_req; + struct fnic_iport_s *iport = &fnic->iport; + struct fnic_tport_s *tport = (struct fnic_tport_s *) nvfnic_ls_req->tport; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + uint16_t oxid = nvfnic_ls_req->oxid; + int timeout; + + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport: 0x%x lsreq: 0x%x state: %d timeout\n", + tport->fcid, nvfnic_ls_req->oxid, + nvfnic_ls_req->state); + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + + if ((ls_req->private == NULL) || + (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_STARTED)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x lsreq: 0x%x already aborted\n", + tport->fcid, nvfnic_ls_req->oxid); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x lsreq: 0x%x abort timeout\n", + tport->fcid, nvfnic_ls_req->oxid); + + ls_req = nvfnic_ls_req->ls_req; + nvfnic_ls_req_cleanup(iport, ls_req, oxid); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + ls_req->done(ls_req, -ETIMEDOUT); + return; + } else if ((nvfnic_ls_req->state == FNIC_LS_REQ_CMD_PENDING) && + (nvfnic_transport_ready(iport, tport))) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x lsreq: 0x%x sending abort\n", + tport->fcid, nvfnic_ls_req->oxid); + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_PENDING; + atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_aborts); + + if (fdls_send_ls_req_abts(iport, tport, nvfnic_ls_req->oxid) == 0) { + timeout = FNIC_LS_REQ_TMO_MSECS(ls_req->timeout); + mod_timer(&nvfnic_ls_req->ls_req_timer, + round_jiffies(jiffies + msecs_to_jiffies(timeout))); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x lsreq: 0x%x cannot send abort\n", + tport->fcid, oxid); + } + + if (ls_req->private == NULL) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + ls_req = nvfnic_ls_req->ls_req; + nvfnic_ls_req_cleanup(iport, ls_req, oxid); + + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + ls_req->done(ls_req, -ETIMEDOUT); +} + +/** + * nvfnic_ls_req_send - Send NVMe FC link service (LS) request + * @lport: Pointer to local NVMe FC port structure + * @rport: Pointer to remote NVMe FC port structure + * @ls_req: Pointer to the link service request structure + * + * This function is used to send link service (LS) commands to an NVMe + * Discovery Controller for discovery operations, as well as to regular + * NVMe subsystems during association. It encapsulates the logic for + * transmitting LS requests over the NVMe over Fabrics (NVMe-oF) FC + * transport. + * + * Returns: 0 on success, or a negative error code on failure. + */ +int nvfnic_ls_req_send(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_req *ls_req) +{ + int timeout; + uint8_t *frame; + uint8_t fcid[3]; + unsigned long flags = 0; + struct fnic_iport_s *iport = lport->private; + uint8_t *ls_req_payload; + struct fnic *fnic = iport->fnic; + struct fc_frame_header *fchdr; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + struct nvfnic_ls_req *nvfnic_ls_req = ls_req->private; + uint16_t frame_size = FNIC_ETH_FCOE_HDRS_OFFSET + + sizeof(struct fc_frame_header) + ls_req->rqstlen; + struct fnic_tport_s *tport; + int ret; + + spin_lock_irqsave(&fnic->fnic_lock, flags); + + tport = (struct fnic_tport_s *)rport->private; + INIT_LIST_HEAD(&nvfnic_ls_req->list); + + if (!nvfnic_transport_ready(iport, tport)) { + if (tport != NULL) + FNIC_NVME_DBG(KERN_INFO, fnic, + "iport: 0x%x tport: 0x%x transport not ready\n", + iport->fcid, tport->fcid); + else + FNIC_NVME_DBG(KERN_INFO, fnic, + "iport: 0x%x transport not ready\n", + iport->fcid); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return -ENOLINK; + } + + frame = fdls_alloc_frame(iport); + if (frame == NULL) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Failed to allocate frame to send NVME LS REQ"); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return -ENOMEM; + } + + if (fdls_alloc_oxid(iport, FNIC_FRAME_TYPE_NVME_LS, + &nvfnic_ls_req->oxid) == FNIC_UNASSIGNED_OXID) { + FNIC_FCS_DBG(KERN_INFO, fnic, + "0x%x: Failed to allocate OXID to send NVME LS REQ", + iport->fcid); + mempool_free(frame, fnic->frame_pool); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return -EAGAIN; + } + + atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_requests); + timer_setup(&nvfnic_ls_req->ls_req_timer, nvfnic_ls_req_timeout, + 0UL); + + nvfnic_ls_req->fnic = fnic; + nvfnic_ls_req->tport = tport; + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_INIT; + nvfnic_ls_req->ls_req = ls_req; + + fchdr = (struct fc_frame_header *)(frame + FNIC_ETH_FCOE_HDRS_OFFSET); + *fchdr = (struct fc_frame_header) { + .fh_r_ctl = FC_RCTL_ELS4_REQ, + .fh_type = FC_TYPE_NVME, + .fh_f_ctl = {FNIC_ELS_REQ_FCTL, 0, 0}, + .fh_rx_id = cpu_to_be16(FNIC_UNASSIGNED_RXID) + }; + + hton24(fcid, iport->fcid); + FNIC_STD_SET_S_ID(*fchdr, fcid); + + hton24(fcid, tport->fcid); + FNIC_STD_SET_D_ID(*fchdr, fcid); + + FNIC_STD_SET_OX_ID(*fchdr, nvfnic_ls_req->oxid); + + ls_req_payload = frame + FNIC_ETH_FCOE_HDRS_OFFSET + sizeof(*fchdr); + memcpy(ls_req_payload, ls_req->rqstaddr, ls_req->rqstlen); + + FNIC_NVME_DBG(KERN_INFO, fnic, + "0x%x: NVME send ls req with oxid: 0x%x type: 0x%02x len: %d", + iport->fcid, nvfnic_ls_req->oxid, *((uint8_t *) ls_req->rqstaddr), + ls_req->rqstlen); + + FNIC_NVME_DBG(KERN_INFO, fnic, + "0x%x: ls_reqs count: %lld", + iport->fcid, + (u64) atomic64_read(&fnic_stats->nvme_stats.nvme_ls_requests)); + + list_add_tail(&nvfnic_ls_req->list, &tport->ls_req_list); + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_PENDING; + + ret = fnic_send_fcoe_frame(iport, frame, frame_size); + if (ret) { + nvfnic_ls_req_cleanup(iport, ls_req, nvfnic_ls_req->oxid); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + mempool_free(frame, fnic->frame_pool); + return ret; + } + + timeout = FNIC_LS_REQ_TMO_MSECS(ls_req->timeout); + mod_timer(&nvfnic_ls_req->ls_req_timer, + round_jiffies(jiffies + msecs_to_jiffies(timeout))); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + return 0; +} + +void nvfnic_local_port_delete(struct nvme_fc_local_port *lport) +{ + struct fnic_iport_s *iport = (struct fnic_iport_s *) lport->private; + struct fnic *fnic = iport->fnic; + unsigned long flags = 0; + + FNIC_NVME_DBG(KERN_INFO, fnic, "lport delete 0x%x\n", + iport->fcid); + + spin_lock_irqsave(&fnic->fnic_lock, flags); + if (fnic->nvme_lport_unreg_done) + complete(fnic->nvme_lport_unreg_done); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); +} + +void nvfnic_remote_port_delete(struct nvme_fc_remote_port *rport) +{ + /* + * Read rport->private without the lock only to find fnic. + * Re-read it under fnic_lock to claim this delete callback, since + * another callback may already have cleared it. + */ + struct fnic_tport_s *tport = (struct fnic_tport_s *)rport->private; + struct fnic_iport_s *iport; + struct fnic *fnic = NULL; + unsigned long flags = 0; + + if (tport == NULL) { + pr_err("Attempt to delete already deleted tport\n"); + return; + } + + iport = tport->iport; + fnic = iport->fnic; + FNIC_NVME_DBG(KERN_INFO, fnic, "0x%x tport 0x%x\n", + iport->fcid, tport->fcid); + + spin_lock_irqsave(&fnic->fnic_lock, flags); + tport = (struct fnic_tport_s *)rport->private; + if (tport == NULL) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "NVMe tport callback after NULL set %p\n", + rport); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + iport = tport->iport; + rport->private = NULL; + + if (tport->timer_pending) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport: 0x%x canceling discovery timer\n", + tport->fcid); + /* + * The retry timer callback takes fnic_lock and dereferences + * this tport. Set del_timer_inprogress before dropping the + * lock so a callback that is already running observes teardown + * and exits. + * + * timer_delete_sync() then waits until no callback can still + * hold a reference before the remoteport-delete path completes + * or frees the target port. + */ + tport->del_timer_inprogress = 1; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + timer_delete_sync(&tport->retry_timer); + spin_lock_irqsave(&fnic->fnic_lock, flags); + tport->del_timer_inprogress = 0; + tport->timer_pending = 0; + } + + if (tport->flags & FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport %8x waiting on clean pending\n", + tport->fcid); + } + + while (tport->flags & FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING) { + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + msleep(2000); + spin_lock_irqsave(&fnic->fnic_lock, flags); + } + + list_del(&tport->links); + + if (tport->tport_del_done) { + tport->flags |= FNIC_TPORT_CAN_BE_FREED; + complete(tport->tport_del_done); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + kfree(tport); +} + +int nvfnic_create_queue(struct nvme_fc_local_port *lport, + unsigned int idx, u16 size, void **handle) +{ + struct fnic_iport_s *iport = (struct fnic_iport_s *)lport->private; + struct fnic *fnic = iport->fnic; + + FNIC_NVME_DBG(KERN_DEBUG, fnic, + "iport:0x%x queue:%d size:%d\n", iport->fcid, idx, size); + + if (idx > fnic->wq_copy_count) + return -EINVAL; + + if (idx == 0) { + /* Admin queue */ + *handle = &fnic->hw_copy_wq[0]; + } else { + /* IO queues */ + *handle = &fnic->hw_copy_wq[idx-1]; + } + + return 0; +} + +void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_req *lsreq) +{ + struct fnic_iport_s *iport = lport->private; + struct fnic *fnic = iport->fnic; + struct fnic_tport_s *tport; + struct nvfnic_ls_req *nvfnic_ls_req; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + uint16_t oxid; + int timeout; + int ret; + + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + + tport = (struct fnic_tport_s *) rport->private; + /* find the request */ + nvfnic_ls_req = lsreq->private; + + if (nvfnic_ls_req == NULL) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "0x%x null lsreq already scheduled for abort\n", + iport->fcid); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + if (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "0x%x lsreq 0x%x already scheduled for abort\n", + iport->fcid, nvfnic_ls_req->oxid); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + FNIC_NVME_DBG(KERN_INFO, fnic, + "0x%x lsreq 0x%x abts\n", + iport->fcid, nvfnic_ls_req->oxid); + + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_STARTED; + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + timer_delete_sync(&nvfnic_ls_req->ls_req_timer); + + spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags); + nvfnic_ls_req = lsreq->private; + + if ((nvfnic_ls_req == NULL) || + (nvfnic_ls_req->state == FNIC_LS_REQ_CMD_ABTS_PENDING)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "lsreq timeout raced with midlayer abort\n"); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + /* Basic validations of the state */ + if (!nvfnic_transport_ready(iport, tport)) { + /* If iport or tport offline, it will be handled from that event */ + oxid = nvfnic_ls_req->oxid; + lsreq->private = NULL; + list_del(&nvfnic_ls_req->list); + fdls_free_oxid(iport, oxid, &nvfnic_ls_req->oxid); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + lsreq->done(lsreq, -ENXIO); + FNIC_NVME_DBG(KERN_ERR, fnic, + "nvfnic_lsreq_abort transport not ready\n"); + return; + } + + /* Mark the state and flags */ + nvfnic_ls_req->state = FNIC_LS_REQ_CMD_ABTS_PENDING; + atomic64_inc(&fnic_stats->nvme_stats.nvme_ls_aborts); + oxid = nvfnic_ls_req->oxid; + + ret = fdls_send_ls_req_abts(iport, tport, oxid); + if (!ret) { + timeout = FNIC_LS_REQ_TMO_MSECS(lsreq->timeout); + mod_timer(&nvfnic_ls_req->ls_req_timer, + round_jiffies(jiffies + msecs_to_jiffies(timeout))); + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + if (!nvfnic_ls_req_cleanup(iport, lsreq, oxid)) { + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + return; + } + + spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags); + lsreq->done(lsreq, -EAGAIN); +} + +bool nvfnic_queue_abort_io_req(struct fnic *fnic, int tag, + u32 task_req, struct fnic_io_req *io_req) +{ + int idx; + unsigned long flags; + struct misc_stats *misc_stats = &fnic->fnic_stats.misc_stats; + + idx = io_req->wq - &fnic->hw_copy_wq[0]; + + atomic_inc(&fnic->in_flight); + + spin_lock_irqsave(&fnic->wq_copy_lock[idx], flags); + + if (vnic_wq_copy_desc_avail(io_req->wq) <= fnic->wq_copy_desc_low[idx]) + free_wq_copy_descs(fnic, io_req->wq, idx); + + if (!vnic_wq_copy_desc_avail(io_req->wq)) { + spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], flags); + atomic_dec(&fnic->in_flight); + FNIC_NVME_DBG(KERN_ERR, fnic, + "tag 0x%x failure: no descriptors\n", tag); + atomic64_inc(&misc_stats->abts_cpwq_alloc_failures); + return false; + } + fnic_queue_wq_copy_desc_itmf(io_req->wq, tag | FNIC_TAG_ABORT, + 0, task_req, tag, NULL, io_req->port_id, + fnic->config.ra_tov, fnic->config.ed_tov); + + atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs); + if (atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs) > + atomic64_read(&fnic->fnic_stats.fw_stats.max_fw_reqs)) + atomic64_set(&fnic->fnic_stats.fw_stats.max_fw_reqs, + atomic64_read(&fnic->fnic_stats.fw_stats.active_fw_reqs)); + + spin_unlock_irqrestore(&fnic->wq_copy_lock[idx], flags); + atomic_dec(&fnic->in_flight); + + return true; +} + +void nvfnic_fcpio_abort(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + void *hw_queue_handle, struct nvmefc_fcp_req *fcp_req) +{ + struct fnic_iport_s *iport = lport->private; + struct fnic *fnic = iport->fnic; + struct nvme_fc_cmd_iu *cmd_iu = fcp_req->cmdaddr; + struct fnic_io_req *io_req = (struct fnic_io_req *)fcp_req->private; + unsigned int tag = io_req->tag; + struct fnic_stats *fnic_stats = &fnic->fnic_stats; + struct abort_stats *abts_stats; + unsigned long flags = 0; + unsigned long abt_issued_time; + unsigned int task_req; + enum fnic_ioreq_state old_ioreq_state; + unsigned long num_ios_waitq, waitq_2sec, waitq_max_time; + + spin_lock_irqsave(&fnic->fnic_lock, flags); + + if (io_req->tag == FNIC_NVME_NO_FREE_TAG) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tag: (0x%x) tport_fcid: 0x%x\n", + io_req->tag, io_req->tport->fcid); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + if (io_req != nvfnic_find_io_req_by_tag(fnic, io_req->tag)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "cmd tag freed or not issued:0x%x sn:0x%08x\n", + io_req->tag, be32_to_cpu(cmd_iu->csn)); + num_ios_waitq = + atomic64_read(&fnic_stats->io_stats.nvme_num_ios_in_waitq); + waitq_2sec = + atomic64_read(&fnic_stats->io_stats.nvme_ios_in_waitq_3000_msec); + waitq_max_time = + atomic64_read(&fnic_stats->io_stats.nvme_ios_in_waitq_max_time); + FNIC_NVME_DBG(KERN_INFO, fnic, + "waitq:%ld waitq_2sec:%ld max_wait:%ld\n", + num_ios_waitq, waitq_2sec, waitq_max_time); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + if (io_req->cmd_state == FNIC_IOREQ_CMD_COMPLETE) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "IO already completed before abort: 0x%x\n", tag); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + if ((io_req->cmd_state == FNIC_IOREQ_ABTS_PENDING) || + (io_req->cmd_state == FNIC_DEV_RST_TERM_ISSUED)) { + FNIC_NVME_DBG(KERN_INFO, fnic, "abort already pending %d\n", + tag); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + if (io_req->cmd_state != FNIC_IOREQ_CMD_PENDING) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "io_req completed or aborted for tag:0x%x\n", + tag); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + + FNIC_NVME_DBG(KERN_INFO, fnic, "in abort cmd_sn:%08x %llx tag: 0x%x\n", + be32_to_cpu(cmd_iu->csn), + le64_to_cpu(cmd_iu->sqe.rw.slba), io_req->tag); + + if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "abort tag:0x%x returned during fw reset\n", + io_req->tag); + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + return; + } + atomic_inc(&fnic->in_flight); + + if (fdls_tport_is_offline(io_req->tport) || + (io_req->cmd_state == FNIC_IOREQ_RESET_TERM)) { + task_req = FCPIO_ITMF_ABT_TASK_TERM; + } else { + task_req = FCPIO_ITMF_ABT_TASK; + } + + abts_stats = &fnic->fnic_stats.abts_stats; + atomic64_inc(&abts_stats->aborts); + + abt_issued_time = jiffies_to_msecs(jiffies - io_req->start_time); + if (abt_issued_time <= 6000) + atomic64_inc(&abts_stats->abort_issued_btw_0_to_6_sec); + else if (abt_issued_time > 6000 && abt_issued_time <= 20000) + atomic64_inc(&abts_stats->abort_issued_btw_6_to_20_sec); + else if (abt_issued_time > 20000 && abt_issued_time <= 30000) + atomic64_inc(&abts_stats->abort_issued_btw_20_to_30_sec); + else if (abt_issued_time > 30000 && abt_issued_time <= 40000) + atomic64_inc(&abts_stats->abort_issued_btw_30_to_40_sec); + else if (abt_issued_time > 40000 && abt_issued_time <= 50000) + atomic64_inc(&abts_stats->abort_issued_btw_40_to_50_sec); + else if (abt_issued_time > 50000 && abt_issued_time <= 60000) + atomic64_inc(&abts_stats->abort_issued_btw_50_to_60_sec); + else + atomic64_inc(&abts_stats->abort_issued_greater_than_60_sec); + + old_ioreq_state = io_req->cmd_state; + io_req->cmd_state = FNIC_IOREQ_ABTS_PENDING; + io_req->abts_state = FCPIO_INVALID_CODE; + + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + + if (!nvfnic_queue_abort_io_req(fnic, io_req->tag, task_req, io_req)) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "Abort io req queue failed\n"); + spin_lock_irqsave(&fnic->fnic_lock, flags); + io_req->cmd_state = old_ioreq_state; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + } + atomic_dec(&fnic->in_flight); +} + +struct +nvme_fc_port_template nvfnic_port = { + .localport_delete = nvfnic_local_port_delete, + .remoteport_delete = nvfnic_remote_port_delete, + .create_queue = nvfnic_create_queue, + .delete_queue = NULL, + .ls_req = nvfnic_ls_req_send, + .ls_abort = nvfnic_ls_req_abort, + .fcp_io = nvfnic_fcpio_send, + .fcp_abort = nvfnic_fcpio_abort, + .max_hw_queues = 1, + .max_sgl_segments = 256, + .max_dif_sgl_segments = 64, + .dma_boundary = 0xFFFFFFFF, + .local_priv_sz = sizeof(struct fnic_iport_s *), + .remote_priv_sz = sizeof(struct fnic_tport_s *), + .lsrqst_priv_sz = sizeof(struct nvfnic_ls_req), + .fcprqst_priv_sz = sizeof(struct fnic_io_req), +}; + +void nvfnic_flush_nvme_io_list(struct fnic *fnic) +{ + queue_work(fnic_cmpl_queue, &fnic->nvme_io_cmpl_work); + flush_work(&fnic->nvme_io_cmpl_work); +} + +void nvfnic_nvme_iodone_work(struct work_struct *work) +{ + struct fnic *fnic = container_of(work, struct fnic, nvme_io_cmpl_work); + struct llist_node *llnode; + struct fnic_io_req *io_req, *tmp; + + llnode = llist_del_all(&fnic->nvme_io_event_llist); + llist_for_each_entry_safe(io_req, tmp, llnode, nvfnic_io_cmpl) { + atomic_dec(&fnic->nvme_io_event_queued); + atomic64_dec(&fnic->fnic_stats.io_stats.nvme_num_ios_in_waitq); + io_req->fcp_req->done(io_req->fcp_req); + } +} + +void nvfnic_exch_reset(struct fnic_iport_s *iport, struct fnic_tport_s *tport) +{ + FNIC_NVME_DBG(KERN_DEBUG, iport->fnic, + "0x%x: Exchange reset scheduled for tport: 0x%x\n", + iport->fcid, tport->fcid); + + nvfnic_terminate_tport_ls_reqs(iport->fnic, tport); + nvfnic_terminate_tport_ios(iport->fnic, tport); +} + +void nvfnic_delete_tport(struct fnic_iport_s *iport, + struct fnic_tport_s *tport, + unsigned long flags) +{ + struct fnic *fnic = iport->fnic; + int ret; + unsigned int time_wait = FNIC_NVME_TPORT_REMOVE_WAIT; + unsigned int time_remain; + DECLARE_COMPLETION_ONSTACK(tm_done); + unsigned int fcid; + int count = 0; + + if (!tport) + return; + + fcid = tport->fcid; + fdls_set_tport_state(tport, FDLS_TGT_STATE_OFFLINE); + + FNIC_NVME_DBG(KERN_DEBUG, fnic, + "0x%x: scheduled deletion for tport: 0x%x\n", + iport->fcid, tport->fcid); + + if (!(tport->flags & FNIC_FDLS_NVME_REGISTERED)) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "0x%x: tport: 0x%x not registered. Freeing\n", + iport->fcid, tport->fcid); + list_del(&tport->links); + kfree(tport); + return; + } + + tport->tport_del_done = &tm_done; + + tport->flags |= FNIC_FDLS_TPORT_DELETED; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + ret = nvme_fc_unregister_remoteport(tport->nv_rport); + if (ret) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x unregister failed %d\n", + tport->fcid, ret); + + nvfnic_terminate_tport_ls_reqs(fnic, tport); + + spin_lock_irqsave(&fnic->fnic_lock, flags); + tport->tport_del_done = NULL; + if (tport->nv_rport && tport->nv_rport->private == tport) + tport->nv_rport->private = NULL; + list_del(&tport->links); + kfree(tport); + return; + } + time_remain = wait_for_completion_timeout(tport->tport_del_done, + msecs_to_jiffies(time_wait)); + + FNIC_NVME_DBG(KERN_DEBUG, fnic, + "tport: 0x%x wait for deletion done\n", + tport->fcid); + + spin_lock_irqsave(&fnic->fnic_lock, flags); + tport->tport_del_done = NULL; + + if (!time_remain) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "tport: 0x%x nvme midlayer completion timed out\n", + tport->fcid); + + if (tport->flags & FNIC_TPORT_CAN_BE_FREED) { + kfree(tport); + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport: 0x%x delete complete\n", fcid); + return; + } + + tport->flags |= FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + nvfnic_cleanup_tport_io(fnic, tport); + spin_lock_irqsave(&fnic->fnic_lock, flags); + tport->flags &= ~FNIC_FDLS_NVME_TPORT_CLEANUP_PENDING; + return; + } + while (!(tport->flags & FNIC_TPORT_CAN_BE_FREED) && + (count < FNIC_TPORT_CLEANUP_WAIT_COUNT)) { + count++; + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + msleep(2000); + spin_lock_irqsave(&fnic->fnic_lock, flags); + } + if (tport->flags & FNIC_TPORT_CAN_BE_FREED) + kfree(tport); + + FNIC_NVME_DBG(KERN_INFO, fnic, + "tport: 0x%x delete complete\n", fcid); +} + +int nvfnic_add_tport(struct fnic *fnic, struct fnic_tport_s *tport, + unsigned long flags) +{ + struct fnic_iport_s *iport = &fnic->iport; + struct nvme_fc_port_info pinfo; + int ret = 0; + + FNIC_NVME_DBG(KERN_INFO, fnic, + "Adding tport to nvme wwpn: 0x%llx\n", + tport->wwpn); + + memset(&pinfo, 0, sizeof(struct nvme_fc_port_info)); + + pinfo.port_name = tport->wwpn; + pinfo.node_name = tport->wwnn; + pinfo.port_role = FC_PORT_ROLE_NVME_DISCOVERY | FC_PORT_ROLE_NVME_TARGET; + pinfo.port_id = tport->fcid; + pinfo.dev_loss_tmo = nvme_dev_loss_tmo; + + spin_unlock_irqrestore(&fnic->fnic_lock, flags); + ret = nvme_fc_register_remoteport(iport->nv_lport, &pinfo, + &tport->nv_rport); + spin_lock_irqsave(&fnic->fnic_lock, flags); + if (ret) { + FNIC_NVME_DBG(KERN_INFO, fnic, + "Failed to register tport wwpn: 0x%llx ret: %d\n", + tport->wwpn, ret); + return ret; + } + tport->flags |= FNIC_FDLS_NVME_REGISTERED; + tport->nv_rport->private = tport; + + snprintf(tport->str_wwpn, sizeof(tport->str_wwpn), "0x%llx", tport->wwpn); + snprintf(tport->str_wwnn, sizeof(tport->str_wwnn), "0x%llx", tport->wwnn); + return ret; +} + +int nvfnic_add_lport(struct fnic *fnic) +{ + struct nvme_fc_port_info pinfo; + struct fnic_iport_s *iport = &fnic->iport; + int ret = 0; + + FNIC_NVME_DBG(KERN_INFO, fnic, + "Adding lport nvme wwpn: 0x%llx\n", + iport->wwpn); + + pinfo.node_name = iport->wwnn; + pinfo.port_name = iport->wwpn; + pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR; + pinfo.port_id = iport->fcid; + + nvfnic_reset_fcpio_tag_pool(iport); + + if (!(iport->flags & FNIC_LPORT_NVME_REGISTERED)) { + iport->nv_tmpl = kzalloc_obj(struct nvme_fc_port_template, GFP_ATOMIC); + if (!iport->nv_tmpl) { + FNIC_FCS_DBG(KERN_INFO, fnic, + "iport:0x%x NVMe tmpl alloc failed\n", + iport->fcid); + return -ENOMEM; + } + memcpy(iport->nv_tmpl, &nvfnic_port, + sizeof(struct nvme_fc_port_template)); + iport->nv_tmpl->max_hw_queues = fnic->wq_copy_count; + + ret = nvme_fc_register_localport(&pinfo, iport->nv_tmpl, + &fnic->pdev->dev, &iport->nv_lport); + if (ret) { + FNIC_NVME_DBG(KERN_ERR, fnic, + "Failed to add wwpn: 0x%llx ret: %d\n", + iport->wwpn, ret); + kfree(iport->nv_tmpl); + return ret; + } + iport->flags |= FNIC_LPORT_NVME_REGISTERED; + iport->nv_lport->private = iport; + } + + sprintf(iport->str_wwpn, "0x%llx", iport->wwpn); + sprintf(iport->str_wwnn, "0x%llx", iport->wwnn); + + FNIC_NVME_DBG(KERN_INFO, fnic, + "Successfully added lport wwpn: 0x%llx\n", + iport->wwpn); + return 0; +} + +#endif diff --git a/drivers/scsi/fnic/fnic_nvme.h b/drivers/scsi/fnic/fnic_nvme.h new file mode 100644 index 000000000000..c05908b68e36 --- /dev/null +++ b/drivers/scsi/fnic/fnic_nvme.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2008 Cisco Systems, Inc. All rights reserved. + * Copyright 2007 Nuova Systems, Inc. All rights reserved. + */ +#ifndef _FNIC_NVME_H +#define _FNIC_NVME_H + +#include "fdls_fc.h" +#include "fnic_fdls.h" + +#define FNIC_NVME_TPORT_REMOVE_WAIT (5 * 1000) +#define FNIC_NVME_TPORT_LIST_EMPTY_WAIT (FNIC_NVME_TPORT_REMOVE_WAIT * 2) +#define FNIC_TPORT_CLEANUP_WAIT_COUNT 8 +#define FNIC_NVME_LPORT_REMOVE_WAIT (2 * 60 * 1000) + +#define FNIC_LS_REQ_FLAGS_NONE 0x0 +#define FNIC_LS_REQ_FLAGS_ABORTED 0x1 +#define FNIC_LS_REQ_FLAGS_DONE 0x2 +#define FNIC_LS_REQ_ABORT_COMPLETED 0x4 +#define FNIC_STATUS_LS_REQ_ABORTED 0x1 + +#define FNIC_LS_REQ_MIN_TMO_SECS (2) +#define FNIC_LS_REQ_MAX_TMO_SECS (5) +#define FNIC_NVME_ADMIN_IO_TIMEOUT 30000 /* mSec */ +#define FNIC_NVME_NO_FREE_TAG (0xFFFF) + +#define FNIC_LS_REQ_TMO_MSECS(tmo) (((tmo >= FNIC_LS_REQ_MIN_TMO_SECS) && \ + (tmo <= FNIC_LS_REQ_MAX_TMO_SECS)) ? \ + (tmo * 1000) : (FNIC_LS_REQ_MIN_TMO_SECS * 1000)) + +#define IS_ADMIN_IO(_io_req) \ + (NVME_CMD_FLAGS(_io_req) & FNIC_NVME_ADMIN_IO_TIMER_PENDING) + +extern unsigned int nvme_max_ios_to_process; +extern unsigned int nvme_dev_loss_tmo; +extern spinlock_t fnic_list_lock; + +enum nvfnic_lsreq_state_e { + FNIC_LS_REQ_CMD_INIT = 0, + FNIC_LS_REQ_CMD_PENDING, + FNIC_LS_REQ_CMD_ABTS_PENDING, + FNIC_LS_REQ_CMD_COMPLETE, + FNIC_LS_REQ_ABTS_COMPLETE, + FNIC_LS_REQ_CMD_ABTS_STARTED, +}; + +struct fnic_nvme_tag { + struct list_head free_list; + int tag_id; +}; + +struct nvfnic_ls_req { + struct list_head list; + struct nvmefc_ls_req *ls_req; + uint16_t oxid; + struct timer_list ls_req_timer; + struct fnic *fnic; + struct fnic_tport_s *tport; + int state; + unsigned int flags; +}; + +#if IS_REACHABLE(CONFIG_NVME_FC) +int nvfnic_nvme_io_done_handler(void *arg); +struct fnic_io_req *nvfnic_find_io_req_by_tag(struct fnic *fnic, uint16_t tag); +void nvfnic_reset_fcpio_tag_pool(struct fnic_iport_s *iport); +int nvfnic_add_lport(struct fnic *fnic); +void nvfnic_fcpio_abort(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + void *hw_queue_handle, struct nvmefc_fcp_req *fcp_req); +void nvfnic_remote_port_delete(struct nvme_fc_remote_port *rport); +void nvfnic_local_port_delete(struct nvme_fc_local_port *lport); +int nvfnic_dma_map_sgl(struct fnic *fnic, struct fnic_io_req *io_req, + int sg_count); +void nvfnic_dma_unmap_sgl(struct fnic *fnic, struct fnic_io_req *io_req); +int nvfnic_get_sg_count(struct fnic_io_req *io_req); +void nvfnic_release_nvme_ioreq_buf(struct fnic_iport_s *iport, + struct fnic_io_req *io_req); +void nvfnic_dump_nvcmd(struct fnic_io_req *io_req, uint8_t flags); +bool _cleanup_tport_io(struct sbitmap *map, unsigned int tag, void *data); +void nvfnic_flush_nvme_io_list(struct fnic *fnic); +void nvfnic_fcpio_cmpl(struct fnic_io_req *io_req); +bool nvfnic_transport_ready(struct fnic_iport_s *iport, + struct fnic_tport_s *tport); +int nvfnic_alloc_fcpio_tag(struct fnic_iport_s *iport, + struct fnic_io_req *io_req); +int nvfnic_queuecommand(struct fnic_io_req *io_req); +void nvfnic_free_fcpio_tag(struct fnic_iport_s *iport, + struct fnic_io_req *io_req); +void nvfnic_delete_lport(struct fnic_iport_s *iport); +int nvfnic_add_tport(struct fnic *fnic, struct fnic_tport_s *tport, + unsigned long flags); +void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic); +void nvfnic_delete_tport_work(struct work_struct *work); +void nvfnic_delete_tport(struct fnic_iport_s *iport, + struct fnic_tport_s *tport, unsigned long flags); +void nvfnic_fcpio_nvme_fast_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc); +void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport, + struct fc_frame_header *fchdr, int len); +void nvfnic_process_ls_abts_rsp(struct fnic_iport_s *iport, + struct fc_frame_header *fchdr); +void nvfnic_admin_io_timeout(struct timer_list *t); +void nvfnic_fcpio_nvme_itmf_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc); +void nvfnic_nvme_zero_devloss_tports(struct fnic *fnic); +bool nvfnic_queue_abort_io_req(struct fnic *fnic, int tag, u32 task_req, + struct fnic_io_req *io_req); +void nvfnic_ls_req_abort(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_req *lsreq); +int nvfnic_create_queue(struct nvme_fc_local_port *lport, unsigned int idx, + u16 size, void **handle); +int nvfnic_ls_req_send(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_req *ls_req); +void nvfnic_ls_req_timeout(struct timer_list *t); +uint16_t nvfnic_alloc_ls_req_oxid(struct fnic_iport_s *iport); +struct nvfnic_ls_req *nvfnic_find_ls_req(struct fnic_tport_s *tport, + uint16_t oxid); +void nvfnic_terminate_tport_ios(struct fnic *fnic, struct fnic_tport_s *tport); +bool _terminate_tport_ios(struct sbitmap *map, unsigned int tag, void *data); +bool _cleanup_all_nvme_io(struct sbitmap *map, unsigned int tag, void *data); +void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic); +int nvfnic_fcpio_send(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, void *hw_queue_handle, + struct nvmefc_fcp_req *fcp_req); +void nvfnic_fcpio_ersp_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc, int sw_flag); +void nvfnic_terminate_tport_ls_reqs(struct fnic *fnic, + struct fnic_tport_s *tport); +void nvfnic_terminate_tport_admin_ios(struct fnic *fnic, + struct fnic_tport_s *tport); +void nvfnic_cleanup_tport_io(struct fnic *fnic, struct fnic_tport_s *tport); +void nvfnic_nvme_unload(struct fnic *fnic); +int nvfnic_get_nvmef_info(struct fnic *fnic, struct fnic_nvmef_info *info); +void nvfnic_exch_reset(struct fnic_iport_s *iport, struct fnic_tport_s *tport); +void nvfnic_nvme_iodone_work(struct work_struct *work); +#else +static inline int nvfnic_add_lport(struct fnic *fnic) +{ + return -EOPNOTSUPP; +} + +static inline void nvfnic_nvme_unload(struct fnic *fnic) +{ +} + +static inline void nvfnic_flush_nvme_io_list(struct fnic *fnic) +{ +} + +static inline void nvfnic_nvme_iodone_work(struct work_struct *work) +{ +} + +static inline void nvfnic_exch_reset(struct fnic_iport_s *iport, + struct fnic_tport_s *tport) +{ +} + +static inline void nvfnic_process_ls_abts_rsp(struct fnic_iport_s *iport, + struct fc_frame_header *fchdr) +{ +} + +static inline void nvfnic_ls_rsp_recv(struct fnic_iport_s *iport, + struct fc_frame_header *fchdr, int len) +{ +} + +static inline int nvfnic_add_tport(struct fnic *fnic, + struct fnic_tport_s *tport, + unsigned long flags) +{ + return 0; +} + +static inline void nvfnic_delete_tport(struct fnic_iport_s *iport, + struct fnic_tport_s *tport, + unsigned long flags) +{ +} + +static inline void nvfnic_cleanup_all_nvme_ios(struct fnic *fnic) +{ +} + +static inline void nvfnic_fcpio_nvme_fast_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc) +{ +} + +static inline void nvfnic_fcpio_ersp_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc, + int sw_flag) +{ +} + +static inline void nvfnic_fcpio_nvme_itmf_cmpl_handler(struct fnic *fnic, + struct fcpio_fw_req *desc) +{ +} + +static inline int nvfnic_get_nvmef_info(struct fnic *fnic, + struct fnic_nvmef_info *info) +{ + return 0; +} +#endif + +extern const char *fnic_fcpio_status_to_str(unsigned int status); +#endif /* _FNIC_NVME_H */ diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c index 9801e5fbb0dd..d75d7046c7f6 100644 --- a/drivers/scsi/fnic/fnic_res.c +++ b/drivers/scsi/fnic/fnic_res.c @@ -22,6 +22,7 @@ int fnic_get_vnic_config(struct fnic *fnic) { struct vnic_fc_config *c = &fnic->config; + u32 role; int err; #define GET_CONFIG(m) \ @@ -56,11 +57,35 @@ int fnic_get_vnic_config(struct fnic *fnic) GET_CONFIG(port_down_io_retries); GET_CONFIG(luns_per_tgt); GET_CONFIG(intr_mode); + GET_CONFIG(lun_queue_depth); + GET_CONFIG(io_timeout_retry); GET_CONFIG(wq_copy_count); - if ((c->flags & (VFCF_FC_INITIATOR)) == 0) { - dev_info(&fnic->pdev->dev, "vNIC role not defined (def role: FC Init)\n"); + role = c->flags & FNIC_ROLE_CONFIG_MASK; + switch (role) { + case 0: + dev_info(&fnic->pdev->dev, + "vNIC role not defined (def role: FC Init)\n"); c->flags |= VFCF_FC_INITIATOR; + break; + case VFCF_FC_INITIATOR: + case VFCF_FC_NVME_INITIATOR: + break; + case VFCF_FC_TARGET: + dev_info(&fnic->pdev->dev, + "vNIC role is FC Target (unsupported)\n"); + break; + case VFCF_FC_NVME_TARGET: + dev_info(&fnic->pdev->dev, + "vNIC role is FC-NVMe Target (unsupported)\n"); + break; + default: + dev_info(&fnic->pdev->dev, + "vNIC role not supported (0x%x), defaulting to FC Init\n", + role); + c->flags &= ~FNIC_ROLE_CONFIG_MASK; + c->flags |= VFCF_FC_INITIATOR; + break; } c->wq_enet_desc_count = @@ -163,6 +188,8 @@ int fnic_get_vnic_config(struct fnic *fnic) c->port_down_io_retries, c->port_down_timeout); dev_info(&fnic->pdev->dev, "fNIC wq_copy_count: %d\n", c->wq_copy_count); dev_info(&fnic->pdev->dev, "fNIC intr mode: %d\n", c->intr_mode); + dev_info(&fnic->pdev->dev, "fNIC role flags: 0x%x\n", + (c->flags & FNIC_ROLE_CONFIG_MASK)); return 0; } diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h index 92a2fcfd3ea9..12a2a356dc13 100644 --- a/drivers/scsi/fnic/fnic_res.h +++ b/drivers/scsi/fnic/fnic_res.h @@ -126,7 +126,8 @@ static inline void fnic_queue_wq_copy_desc_itmf(struct vnic_wq_copy *wq, desc->u.itmf.tm_req = tm_req; /* SCSI Task Management request */ desc->u.itmf.t_tag = tm_id; /* tag of fcpio to be aborted */ desc->u.itmf._resvd = 0; - memcpy(desc->u.itmf.lun, lun, LUN_ADDRESS); /* LUN address */ + if (lun) + memcpy(desc->u.itmf.lun, lun, LUN_ADDRESS); /* LUN address */ desc->u.itmf._resvd1 = 0; hton24(desc->u.itmf.d_id, d_id); /* FC vNIC only: Target D_ID */ desc->u.itmf.r_a_tov = r_a_tov; /* FC vNIC only: R_A_TOV in msec */ @@ -222,6 +223,35 @@ static inline void fnic_queue_rq_desc(struct vnic_rq *rq, vnic_rq_post(rq, os_buf, 0, dma_addr, len); } +static inline void fnic_queue_wq_copy_desc_nvme_io(struct vnic_wq_copy *wq, + u32 req_id, + u8 spl_flags, + u32 sgl_cnt, + u64 sgl_addr, + u8 flags, u8 *nvme_cmd_iu, + u16 cmd_len, + u32 data_len, + u32 d_id, u32 mss, + u32 ratov, u32 edtov) +{ + struct fcpio_host_req *desc = vnic_wq_copy_next_desc(wq); + + desc->hdr.type = FCPIO_NVME_CMD; /* enum fcpio_type */ + desc->hdr.status = 0; /* header status entry */ + desc->hdr._resvd = 0; /* reserved */ + desc->hdr.tag.u.req_id = req_id; /* id for this request */ + desc->u.nvcmnd.sgl_cnt = sgl_cnt; /* scatter-gather list count */ + desc->u.nvcmnd.sgl_addr = sgl_addr; /* scatter-gather list addr */ + desc->u.nvcmnd._resvd1 = 0; /* reserved: should be 0 */ + desc->u.nvcmnd.flags = flags; /* command flags */ + memset(desc->u.nvcmnd.nvme_cmnd, 0, NVME_CMD_SZ); + memcpy(desc->u.nvcmnd.nvme_cmnd, nvme_cmd_iu, cmd_len); /* SCSI CDB */ + desc->u.nvcmnd.cmd_len = cmd_len; + desc->u.nvcmnd.data_len = data_len; /* length of data expected */ + hton24(desc->u.nvcmnd.d_id, d_id); /* FC vNIC only: Target D_ID */ + + vnic_wq_copy_post(wq); +} struct fnic; diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 6ee3c559e129..9607684bc610 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -96,7 +96,7 @@ static const char *fnic_ioreq_state_to_str(unsigned int state) return fnic_ioreq_state_str[state]; } -static const char *fnic_fcpio_status_to_str(unsigned int status) +const char *fnic_fcpio_status_to_str(unsigned int status) { if (status >= ARRAY_SIZE(fcpio_status_str) || !fcpio_status_str[status]) return "unknown"; @@ -145,10 +145,11 @@ unsigned int fnic_count_ioreqs(struct fnic *fnic, u32 portid) { unsigned int count = 0; - fnic_scsi_io_iter(fnic, fnic_count_portid_ioreqs_iter, - &portid, &count); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_scsi_io_iter(fnic, fnic_count_portid_ioreqs_iter, + &portid, &count); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "portid = 0x%x count = %u\n", portid, count); return count; } @@ -180,13 +181,13 @@ fnic_count_lun_ioreqs(struct fnic *fnic, struct scsi_device *scsi_device) fnic_scsi_io_iter(fnic, fnic_count_lun_ioreqs_iter, scsi_device, &count); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "lun = %p count = %u\n", scsi_device, count); return count; } /* Free up Copy Wq descriptors. Called with copy_wq lock held */ -static int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq, unsigned int hwq) +int free_wq_copy_descs(struct fnic *fnic, struct vnic_wq_copy *wq, unsigned int hwq) { /* if no Ack received from firmware, then nothing to clean */ if (!fnic->fw_ack_recd[hwq]) @@ -268,7 +269,7 @@ int fnic_fw_reset_handler(struct fnic *fnic) if (!vnic_wq_copy_desc_avail(wq)) ret = -EAGAIN; else { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "ioreq_count: %u\n", ioreq_count); fnic_queue_wq_copy_desc_fw_reset(wq, SCSI_NO_TAG); atomic64_inc(&fnic->fnic_stats.fw_stats.active_fw_reqs); @@ -283,11 +284,11 @@ int fnic_fw_reset_handler(struct fnic *fnic) if (!ret) { atomic64_inc(&fnic->fnic_stats.reset_stats.fw_resets); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "Issued fw reset\n"); } else { fnic_clear_state_flags(fnic, FNIC_FLAGS_FWRESET); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Failed to issue fw reset\n"); } @@ -326,13 +327,13 @@ int fnic_flogi_reg_handler(struct fnic *fnic, u32 fc_id) fc_id, gw_mac, fnic->iport.fpma, iport->r_a_tov, iport->e_d_tov); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "FLOGI FIP reg issued fcid: 0x%x src %p dest %p\n", fc_id, fnic->iport.fpma, gw_mac); } else { fnic_queue_wq_copy_desc_flogi_reg(wq, SCSI_NO_TAG, format, fc_id, gw_mac); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "FLOGI reg issued fcid 0x%x dest %p\n", fc_id, gw_mac); } @@ -413,7 +414,7 @@ static inline int fnic_queue_wq_copy_desc(struct fnic *fnic, free_wq_copy_descs(fnic, wq, hwq); if (unlikely(!vnic_wq_copy_desc_avail(wq))) { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "fnic_queue_wq_copy_desc failure - no descriptors\n"); atomic64_inc(&misc_stats->io_cpwq_alloc_failures); return SCSI_MLQUEUE_HOST_BUSY; @@ -478,7 +479,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, rport = starget_to_rport(scsi_target(sc->device)); if (!rport) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "returning DID_NO_CONNECT for IO as rport is NULL\n"); sc->result = DID_NO_CONNECT << 16; done(sc); @@ -487,7 +488,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, ret = fc_remote_port_chkready(rport); if (ret) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "rport is not ready\n"); atomic64_inc(&fnic_stats->misc_stats.tport_not_ready); sc->result = ret; @@ -501,7 +502,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, if (iport->state != FNIC_IPORT_STATE_READY) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "returning DID_NO_CONNECT for IO as iport state: %d\n", iport->state); sc->result = DID_NO_CONNECT << 16; @@ -515,13 +516,13 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, rdd_data = rport->dd_data; tport = rdd_data->tport; if (!tport || (rdd_data->iport != iport)) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "dd_data not yet set in SCSI for rport portid: 0x%x\n", rport->port_id); tport = fnic_find_tport_by_fcid(iport, rport->port_id); if (!tport) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "returning DID_BUS_BUSY for IO as tport not found for: 0x%x\n", rport->port_id); sc->result = DID_BUS_BUSY << 16; @@ -544,7 +545,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, if ((tport->state != FDLS_TGT_STATE_READY) && (tport->state != FDLS_TGT_STATE_ADISC)) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "returning DID_NO_CONNECT for IO as tport state: %d\n", tport->state); sc->result = DID_NO_CONNECT << 16; @@ -564,7 +565,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_FWRESET))) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "fnic flags FW reset: 0x%lx. Returning SCSI_MLQUEUE_HOST_BUSY\n", fnic->state_flags); return SCSI_MLQUEUE_HOST_BUSY; @@ -704,7 +705,7 @@ enum scsi_qc_status fnic_queuecommand(struct Scsi_Host *shost, atomic_dec(&tport->in_flight); if (lun0_delay) { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "LUN0 delay\n"); mdelay(LUN0_DELAY_TIME); } @@ -732,7 +733,10 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, atomic64_inc(&reset_stats->fw_reset_completions); /* Clean up all outstanding io requests */ - fnic_cleanup_io(fnic, SCSI_NO_TAG); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_cleanup_io(fnic, SCSI_NO_TAG); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_cleanup_all_nvme_ios(fnic); atomic64_set(&fnic->fnic_stats.fw_stats.active_fw_reqs, 0); atomic64_set(&fnic->fnic_stats.io_stats.active_ios, 0); @@ -744,12 +748,12 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, if (fnic->state == FNIC_IN_FC_TRANS_ETH_MODE) { /* Check status of reset completion */ if (!hdr_status) { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "reset cmpl success\n"); /* Ready to send flogi out */ fnic->state = FNIC_IN_ETH_MODE; } else { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "reset failed with header status: %s\n", fnic_fcpio_status_to_str(hdr_status)); @@ -758,7 +762,7 @@ static int fnic_fcpio_fw_reset_cmpl_handler(struct fnic *fnic, ret = -1; } } else { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Unexpected state while processing reset completion: %s\n", fnic_state_to_str(fnic->state)); atomic64_inc(&reset_stats->fw_reset_failures); @@ -810,19 +814,18 @@ static int fnic_fcpio_flogi_reg_cmpl_handler(struct fnic *fnic, /* Check flogi registration completion status */ if (!hdr_status) { - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "FLOGI reg succeeded\n"); fnic->state = FNIC_IN_FC_MODE; } else { - FNIC_SCSI_DBG(KERN_DEBUG, - fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "fnic flogi reg failed: %s\n", fnic_fcpio_status_to_str(hdr_status)); fnic->state = FNIC_IN_ETH_MODE; ret = -1; } } else { - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Unexpected fnic state %s while" " processing flogi reg completion\n", fnic_state_to_str(fnic->state)); @@ -895,7 +898,7 @@ static inline void fnic_fcpio_ack_handler(struct fnic *fnic, spin_unlock_irqrestore(&fnic->wq_copy_lock[wq_index], flags); FNIC_TRACE(fnic_fcpio_ack_handler, - fnic->host->host_no, 0, 0, ox_id_tag[2], ox_id_tag[3], + fnic->fnic_num, 0, 0, ox_id_tag[2], ox_id_tag[3], ox_id_tag[4], ox_id_tag[5]); } @@ -933,19 +936,19 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind hwq = blk_mq_unique_tag_to_hwq(mqtag); if (hwq != cq_index) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ", hwq, mqtag, tag, cq_index); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hdr status: %s icmnd completion on the wrong queue\n", fnic_fcpio_status_to_str(hdr_status)); } if (tag >= fnic->fnic_max_tag_id) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ", hwq, mqtag, tag, cq_index); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hdr status: %s Out of range tag\n", fnic_fcpio_status_to_str(hdr_status)); return; @@ -957,7 +960,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind if (!sc) { atomic64_inc(&fnic_stats->io_stats.sc_null); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "icmnd_cmpl sc is null - " "hdr status = %s tag = 0x%x desc = 0x%p\n", fnic_fcpio_status_to_str(hdr_status), id, desc); @@ -985,7 +988,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind atomic64_inc(&fnic_stats->io_stats.ioreq_null); fnic_priv(sc)->flags |= FNIC_IO_REQ_NULL; spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "icmnd_cmpl io_req is null - " "hdr status = %s tag = 0x%x sc 0x%p\n", fnic_fcpio_status_to_str(hdr_status), id, sc); @@ -1012,7 +1015,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind if(FCPIO_ABORTED == hdr_status) fnic_priv(sc)->flags |= FNIC_IO_ABORTED; - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "icmnd_cmpl abts pending " "hdr status = %s tag = 0x%x sc = 0x%p " "scsi_status = %x residual = %d\n", @@ -1044,7 +1047,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind if (icmnd_cmpl->scsi_status == SAM_STAT_TASK_SET_FULL) atomic64_inc(&fnic_stats->misc_stats.queue_fulls); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "xfer_len: %llu", xfer_len); break; @@ -1107,7 +1110,7 @@ static void fnic_fcpio_icmnd_cmpl_handler(struct fnic *fnic, unsigned int cq_ind if (hdr_status != FCPIO_SUCCESS) { atomic64_inc(&fnic_stats->io_stats.io_failures); - shost_printk(KERN_ERR, fnic->host, "hdr status = %s\n", + fnic_printk(KERN_ERR, fnic, "hdr status = %s\n", fnic_fcpio_status_to_str(hdr_status)); } @@ -1200,27 +1203,27 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde hwq = blk_mq_unique_tag_to_hwq(id & FNIC_TAG_MASK); if (hwq != cq_index) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ", hwq, mqtag, tag, cq_index); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hdr status: %s ITMF completion on the wrong queue\n", fnic_fcpio_status_to_str(hdr_status)); } if (tag > fnic->fnic_max_tag_id) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ", hwq, mqtag, tag, cq_index); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hdr status: %s Tag out of range\n", fnic_fcpio_status_to_str(hdr_status)); return; } else if ((tag == fnic->fnic_max_tag_id) && !(id & FNIC_TAG_DEV_RST)) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x cq index: %d ", hwq, mqtag, tag, cq_index); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hdr status: %s Tag out of range\n", fnic_fcpio_status_to_str(hdr_status)); return; @@ -1243,7 +1246,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde if (!sc) { atomic64_inc(&fnic_stats->io_stats.sc_null); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "itmf_cmpl sc is null - hdr status = %s tag = 0x%x\n", fnic_fcpio_status_to_str(hdr_status), tag); return; @@ -1255,7 +1258,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde atomic64_inc(&fnic_stats->io_stats.ioreq_null); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); fnic_priv(sc)->flags |= FNIC_IO_ABT_TERM_REQ_NULL; - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "itmf_cmpl io_req is null - " "hdr status = %s tag = 0x%x sc 0x%p\n", fnic_fcpio_status_to_str(hdr_status), tag, sc); @@ -1266,7 +1269,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde if ((id & FNIC_TAG_ABORT) && (id & FNIC_TAG_DEV_RST)) { /* Abort and terminate completion of device reset req */ /* REVISIT : Add asserts about various flags */ - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Abt/term completion received\n", hwq, mqtag, tag, fnic_fcpio_status_to_str(hdr_status)); @@ -1278,7 +1281,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); } else if (id & FNIC_TAG_ABORT) { /* Completion of abort cmd */ - shost_printk(KERN_DEBUG, fnic->host, + fnic_printk(KERN_DEBUG, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x Abort header status: %s\n", hwq, mqtag, tag, fnic_fcpio_status_to_str(hdr_status)); @@ -1293,7 +1296,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde &term_stats->terminate_fw_timeouts); break; case FCPIO_ITMF_REJECTED: - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "abort reject recd. id %d\n", (int)(id & FNIC_TAG_MASK)); break; @@ -1328,7 +1331,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde if (!(fnic_priv(sc)->flags & (FNIC_IO_ABORTED | FNIC_IO_DONE))) atomic64_inc(&misc_stats->no_icmnd_itmf_cmpls); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "abts cmpl recd. id %d status %s\n", (int)(id & FNIC_TAG_MASK), fnic_fcpio_status_to_str(hdr_status)); @@ -1341,11 +1344,11 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde if (io_req->abts_done) { complete(io_req->abts_done); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - shost_printk(KERN_INFO, fnic->host, + fnic_printk(KERN_INFO, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x Waking up abort thread\n", hwq, mqtag, tag); } else { - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Completing IO\n", hwq, mqtag, tag, fnic_fcpio_status_to_str(hdr_status)); @@ -1376,7 +1379,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde } } else if (id & FNIC_TAG_DEV_RST) { /* Completion of device reset */ - shost_printk(KERN_INFO, fnic->host, + fnic_printk(KERN_INFO, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x DR hst: %s\n", hwq, mqtag, tag, fnic_fcpio_status_to_str(hdr_status)); @@ -1388,7 +1391,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde sc->device->host->host_no, id, sc, jiffies_to_msecs(jiffies - start_time), desc, 0, fnic_flags_and_state(sc)); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s Terminate pending\n", hwq, mqtag, tag, fnic_fcpio_status_to_str(hdr_status)); @@ -1401,7 +1404,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde sc->device->host->host_no, id, sc, jiffies_to_msecs(jiffies - start_time), desc, 0, fnic_flags_and_state(sc)); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "dev reset cmpl recd after time out. " "id %d status %s\n", (int)(id & FNIC_TAG_MASK), @@ -1410,7 +1413,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde } fnic_priv(sc)->state = FNIC_IOREQ_CMD_COMPLETE; fnic_priv(sc)->flags |= FNIC_DEV_RST_DONE; - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x hst: %s DR completion received\n", hwq, mqtag, tag, fnic_fcpio_status_to_str(hdr_status)); @@ -1419,7 +1422,7 @@ static void fnic_fcpio_itmf_cmpl_handler(struct fnic *fnic, unsigned int cq_inde spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); } else { - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "%s: Unexpected itmf io state: hwq: %d tag 0x%x %s\n", __func__, hwq, id, fnic_ioreq_state_to_str(fnic_priv(sc)->state)); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); @@ -1443,6 +1446,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev, case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */ case FCPIO_FLOGI_FIP_REG_CMPL: /* fw completed flogi_fip_reg */ case FCPIO_RESET_CMPL: /* fw completed reset */ + case FCPIO_NVME_ERSP_HW_CMPL: /* fw completed NVMe ERSP */ atomic64_dec(&fnic->fnic_stats.fw_stats.active_fw_reqs); break; default: @@ -1457,11 +1461,22 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev, break; case FCPIO_ICMND_CMPL: /* fw completed a command */ - fnic_fcpio_icmnd_cmpl_handler(fnic, cq_index, desc); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_fcpio_icmnd_cmpl_handler(fnic, cq_index, desc); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_fcpio_nvme_fast_cmpl_handler(fnic, desc); + break; + + case FCPIO_NVME_ERSP_HW_CMPL: /* fw completed NVMe ERSP */ + if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_fcpio_ersp_cmpl_handler(fnic, desc, 1); break; case FCPIO_ITMF_CMPL: /* fw completed itmf (abort cmd, lun reset)*/ - fnic_fcpio_itmf_cmpl_handler(fnic, cq_index, desc); + if (IS_FNIC_FCP_INITIATOR(fnic)) + fnic_fcpio_itmf_cmpl_handler(fnic, cq_index, desc); + else if (IS_FNIC_NVME_INITIATOR(fnic)) + nvfnic_fcpio_nvme_itmf_cmpl_handler(fnic, desc); break; case FCPIO_FLOGI_REG_CMPL: /* fw completed flogi_reg */ @@ -1474,7 +1489,7 @@ static int fnic_fcpio_cmpl_handler(struct vnic_dev *vdev, break; default: - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "firmware completion type %d\n", desc->hdr.type); break; @@ -1535,7 +1550,7 @@ static bool fnic_cleanup_io_iter(struct scsi_cmnd *sc, void *data) io_req = fnic_priv(sc)->io_req; if (!io_req) { spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d mqtag: 0x%x tag: 0x%x flags: 0x%x No ioreq. Returning\n", hwq, mqtag, tag, fnic_priv(sc)->flags); return true; @@ -1573,7 +1588,7 @@ static bool fnic_cleanup_io_iter(struct scsi_cmnd *sc, void *data) mempool_free(io_req, fnic->io_req_pool); sc->result = DID_TRANSPORT_DISRUPTED << 16; - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "mqtag: 0x%x tag: 0x%x sc: 0x%p duration = %lu DID_TRANSPORT_DISRUPTED\n", mqtag, tag, sc, (jiffies - start_time)); @@ -1605,7 +1620,7 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id) struct scsi_cmnd *sc = NULL; io_count = fnic_count_all_ioreqs(fnic); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Outstanding ioreq count: %d active io count: %lld Waiting\n", io_count, atomic64_read(&fnic->fnic_stats.io_stats.active_ios)); @@ -1630,7 +1645,7 @@ static void fnic_cleanup_io(struct fnic *fnic, int exclude_id) spin_unlock_irqrestore(&fnic->wq_copy_lock[0], flags); while ((io_count = fnic_count_all_ioreqs(fnic))) { - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Outstanding ioreq count: %d active io count: %lld Waiting\n", io_count, atomic64_read(&fnic->fnic_stats.io_stats.active_ios)); @@ -1650,6 +1665,15 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq, unsigned long start_time = 0; uint16_t hwq; + /* + * Clean up outstanding NVMe requests if firmware reset did not + * complete them before WQ copy cleanup. + */ + if (IS_FNIC_NVME_INITIATOR(fnic)) { + nvfnic_cleanup_all_nvme_ios(fnic); + return; + } + /* get the tag reference */ fcpio_tag_id_dec(&desc->hdr.tag, &id); id &= FNIC_TAG_MASK; @@ -1686,7 +1710,7 @@ void fnic_wq_copy_cleanup_handler(struct vnic_wq_copy *wq, wq_copy_cleanup_scsi_cmd: sc->result = DID_NO_CONNECT << 16; - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "wq_copy_cleanup_handler:" + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "wq_copy_cleanup_handler:" " DID_NO_CONNECT\n"); FNIC_TRACE(fnic_wq_copy_cleanup_handler, @@ -1730,7 +1754,7 @@ static inline int fnic_queue_abort_io_req(struct fnic *fnic, int tag, spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); atomic_dec(&fnic->in_flight); atomic_dec(&tport->in_flight); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "fnic_queue_abort_io_req: failure: no descriptors\n"); atomic64_inc(&misc_stats->abts_cpwq_alloc_failures); return 1; @@ -1775,7 +1799,7 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data) hwq = blk_mq_unique_tag_to_hwq(abt_tag); if (!sc) { - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "sc is NULL abt_tag: 0x%x hwq: %d\n", abt_tag, hwq); return true; } @@ -1789,7 +1813,7 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data) if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) && !(fnic_priv(sc)->flags & FNIC_DEV_RST_ISSUED)) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d abt_tag: 0x%x flags: 0x%x Device reset is not pending\n", hwq, abt_tag, fnic_priv(sc)->flags); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); @@ -1806,16 +1830,16 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data) } if (io_req->abts_done) { - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "fnic_rport_exch_reset: io_req->abts_done is set state is %s\n", fnic_ioreq_state_to_str(fnic_priv(sc)->state)); } if (!(fnic_priv(sc)->flags & FNIC_IO_ISSUED)) { - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "rport_exch_reset IO not yet issued %p abt_tag 0x%x", sc, abt_tag); - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "flags %x state %d\n", fnic_priv(sc)->flags, fnic_priv(sc)->state); } @@ -1826,13 +1850,13 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data) if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) { atomic64_inc(&reset_stats->device_reset_terminates); abt_tag |= FNIC_TAG_DEV_RST; - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "dev reset sc 0x%p\n", sc); } - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "fnic_rport_exch_reset: dev rst sc 0x%p\n", sc); WARN_ON_ONCE(io_req->abts_done); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "fnic_rport_reset_exch: Issuing abts\n"); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); @@ -1850,7 +1874,7 @@ static bool fnic_rport_abort_io_iter(struct scsi_cmnd *sc, void *data) * lun reset */ spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d abt_tag: 0x%x flags: 0x%x Queuing abort failed\n", hwq, abt_tag, fnic_priv(sc)->flags); if (fnic_priv(sc)->state == FNIC_IOREQ_ABTS_PENDING) @@ -1881,7 +1905,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id) .term_cnt = 0, }; - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "fnic rport exchange reset for tport: 0x%06x\n", port_id); @@ -1889,7 +1913,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id) return; io_count = fnic_count_ioreqs(fnic, port_id); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Starting terminates: rport:0x%x portid-io-count: %d active-io-count: %lld\n", port_id, io_count, atomic64_read(&fnic->fnic_stats.io_stats.active_ios)); @@ -1915,7 +1939,7 @@ void fnic_rport_exch_reset(struct fnic *fnic, u32 port_id) while ((io_count = fnic_count_ioreqs(fnic, port_id))) schedule_timeout(msecs_to_jiffies(1000)); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "rport: 0x%x remaining portid-io-count: %d ", port_id, io_count); } @@ -2040,10 +2064,10 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) tport = rdd_data->tport; if (!tport) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Abort cmd called after tport delete! rport fcid: 0x%x", rport->port_id); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "lun: %llu hwq: 0x%x mqtag: 0x%x Op: 0x%x flags: 0x%x\n", sc->device->lun, hwq, mqtag, sc->cmnd[0], fnic_priv(sc)->flags); @@ -2052,18 +2076,18 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) goto fnic_abort_cmd_end; } - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "Abort cmd called rport fcid: 0x%x lun: %llu hwq: 0x%x mqtag: 0x%x", rport->port_id, sc->device->lun, hwq, mqtag); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "Op: 0x%x flags: 0x%x\n", sc->cmnd[0], fnic_priv(sc)->flags); if (iport->state != FNIC_IPORT_STATE_READY) { atomic64_inc(&fnic_stats->misc_stats.iport_not_ready); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "iport NOT in READY state"); ret = FAILED; spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -2072,7 +2096,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) if ((tport->state != FDLS_TGT_STATE_READY) && (tport->state != FDLS_TGT_STATE_ADISC)) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "tport state: %d\n", tport->state); ret = FAILED; spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -2123,7 +2147,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) else atomic64_inc(&abts_stats->abort_issued_greater_than_60_sec); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "CDB Opcode: 0x%02x Abort issued time: %lu msec\n", sc->cmnd[0], abt_issued_time); /* @@ -2214,7 +2238,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) if (!(fnic_priv(sc)->flags & (FNIC_IO_ABORTED | FNIC_IO_DONE))) { spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Issuing host reset due to out of order IO\n"); ret = FAILED; @@ -2262,7 +2286,7 @@ int fnic_abort_cmd(struct scsi_cmnd *sc) (u64)sc->cmnd[4] << 8 | sc->cmnd[5]), fnic_flags_and_state(sc)); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Returning from abort cmd type %x %s\n", task_req, (ret == SUCCESS) ? "SUCCESS" : "FAILED"); @@ -2303,7 +2327,7 @@ static inline int fnic_queue_dr_io_req(struct fnic *fnic, free_wq_copy_descs(fnic, wq, hwq); if (!vnic_wq_copy_desc_avail(wq)) { - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "queue_dr_io_req failure - no descriptors\n"); atomic64_inc(&misc_stats->devrst_cpwq_alloc_failures); ret = -EAGAIN; @@ -2371,7 +2395,7 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data) * Found IO that is still pending with firmware and * belongs to the LUN that we are resetting */ - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Found IO in %s on lun\n", fnic_ioreq_state_to_str(fnic_priv(sc)->state)); @@ -2381,14 +2405,14 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data) } if ((fnic_priv(sc)->flags & FNIC_DEVICE_RESET) && (!(fnic_priv(sc)->flags & FNIC_DEV_RST_ISSUED))) { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "dev rst not pending sc 0x%p\n", sc); spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); return true; } if (io_req->abts_done) - shost_printk(KERN_ERR, fnic->host, + fnic_printk(KERN_ERR, fnic, "%s: io_req->abts_done is set state is %s\n", __func__, fnic_ioreq_state_to_str(fnic_priv(sc)->state)); old_ioreq_state = fnic_priv(sc)->state; @@ -2404,7 +2428,7 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data) BUG_ON(io_req->abts_done); if (fnic_priv(sc)->flags & FNIC_DEVICE_RESET) { - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "dev rst sc 0x%p\n", sc); } @@ -2426,7 +2450,7 @@ static bool fnic_pending_aborts_iter(struct scsi_cmnd *sc, void *data) fnic_priv(sc)->state = old_ioreq_state; spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); iter_data->ret = FAILED; - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "hwq: %d abt_tag: 0x%lx Abort could not be queued\n", hwq, abt_tag); return false; @@ -2518,7 +2542,7 @@ static int fnic_clean_pending_aborts(struct fnic *fnic, ret = 1; clean_pending_aborts_end: - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "exit status: %d\n", ret); return ret; } @@ -2568,7 +2592,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) rport = starget_to_rport(scsi_target(sc->device)); spin_lock_irqsave(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "fcid: 0x%x lun: %llu hwq: %d mqtag: 0x%x flags: 0x%x Device reset\n", rport->port_id, sc->device->lun, hwq, mqtag, fnic_priv(sc)->flags); @@ -2576,7 +2600,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) rdd_data = rport->dd_data; tport = rdd_data->tport; if (!tport) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Dev rst called after tport delete! rport fcid: 0x%x lun: %llu\n", rport->port_id, sc->device->lun); spin_unlock_irqrestore(&fnic->fnic_lock, flags); @@ -2585,7 +2609,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) if (iport->state != FNIC_IPORT_STATE_READY) { atomic64_inc(&fnic_stats->misc_stats.iport_not_ready); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "iport NOT in READY state"); spin_unlock_irqrestore(&fnic->fnic_lock, flags); goto fnic_device_reset_end; @@ -2593,7 +2617,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) if ((tport->state != FDLS_TGT_STATE_READY) && (tport->state != FDLS_TGT_STATE_ADISC)) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "tport state: %d\n", tport->state); spin_unlock_irqrestore(&fnic->fnic_lock, flags); goto fnic_device_reset_end; @@ -2656,7 +2680,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) fnic_priv(sc)->lr_status = FCPIO_INVALID_CODE; spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, "TAG %x\n", mqtag); + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "TAG %x\n", mqtag); /* * issue the device reset, if enqueue failed, clean up the ioreq @@ -2707,13 +2731,13 @@ int fnic_device_reset(struct scsi_cmnd *sc) io_req = fnic_priv(sc)->io_req; if (!io_req) { spin_unlock_irqrestore(&fnic->wq_copy_lock[hwq], flags); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "io_req is null mqtag 0x%x sc 0x%p\n", mqtag, sc); goto fnic_device_reset_end; } if (exit_dr) { - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Host reset called for fnic. Exit device reset\n"); io_req->dr_done = NULL; goto fnic_device_reset_clean; @@ -2728,7 +2752,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) */ if (status == FCPIO_INVALID_CODE) { atomic64_inc(&reset_stats->device_reset_timeouts); - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Device reset timed out\n"); fnic_priv(sc)->flags |= FNIC_DEV_RST_TIMED_OUT; int_to_scsilun(sc->device->lun, &fc_lun); @@ -2740,8 +2764,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) /* Completed, but not successful, clean up the io_req, return fail */ if (status != FCPIO_SUCCESS) { spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags); - FNIC_SCSI_DBG(KERN_DEBUG, - fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Device reset completed - failed\n"); io_req = fnic_priv(sc)->io_req; goto fnic_device_reset_clean; @@ -2757,7 +2780,7 @@ int fnic_device_reset(struct scsi_cmnd *sc) if (fnic_clean_pending_aborts(fnic, sc, new_sc)) { spin_lock_irqsave(&fnic->wq_copy_lock[hwq], flags); io_req = fnic_priv(sc)->io_req; - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Device reset failed: Cannot abort all IOs\n"); goto fnic_device_reset_clean; } @@ -2811,13 +2834,13 @@ int fnic_device_reset(struct scsi_cmnd *sc) ret = FAILED; break; } - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "Cannot clean up all IOs for the LUN\n"); schedule_timeout(msecs_to_jiffies(1000)); count++; } - FNIC_SCSI_DBG(KERN_DEBUG, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_DEBUG, fnic, "Returning from device reset %s\n", (ret == SUCCESS) ? "SUCCESS" : "FAILED"); @@ -2852,13 +2875,13 @@ void fnic_reset(struct Scsi_Host *shost) fnic = *((struct fnic **) shost_priv(shost)); reset_stats = &fnic->fnic_stats.reset_stats; - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "Issuing fnic reset\n"); atomic64_inc(&reset_stats->fnic_resets); fnic_post_flogo_linkflap(fnic); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "Returning from fnic reset"); atomic64_inc(&reset_stats->fnic_reset_completions); @@ -2869,7 +2892,7 @@ int fnic_issue_fc_host_lip(struct Scsi_Host *shost) int ret = 0; struct fnic *fnic = *((struct fnic **) shost_priv(shost)); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "FC host lip issued"); ret = fnic_host_reset(shost); @@ -2895,7 +2918,7 @@ int fnic_host_reset(struct Scsi_Host *shost) spin_lock_irqsave(&fnic->fnic_lock, flags); if (fnic->reset_in_progress == IN_PROGRESS) { spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_WARNING, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_WARNING, fnic, "Firmware reset in progress. Skipping another host reset\n"); return SUCCESS; } @@ -2933,7 +2956,7 @@ int fnic_host_reset(struct Scsi_Host *shost) } spin_unlock_irqrestore(&fnic->fnic_lock, flags); - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "host reset return status: %d\n", ret); return ret; } @@ -2973,7 +2996,7 @@ static bool fnic_abts_pending_iter(struct scsi_cmnd *sc, void *data) * Found IO that is still pending with firmware and * belongs to the LUN that we are resetting */ - FNIC_SCSI_DBG(KERN_INFO, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_INFO, fnic, "hwq: %d tag: 0x%x Found IO in state: %s on lun\n", hwq, tag, fnic_ioreq_state_to_str(fnic_priv(sc)->state)); @@ -3027,7 +3050,7 @@ int fnic_eh_host_reset_handler(struct scsi_cmnd *sc) struct Scsi_Host *shost = sc->device->host; struct fnic *fnic = *((struct fnic **) shost_priv(shost)); - FNIC_SCSI_DBG(KERN_ERR, fnic->host, fnic->fnic_num, + FNIC_SCSI_DBG(KERN_ERR, fnic, "SCSI error handling: fnic host reset"); ret = fnic_host_reset(shost); diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h index 8ddd20401a59..a3ddd7b55729 100644 --- a/drivers/scsi/fnic/fnic_stats.h +++ b/drivers/scsi/fnic/fnic_stats.h @@ -29,6 +29,15 @@ struct io_path_stats { atomic64_t io_greater_than_30000_msec; atomic64_t current_max_io_time; atomic64_t ios[FNIC_MQ_MAX_QUEUES]; + + atomic64_t nvme_io_reqs_rcvd; + atomic64_t nvme_ios_queued_for_rsp; + atomic64_t nvme_io_rsps_unqueued; + atomic64_t nvme_io_rsps_sending; + atomic64_t nvme_io_rsps_sent; + atomic64_t nvme_num_ios_in_waitq; + atomic64_t nvme_ios_in_waitq_3000_msec; + atomic64_t nvme_ios_in_waitq_max_time; }; struct abort_stats { @@ -151,6 +160,17 @@ struct fnic_iport_stats { atomic64_t unsupported_frames_dropped; }; +struct nvme_host_statistics { + atomic64_t nvme_input_requests; + atomic64_t nvme_output_requests; + atomic64_t nvme_control_requests; + atomic64_t nvme_ersps; + atomic64_t nvme_ls_requests; + atomic64_t nvme_ls_responses; + atomic64_t nvme_ls_aborts; + atomic64_t nvme_ls_abort_responses; +}; + struct fnic_stats { struct stats_timestamps stats_timestamps; struct io_path_stats io_stats; @@ -161,6 +181,7 @@ struct fnic_stats { struct vlan_stats vlan_stats; struct fc_host_statistics host_stats; struct misc_stats misc_stats; + struct nvme_host_statistics nvme_stats; }; struct stats_debug_info { @@ -170,6 +191,13 @@ struct stats_debug_info { int buffer_len; }; +struct fnic_nvmef_info { + char *info_buffer; + void *i_private; + int buf_size; + int buffer_len; +}; + int fnic_get_stats_data(struct stats_debug_info *, struct fnic_stats *); const char *fnic_role_to_str(unsigned int role); #endif /* _FNIC_STATS_H_ */ diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c index 4ed57ea1f854..a3f583703664 100644 --- a/drivers/scsi/fnic/fnic_trace.c +++ b/drivers/scsi/fnic/fnic_trace.c @@ -31,7 +31,8 @@ int fnic_fc_trace_cleared = 1; static DEFINE_SPINLOCK(fnic_fc_trace_lock); static const char * const fnic_role_str[] = { - [FNIC_ROLE_FCP_INITIATOR] = "FCP_Initiator", + [FNIC_ROLE_FCP_INITIATOR] = "FCP_Initiator", + [FNIC_ROLE_NVME_INITIATOR] = "NVMeF_Initiator", }; const char *fnic_role_to_str(unsigned int role) diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c index 991c86eb5aff..0e5ae0540d85 100644 --- a/drivers/scsi/fnic/vnic_dev.c +++ b/drivers/scsi/fnic/vnic_dev.c @@ -381,7 +381,7 @@ static int vnic_dev_cmd2(struct vnic_dev *vdev, enum vnic_devcmd_cmd cmd, if (result->color == color) { if (result->error) { err = -(int) result->error; - if (err != ERR_ECMDUNKNOWN || + if (err != -ERR_ECMDUNKNOWN || cmd != CMD_CAPABILITY) pr_err("%s:Error %d devcmd %d\n", pci_name(vdev->pdev), diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h index f876d223b2b4..20878a3e9e26 100644 --- a/drivers/scsi/fnic/vnic_devcmd.h +++ b/drivers/scsi/fnic/vnic_devcmd.h @@ -430,7 +430,7 @@ struct vnic_devcmd_notify { struct vnic_devcmd_provinfo { u8 oui[3]; u8 type; - u8 data[]; + u8 data[0]; }; /* diff --git a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c index 270eae7ac427..41731a7304dd 100644 --- a/drivers/scsi/g_NCR5380.c +++ b/drivers/scsi/g_NCR5380.c @@ -739,7 +739,7 @@ static struct isa_driver generic_NCR5380_isa_driver = { #ifdef CONFIG_PNP static const struct pnp_device_id generic_NCR5380_pnp_ids[] = { { .id = "DTC436e", .driver_data = BOARD_DTC3181E }, - { .id = "" } + { } }; MODULE_DEVICE_TABLE(pnp, generic_NCR5380_pnp_ids); diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index 0420bfe9bd42..79bd64e12adc 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -442,14 +442,15 @@ static void gvp11_remove(struct zorro_dev *z) */ static struct zorro_device_id gvp11_zorro_tbl[] = { - { ZORRO_PROD_GVP_COMBO_030_R3_SCSI, ~0x00ffffff }, - { ZORRO_PROD_GVP_SERIES_II, ~0x00ffffff }, - { ZORRO_PROD_GVP_GFORCE_030_SCSI, ~0x01ffffff }, - { ZORRO_PROD_GVP_A530_SCSI, ~0x01ffffff }, - { ZORRO_PROD_GVP_COMBO_030_R4_SCSI, ~0x01ffffff }, - { ZORRO_PROD_GVP_A1291, ~0x07ffffff }, - { ZORRO_PROD_GVP_GFORCE_040_SCSI_1, ~0x07ffffff }, - { 0 } + /* .driver_data specifies the DMA mask */ + { .id = ZORRO_PROD_GVP_COMBO_030_R3_SCSI, .driver_data = ~0x00ffffff }, + { .id = ZORRO_PROD_GVP_SERIES_II, .driver_data = ~0x00ffffff }, + { .id = ZORRO_PROD_GVP_GFORCE_030_SCSI, .driver_data = ~0x01ffffff }, + { .id = ZORRO_PROD_GVP_A530_SCSI, .driver_data = ~0x01ffffff }, + { .id = ZORRO_PROD_GVP_COMBO_030_R4_SCSI, .driver_data = ~0x01ffffff }, + { .id = ZORRO_PROD_GVP_A1291, .driver_data = ~0x07ffffff }, + { .id = ZORRO_PROD_GVP_GFORCE_040_SCSI_1, .driver_data = ~0x07ffffff }, + { } }; MODULE_DEVICE_TABLE(zorro, gvp11_zorro_tbl); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 213d5b5dea94..8a2500993e19 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device) return rc; } phys_init_v3_hw(hisi_hba); - - /* - * If a directly-attached disk is removed during suspend, a deadlock - * may occur, as the PHYE_RESUME_TIMEOUT processing will require the - * hisi_hba->device to be active, which can only happen when resume - * completes. So don't wait for the HA event workqueue to drain upon - * resume. - */ - sas_resume_ha_no_sync(sha); + sas_resume_ha(sha); clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); dev_warn(dev, "end of resuming controller\n"); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index e047747d4ecf..08ec144f7bf7 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -252,10 +252,8 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev, shost->dma_dev = dma_dev; - if (dma_dev->dma_mask) { - shost->max_sectors = min_t(unsigned int, shost->max_sectors, - dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT); - } + shost->max_sectors = min_not_zero(shost->max_sectors, + dma_max_mapping_size(dma_dev) >> SECTOR_SHIFT); error = scsi_mq_setup_tags(shost); if (error) @@ -357,6 +355,7 @@ static void scsi_host_dev_release(struct device *dev) /* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */ rcu_barrier(); + cancel_work_sync(&shost->eh_work); if (shost->tmf_work_q) destroy_workqueue(shost->tmf_work_q); if (shost->ehandler) @@ -422,6 +421,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv INIT_LIST_HEAD(&shost->starved_list); init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); + INIT_WORK(&shost->eh_work, scsi_rcu_eh_wakeup); index = ida_alloc(&host_index_ida, GFP_KERNEL); if (index < 0) { diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index a1b116cd4723..8edad1830abe 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -5017,6 +5017,10 @@ static int hpsa_scsi_ioaccel2_queue_command(struct ctlr_info *h, if (phys_disk->in_reset) { cmd->result = DID_RESET << 16; + atomic_dec(&phys_disk->ioaccel_cmds_out); + scsi_dma_unmap(cmd); + if (use_sg > h->ioaccel_maxsg) + hpsa_unmap_ioaccel2_sg_chain_block(h, cp); return -1; } diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile index 5eb1cb1a0028..9408c7f4cdee 100644 --- a/drivers/scsi/ibmvscsi/Makefile +++ b/drivers/scsi/ibmvscsi/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +ibmvfc-objs := ibmvfc-core.o ibmvfc-nvme.o + obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c similarity index 87% rename from drivers/scsi/ibmvscsi/ibmvfc.c rename to drivers/scsi/ibmvscsi/ibmvfc-core.c index 3dd2adda195e..93b9f699c2e5 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c @@ -1,10 +1,11 @@ -// SPDX-License-Identifier: GPL-2.0-or-later +/* SPDX-License-Identifier: GPL-2.0-or-later */ + /* * ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter * * Written By: Brian King , IBM Corporation * - * Copyright (C) IBM Corporation, 2008 + * Copyright (C) IBM Corporation, 2008-2026 */ #include @@ -40,15 +41,19 @@ static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT; static u16 max_sectors = IBMVFC_MAX_SECTORS; static u16 scsi_qdepth = IBMVFC_SCSI_QDEPTH; static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS; -static unsigned int ibmvfc_debug = IBMVFC_DEBUG; static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL; static unsigned int cls3_error = IBMVFC_CLS3_ERROR; static unsigned int mq_enabled = IBMVFC_MQ; static unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES; static unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS; +static unsigned int nvme_enabled = IBMVFC_NVME; +static unsigned int nr_nvme_hw_queues = IBMVFC_NVME_HW_QUEUES; +static unsigned int nr_nvme_channels = IBMVFC_NVME_CHANNELS; static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ; static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M; +unsigned int ibmvfc_debug = IBMVFC_DEBUG; + static LIST_HEAD(ibmvfc_head); static DEFINE_SPINLOCK(ibmvfc_driver_lock); static struct scsi_transport_template *ibmvfc_transport_template; @@ -74,6 +79,16 @@ module_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO); MODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. " "[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]"); +module_param_named(nvme, nvme_enabled, uint, S_IRUGO); +MODULE_PARM_DESC(nvme, "Enable NVMe over FC support. " + "[Default=" __stringify(IBMVFC_NVME) "]"); +module_param_named(nvme_host_queues, nr_nvme_hw_queues, uint, S_IRUGO); +MODULE_PARM_DESC(nvme_host_queues, "Number of NVMeoF Host submission queues. " + "[Default=" __stringify(IBMVFC_NVME_HW_QUEUES) "]"); +module_param_named(nvme_hw_channels, nr_nvme_channels, uint, S_IRUGO); +MODULE_PARM_DESC(nvme_hw_channels, "Number of hw NVMeoF channels to request. " + "[Default=" __stringify(IBMVFC_NVME_CHANNELS) "]"); + module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. " "[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]"); @@ -159,6 +174,11 @@ static const struct { { IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." }, }; +static const char *proto_type[] = { + "SCSI", + "NVMe", +}; + static void ibmvfc_npiv_login(struct ibmvfc_host *); static void ibmvfc_tgt_send_prli(struct ibmvfc_target *); static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *); @@ -186,17 +206,18 @@ static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba, return rc; } -static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags) +static int ibmvfc_nvme_active(struct ibmvfc_host *vhost) { - u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities); - - return (host_caps & cap_flags) ? 1 : 0; + return (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF) && + vhost->nvme_scrqs.active_queues); } static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost, struct ibmvfc_cmd *vfc_cmd) { - if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF)) + return &vfc_cmd->v3scsi.iu; + else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) return &vfc_cmd->v2.iu; else return &vfc_cmd->v1.iu; @@ -205,7 +226,9 @@ static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost, static struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost, struct ibmvfc_cmd *vfc_cmd) { - if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF)) + return &vfc_cmd->v3scsi.rsp; + else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) return &vfc_cmd->v2.rsp; else return &vfc_cmd->v1.rsp; @@ -324,7 +347,7 @@ static int ibmvfc_get_err_index(u16 status, u16 error) * Return value: * error description string **/ -static const char *ibmvfc_get_cmd_error(u16 status, u16 error) +const char *ibmvfc_get_cmd_error(u16 status, u16 error) { int rc = ibmvfc_get_err_index(status, error); if (rc >= 0) @@ -468,6 +491,7 @@ static const struct { { IBMVFC_FABRIC_BUSY, "fabric busy" }, { IBMVFC_PORT_BUSY, "port busy" }, { IBMVFC_BASIC_REJECT, "basic reject" }, + { IBMVFC_FC4_LS_REJECT, "fc4 ls reject" }, }; static const char *unknown_fc_type = "unknown fc type"; @@ -682,7 +706,9 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost, ENTER; scsi_block_requests(vhost->host); - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) + ibmvfc_del_tgt(tgt); + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) ibmvfc_del_tgt(tgt); ibmvfc_set_host_state(vhost, state); ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL); @@ -715,7 +741,13 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost) memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE); vhost->async_crq.cur = 0; - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (vhost->client_migrated) + tgt->need_login = 1; + else + ibmvfc_del_tgt(tgt); + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (vhost->client_migrated) tgt->need_login = 1; else @@ -930,6 +962,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) unsigned long flags; ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs); + ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs); /* Re-enable the CRQ */ do { @@ -945,10 +978,13 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost) spin_lock(vhost->crq.q_lock); vhost->do_enquiry = 1; vhost->using_channels = 0; + vhost->do_scsi_login = 0; + vhost->do_nvme_login = 0; spin_unlock(vhost->crq.q_lock); spin_unlock_irqrestore(vhost->host->host_lock, flags); ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs); + ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs); return rc; } @@ -968,6 +1004,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) struct ibmvfc_queue *crq = &vhost->crq; ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs); + ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs); /* Close the CRQ */ do { @@ -982,6 +1019,8 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) vhost->logged_in = 0; vhost->do_enquiry = 1; vhost->using_channels = 0; + vhost->do_scsi_login = 0; + vhost->do_nvme_login = 0; /* Clean out the queue */ memset(crq->msgs.crq, 0, PAGE_SIZE); @@ -1001,6 +1040,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost) spin_unlock_irqrestore(vhost->host->host_lock, flags); ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs); + ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs); return rc; } @@ -1029,7 +1069,7 @@ static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool, * @evt: ibmvfc_event to be freed * **/ -static void ibmvfc_free_event(struct ibmvfc_event *evt) +void ibmvfc_free_event(struct ibmvfc_event *evt) { struct ibmvfc_event_pool *pool = &evt->queue->evt_pool; unsigned long flags; @@ -1052,19 +1092,29 @@ static void ibmvfc_free_event(struct ibmvfc_event *evt) } /** - * ibmvfc_scsi_eh_done - EH done function for queuecommand commands + * ibmvfc_vfc_eh_done - EH done function for queued IO * @evt: ibmvfc event struct * - * This function does not setup any error status, that must be done - * before this function gets called. + * This function does not setup any error status for scsi commands, that must be + * done before this function gets called. **/ -static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt) +static void ibmvfc_vfc_eh_done(struct ibmvfc_event *evt) { struct scsi_cmnd *cmnd = evt->cmnd; + struct nvmefc_ls_req *ls_req = evt->ls_req; + struct nvmefc_fcp_req *fcp_req = evt->fcp_req; if (cmnd) { scsi_dma_unmap(cmnd); scsi_done(cmnd); + } else if (fcp_req) { + fcp_req->rcv_rsplen = 0; + fcp_req->transferred_length = 0; + fcp_req->status = NVME_SC_INTERNAL; + fcp_req->done(fcp_req); + } else if (ls_req) { + ls_req->done(ls_req, -EIO); + kref_put(&evt->tgt->kref, ibmvfc_release_tgt); } ibmvfc_free_event(evt); @@ -1106,8 +1156,10 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code) BUG_ON(!atomic_dec_and_test(&evt->active)); if (evt->cmnd) { evt->cmnd->result = (error_code << 16); - evt->done = ibmvfc_scsi_eh_done; - } else + evt->done = ibmvfc_vfc_eh_done; + } else if (evt->fcp_req || evt->ls_req) + evt->done = ibmvfc_vfc_eh_done; + else evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED); timer_delete(&evt->timer); @@ -1124,13 +1176,16 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code) static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) { struct ibmvfc_event *evt, *pos; - struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs; + struct ibmvfc_queue *scsi_q = vhost->scsi_scrqs.scrqs; + struct ibmvfc_queue *nvme_q = vhost->nvme_scrqs.scrqs; unsigned long flags; - int hwqs = 0; + int shwqs, nhwqs = 0; int i; - if (vhost->using_channels) - hwqs = vhost->scsi_scrqs.active_queues; + if (vhost->using_channels) { + shwqs = vhost->scsi_scrqs.active_queues; + nhwqs = vhost->nvme_scrqs.active_queues; + } ibmvfc_dbg(vhost, "Purging all requests\n"); spin_lock_irqsave(&vhost->crq.l_lock, flags); @@ -1139,14 +1194,24 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code) list_splice_init(&vhost->crq.sent, &vhost->purge); spin_unlock_irqrestore(&vhost->crq.l_lock, flags); - for (i = 0; i < hwqs; i++) { - spin_lock_irqsave(queues[i].q_lock, flags); - spin_lock(&queues[i].l_lock); - list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list) + for (i = 0; i < shwqs; i++) { + spin_lock_irqsave(scsi_q[i].q_lock, flags); + spin_lock(&scsi_q[i].l_lock); + list_for_each_entry_safe(evt, pos, &scsi_q[i].sent, queue_list) ibmvfc_fail_request(evt, error_code); - list_splice_init(&queues[i].sent, &vhost->purge); - spin_unlock(&queues[i].l_lock); - spin_unlock_irqrestore(queues[i].q_lock, flags); + list_splice_init(&scsi_q[i].sent, &vhost->purge); + spin_unlock(&scsi_q[i].l_lock); + spin_unlock_irqrestore(scsi_q[i].q_lock, flags); + } + + for (i = 0; i < nhwqs; i++) { + spin_lock_irqsave(nvme_q[i].q_lock, flags); + spin_lock(&nvme_q[i].l_lock); + list_for_each_entry_safe(evt, pos, &nvme_q[i].sent, queue_list) + ibmvfc_fail_request(evt, error_code); + list_splice_init(&nvme_q[i].sent, &vhost->purge); + spin_unlock(&nvme_q[i].l_lock); + spin_unlock_irqrestore(nvme_q[i].q_lock, flags); } } @@ -1232,7 +1297,7 @@ static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget) struct ibmvfc_host *vhost = shost_priv(shost); struct ibmvfc_target *tgt; - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) if (tgt->target_id == starget->id) { kref_get(&tgt->kref); return tgt; @@ -1363,10 +1428,10 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout) * @kref: kref struct * **/ -static void ibmvfc_release_tgt(struct kref *kref) +void ibmvfc_release_tgt(struct kref *kref) { struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref); - kfree(tgt); + mempool_free(tgt, tgt->vhost->tgt_pool); } /** @@ -1493,13 +1558,13 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ; if (mq_enabled) max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) * - vhost->scsi_scrqs.desired_queues; + (vhost->scsi_scrqs.desired_queues + vhost->nvme_scrqs.desired_queues); memset(login_info, 0, sizeof(*login_info)); login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX); login_info->max_dma_len = cpu_to_be64(max_sectors << 9); - login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu)); + login_info->max_payload = cpu_to_be32(sizeof(struct nvme_fc_cmd_iu)); login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp)); login_info->partition_num = cpu_to_be32(vhost->partition_number); login_info->vfc_frame_version = cpu_to_be32(1); @@ -1511,8 +1576,14 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) login_info->max_cmds = cpu_to_be32(max_cmds); login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN); - if (vhost->mq_enabled || vhost->using_channels) + if (vhost->mq_enabled || vhost->using_channels) { login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS); + if (vhost->nvme_enabled) { + login_info->capabilities |= cpu_to_be64(IBMVFC_YES_NVMEOF); + login_info->capabilities |= cpu_to_be64(IBMVFC_YES_SCSI); + login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_WWPN_ALL); + } + } login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token); login_info->async.len = cpu_to_be32(async_crq->size * @@ -1535,7 +1606,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost) * * Returns a free event from the pool. **/ -static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved) +struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved) { struct ibmvfc_event *evt = NULL; unsigned long flags; @@ -1559,9 +1630,6 @@ static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int r return evt; } -#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0) -#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1) - /** * ibmvfc_locked_done - Calls evt completion with host_lock held * @evt: ibmvfc evt to complete @@ -1586,10 +1654,12 @@ static void ibmvfc_locked_done(struct ibmvfc_event *evt) * @done: Routine to call when the event is responded to * @format: SRP or MAD format **/ -static void ibmvfc_init_event(struct ibmvfc_event *evt, +void ibmvfc_init_event(struct ibmvfc_event *evt, void (*done) (struct ibmvfc_event *), u8 format) { evt->cmnd = NULL; + evt->fcp_req = NULL; + evt->ls_req = NULL; evt->sync_iu = NULL; evt->eh_comp = NULL; evt->crq.format = format; @@ -1711,7 +1781,7 @@ static void ibmvfc_timeout(struct timer_list *t) * * Returns the value returned from ibmvfc_send_crq(). (Zero for success) **/ -static int ibmvfc_send_event(struct ibmvfc_event *evt, +int ibmvfc_send_event(struct ibmvfc_event *evt, struct ibmvfc_host *vhost, unsigned long timeout) { __be64 *crq_as_u64 = (__be64 *) &evt->crq; @@ -1773,9 +1843,13 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt, dev_err(vhost->dev, "Send error (rc=%d)\n", rc); if (evt->cmnd) { evt->cmnd->result = DID_ERROR << 16; - evt->done = ibmvfc_scsi_eh_done; - } else + evt->done = ibmvfc_vfc_eh_done; + } else if (evt->fcp_req || evt->ls_req) { + evt->done = ibmvfc_vfc_eh_done; + } else { evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR); + evt->done = evt->_done; + } evt->done(evt); } else { @@ -1824,7 +1898,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt) * @sdev: scsi device struct * **/ -static void ibmvfc_relogin(struct scsi_device *sdev) +static void ibmvfc_scsi_relogin(struct scsi_device *sdev) { struct ibmvfc_host *vhost = shost_priv(sdev->host); struct fc_rport *rport = starget_to_rport(scsi_target(sdev)); @@ -1832,7 +1906,7 @@ static void ibmvfc_relogin(struct scsi_device *sdev) unsigned long flags; spin_lock_irqsave(vhost->host->host_lock, flags); - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { if (rport == tgt->rport) { ibmvfc_del_tgt(tgt); break; @@ -1876,7 +1950,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt) memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len); if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) && (be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED)) - ibmvfc_relogin(cmnd->device); + ibmvfc_scsi_relogin(cmnd->device); if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER))) cmnd->result = (DID_ERROR << 16); @@ -1935,11 +2009,13 @@ static struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct s size_t offset; memset(vfc_cmd, 0, sizeof(*vfc_cmd)); - if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) { + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF)) + offset = offsetof(struct ibmvfc_cmd, v3scsi.rsp); + else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) offset = offsetof(struct ibmvfc_cmd, v2.rsp); - vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name); - } else + else offset = offsetof(struct ibmvfc_cmd, v1.rsp); + vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name); vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset); vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp)); vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE); @@ -2130,7 +2206,7 @@ static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id) ENTER; spin_lock_irqsave(vhost->host->host_lock, flags); - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { if (tgt->scsi_id == port_id) { issue_login = 0; break; @@ -3102,7 +3178,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport) spin_lock_irqsave(shost->host_lock, flags); found = 0; - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { if (tgt->scsi_id == rport->port_id) { found++; break; @@ -3242,7 +3318,23 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, case IBMVFC_AE_ELS_LOGO: case IBMVFC_AE_ELS_PRLO: case IBMVFC_AE_ELS_PLOGI: - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (!crq->scsi_id && !crq->wwpn && !crq->node_name) + break; + if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id) + continue; + if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn) + continue; + if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name) + continue; + if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO) + tgt->logo_rcvd = 1; + if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) { + ibmvfc_del_tgt(tgt); + ibmvfc_reinit_host(vhost); + } + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (!crq->scsi_id && !crq->wwpn && !crq->node_name) break; if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id) @@ -4042,11 +4134,12 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); switch (status) { case IBMVFC_MAD_SUCCESS: - tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n", - parms->type, parms->flags, parms->service_parms); + tgt_dbg(tgt, "%s Process Login succeeded: %X %02X %04X\n", + proto_type[tgt->protocol], parms->type, parms->flags, + parms->service_parms); + index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags)); if (parms->type == IBMVFC_SCSI_FCP_TYPE) { - index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags)); if (prli_rsp[index].logged_in) { if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) { tgt->need_login = 0; @@ -4062,6 +4155,22 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); else ibmvfc_del_tgt(tgt); + } else if (parms->type == IBMVFC_NVME_FCP_TYPE) { + if (prli_rsp[index].logged_in) { + /* For FC-NVMe PRLI the Image Pair field is always set to zero (see 6.3.3) */ + tgt->need_login = 0; + tgt->ids.roles = 0; + if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_TARGET) + tgt->ids.roles |= FC_PORT_ROLE_NVME_TARGET; + if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_INITIATOR) + tgt->ids.roles |= FC_PORT_ROLE_NVME_INITIATOR; + if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_DISCOVERY) + tgt->ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY; + tgt->add_rport = 1; + } else if (prli_rsp[index].retry) + ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); + else + ibmvfc_del_tgt(tgt); } else ibmvfc_del_tgt(tgt); break; @@ -4082,9 +4191,10 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) else ibmvfc_del_tgt(tgt); - tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n", - ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), - be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status); + tgt_log(tgt, level, "%s Process Login failed: %s (%x:%x) rc=0x%02X\n", + proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), + be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), + be16_to_cpu(rsp->error), status); break; } @@ -4126,25 +4236,30 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt) } else { prli->common.version = cpu_to_be32(1); } - prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN); + if (tgt->protocol == IBMVFC_PROTO_SCSI) { + prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN); + prli->parms.type = IBMVFC_SCSI_FCP_TYPE; + prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR); + prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC); + prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED); + + if (cls3_error) + prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY); + } else { + prli->common.opcode = cpu_to_be32(IBMVFC_NVMF_PROCESS_LOGIN); + prli->parms.type = IBMVFC_NVME_FCP_TYPE; + prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_NVME_INITIATOR); + } prli->common.length = cpu_to_be16(sizeof(*prli)); prli->scsi_id = cpu_to_be64(tgt->scsi_id); - prli->parms.type = IBMVFC_SCSI_FCP_TYPE; - prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR); - prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC); - prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED); - - if (cls3_error) - prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY); - ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); if (ibmvfc_send_event(evt, vhost, default_timeout)) { vhost->discovery_threads--; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); kref_put(&tgt->kref, ibmvfc_release_tgt); } else - tgt_dbg(tgt, "Sent process login\n"); + tgt_dbg(tgt, "%s Sent process login\n", proto_type[tgt->protocol]); } /** @@ -4164,7 +4279,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); switch (status) { case IBMVFC_MAD_SUCCESS: - tgt_dbg(tgt, "Port Login succeeded\n"); + tgt_dbg(tgt, "%s Port Login succeeded\n", proto_type[tgt->protocol]); if (tgt->ids.port_name && tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) { vhost->reinit = 1; @@ -4192,9 +4307,9 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt) else ibmvfc_del_tgt(tgt); - tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", - ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), - be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), + tgt_log(tgt, level, "%s Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", + proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), + be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type), ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status); break; @@ -4240,7 +4355,10 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt) } else { plogi->common.version = cpu_to_be32(1); } - plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN); + if (tgt->protocol == IBMVFC_PROTO_SCSI) + plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN); + else + plogi->common.opcode = cpu_to_be32(IBMVFC_NVMF_PORT_LOGIN); plogi->common.length = cpu_to_be16(sizeof(*plogi)); plogi->scsi_id = cpu_to_be64(tgt->scsi_id); @@ -4249,7 +4367,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); kref_put(&tgt->kref, ibmvfc_release_tgt); } else - tgt_dbg(tgt, "Sent port login\n"); + tgt_dbg(tgt, "Sent %s port login\n", proto_type[tgt->protocol]); } /** @@ -4270,7 +4388,7 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt) switch (status) { case IBMVFC_MAD_SUCCESS: - tgt_dbg(tgt, "Implicit Logout succeeded\n"); + tgt_dbg(tgt, "%s Implicit Logout succeeded\n", proto_type[tgt->protocol]); break; case IBMVFC_MAD_DRIVER_FAILED: kref_put(&tgt->kref, ibmvfc_release_tgt); @@ -4278,7 +4396,8 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt) return; case IBMVFC_MAD_FAILED: default: - tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status); + tgt_err(tgt, "%s Implicit Logout failed: rc=0x%02X\n", + proto_type[tgt->protocol], status); break; } @@ -4311,7 +4430,10 @@ static struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_t mad = &evt->iu.implicit_logout; memset(mad, 0, sizeof(*mad)); mad->common.version = cpu_to_be32(1); - mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT); + if (tgt->protocol == IBMVFC_PROTO_SCSI) + mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT); + else + mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_IMPLICIT_LOGOUT); mad->common.length = cpu_to_be16(sizeof(*mad)); mad->old_scsi_id = cpu_to_be64(tgt->scsi_id); return evt; @@ -4347,7 +4469,7 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); kref_put(&tgt->kref, ibmvfc_release_tgt); } else - tgt_dbg(tgt, "Sent Implicit Logout\n"); + tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]); } /** @@ -4377,7 +4499,8 @@ static void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt) else ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT); - tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed"); + tgt_dbg(tgt, "%s Implicit Logout %s\n", proto_type[tgt->protocol], + (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed"); kref_put(&tgt->kref, ibmvfc_release_tgt); wake_up(&vhost->work_wait_q); } @@ -4404,13 +4527,21 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt) evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt, ibmvfc_tgt_implicit_logout_and_del_done); + if (!evt) { + vhost->discovery_threads--; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); + kref_put(&tgt->kref, ibmvfc_release_tgt); + __ibmvfc_reset_host(vhost); + return; + } + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT); if (ibmvfc_send_event(evt, vhost, default_timeout)) { vhost->discovery_threads--; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); kref_put(&tgt->kref, ibmvfc_release_tgt); } else - tgt_dbg(tgt, "Sent Implicit Logout\n"); + tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]); } /** @@ -4430,7 +4561,8 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); switch (status) { case IBMVFC_MAD_SUCCESS: - tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id); + tgt_dbg(tgt, "%s Move Login succeeded for new scsi_id: %llX\n", + proto_type[tgt->protocol], tgt->new_scsi_id); tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name); tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name); tgt->scsi_id = tgt->new_scsi_id; @@ -4451,8 +4583,9 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt) level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login); tgt_log(tgt, level, - "Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n", - tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags), + "%s Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n", + proto_type[tgt->protocol], tgt->new_scsi_id, + be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags), status); break; } @@ -4492,7 +4625,10 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt) move = &evt->iu.move_login; memset(move, 0, sizeof(*move)); move->common.version = cpu_to_be32(1); - move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN); + if (tgt->protocol == IBMVFC_PROTO_SCSI) + move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN); + else + move->common.opcode = cpu_to_be32(IBMVFC_NVMF_MOVE_LOGIN); move->common.length = cpu_to_be16(sizeof(*move)); move->old_scsi_id = cpu_to_be64(tgt->scsi_id); @@ -4505,7 +4641,8 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); kref_put(&tgt->kref, ibmvfc_release_tgt); } else - tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id); + tgt_dbg(tgt, "Sent %s Move Login for new scsi_id: %llX\n", + proto_type[tgt->protocol], tgt->new_scsi_id); } /** @@ -4766,7 +4903,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); switch (status) { case IBMVFC_MAD_SUCCESS: - tgt_dbg(tgt, "Query Target succeeded\n"); + tgt_dbg(tgt, "%s Query Target succeeded\n", proto_type[tgt->protocol]); if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id) ibmvfc_del_tgt(tgt); else @@ -4788,9 +4925,9 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt) else ibmvfc_del_tgt(tgt); - tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", - ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), - be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), + tgt_log(tgt, level, "%s Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n", + proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), + be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type), ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status); @@ -4830,7 +4967,10 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt) query_tgt = &evt->iu.query_tgt; memset(query_tgt, 0, sizeof(*query_tgt)); query_tgt->common.version = cpu_to_be32(1); - query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET); + if (tgt->protocol == IBMVFC_PROTO_SCSI) + query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET); + else + query_tgt->common.opcode = cpu_to_be32(IBMVFC_NVMF_QUERY_TARGET); query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt)); query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name); @@ -4840,7 +4980,7 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt) ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); kref_put(&tgt->kref, ibmvfc_release_tgt); } else - tgt_dbg(tgt, "Sent Query Target\n"); + tgt_dbg(tgt, "Sent %s Query Target\n", proto_type[tgt->protocol]); } /** @@ -4852,25 +4992,32 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt) * 0 on success / other on failure **/ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, - struct ibmvfc_discover_targets_entry *target) + struct ibmvfc_discover_targets_entry *target, + enum ibmvfc_protocol protocol) { struct ibmvfc_target *stgt = NULL; struct ibmvfc_target *wtgt = NULL; struct ibmvfc_target *tgt; + struct ibmvfc_channels *channels; unsigned long flags; u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK; u64 wwpn = be64_to_cpu(target->wwpn); + if (protocol == IBMVFC_PROTO_SCSI) + channels = &vhost->scsi_scrqs; + else + channels = &vhost->nvme_scrqs; + /* Look to see if we already have a target allocated for this SCSI ID or WWPN */ spin_lock_irqsave(vhost->host->host_lock, flags); - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &channels->targets, queue) { if (tgt->wwpn == wwpn) { wtgt = tgt; break; } } - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &channels->targets, queue) { if (tgt->scsi_id == scsi_id) { stgt = tgt; break; @@ -4918,6 +5065,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO); memset(tgt, 0, sizeof(*tgt)); + tgt->protocol = protocol; tgt->scsi_id = scsi_id; tgt->wwpn = wwpn; tgt->vhost = vhost; @@ -4927,7 +5075,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); spin_lock_irqsave(vhost->host->host_lock, flags); tgt->cancel_key = vhost->task_set++; - list_add_tail(&tgt->queue, &vhost->targets); + list_add_tail(&tgt->queue, &channels->targets); unlock_out: spin_unlock_irqrestore(vhost->host->host_lock, flags); @@ -4945,8 +5093,12 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost) { int i, rc; - for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++) - rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]); + for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++) + rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i], + vhost->scsi_scrqs.protocol); + for (i = 0; !rc && i < vhost->nvme_scrqs.num_targets; i++) + rc = ibmvfc_alloc_target(vhost, &vhost->nvme_scrqs.disc_buf[i], + vhost->nvme_scrqs.protocol); return rc; } @@ -4960,19 +5112,30 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt) { struct ibmvfc_host *vhost = evt->vhost; struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets; + struct ibmvfc_channels *channels; u32 mad_status = be16_to_cpu(rsp->common.status); + u32 opcode = be32_to_cpu(rsp->common.opcode); int level = IBMVFC_DEFAULT_LOG_LEVEL; + if (opcode == IBMVFC_DISC_TARGETS) + channels = &vhost->scsi_scrqs; + else + channels = &vhost->nvme_scrqs; + switch (mad_status) { case IBMVFC_MAD_SUCCESS: - ibmvfc_dbg(vhost, "Discover Targets succeeded\n"); - vhost->num_targets = min_t(u32, be32_to_cpu(rsp->num_written), - max_targets); + ibmvfc_dbg(vhost, "Discover %s Targets succeeded\n", + proto_type[channels->protocol]); + channels->num_targets = min_t(u32, be32_to_cpu(rsp->num_written), + max_targets); + ibmvfc_dbg(vhost, "%d %s targets found\n", channels->num_targets, + proto_type[channels->protocol]); ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS); break; case IBMVFC_MAD_FAILED: level += ibmvfc_retry_host_init(vhost); - ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n", + ibmvfc_log(vhost, level, "Discover %s Targets failed: %s (%x:%x)\n", + proto_type[channels->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)); break; @@ -4988,6 +5151,32 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt) wake_up(&vhost->work_wait_q); } +static struct ibmvfc_event *ibmvfc_get_disc_event(struct ibmvfc_channels *channels) +{ + struct ibmvfc_discover_targets *mad; + struct ibmvfc_host *vhost = ibmvfc_channels_to_vhost(channels); + struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq); + + if (!evt) + return NULL; + + ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT); + mad = &evt->iu.discover_targets; + memset(mad, 0, sizeof(*mad)); + mad->common.version = cpu_to_be32(1); + if (channels->protocol == IBMVFC_PROTO_SCSI) + mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS); + else + mad->common.opcode = cpu_to_be32(IBMVFC_DISC_NVMF_TARGETS); + mad->common.length = cpu_to_be16(sizeof(*mad)); + mad->bufflen = cpu_to_be32(channels->disc_buf_sz); + mad->buffer.va = cpu_to_be64(channels->disc_buf_dma); + mad->buffer.len = cpu_to_be32(channels->disc_buf_sz); + mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST); + + return evt; +} + /** * ibmvfc_discover_targets - Send Discover Targets MAD * @vhost: ibmvfc host struct @@ -4995,30 +5184,168 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt) **/ static void ibmvfc_discover_targets(struct ibmvfc_host *vhost) { - struct ibmvfc_discover_targets *mad; - struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq); + struct ibmvfc_event *evt = ibmvfc_get_disc_event(&vhost->scsi_scrqs); int level = IBMVFC_DEFAULT_LOG_LEVEL; if (!evt) { - ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n"); + ibmvfc_log(vhost, level, "Discover SCSI Targets failed: no available events\n"); ibmvfc_hard_reset_host(vhost); return; } - ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT); - mad = &evt->iu.discover_targets; + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); + + if (!ibmvfc_send_event(evt, vhost, default_timeout)) + ibmvfc_dbg(vhost, "Sent discover SCSI targets\n"); + else + goto link_down; + + if (!ibmvfc_nvme_active(vhost)) + return; + + evt = ibmvfc_get_disc_event(&vhost->nvme_scrqs); + if (!evt) { + ibmvfc_log(vhost, level, "Discover NVMe Targets failed: no available events\n"); + ibmvfc_hard_reset_host(vhost); + return; + } + + if (!ibmvfc_send_event(evt, vhost, default_timeout)) + ibmvfc_dbg(vhost, "Sent discover NVMe targets\n"); + else + goto link_down; + + return; + +link_down: + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); +} + +static void ibmvfc_fabric_login_nvme_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_host *vhost = evt->vhost; + struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login; + u32 mad_status = be16_to_cpu(rsp->common.status); + int level = IBMVFC_DEFAULT_LOG_LEVEL; + + switch (mad_status) { + case IBMVFC_MAD_SUCCESS: + fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id); + ibmvfc_nvme_register(vhost); + ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n"); + break; + case IBMVFC_MAD_FAILED: + if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error))) + level += ibmvfc_retry_host_init(vhost); + else + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + ibmvfc_log(vhost, level, "NVMe fabric login failed: %s (%x:%x)\n", + ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), + be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)); + ibmvfc_free_event(evt); + return; + case IBMVFC_MAD_DRIVER_FAILED: + ibmvfc_free_event(evt); + return; + default: + dev_err(vhost->dev, "Invalid NVMe fabric login response: 0x%x\n", mad_status); + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + break; + } + + ibmvfc_free_event(evt); + + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); + wake_up(&vhost->work_wait_q); +} + +static void ibmvfc_fabric_login_nvme(struct ibmvfc_host *vhost) +{ + struct ibmvfc_fabric_login_mad *mad; + struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq); + + if (!evt) { + ibmvfc_retry_host_init(vhost); + return; + } + + ibmvfc_init_event(evt, ibmvfc_fabric_login_nvme_done, IBMVFC_MAD_FORMAT); + mad = &evt->iu.fabric_login; memset(mad, 0, sizeof(*mad)); mad->common.version = cpu_to_be32(1); - mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS); + mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_FABRIC_LOGIN); mad->common.length = cpu_to_be16(sizeof(*mad)); - mad->bufflen = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz); - mad->buffer.va = cpu_to_be64(vhost->scsi_scrqs.disc_buf_dma); - mad->buffer.len = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz); - mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST); + + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); + + if (!ibmvfc_send_event(evt, vhost, default_timeout)) + ibmvfc_dbg(vhost, "Send NVMe fabric login\n"); + else + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); +} + +static void ibmvfc_fabric_login_scsi_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_host *vhost = evt->vhost; + struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login; + u32 mad_status = be16_to_cpu(rsp->common.status); + int level = IBMVFC_DEFAULT_LOG_LEVEL; + + switch (mad_status) { + case IBMVFC_MAD_SUCCESS: + fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id); + ibmvfc_dbg(vhost, "SCSI fabric login succeeded\n"); + break; + case IBMVFC_MAD_FAILED: + if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error))) + level += ibmvfc_retry_host_init(vhost); + else + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + ibmvfc_log(vhost, level, "SCSI fabric login failed: %s (%x:%x)\n", + ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)), + be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)); + ibmvfc_free_event(evt); + return; + case IBMVFC_MAD_DRIVER_FAILED: + ibmvfc_free_event(evt); + return; + default: + dev_err(vhost->dev, "Invalid SCSI fabric login response: 0x%x\n", mad_status); + ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); + break; + } + + ibmvfc_free_event(evt); + + if (vhost->do_nvme_login) { + ibmvfc_fabric_login_nvme(vhost); + } else { + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); + wake_up(&vhost->work_wait_q); + } +} + +static void ibmvfc_fabric_login_scsi(struct ibmvfc_host *vhost) +{ + struct ibmvfc_fabric_login_mad *mad; + struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq); + + if (!evt) { + ibmvfc_retry_host_init(vhost); + return; + } + + ibmvfc_init_event(evt, ibmvfc_fabric_login_scsi_done, IBMVFC_MAD_FORMAT); + mad = &evt->iu.fabric_login; + memset(mad, 0, sizeof(*mad)); + mad->common.version = cpu_to_be32(1); + mad->common.opcode = cpu_to_be32(IBMVFC_FABRIC_LOGIN); + mad->common.length = cpu_to_be16(sizeof(*mad)); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT); if (!ibmvfc_send_event(evt, vhost, default_timeout)) - ibmvfc_dbg(vhost, "Sent discover targets\n"); + ibmvfc_dbg(vhost, "Send SCSI fabric login\n"); else ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD); } @@ -5027,10 +5354,11 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) { struct ibmvfc_host *vhost = evt->vhost; struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf; - struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs; + struct ibmvfc_channels *scsi = &vhost->scsi_scrqs; + struct ibmvfc_channels *nvme = &vhost->nvme_scrqs; u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status); int level = IBMVFC_DEFAULT_LOG_LEVEL; - int flags, active_queues, i; + int flags, i; ibmvfc_free_event(evt); @@ -5039,22 +5367,28 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) ibmvfc_dbg(vhost, "Channel Setup succeeded\n"); flags = be32_to_cpu(setup->flags); vhost->do_enquiry = 0; - active_queues = be32_to_cpu(setup->num_scsi_subq_channels); - scrqs->active_queues = active_queues; + scsi->active_queues = be32_to_cpu(setup->num_scsi_subq_channels); + nvme->active_queues = be32_to_cpu(setup->num_nvme_subq_channels); if (flags & IBMVFC_CHANNELS_CANCELED) { ibmvfc_dbg(vhost, "Channels Canceled\n"); vhost->using_channels = 0; - } else { - if (active_queues) - vhost->using_channels = 1; - for (i = 0; i < active_queues; i++) - scrqs->scrqs[i].vios_cookie = - be64_to_cpu(setup->channel_handles[i]); - - ibmvfc_dbg(vhost, "Using %u channels\n", - vhost->scsi_scrqs.active_queues); + break; } + + if (scsi->active_queues || nvme->active_queues) + vhost->using_channels = 1; + for (i = 0; i < scsi->active_queues; i++) + scsi->scrqs[i].vios_cookie = + be64_to_cpu(setup->channel_handles[i]); + for (i = 0; i < nvme->active_queues; i++) + nvme->scrqs[i].vios_cookie = + be64_to_cpu(setup->channel_handles[scsi->active_queues + i]); + + ibmvfc_dbg(vhost, "Using %u SCSI channels\n", + scsi->active_queues); + ibmvfc_dbg(vhost, "Using %u NVMe channels\n", + nvme->active_queues); break; case IBMVFC_MAD_FAILED: level += ibmvfc_retry_host_init(vhost); @@ -5069,8 +5403,12 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt) return; } - ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); - wake_up(&vhost->work_wait_q); + if (vhost->do_scsi_login) { + ibmvfc_fabric_login_scsi(vhost); + } else { + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); + wake_up(&vhost->work_wait_q); + } } static void ibmvfc_channel_setup(struct ibmvfc_host *vhost) @@ -5078,9 +5416,12 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost) struct ibmvfc_channel_setup_mad *mad; struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf; struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq); - struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs; - unsigned int num_channels = - min(scrqs->desired_queues, vhost->max_vios_scsi_channels); + struct ibmvfc_channels *scsi = &vhost->scsi_scrqs; + struct ibmvfc_channels *nvme = &vhost->nvme_scrqs; + unsigned int scsi_channels = + min(scsi->desired_queues, vhost->max_vios_scsi_channels); + unsigned int nvme_channels = + min(nvme->desired_queues, vhost->max_vios_nvme_channels); int level = IBMVFC_DEFAULT_LOG_LEVEL; int i; @@ -5091,12 +5432,17 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost) } memset(setup_buf, 0, sizeof(*setup_buf)); - if (num_channels == 0) + if (!scsi_channels && !nvme_channels) setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS); else { - setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels); - for (i = 0; i < num_channels; i++) - setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie); + setup_buf->num_scsi_subq_channels = cpu_to_be32(scsi_channels); + setup_buf->num_nvme_subq_channels = cpu_to_be32(nvme_channels); + for (i = 0; i < scsi_channels; i++) + setup_buf->channel_handles[i] = + cpu_to_be64(scsi->scrqs[i].cookie); + for (i = 0; i < nvme_channels; i++) + setup_buf->channel_handles[scsi_channels + i] = + cpu_to_be64(nvme->scrqs[i].cookie); } ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT); @@ -5127,6 +5473,7 @@ static void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt) case IBMVFC_MAD_SUCCESS: ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n"); vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels); + vhost->max_vios_nvme_channels = be32_to_cpu(rsp->num_nvme_subq_channels); ibmvfc_free_event(evt); break; case IBMVFC_MAD_FAILED: @@ -5261,8 +5608,14 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt) vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ; vhost->host->max_sectors = npiv_max_sectors; - if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) { - ibmvfc_channel_enquiry(vhost); + + if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS)) { + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI)) + vhost->do_scsi_login = 1; + if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF)) + vhost->do_nvme_login = 1; + if (vhost->do_enquiry) + ibmvfc_channel_enquiry(vhost); } else { vhost->do_enquiry = 0; ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY); @@ -5323,6 +5676,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt) case IBMVFC_MAD_SUCCESS: if (list_empty(&vhost->crq.sent) && vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) { + ibmvfc_nvme_unregister(vhost); ibmvfc_init_host(vhost); return; } @@ -5383,7 +5737,12 @@ static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost) { struct ibmvfc_target *tgt; - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (tgt->action == IBMVFC_TGT_ACTION_INIT || + tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) + return 1; + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (tgt->action == IBMVFC_TGT_ACTION_INIT || tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) return 1; @@ -5403,7 +5762,12 @@ static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost) { struct ibmvfc_target *tgt; - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT || + tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT) + return 1; + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT || tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT) return 1; @@ -5433,10 +5797,16 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) case IBMVFC_HOST_ACTION_QUERY_TGTS: if (vhost->discovery_threads == disc_threads) return 0; - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) if (tgt->action == IBMVFC_TGT_ACTION_INIT) return 1; - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) + if (tgt->action == IBMVFC_TGT_ACTION_INIT) + return 1; + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) + if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) + return 0; + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT) return 0; return 1; @@ -5444,10 +5814,16 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost) case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: if (vhost->discovery_threads == disc_threads) return 0; - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) + if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) + return 1; + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) return 1; - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) + if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT) + return 0; + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT) return 0; return 1; @@ -5500,6 +5876,51 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events) fc_host_post_event(vhost->host, fc_get_event_number(), FCH_EVT_LINKUP, 0); } +/** + * ibmvfc_tgt_add_nvme_rport - Tell the FC transport about a new remote port + * @tgt: ibmvfc target struct + * + **/ +static void ibmvfc_tgt_add_nvme_rport(struct ibmvfc_target *tgt) +{ + struct ibmvfc_host *vhost = tgt->vhost; + struct nvme_fc_remote_port *rport; + unsigned long flags; + + tgt_dbg(tgt, "Adding NVMe rport\n"); + ibmvfc_nvme_register_remoteport(tgt); + spin_lock_irqsave(vhost->host->host_lock, flags); + rport = tgt->nvme_remote_port; + + if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { + tgt_dbg(tgt, "Deleting NVMe rport\n"); + list_del(&tgt->queue); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_nvme_unregister_remoteport(tgt); + timer_delete_sync(&tgt->timer); + kref_put(&tgt->kref, ibmvfc_release_tgt); + return; + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { + tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n"); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT); + tgt->init_retries = 0; + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_nvme_unregister_remoteport(tgt); + return; + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + return; + } + + if (rport) { + tgt_dbg(tgt, "NVMe rport add succeeded\n"); + tgt->target_id = tgt->nvme_remote_port->port_id; + } else + tgt_dbg(tgt, "NVMe rport add failed\n"); + spin_unlock_irqrestore(vhost->host->host_lock, flags); +} + /** * ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port * @tgt: ibmvfc target struct @@ -5564,6 +5985,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) struct ibmvfc_target *tgt; unsigned long flags; struct fc_rport *rport; + struct nvme_fc_remote_port *nvme_rport; LIST_HEAD(purge); int rc; @@ -5579,6 +6001,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) list_splice_init(&vhost->purge, &purge); spin_unlock_irqrestore(vhost->host->host_lock, flags); ibmvfc_complete_purge(&purge); + ibmvfc_nvme_unregister(vhost); rc = ibmvfc_reset_crq(vhost); spin_lock_irqsave(vhost->host->host_lock, flags); @@ -5635,12 +6058,20 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) vhost->job_step(vhost); break; case IBMVFC_HOST_ACTION_QUERY: - list_for_each_entry(tgt, &vhost->targets, queue) + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) + ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target); + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target); ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS); break; case IBMVFC_HOST_ACTION_QUERY_TGTS: - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (tgt->action == IBMVFC_TGT_ACTION_INIT) { + tgt->job_step(tgt); + break; + } + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (tgt->action == IBMVFC_TGT_ACTION_INIT) { tgt->job_step(tgt); break; @@ -5652,7 +6083,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) break; case IBMVFC_HOST_ACTION_TGT_DEL: case IBMVFC_HOST_ACTION_TGT_DEL_FAILED: - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) { + tgt->job_step(tgt); + break; + } + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) { tgt->job_step(tgt); break; @@ -5664,7 +6101,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) return; } - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { tgt_dbg(tgt, "Deleting rport\n"); rport = tgt->rport; @@ -5702,6 +6139,30 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) } } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { + if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) { + tgt_dbg(tgt, "Deleting NVMe rport\n"); + nvme_rport = tgt->nvme_remote_port; + list_del(&tgt->queue); + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT); + spin_unlock_irqrestore(vhost->host->host_lock, flags); + if (nvme_rport) + ibmvfc_nvme_unregister_remoteport(tgt); + timer_delete_sync(&tgt->timer); + kref_put(&tgt->kref, ibmvfc_release_tgt); + return; + } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) { + tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n"); + nvme_rport = tgt->nvme_remote_port; + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT); + tgt->init_retries = 0; + spin_unlock_irqrestore(vhost->host->host_lock, flags); + if (nvme_rport) + ibmvfc_nvme_unregister_remoteport(tgt); + return; + } + } + if (vhost->state == IBMVFC_INITIALIZING) { if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) { if (vhost->reinit) { @@ -5739,7 +6200,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) spin_lock_irqsave(vhost->host->host_lock, flags); break; case IBMVFC_HOST_ACTION_TGT_INIT: - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { + if (tgt->action == IBMVFC_TGT_ACTION_INIT) { + tgt->job_step(tgt); + break; + } + } + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { if (tgt->action == IBMVFC_TGT_ACTION_INIT) { tgt->job_step(tgt); break; @@ -6094,6 +6561,13 @@ static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost) ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs); + if (vhost->nvme_enabled) { + if (ibmvfc_alloc_channels(vhost, &vhost->nvme_scrqs)) + vhost->nvme_enabled = 0; + else + ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs); + } + LEAVE; } @@ -6122,8 +6596,10 @@ static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost) return; ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs); - ibmvfc_release_channels(vhost, &vhost->scsi_scrqs); + + ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs); + ibmvfc_release_channels(vhost, &vhost->nvme_scrqs); LEAVE; } @@ -6148,6 +6624,7 @@ static void ibmvfc_free_mem(struct ibmvfc_host *vhost) mempool_destroy(vhost->tgt_pool); kfree(vhost->trace); ibmvfc_free_disc_buf(vhost->dev, &vhost->scsi_scrqs); + ibmvfc_free_disc_buf(vhost->dev, &vhost->nvme_scrqs); dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf), vhost->login_buf, vhost->login_buf_dma); dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf), @@ -6210,12 +6687,15 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) if (ibmvfc_alloc_disc_buf(dev, &vhost->scsi_scrqs)) goto free_login_buffer; + if (ibmvfc_alloc_disc_buf(dev, &vhost->nvme_scrqs)) + goto free_scsi_disc_buffer; + vhost->trace = kzalloc_objs(struct ibmvfc_trace_entry, IBMVFC_NUM_TRACE_ENTRIES); atomic_set(&vhost->trace_index, -1); if (!vhost->trace) - goto free_scsi_disc_buffer; + goto free_nvme_disc_buffer; vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ, sizeof(struct ibmvfc_target)); @@ -6241,6 +6721,8 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost) mempool_destroy(vhost->tgt_pool); free_trace: kfree(vhost->trace); +free_nvme_disc_buffer: + ibmvfc_free_disc_buf(dev, &vhost->nvme_scrqs); free_scsi_disc_buffer: ibmvfc_free_disc_buf(dev, &vhost->scsi_scrqs); free_login_buffer: @@ -6266,6 +6748,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work) rport_add_work_q); struct ibmvfc_target *tgt; struct fc_rport *rport; + struct nvme_fc_remote_port *nvme_rport; unsigned long flags; int did_work; @@ -6276,7 +6759,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work) if (vhost->state != IBMVFC_ACTIVE) break; - list_for_each_entry(tgt, &vhost->targets, queue) { + list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) { if (tgt->add_rport) { did_work = 1; tgt->add_rport = 0; @@ -6299,7 +6782,28 @@ static void ibmvfc_rport_add_thread(struct work_struct *work) break; } } - } while(did_work); + + list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) { + if (tgt->add_rport) { + did_work = 1; + tgt->add_rport = 0; + kref_get(&tgt->kref); + nvme_rport = tgt->nvme_remote_port; + if (!nvme_rport) { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_tgt_add_nvme_rport(tgt); + } else { + spin_unlock_irqrestore(vhost->host->host_lock, flags); + if (IS_ENABLED(CONFIG_NVME_FC)) + nvme_fc_rescan_remoteport(nvme_rport); + } + + kref_put(&tgt->kref, ibmvfc_release_tgt); + spin_lock_irqsave(vhost->host->host_lock, flags); + break; + } + } + } while (did_work); if (vhost->state == IBMVFC_ACTIVE) vhost->scan_complete = 1; @@ -6322,7 +6826,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) struct device *dev = &vdev->dev; int rc = -ENOMEM; unsigned int online_cpus = num_online_cpus(); - unsigned int max_scsi_queues = min((unsigned int)IBMVFC_MAX_SCSI_QUEUES, online_cpus); + unsigned int max_scsi_queues = min_t(unsigned int, IBMVFC_MAX_SCSI_QUEUES, online_cpus); + unsigned int max_nvme_queues = min_t(unsigned int, IBMVFC_MAX_NVME_QUEUES, online_cpus); ENTER; shost = scsi_host_alloc(&driver_template, sizeof(*vhost)); @@ -6341,7 +6846,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1; vhost = shost_priv(shost); - INIT_LIST_HEAD(&vhost->targets); + INIT_LIST_HEAD(&vhost->scsi_scrqs.targets); + INIT_LIST_HEAD(&vhost->nvme_scrqs.targets); INIT_LIST_HEAD(&vhost->purge); sprintf(vhost->name, IBMVFC_NAME); vhost->host = shost; @@ -6356,6 +6862,10 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) vhost->scsi_scrqs.protocol = IBMVFC_PROTO_SCSI; vhost->using_channels = 0; vhost->do_enquiry = 1; + vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0; + vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels); + vhost->nvme_scrqs.max_queues = max_nvme_queues; + vhost->nvme_scrqs.protocol = IBMVFC_PROTO_NVME; vhost->scan_timeout = 0; strcpy(vhost->partition_name, "UNKNOWN"); @@ -6441,6 +6951,7 @@ static void ibmvfc_remove(struct vio_dev *vdev) ibmvfc_wait_while_resetting(vhost); kthread_stop(vhost->work_thread); + flush_work(&vhost->rport_add_work_q); fc_remove_host(vhost->host); scsi_remove_host(vhost->host); diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c new file mode 100644 index 000000000000..54f80b6265d4 --- /dev/null +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c @@ -0,0 +1,573 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * ibmvfc-nvme.c -- IBM Power Virtual Fibre Channel NVMeoF HBA driver + * + * Written By: Tyrel Datwyler , IBM Corporation + * + * Copyright (C) IBM Corporation, 2026 + */ + +#include +#include + +#include "ibmvfc-nvme.h" + +static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT; + +static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport) +{ + struct ibmvfc_host *vhost = lport->private; + + vhost->nvme_local_port = NULL; + complete(&vhost->nvme_delete_done); +} + +static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport) +{ + struct ibmvfc_target *tgt = rport->private; + + tgt->nvme_remote_port = NULL; + complete(&tgt->nvme_delete_done); +} + +static int ibmvfc_nvme_create_queue(struct nvme_fc_local_port *lport, unsigned int qidx, + u16 qsize, void **handle) +{ + struct ibmvfc_host *vhost = lport->private; + struct ibmvfc_nvme_qhandle *qhandle; + + if (!vhost->nvme_scrqs.active_queues) + return -ENODEV; + + qhandle = kzalloc_obj(struct ibmvfc_nvme_qhandle); + if (!qhandle) + return -ENOMEM; + + qhandle->cpu_id = raw_smp_processor_id(); + qhandle->qidx = qidx; + + /* Admin and first IO queue are both mapped to index 0 */ + if (qidx) + qhandle->index = (qidx - 1) % vhost->nvme_scrqs.active_queues; + else + qhandle->index = qidx; + + qhandle->queue = &vhost->nvme_scrqs.scrqs[qhandle->index]; + + *handle = qhandle; + return 0; +} + +static void ibmvfc_nvme_delete_queue(struct nvme_fc_local_port *lport, unsigned int qidx, + void *handle) +{ + kfree(handle); +} + +static void ibmvfc_ls_req_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_target *tgt = evt->tgt; + struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru; + struct fcnvme_ls_rqst_w0 *ls_rqst; + struct fcnvme_ls_cr_assoc_acc *ls_resp; + u32 status = be16_to_cpu(mad->common.status); + int rc = 0; + + ls_rqst = (struct fcnvme_ls_rqst_w0 *)evt->ls_req->rqstaddr; + ls_resp = (struct fcnvme_ls_cr_assoc_acc *)evt->ls_req->rspaddr; + + switch (status) { + case IBMVFC_MAD_SUCCESS: + tgt_dbg(tgt, "ls_req succeeded\n"); + if ((ls_rqst->ls_cmd == FCNVME_LS_CREATE_ASSOCIATION) && + (ls_resp->hdr.w0.ls_cmd == FCNVME_LS_ACC)) { + tgt->assoc_id = be64_to_cpu(ls_resp->associd.association_id); + tgt_dbg(tgt, "assoc_id 0x%llx\n", tgt->assoc_id); + } + break; + case IBMVFC_MAD_DRIVER_FAILED: + break; + case IBMVFC_MAD_FAILED: + default: + tgt_info(tgt, "ls_req failed: %s (%x:%x) rc=0x%02X\n", + ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)), + be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error), status); + break; + } + + if (status) + rc = -EIO; + + evt->ls_req->done(evt->ls_req, rc); + + kref_put(&tgt->kref, ibmvfc_release_tgt); + ibmvfc_free_event(evt); +} + +static void ibmvfc_init_ls_req(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_req) +{ + struct ibmvfc_passthru_mad *mad = &evt->iu.passthru; + + memset(mad, 0, sizeof(*mad)); + mad->common.version = cpu_to_be32(2); + mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_PASSTHRU); + mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu)); + mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) + + offsetof(struct ibmvfc_passthru_mad, iu)); + mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu)); + mad->iu.cmd_len = cpu_to_be32(ls_req->rqstlen); + mad->iu.rsp_len = cpu_to_be32(ls_req->rsplen); + mad->iu.cmd.va = cpu_to_be64(ls_req->rqstdma); + mad->iu.cmd.len = cpu_to_be32(ls_req->rqstlen); + mad->iu.rsp.va = cpu_to_be64(ls_req->rspdma); + mad->iu.rsp.len = cpu_to_be32(ls_req->rsplen); +} + +static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_req *ls_req) +{ + struct ibmvfc_host *vhost = lport->private; + struct ibmvfc_target *tgt = rport->private; + struct ibmvfc_passthru_mad *mad; + struct ibmvfc_event *evt; + + kref_get(&tgt->kref); + evt = ibmvfc_get_event(&vhost->crq); + if (!evt) { + kref_put(&tgt->kref, ibmvfc_release_tgt); + return -EBUSY; + } + + ibmvfc_init_event(evt, ibmvfc_ls_req_done, IBMVFC_MAD_FORMAT); + evt->tgt = tgt; + evt->ls_req = ls_req; + ls_req->private = evt; + + ibmvfc_init_ls_req(evt, ls_req); + mad = &evt->iu.passthru; + mad->iu.flags = cpu_to_be32(IBMVFC_FC4_LS_DSC_CTRL); + mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id); + mad->iu.cancel_key = cpu_to_be32((u64)evt); + mad->iu.target_wwpn = cpu_to_be64(tgt->wwpn); + + ibmvfc_dbg(vhost, "nvme_ls_req\n"); + if (ibmvfc_send_event(evt, vhost, IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT)) { + kref_put(&tgt->kref, ibmvfc_release_tgt); + return -ENXIO; + } + + return 0; +} + +static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *evt) +{ + /* copy the response back */ + if (evt->sync_iu) + *evt->sync_iu = *evt->xfer_iu; + + complete(&evt->comp); +} + +static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_abort) +{ + struct ibmvfc_tmf *tmf; + struct ibmvfc_event *abt_evt = ls_abort->private; + struct ibmvfc_target *tgt = abt_evt->tgt; + struct ibmvfc_host *vhost = evt->vhost; + + tmf = &evt->iu.tmf; + memset(tmf, 0, sizeof(*tmf)); + tmf->common.version = cpu_to_be32(2); + tmf->target_wwpn = cpu_to_be64(tgt->wwpn); + tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD); + tmf->common.length = cpu_to_be16(sizeof(*tmf)); + if (vhost->state != IBMVFC_ACTIVE) + if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS)) + tmf->flags = cpu_to_be32(IBMVFC_TMF_SUPPRESS_ABTS); + tmf->cancel_key = cpu_to_be32((u64)abt_evt); + tmf->my_cancel_key = cpu_to_be32((u64)evt); + tmf->assoc_id = cpu_to_be64(tgt->assoc_id); + + init_completion(&evt->comp); +} + +static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + struct nvmefc_ls_req *ls_abort) +{ + struct ibmvfc_host *vhost = lport->private; + struct ibmvfc_target *tgt = rport->private; + struct ibmvfc_event *evt; + union ibmvfc_iu rsp; + unsigned long flags; + u16 status; + + evt = ibmvfc_get_event(&vhost->crq); + if (!vhost->logged_in || !evt) + return; + + spin_lock_irqsave(vhost->host->host_lock, flags); + kref_get(&tgt->kref); + ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT); + ibmvfc_init_ls_abort(evt, ls_abort); + evt->sync_iu = &rsp; + + if (ibmvfc_send_event(evt, vhost, default_timeout)) + goto out; + + spin_unlock_irqrestore(vhost->host->host_lock, flags); + + wait_for_completion(&evt->comp); + status = be16_to_cpu(rsp.mad_common.status); + spin_lock_irqsave(vhost->host->host_lock, flags); + ibmvfc_free_event(evt); +out: + spin_unlock_irqrestore(vhost->host->host_lock, flags); + ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status); + kref_put(&tgt->kref, ibmvfc_release_tgt); +} + +static void ibmvfc_nvme_done(struct ibmvfc_event *evt) +{ + struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd; + struct nvmefc_fcp_req *fcp_req = evt->fcp_req; + struct nvme_fc_ersp_iu *ersp = (struct nvme_fc_ersp_iu *)fcp_req->rspaddr; + struct nvme_completion *cqe = &ersp->cqe; + struct nvme_command *sqe = &((struct nvme_fc_cmd_iu *)fcp_req->cmdaddr)->sqe; + + ibmvfc_dbg(evt->vhost, "fc_done: (%x:%x)\n", be16_to_cpu(vfc_cmd->status), + be16_to_cpu(vfc_cmd->error)); + ibmvfc_dbg(evt->vhost, "fc_done: cmdlen: %d, rsplen %d, payload_len %d\n", + fcp_req->cmdlen, fcp_req->rsplen, fcp_req->payload_length); + + fcp_req->status = 0; + if (!vfc_cmd->status) { + fcp_req->rcv_rsplen = NVME_FC_SIZEOF_ZEROS_RSP; + fcp_req->transferred_length = fcp_req->payload_length; + } else if (be16_to_cpu(vfc_cmd->status) & IBMVFC_FC_NVME_STATUS) { + fcp_req->rcv_rsplen = sizeof(struct nvme_fc_ersp_iu); + fcp_req->transferred_length = be32_to_cpu(ersp->xfrd_len); + if (be16_to_cpu(vfc_cmd->error) & IBMVFC_NVMS_VALID_NODMA_CQE) + cqe->command_id = sqe->common.command_id; + } else { + fcp_req->rcv_rsplen = 0; + fcp_req->transferred_length = 0; + fcp_req->status = NVME_SC_INTERNAL; + } + + fcp_req->done(fcp_req); + ibmvfc_free_event(evt); +} + +static struct ibmvfc_cmd *ibmvfc_nvme_init_vfc_cmd(struct ibmvfc_event *evt, + struct nvme_fc_remote_port *rport, + struct nvmefc_fcp_req *fcp_req) +{ + struct ibmvfc_target *tgt = rport->private; + struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd; + + memset(vfc_cmd, 0, sizeof(*vfc_cmd)); + + vfc_cmd->resp.va = cpu_to_be64(fcp_req->rspdma); + vfc_cmd->resp.len = cpu_to_be32(fcp_req->rsplen); + vfc_cmd->frame_type = cpu_to_be32(IBMVFC_NVME_FCP_TYPE); + vfc_cmd->flags |= cpu_to_be16(IBMVFC_NVMEOF_PROTOCOL); + vfc_cmd->payload_len = cpu_to_be32(fcp_req->cmdlen); + vfc_cmd->resp_len = cpu_to_be32(fcp_req->rsplen); + vfc_cmd->cancel_key = cpu_to_be32((u64)evt); + vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name); + vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id); + vfc_cmd->assoc_id = cpu_to_be64(tgt->assoc_id); + + memcpy(&vfc_cmd->v3nvme.iu, fcp_req->cmdaddr, fcp_req->cmdlen); + + return vfc_cmd; +} + +static void ibmvfc_nvme_map_sg_list(struct nvmefc_fcp_req *fcp_req, + struct srp_direct_buf *md) +{ + int i; + struct scatterlist *sg; + + for_each_sg(fcp_req->first_sgl, sg, fcp_req->sg_cnt, i) { + md[i].va = cpu_to_be64(sg_dma_address(sg)); + md[i].len = cpu_to_be32(sg_dma_len(sg)); + md[i].key = 0; + } +} + +static int ibmvfc_nvme_map_sg_data(struct nvmefc_fcp_req *fcp_req, + struct ibmvfc_event *evt, + struct ibmvfc_cmd *vfc_cmd) +{ + struct srp_direct_buf *data = &vfc_cmd->ioba; + struct ibmvfc_host *vhost = evt->vhost; + + if (!fcp_req->sg_cnt) { + vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC); + return 0; + } + + if (fcp_req->io_dir == NVMEFC_FCP_WRITE) + vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE); + else + vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ); + + if (fcp_req->sg_cnt == 1) { + ibmvfc_nvme_map_sg_list(fcp_req, data); + return 0; + } + + vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST); + + if (!evt->ext_list) { + evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC, + &evt->ext_list_token); + + if (!evt->ext_list) + return -ENOMEM; + } + + ibmvfc_nvme_map_sg_list(fcp_req, evt->ext_list); + + data->va = cpu_to_be64(evt->ext_list_token); + data->len = cpu_to_be32(fcp_req->sg_cnt * sizeof(struct srp_direct_buf)); + data->key = 0; + return 0; +} + +static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + void *hw_queue_handle, + struct nvmefc_fcp_req *fcp_req) +{ + struct ibmvfc_host *vhost = lport->private; + struct ibmvfc_nvme_qhandle *qhandle = hw_queue_handle; + struct ibmvfc_cmd *vfc_cmd; + struct ibmvfc_event *evt; + int rc; + + ibmvfc_dbg(vhost, "nvme_fcp_io\n"); + evt = ibmvfc_get_event(qhandle->queue); + if (!evt) + return -EBUSY; + + evt->hwq = qhandle->index; + ibmvfc_dbg(vhost, "vfc-nvme-mq-%d\n", evt->hwq); + + ibmvfc_init_event(evt, ibmvfc_nvme_done, IBMVFC_CMD_FORMAT); + evt->fcp_req = fcp_req; + fcp_req->private = evt; + + vfc_cmd = ibmvfc_nvme_init_vfc_cmd(evt, rport, fcp_req); + + vfc_cmd->correlation = cpu_to_be64((u64)evt); + + if (likely(!(rc = ibmvfc_nvme_map_sg_data(fcp_req, evt, vfc_cmd)))) + return ibmvfc_send_event(evt, vhost, 0); + + ibmvfc_free_event(evt); + + return rc; +} + +static void ibmvfc_init_fcp_abort(struct ibmvfc_event *evt, + struct nvmefc_fcp_req *abort_req) +{ + struct ibmvfc_tmf *tmf; + struct ibmvfc_event *abt_evt = abort_req->private; + struct ibmvfc_target *tgt = abt_evt->tgt; + + tmf = &evt->iu.tmf; + memset(tmf, 0, sizeof(*tmf)); + tmf->common.version = cpu_to_be32(2); + tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD); + tmf->common.length = cpu_to_be16(sizeof(*tmf)); + tmf->flags = cpu_to_be32(IBMVFC_TMF_ABORT_TASK | IBMVFC_TMF_NVMF_ASSOC); + tmf->cancel_key = cpu_to_be32((u64)abt_evt); + tmf->my_cancel_key = cpu_to_be32((u64)evt); + tmf->target_wwpn = cpu_to_be64(tgt->wwpn); + tmf->assoc_id = cpu_to_be64(tgt->assoc_id); + tmf->task_tag = cpu_to_be64((u64)abt_evt); + + init_completion(&evt->comp); +} + +static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport, + struct nvme_fc_remote_port *rport, + void *hw_queue_handle, + struct nvmefc_fcp_req *abort_req) +{ + struct ibmvfc_host *vhost = lport->private; + struct ibmvfc_target *tgt = rport->private; + struct ibmvfc_event *evt, *abt_evt = abort_req->private; + struct ibmvfc_queue *queue; + union ibmvfc_iu rsp; + unsigned long flags; + u16 status = 0; + + if (!abt_evt) + return; + + queue = abt_evt->queue; + if (!vhost->logged_in || !queue) + return; + + evt = ibmvfc_get_event(queue); + if (!evt) + return; + + spin_lock_irqsave(queue->q_lock, flags); + kref_get(&tgt->kref); + ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT); + ibmvfc_init_fcp_abort(evt, abort_req); + evt->sync_iu = &rsp; + + if (ibmvfc_send_event(evt, vhost, default_timeout)) + goto out; + + spin_unlock_irqrestore(queue->q_lock, flags); + + wait_for_completion(&evt->comp); + status = be16_to_cpu(rsp.mad_common.status); + + spin_lock_irqsave(queue->q_lock, flags); + ibmvfc_free_event(evt); +out: + spin_unlock_irqrestore(queue->q_lock, flags); + + if (status) + ibmvfc_dbg(vhost, "fcp_abort: cancel failed with rc=%x\n", status); + + kref_put(&tgt->kref, ibmvfc_release_tgt); +} + +static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = { + .localport_delete = ibmvfc_nvme_localport_delete, + .remoteport_delete = ibmvfc_nvme_remoteport_delete, + .create_queue = ibmvfc_nvme_create_queue, + .delete_queue = ibmvfc_nvme_delete_queue, + .ls_req = ibmvfc_nvme_ls_req, + .ls_abort = ibmvfc_nvme_ls_abort, + .fcp_io = ibmvfc_nvme_fcp_io, + .fcp_abort = ibmvfc_nvme_fcp_abort, + .map_queues = NULL, + .max_hw_queues = IBMVFC_NVME_HW_QUEUES, + .max_sgl_segments = 1024, + .max_dif_sgl_segments = 64, + .dma_boundary = 0xFFFFFFFF, + .local_priv_sz = sizeof(struct ibmvfc_host *), + .remote_priv_sz = sizeof(struct ibmvfc_target *), + .lsrqst_priv_sz = sizeof(struct ibmvfc_event *), + .fcprqst_priv_sz = sizeof(struct ibmvfc_event *), +}; + +int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt) +{ + struct ibmvfc_host *vhost = tgt->vhost; + struct nvme_fc_port_info pinfo; + int rc; + + if (!IS_ENABLED(CONFIG_NVME_FC)) + return 0; + + if (!vhost->nvme_local_port) { + dev_err(vhost->dev, "Attempt to register NVMe fc remoteport without valid localport\n"); + return -EINVAL; + } + + memset(&pinfo, 0, sizeof(struct nvme_fc_port_info)); + pinfo.node_name = tgt->ids.node_name; + pinfo.port_name = tgt->ids.port_name; + pinfo.port_id = tgt->ids.port_id; + pinfo.port_role = FC_PORT_ROLE_NVME_TARGET; + + rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo, + &tgt->nvme_remote_port); + + if (!rc) { + ibmvfc_log(vhost, 2, "register_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n", + pinfo.node_name, pinfo.port_name, pinfo.port_id); + tgt->nvme_remote_port->private = tgt; + } + + return rc; +} + +void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt) +{ + struct ibmvfc_host *vhost = tgt->vhost; + struct nvme_fc_remote_port *rport = tgt->nvme_remote_port; + int rc; + + if (!IS_ENABLED(CONFIG_NVME_FC)) + return; + + if (!tgt->nvme_remote_port) + return; + + ibmvfc_log(vhost, 2, "unregister_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n", + rport->node_name, rport->port_name, rport->port_id); + init_completion(&tgt->nvme_delete_done); + rc = nvme_fc_unregister_remoteport(tgt->nvme_remote_port); + + if (!rc) { + wait_for_completion(&tgt->nvme_delete_done); + } +} + +int ibmvfc_nvme_register(struct ibmvfc_host *vhost) +{ + struct nvme_fc_port_info pinfo; + int rc; + + if (!IS_ENABLED(CONFIG_NVME_FC)) + return 0; + + pinfo.node_name = fc_host_node_name(vhost->host); + pinfo.port_name = fc_host_port_name(vhost->host); + pinfo.port_id = fc_host_port_id(vhost->host); + pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR; + pinfo.dev_loss_tmo = 0; + + rc = nvme_fc_register_localport(&pinfo, &ibmvfc_nvme_fc_transport, + get_device(vhost->dev), + &vhost->nvme_local_port); + + if (!rc) { + ibmvfc_log(vhost, 2, "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n", + pinfo.node_name, pinfo.port_name, pinfo.port_id); + vhost->nvme_local_port->private = vhost; + } else { + dev_err(vhost->dev, "Failed to register NVMe fc localport (%d)\n", rc); + put_device(vhost->dev); + } + + return rc; +} + +void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost) +{ + int rc; + + if (!IS_ENABLED(CONFIG_NVME_FC)) + return; + + if (vhost->nvme_local_port) { + ibmvfc_log(vhost, 2, "unregister_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n", + vhost->nvme_local_port->node_name, + vhost->nvme_local_port->port_name, + vhost->nvme_local_port->port_id); + init_completion(&vhost->nvme_delete_done); + rc = nvme_fc_unregister_localport(vhost->nvme_local_port); + if (!rc) { + wait_for_completion(&vhost->nvme_delete_done); + } else + dev_err(vhost->dev, "Failed to unregister NVMe fc localport (%d)\n", rc); + + put_device(vhost->dev); + } +} diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h new file mode 100644 index 000000000000..e245b6ed0875 --- /dev/null +++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * ibmvfc-nvme.h -- IBM Power Virtual Fibre Channel NVMeoF HBA driver + * + * Written By: Tyrel Datwyler , IBM Corporation + * + * Copyright (C) IBM Corporation, 2026 + */ + +#ifndef _IBMVFC_NVME_H +#define _IBMVFC_NVME_H + +#include +#include +#include +#include +#include + +#include "ibmvfc.h" + +#define IBMVFC_NVME 0 +#define IBMVFC_NVME_HW_QUEUES 8 +#define IBMVFC_MAX_NVME_QUEUES 16 +#define IBMVFC_NVME_CHANNELS 8 + +#define IBMVFC_FC4_LS_TIMEOUT 15 +#define IBMVFC_FC4_LS_CANCEL_TIMEOUT 45 +#define IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT \ + (IBMVFC_FC4_LS_TIMEOUT + IBMVFC_FC4_LS_CANCEL_TIMEOUT) + +extern unsigned int ibmvfc_debug; + +struct ibmvfc_host; +struct ibmvfc_target; +struct ibmvfc_queue; + +struct ibmvfc_nvme_qhandle { + unsigned int qidx; + u16 cpu_id; + unsigned long index; + struct ibmvfc_queue *queue; +}; + +int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt); +void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt); +int ibmvfc_nvme_register(struct ibmvfc_host *vhost); +void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost); +#endif diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index c73ed2314ad0..ca80ceffe53a 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -1,20 +1,27 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + /* * ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter * * Written By: Brian King , IBM Corporation * - * Copyright (C) IBM Corporation, 2008 + * Copyright (C) IBM Corporation, 2008-2026 */ #ifndef _IBMVFC_H #define _IBMVFC_H +#include #include #include +#include #include +#include +#include + +#include "ibmvfc-nvme.h" -#define IBMVFC_NAME "ibmvfc" +#define IBMVFC_NAME "ibmvfc" #define IBMVFC_DRIVER_VERSION "1.0.11" #define IBMVFC_DRIVER_DATE "(April 12, 2013)" @@ -92,6 +99,7 @@ enum ibmvfc_cmd_status_flags { IBMVFC_FC_SCSI_ERROR = 0x0008, IBMVFC_HW_EVENT_LOGGED = 0x0010, IBMVFC_VIOS_LOGGED = 0x0020, + IBMVFC_FC_NVME_STATUS = 0x0040, }; enum ibmvfc_fabric_mapped_errors { @@ -124,20 +132,37 @@ enum ibmvfc_vios_errors { IBMVFC_COMMAND_FAILED = 0x8000, }; +enum ibmvfc_fc_nvme_errors { + IBMVFC_NVMS_VALID_ERSP = 0x0001, + IBMVFC_NVMS_VALID_NODMA_CQE = 0x0002, +}; + enum ibmvfc_mad_types { IBMVFC_NPIV_LOGIN = 0x0001, - IBMVFC_DISC_TARGETS = 0x0002, + IBMVFC_DISC_TARGETS = 0x0002, + IBMVFC_DISC_NVMF_TARGETS = 0x0003, IBMVFC_PORT_LOGIN = 0x0004, - IBMVFC_PROCESS_LOGIN = 0x0008, - IBMVFC_QUERY_TARGET = 0x0010, + IBMVFC_NVMF_PORT_LOGIN = 0x0005, + IBMVFC_PROCESS_LOGIN = 0x0008, + IBMVFC_NVMF_PROCESS_LOGIN = 0x0009, + IBMVFC_QUERY_TARGET = 0x0010, + IBMVFC_NVMF_QUERY_TARGET = 0x0011, IBMVFC_MOVE_LOGIN = 0x0020, - IBMVFC_IMPLICIT_LOGOUT = 0x0040, - IBMVFC_PASSTHRU = 0x0200, - IBMVFC_TMF_MAD = 0x0100, - IBMVFC_NPIV_LOGOUT = 0x0800, - IBMVFC_CHANNEL_ENQUIRY = 0x1000, - IBMVFC_CHANNEL_SETUP = 0x2000, - IBMVFC_CONNECTION_INFO = 0x4000, + IBMVFC_NVMF_MOVE_LOGIN = 0x0021, + IBMVFC_IMPLICIT_LOGOUT = 0x0040, + IBMVFC_NVMF_IMPLICIT_LOGOUT = 0x0041, + IBMVFC_RNID = 0x0080, + IBMVFC_NVMF_RNID = 0x0081, + IBMVFC_TMF_MAD = 0x0100, + IBMVFC_NVMF_TMF_MAD = 0x0101, + IBMVFC_PASSTHRU = 0x0200, + IBMVFC_NVMF_PASSTHRU = 0x0201, + IBMVFC_NPIV_LOGOUT = 0x0800, + IBMVFC_CHANNEL_ENQUIRY = 0x1000, + IBMVFC_CHANNEL_SETUP = 0x2000, + IBMVFC_CONNECTION_INFO = 0x4000, + IBMVFC_FABRIC_LOGIN = 0x8000, + IBMVFC_NVMF_FABRIC_LOGIN = 0x8001, }; struct ibmvfc_mad_common { @@ -175,11 +200,16 @@ struct ibmvfc_npiv_login { #define IBMVFC_FLUSH_ON_HALT 0x02 __be32 max_cmds; __be64 capabilities; -#define IBMVFC_CAN_MIGRATE 0x01 -#define IBMVFC_CAN_USE_CHANNELS 0x02 -#define IBMVFC_CAN_HANDLE_FPIN 0x04 -#define IBMVFC_CAN_USE_MAD_VERSION 0x08 -#define IBMVFC_CAN_SEND_VF_WWPN 0x10 +#define IBMVFC_CAN_MIGRATE 0x001 +#define IBMVFC_CAN_USE_CHANNELS 0x002 +#define IBMVFC_CAN_HANDLE_FPIN 0x004 +#define IBMVFC_CAN_USE_MAD_VERSION 0x008 +#define IBMVFC_CAN_SEND_VF_WWPN 0x010 +#define IBMVFC_YES_NVMEOF 0x020 +#define IBMVFC_YES_SCSI 0x040 +#define IBMVFC_CAN_USE_WWPN_ALL 0x080 +#define IBMVFC_USE_ASYNC_SUBQ 0x100 +#define IBMVFC_CAN_USE_NOOP_CMD 0x200 __be64 node_name; struct srp_direct_buf async; u8 partition_name[IBMVFC_MAX_NAME]; @@ -219,13 +249,18 @@ struct ibmvfc_npiv_login_resp { __be16 error; __be32 flags; #define IBMVFC_NATIVE_FC 0x01 - __be32 reserved; + __be32 possible_nports; __be64 capabilities; -#define IBMVFC_CAN_FLUSH_ON_HALT 0x08 -#define IBMVFC_CAN_SUPPRESS_ABTS 0x10 -#define IBMVFC_MAD_VERSION_CAP 0x20 -#define IBMVFC_HANDLE_VF_WWPN 0x40 -#define IBMVFC_CAN_SUPPORT_CHANNELS 0x80 +#define IBMVFC_CAN_FLUSH_ON_HALT 0x0008 +#define IBMVFC_CAN_SUPPRESS_ABTS 0x0010 +#define IBMVFC_MAD_VERSION_CAP 0x0020 +#define IBMVFC_HANDLE_VF_WWPN 0x0040 +#define IBMVFC_CAN_SUPPORT_CHANNELS 0x0080 +#define IBMVFC_SUPPORT_NVMEOF 0x0100 +#define IBMVFC_SUPPORT_SCSI 0x0200 +#define IBMVFC_SUPPORT_WWPN_ALL 0x0400 +#define IBMVFC_ASYNC_SUBQ 0x0800 +#define IBMVFC_SUPPORT_NOOP_CMD 0x1000 __be32 max_cmds; __be32 scsi_id_sz; __be64 max_dma_len; @@ -238,7 +273,7 @@ struct ibmvfc_npiv_login_resp { u8 port_loc_code[IBMVFC_MAX_NAME]; u8 drc_name[IBMVFC_MAX_NAME]; struct ibmvfc_service_parms service_parms; - __be64 reserved2; + __be64 reserved; } __packed __aligned(8); union ibmvfc_npiv_login_data { @@ -246,6 +281,17 @@ union ibmvfc_npiv_login_data { struct ibmvfc_npiv_login_resp resp; } __packed __aligned(8); +struct ibmvfc_fabric_login_mad { + struct ibmvfc_mad_common common; + __be64 flags; + __be64 capabilities; + __be64 nport_id; + __be16 status; + __be16 error; + __be32 reserved; + __be64 reserved2[16]; +} __packed __aligned(8); + struct ibmvfc_discover_targets_entry { __be32 scsi_id; __be32 pad; @@ -287,6 +333,7 @@ enum ibmvfc_fc_type { IBMVFC_FABRIC_BUSY = 0x04, IBMVFC_PORT_BUSY = 0x05, IBMVFC_BASIC_REJECT = 0x06, + IBMVFC_FC4_LS_REJECT = 0x07, }; enum ibmvfc_gs_explain { @@ -334,6 +381,7 @@ struct ibmvfc_move_login { struct ibmvfc_prli_svc_parms { u8 type; #define IBMVFC_SCSI_FCP_TYPE 0x08 +#define IBMVFC_NVME_FCP_TYPE 0x28 u8 type_ext; __be16 flags; #define IBMVFC_PRLI_ORIG_PA_VALID 0x8000 @@ -349,6 +397,11 @@ struct ibmvfc_prli_svc_parms { #define IBMVFC_PRLI_TARGET_FUNC 0x00000010 #define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED 0x00000002 #define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED 0x00000001 +#define IBMVFC_PRLI_NVME_PI_CTRL 0x00000200 +#define IBMVFC_PRLI_NVME_SLER 0x00000100 +#define IBMVFC_PRLI_NVME_INITIATOR 0x00000020 +#define IBMVFC_PRLI_NVME_TARGET 0x00000010 +#define IBMVFC_PRLI_NVME_DISCOVERY 0x00000008 } __packed __aligned(4); struct ibmvfc_process_login { @@ -377,20 +430,27 @@ struct ibmvfc_query_tgt { struct ibmvfc_implicit_logout { struct ibmvfc_mad_common common; __be64 old_scsi_id; - __be64 reserved[2]; + __be64 reserved[8]; + __be64 target_wwpn; } __packed __aligned(8); struct ibmvfc_tmf { struct ibmvfc_mad_common common; __be64 scsi_id; - struct scsi_lun lun; + union { + struct scsi_lun lun; + __be64 assoc_id; + }; __be32 flags; -#define IBMVFC_TMF_ABORT_TASK 0x02 -#define IBMVFC_TMF_ABORT_TASK_SET 0x04 -#define IBMVFC_TMF_LUN_RESET 0x10 -#define IBMVFC_TMF_TGT_RESET 0x20 -#define IBMVFC_TMF_LUA_VALID 0x40 -#define IBMVFC_TMF_SUPPRESS_ABTS 0x80 +#define IBMVFC_TMF_ABORT_TASK 0x002 +#define IBMVFC_TMF_ABORT_TASK_SET 0x004 +#define IBMVFC_TMF_LUN_RESET 0x010 +#define IBMVFC_TMF_TGT_RESET 0x020 +#define IBMVFC_TMF_LUA_VALID 0x040 +#define IBMVFC_TMF_SUPPRESS_ABTS 0x080 +#define IBMVFC_TMF_NVMF_ASSOC 0x100 +#define IBMVFC_TMF_NVMF_TARGET 0x200 +#define IBMVFC_TMF_BITMASK_VALID 0x400 __be32 cancel_key; __be32 my_cancel_key; __be32 pad; @@ -446,6 +506,8 @@ enum ibmvfc_cmd_flags { IBMVFC_WRITE = 0x0008, IBMVFC_TMF = 0x0080, IBMVFC_CLASS_3_ERR = 0x0100, + IBMVFC_NVMEOF_PROTOCOL = 0x0200, + IBMVFC_NVMF_SLER = 0x0400, }; enum ibmvfc_fc_task_attr { @@ -493,7 +555,7 @@ struct ibmvfc_cmd { __be64 tgt_scsi_id; __be64 tag; __be64 target_wwpn; - __be64 reserved3; + __be64 assoc_id; union { struct { struct ibmvfc_fcp_cmd_iu iu; @@ -504,6 +566,15 @@ struct ibmvfc_cmd { struct ibmvfc_fcp_cmd_iu iu; struct ibmvfc_fcp_rsp rsp; } v2; + struct { + __be64 reserved5[4]; + struct ibmvfc_fcp_cmd_iu iu; + struct ibmvfc_fcp_rsp rsp; + } v3scsi; + struct { + __be64 reserved[4]; + struct nvme_fc_cmd_iu iu; + } v3nvme; }; } __packed __aligned(8); @@ -522,6 +593,9 @@ struct ibmvfc_passthru_iu { __be32 flags; #define IBMVFC_FC_ELS 0x01 #define IBMVFC_FC_CT_IU 0x02 +#define IBMVFC_PT_PRLI 0x04 +#define IBMVFC_FC4_LS_OTH 0x08 +#define IBMVFC_FC4_LS_DSC_CTRL 0x10 __be32 cancel_key; #define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000 #define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001 @@ -549,9 +623,9 @@ struct ibmvfc_channel_enquiry { #define IBMVFC_SUPPORT_VARIABLE_SUBQ_MSG 0x02 #define IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT 0x04 __be32 num_scsi_subq_channels; - __be32 num_nvmeof_subq_channels; + __be32 num_nvme_subq_channels; __be32 num_scsi_vas_channels; - __be32 num_nvmeof_vas_channels; + __be32 num_nvme_vas_channels; } __packed __aligned(8); struct ibmvfc_channel_setup_mad { @@ -559,7 +633,7 @@ struct ibmvfc_channel_setup_mad { struct srp_direct_buf buffer; } __packed __aligned(8); -#define IBMVFC_MAX_CHANNELS 502 +#define IBMVFC_MAX_CHANNELS 501 struct ibmvfc_channel_setup { __be32 flags; @@ -568,12 +642,13 @@ struct ibmvfc_channel_setup { #define IBMVFC_CHANNELS_CANCELED 0x04 __be32 reserved; __be32 num_scsi_subq_channels; - __be32 num_nvmeof_subq_channels; + __be32 num_nvme_subq_channels; __be32 num_scsi_vas_channels; - __be32 num_nvmeof_vas_channels; + __be32 num_nvme_vas_channels; struct srp_direct_buf buffer; __be64 reserved2[5]; __be64 channel_handles[IBMVFC_MAX_CHANNELS]; + __be64 async_sub_crq_handle; } __packed __aligned(8); struct ibmvfc_connection_info { @@ -620,7 +695,8 @@ struct ibmvfc_trace_entry { enum ibmvfc_crq_formats { IBMVFC_CMD_FORMAT = 0x01, - IBMVFC_ASYNC_EVENT = 0x02, + IBMVFC_ASYNC_EVENT = 0x02, + IBMVFC_NOOP = 0x03, IBMVFC_MAD_FORMAT = 0x04, }; @@ -639,6 +715,9 @@ enum ibmvfc_async_event { IBMVFC_AE_RESUME = 0x0800, IBMVFC_AE_ADAPTER_FAILED = 0x1000, IBMVFC_AE_FPIN = 0x2000, + IBMVFC_NVME_DISCONNECT = 0x4000, + IBMVFC_NVMEOF_PROTO_AVAIL = 0x40000000, + IBMVFC_SCSI_PROTO_AVAIL = 0x80000000, }; struct ibmvfc_async_desc { @@ -683,13 +762,30 @@ struct ibmvfc_async_crq { volatile __be64 scsi_id; volatile __be64 wwpn; volatile __be64 node_name; - __be64 reserved; + __be64 assoc_id; +} __packed __aligned(8); + +struct ibmvfc_async_sub_crq { + volatile u8 valid; + u8 flags; +#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01 + u8 link_state; + u8 fpin_status; + __be16 event; + __be16 pad; + __be64 wwpn; + __be64 nport_id; + union { + __be64 node_name; + __be64 assoc_id; + } id; } __packed __aligned(8); union ibmvfc_iu { struct ibmvfc_mad_common mad_common; struct ibmvfc_npiv_login_mad npiv_login; struct ibmvfc_npiv_logout_mad npiv_logout; + struct ibmvfc_fabric_login_mad fabric_login; struct ibmvfc_discover_targets discover_targets; struct ibmvfc_port_login plogi; struct ibmvfc_process_login prli; @@ -728,6 +824,7 @@ struct ibmvfc_target { u64 scsi_id; u64 wwpn; u64 new_scsi_id; + u64 assoc_id; struct fc_rport *rport; int target_id; enum ibmvfc_target_action action; @@ -743,6 +840,8 @@ struct ibmvfc_target { void (*job_step) (struct ibmvfc_target *); struct timer_list timer; struct kref kref; + struct completion nvme_delete_done; + struct nvme_fc_remote_port *nvme_remote_port; }; /* a unit of work for the hosting partition */ @@ -753,6 +852,8 @@ struct ibmvfc_event { struct ibmvfc_queue *queue; struct ibmvfc_target *tgt; struct scsi_cmnd *cmnd; + struct nvmefc_ls_req *ls_req; + struct nvmefc_fcp_req *fcp_req; atomic_t free; atomic_t active; union ibmvfc_iu *xfer_iu; @@ -828,6 +929,8 @@ struct ibmvfc_channels { unsigned int active_queues; unsigned int desired_queues; unsigned int max_queues; + int num_targets; + struct list_head targets; int disc_buf_sz; struct ibmvfc_discover_targets_entry *disc_buf; dma_addr_t disc_buf_dma; @@ -871,8 +974,6 @@ struct ibmvfc_host { #define IBMVFC_TRACE_SIZE (sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES) struct ibmvfc_trace_entry *trace; atomic_t trace_index; - int num_targets; - struct list_head targets; struct list_head purge; struct device *dev; struct dma_pool *sg_pool; @@ -880,6 +981,7 @@ struct ibmvfc_host { struct ibmvfc_queue crq; struct ibmvfc_queue async_crq; struct ibmvfc_channels scsi_scrqs; + struct ibmvfc_channels nvme_scrqs; struct ibmvfc_npiv_login login_info; union ibmvfc_npiv_login_data *login_buf; dma_addr_t login_buf_dma; @@ -888,6 +990,7 @@ struct ibmvfc_host { int log_level; struct mutex passthru_mutex; unsigned int max_vios_scsi_channels; + unsigned int max_vios_nvme_channels; int task_set; int init_retries; int discovery_threads; @@ -899,6 +1002,9 @@ struct ibmvfc_host { unsigned int mq_enabled:1; unsigned int using_channels:1; unsigned int do_enquiry:1; + unsigned int nvme_enabled:1; + unsigned int do_scsi_login:1; + unsigned int do_nvme_login:1; unsigned int aborting_passthru:1; unsigned int scan_complete:1; int scan_timeout; @@ -914,8 +1020,37 @@ struct ibmvfc_host { struct work_struct rport_add_work_q; wait_queue_head_t init_wait_q; wait_queue_head_t work_wait_q; + struct nvme_fc_local_port *nvme_local_port; + struct completion nvme_delete_done; }; +struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved); +#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0) +#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1) + +void ibmvfc_init_event(struct ibmvfc_event *evt, void (*done) (struct ibmvfc_event *), u8 format); +void ibmvfc_free_event(struct ibmvfc_event *evt); +void ibmvfc_release_tgt(struct kref *kref); +int ibmvfc_send_event(struct ibmvfc_event *evt, struct ibmvfc_host *vhost, unsigned long timeout); +const char *ibmvfc_get_cmd_error(u16 status, u16 error); + +static inline int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags) +{ + u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities); + + return (host_caps & cap_flags) ? 1 : 0; +} + +static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channels *channels) +{ + if (channels->protocol == IBMVFC_PROTO_SCSI) + return container_of(channels, struct ibmvfc_host, scsi_scrqs); + else if (channels->protocol == IBMVFC_PROTO_NVME) + return container_of(channels, struct ibmvfc_host, nvme_scrqs); + + return NULL; +} + #define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0) #define tgt_dbg(t, fmt, ...) \ diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h index e56cd0fdc35f..f258f4014e7f 100644 --- a/drivers/scsi/isci/scu_task_context.h +++ b/drivers/scsi/isci/scu_task_context.h @@ -193,21 +193,7 @@ typedef enum { #define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_SHIFT 18 #define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK 0x001C0000 -#define scu_get_command_request_subtype(x) \ - ((x) & SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK) - -#define SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK \ - (\ - SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK \ - | SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK \ - ) -#define scu_get_command_request_full_type(x) \ - ((x) & SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK) - #define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT 16 -#define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK 0x00010000 -#define scu_get_command_protocl_engine_group(x) \ - ((x) & SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK) #define SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT 12 #define SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK 0x00007000 diff --git a/drivers/scsi/leapraid/Kconfig b/drivers/scsi/leapraid/Kconfig new file mode 100644 index 000000000000..f4ece78f80d3 --- /dev/null +++ b/drivers/scsi/leapraid/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config SCSI_LEAPRAID + tristate "LeapIO RAID Adapter" + depends on PCI && SCSI + select SCSI_SAS_ATTRS + help + This driver supports LeapIO PCIe-based storage + and RAID controllers. + + + + To compile this driver as a module, choose M here: the + resulting kernel module will be named leapraid. diff --git a/drivers/scsi/leapraid/Makefile b/drivers/scsi/leapraid/Makefile new file mode 100644 index 000000000000..bdafc036cd00 --- /dev/null +++ b/drivers/scsi/leapraid/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the LEAPRAID drivers. +# + +obj-$(CONFIG_SCSI_LEAPRAID) += leapraid.o +leapraid-objs += leapraid_func.o \ + leapraid_os.o \ + leapraid_transport.o \ + leapraid_app.o diff --git a/drivers/scsi/leapraid/leapraid.h b/drivers/scsi/leapraid/leapraid.h new file mode 100644 index 000000000000..c2d28359be5b --- /dev/null +++ b/drivers/scsi/leapraid/leapraid.h @@ -0,0 +1,2048 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 LeapIO Tech Inc. + * + * LeapRAID storage and RAID controller driver. + */ +#ifndef LEAPRAID_H +#define LEAPRAID_H + +/* Doorbell register definitions. */ +#define LEAPRAID_DB_RESET 0x00000000 +#define LEAPRAID_DB_READY 0x10000000 +#define LEAPRAID_DB_OPERATIONAL 0x20000000 +#define LEAPRAID_DB_FAULT 0x40000000 + +#define LEAPRAID_DB_MASK 0xF0000000 + +#define LEAPRAID_DB_OVER_TEMPERATURE 0x2810 + +#define LEAPRAID_DB_USED 0x08000000 +#define LEAPRAID_DB_DATA_MASK 0x0000FFFF +#define LEAPRAID_DB_FUNC_SHIFT 24 +#define LEAPRAID_DB_ADD_DWORDS_SHIFT 16 + +/* Maximum number of retries waiting for doorbell to become ready. */ +#define LEAPRAID_DB_RETRY_COUNT_MAX 10 +/* Maximum number of retries waiting for doorbell to become operational. */ +#define LEAPRAID_DB_WAIT_OP_SHORT 10 +#define LEAPRAID_DB_WAIT_OP_LONG 200 + +/* Maximum number of retries waiting for host to end recovery. */ +#define LEAPRAID_WAIT_SHOST_RECOVERY 400 + +/* Diagnostic register definitions. */ +#define LEAPRAID_DIAG_WRITE_ENABLE 0x00000080 +#define LEAPRAID_DIAG_RESET 0x00000004 + +/* Interrupt status register definitions. */ +#define LEAPRAID_HOST2ADAPTER_DB_STATUS 0x80000000 +#define LEAPRAID_ADAPTER2HOST_DB_STATUS 0x00000001 + +/* The number of debug register. */ +#define LEAPRAID_DEBUGLOG_SZ_MAX 16 + +/* Reply post host register definitions. */ +#define REP_POST_HOST_IDX_REG_CNT 16 +#define LEAPRAID_RPHI_MSIX_IDX_SHIFT 24 + +/* Virtual PHY flags. */ +#define LEAPRAID_SAS_PHYINFO_VPHY 0x00001000 + +/* Linux driver init firmware. */ +#define LEAPRAID_WHOINIT_LINUX_DRIVER 0x04 + +/* Reply descriptor post queue array mode. */ +#define LEAPRAID_ADAPTER_INIT_MSGFLG_RDPQ_ARRAY_MODE 0x01 + +/* Request description flags. */ +#define LEAPRAID_REQ_DESC_FLG_SCSI_IO 0x00 +#define LEAPRAID_REQ_DESC_FLG_HPR 0x06 +#define LEAPRAID_REQ_DESC_FLG_DFLT_TYPE 0x08 + +/* Reply description flags. */ +#define LEAPRAID_RPY_DESC_FLG_TYPE_MASK 0x0F +#define LEAPRAID_RPY_DESC_FLG_SCSI_IO_SUCCESS 0x00 +#define LEAPRAID_RPY_DESC_FLG_ADDRESS_REPLY 0x01 +#define LEAPRAID_RPY_DESC_FLG_FP_SCSI_IO_SUCCESS 0x06 +#define LEAPRAID_RPY_DESC_FLG_UNUSED 0x0F + +/* Request and reply messages share the same set of function codes. + * + * Note: SCSIIO (SCSI I/O) represents SCSI I/O operations and is used + * consistently throughout the driver for all SCSI command handling. + */ +#define LEAPRAID_FUNC_SCSIIO 0x00 +#define LEAPRAID_FUNC_SCSI_TMF 0x01 +#define LEAPRAID_FUNC_ADAPTER_INIT 0x02 +#define LEAPRAID_FUNC_GET_ADAPTER_FEATURES 0x03 +#define LEAPRAID_FUNC_CONFIG_OP 0x04 +#define LEAPRAID_FUNC_SCAN_DEV 0x06 +#define LEAPRAID_FUNC_EVENT_NOTIFY 0x07 +#define LEAPRAID_FUNC_FW_DOWNLOAD 0x09 +#define LEAPRAID_FUNC_FW_UPLOAD 0x12 +#define LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH 0x16 +#define LEAPRAID_FUNC_SCSI_ENC_PROCESSOR 0x18 +#define LEAPRAID_FUNC_SMP_PASSTHROUGH 0x1A +#define LEAPRAID_FUNC_SAS_IO_UNIT_CTRL 0x1B +#define LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH 0x1C +#define LEAPRAID_FUNC_ADAPTER_UNIT_RESET 0x40 +#define LEAPRAID_FUNC_HANDSHAKE 0x42 +#define LEAPRAID_FUNC_LOGBUF_INIT 0x57 + +/* Adapter status values. */ +#define LEAPRAID_ADAPTER_STATUS_MASK 0x7FFF +#define LEAPRAID_ADAPTER_STATUS_SUCCESS 0x0000 +#define LEAPRAID_ADAPTER_STATUS_BUSY 0x0002 +#define LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR 0x0004 +#define LEAPRAID_ADAPTER_STATUS_INSUFFICIENT_RESOURCES 0x0006 +#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_ACTION 0x0020 +#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_TYPE 0x0021 +#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE 0x0022 +#define LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_DATA 0x0023 +#define LEAPRAID_ADAPTER_STATUS_CONFIG_NO_DEFAULTS 0x0024 +#define LEAPRAID_ADAPTER_STATUS_CONFIG_CANT_COMMIT 0x0025 +#define LEAPRAID_ADAPTER_STATUS_SCSI_RECOVERED_ERROR 0x0040 +#define LEAPRAID_ADAPTER_STATUS_SCSI_DEVICE_NOT_THERE 0x0043 +#define LEAPRAID_ADAPTER_STATUS_SCSI_DATA_OVERRUN 0x0044 +#define LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN 0x0045 +#define LEAPRAID_ADAPTER_STATUS_SCSI_IO_DATA_ERROR 0x0046 +#define LEAPRAID_ADAPTER_STATUS_SCSI_PROTOCOL_ERROR 0x0047 +#define LEAPRAID_ADAPTER_STATUS_SCSI_TASK_TERMINATED 0x0048 +#define LEAPRAID_ADAPTER_STATUS_SCSI_RESIDUAL_MISMATCH 0x0049 +#define LEAPRAID_ADAPTER_STATUS_SCSI_TASK_MGMT_FAILED 0x004A +#define LEAPRAID_ADAPTER_STATUS_SCSI_ADAPTER_TERMINATED 0x004B +#define LEAPRAID_ADAPTER_STATUS_SCSI_EXT_TERMINATED 0x004C + +/* SGE flags. */ +#define LEAPRAID_SGE_FLG_LAST_ONE 0x80 +#define LEAPRAID_SGE_FLG_EOB 0x40 +#define LEAPRAID_SGE_FLG_EOL 0x01 +#define LEAPRAID_SGE_FLG_SHIFT 24 +#define LEAPRAID_SGE_FLG_SIMPLE_ONE 0x10 +#define LEAPRAID_SGE_FLG_SYSTEM_ADDR 0x00 +#define LEAPRAID_SGE_FLG_H2C 0x04 +#define LEAPRAID_SGE_FLG_32 0x00 +#define LEAPRAID_SGE_FLG_64 0x02 + +#define LEAPRAID_IEEE_SGE_FLG_EOL 0x40 +#define LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE 0x00 +#define LEAPRAID_IEEE_SGE_FLG_CHAIN_ONE 0x80 +#define LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR 0x00 + +#define LEAPRAID_SGE_NO_DATA_ADDR -1 +#define LEAPRAID_SGE_OFFSET_SIZE 4 + +/* The type of page. */ +#define LEAPRAID_CFG_PT_IO_UNIT 0x00 +#define LEAPRAID_CFG_PT_ADAPTER 0x01 +#define LEAPRAID_CFG_PT_BIOS 0x02 +#define LEAPRAID_CFG_PT_RAID_VOLUME 0x08 +#define LEAPRAID_CFG_PT_MANUFACTURING 0x09 +#define LEAPRAID_CFG_PT_RAID_PHYSDISK 0x0A +#define LEAPRAID_CFG_PT_EXTENDED 0x0F + +/* The type of extended page. */ +#define LEAPRAID_CFG_EXTPT_SAS_IO_UNIT 0x10 +#define LEAPRAID_CFG_EXTPT_SAS_EXP 0x11 +#define LEAPRAID_CFG_EXTPT_SAS_DEV 0x12 +#define LEAPRAID_CFG_EXTPT_SAS_PHY 0x13 +#define LEAPRAID_CFG_EXTPT_ENC 0x15 +#define LEAPRAID_CFG_EXTPT_RAID_CONFIG 0x16 + +/* Config page address. */ +#define LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP 0x00000000 +#define LEAPRAID_SAS_ENC_CFG_PGAD_HDL 0x10000000 +#define LEAPRAID_SAS_DEV_CFG_PGAD_HDL 0x20000000 +#define LEAPRAID_SAS_EXP_CFG_PGAD_HDL_PHY_NUM 0x10000000 +#define LEAPRAID_SAS_EXP_CFD_PGAD_HDL 0x20000000 +#define LEAPRAID_SAS_EXP_CFG_PGAD_PHYNUM_SHIFT 16 +#define LEAPRAID_RAID_VOL_CFG_PGAD_HDL 0x10000000 +#define LEAPRAID_SAS_PHY_CFG_PGAD_PHY_NUMBER 0x00000000 +#define LEAPRAID_PHYSDISK_CFG_PGAD_PHYSDISKNUM 0x10000000 + +/* Config page operations. */ +#define LEAPRAID_CFG_ACT_PAGE_HEADER 0x00 +#define LEAPRAID_CFG_ACT_PAGE_READ_CUR 0x01 +#define LEAPRAID_CFG_ACT_PAGE_WRITE_CUR 0x02 + +/* BIOS page number. */ +#define LEAPRAID_CFG_PAGE_NUM_BIOS2 0x2 +#define LEAPRAID_CFG_PAGE_NUM_BIOS3 0x3 + +/* Manufacturing page number. */ +#define LEAPRAID_CFG_PAGE_NUM_MANU0 0x0 + +/* SAS device page number. */ +#define LEAPRAID_CFG_PAGE_NUM_DEV0 0x0 + +/* SAS device page 0 flags. */ +#define LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID 0x0002 +#define LEAPRAID_SAS_DEV_P0_FLG_DEV_PRESENT 0x0001 +#define LEAPRAID_SAS_DEV_P0_CON_NAME_LEN 4 + +/* SAS I/O unit page number. */ +#define LEAPRAID_CFG_PAGE_NUM_IOUNIT0 0x0 +#define LEAPRAID_CFG_PAGE_NUM_IOUNIT1 0x1 + +/* SAS expander page number. */ +#define LEAPRAID_CFG_PAGE_NUM_EXP0 0x0 +#define LEAPRAID_CFG_PAGE_NUM_EXP1 0x1 + +/* SAS enclosure page number. */ +#define LEAPRAID_CFG_PAGE_NUM_ENC0 0x0 + +/* SAS PHY page number. */ +#define LEAPRAID_CFG_PAGE_NUM_PHY0 0x0 + +/* RAID volume page number. */ +#define LEAPRAID_CFG_PAGE_NUM_VOL0 0x0 +#define LEAPRAID_CFG_PAGE_NUM_VOL1 0x1 + +/* Physical disk page number. */ +#define LEAPRAID_CFG_PAGE_NUM_PD0 0x0 + +#define LEAPRAID_CFG_UNIT_SIZE 4 + +/* Raid volume type and state. */ +#define LEAPRAID_VOL_STATE_ONLINE 0x03 +#define LEAPRAID_VOL_STATE_DEGRADED 0x04 +#define LEAPRAID_VOL_STATE_OPTIMAL 0x05 +#define LEAPRAID_VOL_TYPE_RAID0 0x00 +#define LEAPRAID_VOL_TYPE_RAID1E 0x01 +#define LEAPRAID_VOL_TYPE_RAID1 0x02 +#define LEAPRAID_VOL_TYPE_RAID10 0x05 +#define LEAPRAID_VOL_TYPE_UNKNOWN 0xFF + +/* Raid volume element flags. */ +#define LEAPRAID_RAIDCFG_P0_EFLG_MASK_ELEMENT_TYPE 0x000F +#define LEAPRAID_RAIDCFG_P0_EFLG_VOL_PHYS_DISK_ELEMENT 0x0001 +#define LEAPRAID_RAIDCFG_P0_EFLG_HOT_SPARE_ELEMENT 0x0002 +#define LEAPRAID_RAIDCFG_P0_EFLG_OCE_ELEMENT 0x0003 + +/* SAS negotiated link rates. */ +#define LEAPRAID_SAS_NEG_LINK_RATE_MASK_PHYSICAL 0x0F +#define LEAPRAID_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE 0x00 +#define LEAPRAID_SAS_NEG_LINK_RATE_PHY_DISABLED 0x01 +#define LEAPRAID_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED 0x02 +#define LEAPRAID_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE 0x03 +#define LEAPRAID_SAS_NEG_LINK_RATE_PORT_SELECTOR 0x04 +#define LEAPRAID_SAS_NEG_LINK_RATE_SMP_RESETTING 0x05 +#define LEAPRAID_SAS_NEG_LINK_RATE_SHIFT 4 + +#define LEAPRAID_SAS_NEG_LINK_RATE_1_5 0x08 +#define LEAPRAID_SAS_NEG_LINK_RATE_3_0 0x09 +#define LEAPRAID_SAS_NEG_LINK_RATE_6_0 0x0A +#define LEAPRAID_SAS_NEG_LINK_RATE_12_0 0x0B + +#define LEAPRAID_SAS_PRATE_MIN_RATE_MASK 0x0F +#define LEAPRAID_SAS_HWRATE_MIN_RATE_MASK 0x0F + +/* + * Control flags for a SCSI I/O Request. + */ +#define LEAPRAID_SCSIIO_CTRL_CDB_32BYTE 4 +#define LEAPRAID_SCSIIO_CTRL_CDB_LEN_SHIFT 26 +#define LEAPRAID_SCSIIO_CTRL_NODATATRANSFER 0x00000000 +#define LEAPRAID_SCSIIO_CTRL_WRITE 0x01000000 +#define LEAPRAID_SCSIIO_CTRL_READ 0x02000000 +#define LEAPRAID_SCSIIO_CTRL_BIDIRECTIONAL 0x03000000 +#define LEAPRAID_SCSIIO_CTRL_SIMPLEQ 0x00000000 +#define LEAPRAID_SCSIIO_CTRL_ORDEREDQ 0x00000200 +#define LEAPRAID_SCSIIO_CTRL_CMDPRI 0x00000800 + +/* SCSI state and status. */ +#define LEAPRAID_SCSI_STATUS_BUSY 0x08 +#define LEAPRAID_SCSI_STATUS_RESERVATION_CONFLICT 0x18 +#define LEAPRAID_SCSI_STATUS_TASK_SET_FULL 0x28 + +#define LEAPRAID_SCSI_STATE_RESPONSE_INFO_VALID 0x10 +#define LEAPRAID_SCSI_STATE_TERMINATED 0x08 +#define LEAPRAID_SCSI_STATE_NO_SCSI_STATUS 0x04 +#define LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED 0x02 +#define LEAPRAID_SCSI_STATE_AUTOSENSE_VALID 0x01 + +/* SCSI task management defines. */ +#define LEAPRAID_TM_TASKTYPE_ABORT_TASK 0x01 +#define LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET 0x02 +#define LEAPRAID_TM_TASKTYPE_TARGET_RESET 0x03 +#define LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET 0x05 +#define LEAPRAID_TM_TASKTYPE_CLEAR_TASK_SET 0x06 +#define LEAPRAID_TM_TASKTYPE_QUERY_TASK 0x07 +#define LEAPRAID_TM_TASKTYPE_CLEAR_ACA 0x08 +#define LEAPRAID_TM_TASKTYPE_QUERY_TASK_SET 0x09 +#define LEAPRAID_TM_TASKTYPE_QUERY_ASYNC_EVENT 0x0A + +#define LEAPRAID_TM_MSGFLAGS_LINK_RESET 0x00 +#define LEAPRAID_TM_RSP_INVALID_FRAME 0x02 + +/* SCSI enclosure processor request defines. */ +#define LEAPRAID_SEP_REQ_ACT_WRITE_STATUS 0x00 +#define LEAPRAID_SEP_REQ_FLG_DEVHDL_ADDRESS 0x00 +#define LEAPRAID_SEP_REQ_FLG_ENCLOSURE_SLOT_ADDRESS 0x01 +#define LEAPRAID_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT 0x00000040 + +/* The capabilities of the adapter. */ +#define LEAPRAID_ADAPTER_FEATURES_CAP_ATOMIC_REQ 0x00080000 +#define LEAPRAID_ADAPTER_FEATURES_CAP_INTEGRATED_RAID 0x00001000 + +/* Event code definitions for the firmware. */ +#define LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE 0x000F +#define LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST 0x001C +#define LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE 0x001D +#define LEAPRAID_EVT_IR_CHANGE 0x0020 +#define LEAPRAID_EVT_TURN_ON_PFA_LED 0xFFFC +#define LEAPRAID_EVT_SCAN_DEV_DONE 0xFFFD +#define LEAPRAID_EVT_REMOVE_DEAD_DEV 0xFFFF +#define LEAPRAID_MAX_EVENT_NUM 128 + +#define LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET 0x08 +#define LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET 0x0E + +/* RAID configuration change event. */ +#define LEAPRAID_EVT_IR_RC_VOLUME_ADD 0x01 +#define LEAPRAID_EVT_IR_RC_VOLUME_DELETE 0x02 +#define LEAPRAID_EVT_IR_RC_PD_HIDDEN_TO_ADD 0x03 +#define LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE 0x04 +#define LEAPRAID_EVT_IR_RC_PD_CREATED_TO_HIDE 0x05 +#define LEAPRAID_EVT_IR_RC_PD_DELETED_TO_EXPOSE 0x06 +#define LEAPRAID_EVT_IR_RC_VOLUME_HIDE 0x0A +#define LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE 0x0B + +/* SAS topology change event. */ +#define LEAPRAID_EVT_SAS_TOPO_ES_NO_EXPANDER 0x00 +#define LEAPRAID_EVT_SAS_TOPO_ES_ADDED 0x01 +#define LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING 0x02 +#define LEAPRAID_EVT_SAS_TOPO_ES_RESPONDING 0x03 + +#define LEAPRAID_EVT_SAS_TOPO_RC_MASK 0x0F +#define LEAPRAID_EVT_SAS_TOPO_RC_TARG_ADDED 0x01 +#define LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING 0x02 + +/* Enclosure device status change event. */ +#define LEAPRAID_EVT_SAS_ENCL_RC_ADDED 0x01 +#define LEAPRAID_EVT_SAS_ENCL_RC_NOT_RESPONDING 0x02 + +/* Device type and identifiers. */ +#define LEAPRAID_DEVTYP_SEP 0x00004000 +#define LEAPRAID_DEVTYP_SSP_TGT 0x00000400 +#define LEAPRAID_DEVTYP_STP_TGT 0x00000200 +#define LEAPRAID_DEVTYP_SMP_TGT 0x00000100 +#define LEAPRAID_DEVTYP_SATA_DEV 0x00000080 +#define LEAPRAID_DEVTYP_SSP_INIT 0x00000040 +#define LEAPRAID_DEVTYP_STP_INIT 0x00000020 +#define LEAPRAID_DEVTYP_SMP_INIT 0x00000010 +#define LEAPRAID_DEVTYP_SATA_HOST 0x00000008 + +#define LEAPRAID_DEVTYP_MASK_DEV_TYPE 0x00000007 +#define LEAPRAID_DEVTYP_NO_DEV 0x00000000 +#define LEAPRAID_DEVTYP_END_DEV 0x00000001 +#define LEAPRAID_DEVTYP_EDGE_EXPANDER 0x00000002 +#define LEAPRAID_DEVTYP_FANOUT_EXPANDER 0x00000003 + +/* SAS control operation. */ +#define LEAPRAID_SAS_OP_PHY_LINK_RESET 0x06 +#define LEAPRAID_SAS_OP_PHY_HARD_RESET 0x07 +#define LEAPRAID_SAS_OP_SET_PARAMETER 0x0F + +/* Boot device defines */ +#define LEAPRAID_BOOTDEV_FORM_MASK 0x0F +#define LEAPRAID_BOOTDEV_FORM_NONE 0x00 +#define LEAPRAID_BOOTDEV_FORM_SAS_WWID 0x05 +#define LEAPRAID_BOOTDEV_FORM_ENC_SLOT 0x06 +#define LEAPRAID_BOOTDEV_FORM_DEV_NAME 0x07 + +/** + * struct leapraid_reg_base - Register layout of the LeapRAID controller + * + * @db: Doorbell register used to signal commands or status to firmware. + * @ws: Write sequence register for synchronizing doorbell operations. + * @host_diag: Diagnostic register used for status or debug reporting. + * @r1: Reserved. + * @host_int_status: Interrupt status register reporting active interrupts. + * @host_int_mask: Interrupt mask register enabling or disabling sources. + * @r2: Reserved. + * @rep_msg_host_idx: Reply message index for the next available reply slot. + * @r3: Reserved. + * @debug_log: DebugLog registers for firmware debug and diagnostic output. + * @r4: Reserved. + * @atomic_req_desc_post: Atomic register for single descriptor posting. + * @adapter_log_buf_pos: Adapter log buffer write position. + * @host_log_buf_pos: Host log buffer write position. + * @r5: Reserved. + * @rep_post_reg_idx: Array of reply post index registers, one per queue. + * The number of entries is defined by + * REP_POST_HOST_IDX_REG_CNT. + */ +struct leapraid_reg_base { + __le32 db; + __le32 ws; + __le32 host_diag; + __le32 r1[9]; + __le32 host_int_status; + __le32 host_int_mask; + __le32 r2[4]; + __le32 rep_msg_host_idx; + __le32 r3[13]; + __le32 debug_log[LEAPRAID_DEBUGLOG_SZ_MAX]; + __le32 r4[2]; + __le32 atomic_req_desc_post; + __le32 adapter_log_buf_pos; + __le32 host_log_buf_pos; + __le32 r5[142]; + struct leapraid_rep_post_reg_idx { + __le32 idx; + __le32 r1; + __le32 r2; + __le32 r3; + } rep_post_reg_idx[REP_POST_HOST_IDX_REG_CNT]; +} __packed; + +/** + * struct leapraid_atomic_req_desc - Atomic request descriptor + * + * @flg: Descriptor flag indicating the type of request (e.g. SCSI I/O). + * @msix_idx: MSI-X vector index used for interrupt routing. + * @taskid: Unique task identifier associated with this request. + */ +struct leapraid_atomic_req_desc { + u8 flg; + u8 msix_idx; + __le16 taskid; +}; + +/** + * union leapraid_rep_desc_union - Unified reply descriptor format + * + * @dflt_rep: Default reply descriptor containing basic completion info. + * @dflt_rep.rep_flg: Reply flag indicating reply type or status. + * @dflt_rep.msix_idx: MSI-X index for interrupt routing. + * @dflt_rep.taskid: Task identifier matching the submitted request. + * @r1: Reserved. + * + * @addr_rep: Address reply descriptor used when firmware returns a + * memory address associated with the reply. + * @addr_rep.rep_flg: Reply flag indicating reply type or status. + * @addr_rep.msix_idx: MSI-X index for interrupt routing. + * @addr_rep.taskid: Task identifier matching the submitted request. + * @addr_rep.rep_frame_addr: Physical address of the reply frame. + * + * @words: Raw 64-bit representation of the reply descriptor. + * @u: Alternative access using 32-bit low/high words. + * @u.low: Lower 32 bits of the descriptor. + * @u.high: Upper 32 bits of the descriptor. + */ +union leapraid_rep_desc_union { + struct leapraid_rep_desc { + u8 rep_flg; + u8 msix_idx; + __le16 taskid; + u8 r1[4]; + } dflt_rep; + struct leapraid_add_rep_desc { + u8 rep_flg; + u8 msix_idx; + __le16 taskid; + __le32 rep_frame_addr; + } addr_rep; + __le64 words; + struct { + u32 low; + u32 high; + } u; +} __packed __aligned(4); + +/** + * struct leapraid_req - Generic request header + * + * @func_dep1: Function-dependent parameter (low 16 bits). + * @r1: Reserved. + * @func: Function code identifying the command type. + * @r2: Reserved. + */ +struct leapraid_req { + __le16 func_dep1; + u8 r1; + u8 func; + u8 r2[8]; +}; + +/** + * struct leapraid_rep - Generic reply header + * + * @r1: Reserved. + * @msg_len: Length of the reply message in bytes. + * @function: Function code corresponding to the request. + * @r2: Reserved. + * @adapter_status: Status code reported by the adapter. + * @r3: Reserved. + */ +struct leapraid_rep { + u8 r1[2]; + u8 msg_len; + u8 function; + u8 r2[10]; + __le16 adapter_status; + u8 r3[4]; +}; + +/** + * struct leapraid_sge_simple32 - 32-bit simple scatter-gather entry + * + * @flg_and_len: Combined field for flags and segment length. + * @addr: 32-bit physical address of the data buffer. + */ +struct leapraid_sge_simple32 { + __le32 flg_and_len; + __le32 addr; +}; + +/** + * struct leapraid_sge_simple64 - 64-bit simple scatter-gather entry + * + * @flg_and_len: Combined field for flags and segment length. + * @addr: 64-bit physical address of the data buffer. + */ +struct leapraid_sge_simple64 { + __le32 flg_and_len; + __le64 addr; +} __packed __aligned(4); + +/** + * struct leapraid_sge_simple_union - Unified 32/64-bit SGE representation + * + * @flg_and_len: Combined field for flags and segment length. + * @u.addr32: 32-bit address field. + * @u.addr64: 64-bit address field. + */ +struct leapraid_sge_simple_union { + __le32 flg_and_len; + union { + __le32 addr32; + __le64 addr64; + } __packed __aligned(4) u; +} __packed __aligned(4); + +/** + * struct leapraid_sge_chain_union - Chained scatter-gather entry + * + * @len: Length of the chain descriptor. + * @next_chain_offset: Offset to the next SGE chain. + * @flg: Flags indicating chain or termination properties. + * @u.addr32: 32-bit physical address. + * @u.addr64: 64-bit physical address. + */ +struct leapraid_sge_chain_union { + __le16 len; + u8 next_chain_offset; + u8 flg; + union { + __le32 addr32; + __le64 addr64; + } __packed __aligned(4) u; +} __packed __aligned(4); + +/** + * struct leapraid_ieee_sge_simple32 - IEEE 32-bit simple SGE format + * + * @addr: 32-bit physical address of the data buffer. + * @flg_and_len: Combined field for flags and data length. + */ +struct leapraid_ieee_sge_simple32 { + __le32 addr; + __le32 flg_and_len; +}; + +/** + * struct leapraid_ieee_sge_simple64 - IEEE 64-bit simple SGE format + * + * @addr: 64-bit physical address of the data buffer. + * @len: Length of the data segment. + * @r1: Reserved. + * @flg: Flags indicating transfer properties. + */ +struct leapraid_ieee_sge_simple64 { + __le64 addr; + __le32 len; + u8 r1[3]; + u8 flg; +} __packed __aligned(4); + +/** + * union leapraid_ieee_sge_simple_union - Unified IEEE SGE format + * + * @simple32: IEEE 32-bit simple SGE entry. + * @simple64: IEEE 64-bit simple SGE entry. + */ +union leapraid_ieee_sge_simple_union { + struct leapraid_ieee_sge_simple32 simple32; + struct leapraid_ieee_sge_simple64 simple64; +}; + +/** + * union leapraid_ieee_sge_chain_union - Unified IEEE SGE chain format + * + * @chain32: IEEE 32-bit chain SGE entry. + * @chain64: IEEE 64-bit chain SGE entry. + */ +union leapraid_ieee_sge_chain_union { + struct leapraid_ieee_sge_simple32 chain32; + struct leapraid_ieee_sge_simple64 chain64; +}; + +/** + * struct leapraid_chain64_ieee_sg - 64-bit IEEE chain SGE descriptor + * + * @addr: Physical address of the next chain segment. + * @len: Length of the current SGE. + * @r1: Reserved. + * @next_chain_offset: Offset to the next chain element. + * @flg: Flags that describe SGE attributes. + */ +struct leapraid_chain64_ieee_sg { + __le64 addr; + __le32 len; + u8 r1[2]; + u8 next_chain_offset; + u8 flg; +} __packed __aligned(4); + +/** + * union leapraid_ieee_sge_io_union - IEEE-style SGE union for I/O + * + * @ieee_simple: Simple IEEE SGE descriptor. + * @ieee_chain: IEEE chain SGE descriptor. + */ +union leapraid_ieee_sge_io_union { + struct leapraid_ieee_sge_simple64 ieee_simple; + struct leapraid_chain64_ieee_sg ieee_chain; +}; + +/** + * union leapraid_simple_sge_union - Union of simple SGE descriptors + * + * @leapraid_simple: LeapRAID simple SGE. + * @ieee_simple: IEEE-style simple SGE. + */ +union leapraid_simple_sge_union { + struct leapraid_sge_simple_union leapraid_simple; + union leapraid_ieee_sge_simple_union ieee_simple; +}; + +/** + * union leapraid_sge_io_union - Combined SGE union for all I/O types + * + * @leapraid_simple: LeapRAID simple SGE format. + * @leapraid_chain: LeapRAID chain SGE format. + * @ieee_simple: IEEE simple SGE format. + * @ieee_chain: IEEE chain SGE format. + */ +union leapraid_sge_io_union { + struct leapraid_sge_simple_union leapraid_simple; + struct leapraid_sge_chain_union leapraid_chain; + union leapraid_ieee_sge_simple_union ieee_simple; + union leapraid_ieee_sge_chain_union ieee_chain; +}; + +/** + * struct leapraid_cfg_pg_header - Standard configuration page header + * + * @r1: Reserved. + * @page_len: Length of the page in 4-byte units. + * @page_num: Page number. + * @page_type: Page type. + */ +struct leapraid_cfg_pg_header { + u8 r1; + u8 page_len; + u8 page_num; + u8 page_type; +}; + +/** + * struct leapraid_cfg_ext_pg_header - Extended configuration page header + * + * @r1: Reserved. + * @r2: Reserved. + * @page_num: Page number. + * @page_type: Page type. + * @ext_page_len: Extended page length. + * @ext_page_type: Extended page type. + * @r3: Reserved. + */ +struct leapraid_cfg_ext_pg_header { + u8 r1; + u8 r2; + u8 page_num; + u8 page_type; + __le16 ext_page_len; + u8 ext_page_type; + u8 r3; +}; + +/** + * struct leapraid_cfg_req - Configuration request message + * + * @action: Requested action type. + * @sgl_flag: SGL flag field. + * @chain_offset: Offset to next chain SGE. + * @func: Function code. + * @ext_page_len: Extended page length. + * @ext_page_type: Extended page type. + * @msg_flag: Message flags. + * @r1: Reserved. + * @header: Configuration page header. + * @page_addr: Address of the page buffer. + * @page_buf_sge: SGE describing the page buffer. + */ +struct leapraid_cfg_req { + u8 action; + u8 sgl_flag; + u8 chain_offset; + u8 func; + __le16 ext_page_len; + u8 ext_page_type; + u8 msg_flag; + u8 r1[12]; + struct leapraid_cfg_pg_header header; + __le32 page_addr; + union leapraid_sge_io_union page_buf_sge; +}; + +/** + * struct leapraid_cfg_rep - Configuration reply message + * + * @action: Action type from the request. + * @r1: Reserved. + * @msg_len: Message length in bytes. + * @func: Function code. + * @ext_page_len: Extended page length. + * @ext_page_type: Extended page type. + * @msg_flag: Message flags. + * @r2: Reserved. + * @adapter_status: Adapter status code. + * @r3: Reserved. + * @header: Configuration page header. + */ +struct leapraid_cfg_rep { + u8 action; + u8 r1; + u8 msg_len; + u8 func; + __le16 ext_page_len; + u8 ext_page_type; + u8 msg_flag; + u8 r2[6]; + __le16 adapter_status; + u8 r3[4]; + struct leapraid_cfg_pg_header header; +}; + +/** + * struct leapraid_boot_dev_format_sas_wwid - Boot device identified by wwid + * + * @sas_addr: SAS address of the device. + * @lun: Logical unit number. + * @r1: Reserved. + */ +struct leapraid_boot_dev_format_sas_wwid { + __le64 sas_addr; + u8 lun[8]; + u8 r1[8]; +} __packed __aligned(4); + +/** + * struct leapraid_boot_dev_format_enc_slot - identified by enclosure + * + * @enc_lid: Enclosure logical ID. + * @r1: Reserved. + * @slot_num: Slot number in the enclosure. + * @r2: Reserved. + */ +struct leapraid_boot_dev_format_enc_slot { + __le64 enc_lid; + u8 r1[8]; + __le16 slot_num; + u8 r2[6]; +} __packed __aligned(4); + +/** + * struct leapraid_boot_dev_format_dev_name - Boot device by device name + * + * @dev_name: Device name identifier. + * @lun: Logical unit number. + * @r1: Reserved. + */ +struct leapraid_boot_dev_format_dev_name { + __le64 dev_name; + u8 lun[8]; + u8 r1[8]; +} __packed __aligned(4); + +/** + * union leapraid_boot_dev_format - Boot device format union + * + * @sas_wwid: Format using SAS WWID and LUN. + * @enc_slot: Format using enclosure slot and ID. + * @dev_name: Format using device name and LUN. + */ +union leapraid_boot_dev_format { + struct leapraid_boot_dev_format_sas_wwid sas_wwid; + struct leapraid_boot_dev_format_enc_slot enc_slot; + struct leapraid_boot_dev_format_dev_name dev_name; +}; + +/** + * struct leapraid_manufacturing_p0 - Manufacturing configuration page 0 + * + * @header: Configuration page header. + * @chip_name: Chip name as a 16-byte ASCII string. + * @chip_revision: Chip revision as an 8-byte ASCII string. + * @board_name: Board name as a 16-byte ASCII string. + * @board_assembly: Board assembly as a 16-byte ASCII string. + * @board_tracer_number: Board tracer number as a 16-byte ASCII string. + */ +struct leapraid_manufacturing_p0 { + struct leapraid_cfg_pg_header header; + u8 chip_name[16]; + u8 chip_revision[8]; + u8 board_name[16]; + u8 board_assembly[16]; + u8 board_tracer_number[16]; +}; + +/** + * struct leapraid_bios_page2 - BIOS configuration page 2 + * + * @header: Configuration page header. + * @r1: Reserved. + * @requested_boot_dev_form: Format type of the requested boot device. + * @r2: Reserved. + * @requested_boot_dev: Boot device requested by BIOS or user. + * @requested_alt_boot_dev_form: Format of the alternate boot device. + * @r3: Reserved. + * @requested_alt_boot_dev: Alternate boot device requested. + * @current_boot_dev_form: Format type of the active boot device. + * @r4: Reserved. + * @current_boot_dev: Currently active boot device in use. + */ +struct leapraid_bios_page2 { + struct leapraid_cfg_pg_header header; + u8 r1[24]; + u8 requested_boot_dev_form; + u8 r2[3]; + union leapraid_boot_dev_format requested_boot_dev; + u8 requested_alt_boot_dev_form; + u8 r3[3]; + union leapraid_boot_dev_format requested_alt_boot_dev; + u8 current_boot_dev_form; + u8 r4[3]; + union leapraid_boot_dev_format current_boot_dev; +}; + +/** + * struct leapraid_bios_page3 - BIOS configuration page 3 + * + * @header: Configuration page header. + * @r1: Reserved. + * @bios_version: BIOS firmware version number. + * @r2: Reserved. + */ +struct leapraid_bios_page3 { + struct leapraid_cfg_pg_header header; + u8 r1[4]; + __le32 bios_version; + u8 r2[84]; +}; + +/** + * struct leapraid_raidvol0_phys_disk - Physical disk in RAID volume + * + * @r1: Reserved. + * @phys_disk_num: Physical disk number within the RAID volume. + * @r2: Reserved. + */ +struct leapraid_raidvol0_phys_disk { + u8 r1[2]; + u8 phys_disk_num; + u8 r2; +}; + +/** + * struct leapraid_raidvol_p0 - RAID volume configuration page 0 + * + * @header: Configuration page header. + * @dev_hdl: Device handle for the RAID volume. + * @volume_state: State of the RAID volume. + * @volume_type: RAID type. + * @r1: Reserved. + * @num_phys_disks: Number of physical disks in the volume. + * @r2: Reserved. + * @phys_disk: Array of physical disks in this volume. + */ +struct leapraid_raidvol_p0 { + struct leapraid_cfg_pg_header header; + __le16 dev_hdl; + u8 volume_state; + u8 volume_type; + u8 r1[28]; + u8 num_phys_disks; + u8 r2[3]; + struct leapraid_raidvol0_phys_disk phys_disk[]; +}; + +/** + * struct leapraid_raidvol_p1 - RAID volume configuration page 1 + * + * @header: Configuration page header. + * @dev_hdl: Device handle of the RAID volume. + * @r1: Reserved. + * @wwid: World-wide identifier for the volume. + * @r2: Reserved. + */ +struct leapraid_raidvol_p1 { + struct leapraid_cfg_pg_header header; + __le16 dev_hdl; + u8 r1[42]; + __le64 wwid; + u8 r2[8]; +} __packed __aligned(4); + +/** + * struct leapraid_raidpd_p0 - Physical disk configuration page 0 + * + * @header: Configuration page header. + * @dev_hdl: Device handle of the physical disk. + * @r1: Reserved. + * @phys_disk_num: Physical disk number. + * @r2: Reserved. + */ +struct leapraid_raidpd_p0 { + struct leapraid_cfg_pg_header header; + __le16 dev_hdl; + u8 r1; + u8 phys_disk_num; + u8 r2[112]; +}; + +/** + * struct leapraid_sas_io_unit0_phy_info - PHY info for SAS I/O unit + * + * @port: Port number the PHY belongs to. + * @port_flg: Flags describing port status. + * @phy_flg: Flags describing PHY status. + * @neg_link_rate: Negotiated link rate of the PHY. + * @controller_phy_dev_info: Controller PHY device info. + * @attached_dev_hdl: Handle of attached device. + * @controller_dev_hdl: Handle of the controller device. + * @r1: Reserved. + */ +struct leapraid_sas_io_unit0_phy_info { + u8 port; + u8 port_flg; + u8 phy_flg; + u8 neg_link_rate; + __le32 controller_phy_dev_info; + __le16 attached_dev_hdl; + __le16 controller_dev_hdl; + u8 r1[8]; +}; + +/** + * struct leapraid_sas_io_unit_p0 - SAS I/O unit configuration page 0 + * + * @header: Extended configuration page header. + * @r1: Reserved. + * @phy_num: Number of PHYs in this unit. + * @r2: Reserved. + * @phy_info: Array of PHY information. + */ +struct leapraid_sas_io_unit_p0 { + struct leapraid_cfg_ext_pg_header header; + u8 r1[4]; + u8 phy_num; + u8 r2[3]; + struct leapraid_sas_io_unit0_phy_info phy_info[]; +}; + +/** + * struct leapraid_exp_p0 - SAS expander page 0 + * + * @header: Extended page header. + * @physical_port: Physical port number. + * @r1: Reserved. + * @enc_hdl: Enclosure handle. + * @sas_address: SAS address of the expander. + * @r2: Reserved. + * @dev_hdl: Device handle of this expander. + * @parent_dev_hdl: Device handle of parent expander. + * @r3: Reserved. + * @phy_num: Number of PHYs. + * @r4: Reserved. + */ +struct leapraid_exp_p0 { + struct leapraid_cfg_ext_pg_header header; + u8 physical_port; + u8 r1; + __le16 enc_hdl; + __le64 sas_address; + u8 r2[4]; + __le16 dev_hdl; + __le16 parent_dev_hdl; + u8 r3[4]; + u8 phy_num; + u8 r4[27]; +} __packed __aligned(4); + +/** + * struct leapraid_exp_p1 - SAS expander page 1 + * + * @header: Extended page header. + * @r1: Reserved. + * @p_link_rate: PHY link rate. + * @hw_link_rate: Hardware supported link rate. + * @attached_dev_hdl: Attached device handle. + * @r2: Reserved. + * @neg_link_rate: Negotiated link rate. + * @r3: Reserved. + */ +struct leapraid_exp_p1 { + struct leapraid_cfg_ext_pg_header header; + u8 r1[8]; + u8 p_link_rate; + u8 hw_link_rate; + __le16 attached_dev_hdl; + u8 r2[11]; + u8 neg_link_rate; + u8 r3[12]; +}; + +/** + * struct leapraid_sas_dev_p0 - SAS device page 0 + * + * @header: Extended configuration page header. + * @slot: Slot number. + * @enc_hdl: Enclosure handle. + * @sas_address: SAS address. + * @parent_dev_hdl: Parent device handle. + * @phy_num: Number of PHYs. + * @r1: Reserved. + * @dev_hdl: Device handle. + * @r2: Reserved. + * @dev_info: Device information. + * @flg: Flags. + * @physical_port: Physical port number. + * @max_port_connections: Maximum port connections. + * @dev_name: Device name. + * @port_groups: Number of port groups. + * @r3: Reserved. + * @enc_level: Enclosure level. + * @connector_name: Connector identifier. + * @r4: Reserved. + */ +struct leapraid_sas_dev_p0 { + struct leapraid_cfg_ext_pg_header header; + __le16 slot; + __le16 enc_hdl; + __le64 sas_address; + __le16 parent_dev_hdl; + u8 phy_num; + u8 r1; + __le16 dev_hdl; + u8 r2[2]; + __le32 dev_info; + __le16 flg; + u8 physical_port; + u8 max_port_connections; + __le64 dev_name; + u8 port_groups; + u8 r3[2]; + u8 enc_level; + u8 connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN]; + u8 r4[4]; +} __packed __aligned(4); + +/** + * struct leapraid_sas_phy_p0 - SAS PHY configuration page 0 + * + * @header: Extended configuration page header. + * @r1: Reserved. + * @attached_dev_hdl: Handle of attached device. + * @r2: Reserved. + * @p_link_rate: PHY link rate. + * @hw_link_rate: Hardware supported link rate. + * @r3: Reserved. + * @phy_info: PHY information. + * @neg_link_rate: Negotiated link rate. + * @r4: Reserved. + */ +struct leapraid_sas_phy_p0 { + struct leapraid_cfg_ext_pg_header header; + u8 r1[4]; + __le16 attached_dev_hdl; + u8 r2[6]; + u8 p_link_rate; + u8 hw_link_rate; + u8 r3[2]; + __le32 phy_info; + u8 neg_link_rate; + u8 r4[3]; +}; + +/** + * struct leapraid_enc_p0 - SAS enclosure page 0 + * + * @header: Extended configuration page header. + * @r1: Reserved. + * @enc_lid: Enclosure logical ID. + * @r2: Reserved. + * @enc_hdl: Enclosure handle. + * @r3: Reserved. + */ +struct leapraid_enc_p0 { + struct leapraid_cfg_ext_pg_header header; + u8 r1[4]; + __le64 enc_lid; + u8 r2[2]; + __le16 enc_hdl; + u8 r3[15]; +} __packed __aligned(4); + +/** + * struct leapraid_raid_cfg_p0_element - RAID configuration element + * + * @element_flg: Element flags. + * @vol_dev_hdl: Volume device handle. + * @r1: Reserved. + * @phys_disk_dev_hdl: Physical disk device handle. + */ +struct leapraid_raid_cfg_p0_element { + __le16 element_flg; + __le16 vol_dev_hdl; + u8 r1[2]; + __le16 phys_disk_dev_hdl; +}; + +/** + * struct leapraid_raid_cfg_p0 - RAID configuration page 0 + * + * @header: Extended configuration page header. + * @r1: Reserved. + * @cfg_num: Configuration number. + * @r2: Reserved. + * @elements_num: Number of RAID elements. + * @r3: Reserved. + * @cfg_element: Array of RAID elements. + */ +struct leapraid_raid_cfg_p0 { + struct leapraid_cfg_ext_pg_header header; + u8 r1[3]; + u8 cfg_num; + u8 r2[32]; + u8 elements_num; + u8 r3[3]; + struct leapraid_raid_cfg_p0_element cfg_element[]; +}; + +/** + * union leapraid_mpi_scsi_io_cdb_union - SCSI I/O CDB or simple SGE + * + * @cdb32: 32-byte SCSI command descriptor block. + * @sge: Simple SGE format. + */ +union leapraid_mpi_scsi_io_cdb_union { + u8 cdb32[32]; + struct leapraid_sge_simple_union sge; +}; + +/** + * struct leapraid_mpi_scsiio_req - MPI SCSI I/O request + * + * @dev_hdl: Device handle for the target. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @r1: Reserved. + * @msg_flg: Message flags. + * @r2: Reserved. + * @sense_buffer_low_add: Lower 32-bit address of sense buffer. + * @dma_flag: DMA flags. + * @r3: Reserved. + * @sense_buffer_len: Sense buffer length. + * @r4: Reserved. + * @sgl_offset0..3: SGL offsets. + * @skip_count: Bytes to skip before transfer. + * @data_len: Length of data transfer. + * @bi_dir_data_len: Bi-directional transfer length. + * @io_flg: I/O flags. + * @eedp_flag: End-to-end data protection flags. + * @eedp_block_size: End-to-end data protection block size. + * @r5: Reserved. + * @secondary_ref_tag: Secondary reference tag. + * @secondary_app_tag: Secondary application tag. + * @app_tag_trans_mask: Application tag mask. + * @lun: Logical Unit Number. + * @ctrl: Control flags. + * @cdb: SCSI Command Descriptor Block or simple SGE. + * @sgl: Scatter-gather list. + */ +struct leapraid_mpi_scsiio_req { + __le16 dev_hdl; + u8 chain_offset; + u8 func; + u8 r1[3]; + u8 msg_flg; + u8 r2[4]; + __le32 sense_buffer_low_add; + u8 dma_flag; + u8 r3; + u8 sense_buffer_len; + u8 r4; + u8 sgl_offset0; + u8 sgl_offset1; + u8 sgl_offset2; + u8 sgl_offset3; + __le32 skip_count; + __le32 data_len; + __le32 bi_dir_data_len; + __le16 io_flg; + __le16 eedp_flag; + __le16 eedp_block_size; + u8 r5[2]; + __le32 secondary_ref_tag; + __le16 secondary_app_tag; + __le16 app_tag_trans_mask; + u8 lun[8]; + __le32 ctrl; + union leapraid_mpi_scsi_io_cdb_union cdb; + union leapraid_sge_io_union sgl; +}; + +/** + * union leapraid_scsi_io_cdb_union - SCSI I/O CDB or IEEE simple SGE + * + * @cdb32: 32-byte SCSI CDB. + * @sge: IEEE simple 64-bit SGE. + */ +union leapraid_scsi_io_cdb_union { + u8 cdb32[32]; + struct leapraid_ieee_sge_simple64 sge; +}; + +/** + * struct leapraid_scsiio_req - SCSI I/O request + * + * @dev_hdl: Device handle. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @r1: Reserved. + * @msg_flg: Message flags. + * @r2: Reserved. + * @sense_buffer_low_add: Lower 32-bit address of sense buffer. + * @dma_flag: DMA flag. + * @r3: Reserved. + * @sense_buffer_len: Sense buffer length. + * @r4: Reserved. + * @sgl_offset0-3: SGL offsets. + * @skip_count: Bytes to skip before transfer. + * @data_len: Length of data transfer. + * @bi_dir_data_len: Bi-directional transfer length. + * @io_flg: I/O flags. + * @eedp_flag: End-to-end data protection flags. + * @eedp_block_size: End-to-end data protection block size. + * @r5: Reserved. + * @secondary_ref_tag: Secondary reference tag. + * @secondary_app_tag: Secondary application tag. + * @app_tag_trans_mask: Application tag mask. + * @lun: Logical Unit Number. + * @ctrl: Control flags. + * @cdb: SCSI Command Descriptor Block or simple SGE. + * @sgl: Scatter-gather list. + */ +struct leapraid_scsiio_req { + __le16 dev_hdl; + u8 chain_offset; + u8 func; + u8 r1[3]; + u8 msg_flg; + u8 r2[4]; + __le32 sense_buffer_low_add; + u8 dma_flag; + u8 r3; + u8 sense_buffer_len; + u8 r4; + u8 sgl_offset0; + u8 sgl_offset1; + u8 sgl_offset2; + u8 sgl_offset3; + __le32 skip_count; + __le32 data_len; + __le32 bi_dir_data_len; + __le16 io_flg; + __le16 eedp_flag; + __le16 eedp_block_size; + u8 r5[2]; + __le32 secondary_ref_tag; + __le16 secondary_app_tag; + __le16 app_tag_trans_mask; + u8 lun[8]; + __le32 ctrl; + union leapraid_scsi_io_cdb_union cdb; + union leapraid_ieee_sge_io_union sgl; +}; + +/** + * struct leapraid_scsiio_rep - SCSI I/O response + * + * @dev_hdl: Device handle. + * @msg_len: Length of response message. + * @func: Function code. + * @r1: Reserved. + * @msg_flg: Message flags. + * @r2: Reserved. + * @scsi_status: SCSI status. + * @scsi_state: SCSI state. + * @adapter_status: Adapter status. + * @r3: Reserved. + * @transfer_count: Number of bytes transferred. + * @sense_count: Number of sense bytes. + * @resp_info: Additional response info. + * @task_tag: Task identifier. + * @scsi_status_qualifier: SCSI status qualifier. + * @bi_dir_trans_count: Bi-directional transfer count. + * @r4: Reserved. + */ +struct leapraid_scsiio_rep { + __le16 dev_hdl; + u8 msg_len; + u8 func; + u8 r1[3]; + u8 msg_flg; + u8 r2[4]; + u8 scsi_status; + u8 scsi_state; + __le16 adapter_status; + u8 r3[4]; + __le32 transfer_count; + __le32 sense_count; + __le32 resp_info; + __le16 task_tag; + __le16 scsi_status_qualifier; + __le32 bi_dir_trans_count; + __le32 r4[3]; +}; + +/** + * struct leapraid_scsi_tm_req - SCSI Task Management request + * + * @dev_hdl: Device handle. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @r1: Reserved. + * @task_type: Task management function type. + * @r2: Reserved. + * @msg_flg: Message flags. + * @r3: Reserved. + * @lun: Logical Unit Number. + * @r4: Reserved. + * @task_mid: Task identifier. + * @r5: Reserved. + */ +struct leapraid_scsi_tm_req { + __le16 dev_hdl; + u8 chain_offset; + u8 func; + u8 r1; + u8 task_type; + u8 r2; + u8 msg_flg; + u8 r3[4]; + u8 lun[8]; + u8 r4[28]; + __le16 task_mid; + u8 r5[2]; +}; + +/** + * struct leapraid_scsi_tm_rep - SCSI Task Management response + * + * @dev_hdl: Device handle. + * @msg_len: Length of response message. + * @func: Function code. + * @resp_code: Response code. + * @task_type: Task management type. + * @r1: Reserved. + * @msg_flag: Message flags. + * @r2: Reserved. + * @adapter_status: Adapter status. + * @r3: Reserved. + * @termination_count: Count of terminated tasks. + * @response_info: Additional response info. + */ +struct leapraid_scsi_tm_rep { + __le16 dev_hdl; + u8 msg_len; + u8 func; + u8 resp_code; + u8 task_type; + u8 r1; + u8 msg_flag; + u8 r2[6]; + __le16 adapter_status; + u8 r3[4]; + __le32 termination_count; + __le32 response_info; +}; + +/** + * struct leapraid_sep_req - SEP (SCSI Enclosure Processor) request + * + * @dev_hdl: Device handle. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @act: Action to perform. + * @flg: Flags. + * @r1: Reserved. + * @msg_flag: Message flags. + * @r2: Reserved. + * @slot_status: Slot status. + * @r3: Reserved. + * @slot: Slot number. + * @enc_hdl: Enclosure handle. + */ +struct leapraid_sep_req { + __le16 dev_hdl; + u8 chain_offset; + u8 func; + u8 act; + u8 flg; + u8 r1; + u8 msg_flag; + u8 r2[4]; + __le32 slot_status; + u8 r3[12]; + __le16 slot; + __le16 enc_hdl; +}; + +/** + * struct leapraid_sep_rep - SEP response + * + * @dev_hdl: Device handle. + * @msg_len: Message length. + * @func: Function code. + * @act: Action performed. + * @flg: Flags. + * @msg_flag: Message flags. + * @r1: Reserved. + * @adapter_status: Adapter status. + * @r2: Reserved. + * @slot_status: Slot status. + * @r3: Reserved. + * @slot: Slot number. + * @enc_hdl: Enclosure handle. + */ +struct leapraid_sep_rep { + __le16 dev_hdl; + u8 msg_len; + u8 func; + u8 act; + u8 flg; + u8 r1; + u8 msg_flag; + u8 r2[6]; + __le16 adapter_status; + u8 r3[4]; + __le32 slot_status; + u8 r4[4]; + __le16 slot; + __le16 enc_hdl; +}; + +/** + * struct leapraid_adapter_init_req - Adapter initialization request + * + * @who_init: Initiator of the initialization. + * @r1: Reserved. + * @chain_offset: Chain offset. + * @func: Function code. + * @r2: Reserved. + * @msg_flg: Message flags. + * @r3: Reserved. + * @msg_ver: Message version. + * @header_ver: Header version. + * @host_buf_addr: Host buffer address (non adapter-ref). + * @r4: Reserved. + * @host_buf_size: Host buffer size (non adapter-ref). + * @host_msix_vectors: Number of host MSI-X vectors. + * @r6: Reserved. + * @req_frame_size: Request frame size. + * @rep_desc_qd: Reply descriptor queue depth. + * @rep_msg_qd: Reply message queue depth. + * @sense_buffer_add_high: High 32-bit of sense buffer address. + * @rep_msg_dma_high: High 32-bit of reply message DMA address. + * @task_desc_base_addr: Base address of task descriptors. + * @rep_desc_q_arr_addr: Address of reply descriptor queue array. + * @rep_msg_addr_dma: Reply message DMA address. + * @time_stamp: Timestamp. + */ +struct leapraid_adapter_init_req { + u8 who_init; + u8 r1; + u8 chain_offset; + u8 func; + u8 r2[3]; + u8 msg_flg; + __le32 driver_ver; + __le16 msg_ver; + __le16 header_ver; + __le32 host_buf_addr; + u8 r4[2]; + u8 host_buf_size; + u8 host_msix_vectors; + u8 r6[2]; + __le16 req_frame_size; + __le16 rep_desc_qd; + __le16 rep_msg_qd; + __le32 sense_buffer_add_high; + __le32 rep_msg_dma_high; + __le64 task_desc_base_addr; + __le64 rep_desc_q_arr_addr; + __le64 rep_msg_addr_dma; + __le64 time_stamp; +} __packed __aligned(4); + +/** + * struct leapraid_rep_desc_q_arr - Reply descriptor queue array + * + * @rep_desc_base_addr: Base address of the reply descriptors. + * @r1: Reserved. + */ +struct leapraid_rep_desc_q_arr { + __le64 rep_desc_base_addr; + __le64 r1; +} __packed __aligned(4); + +/** + * struct leapraid_adapter_init_rep - Adapter initialization reply + * + * @who_init: Initiator of the initialization. + * @r1: Reserved. + * @msg_len: Length of reply message. + * @func: Function code. + * @r2: Reserved. + * @msg_flag: Message flags. + * @r3: Reserved. + * @adapter_status: Adapter status. + * @r4: Reserved. + */ +struct leapraid_adapter_init_rep { + u8 who_init; + u8 r1; + u8 msg_len; + u8 func; + u8 r2[3]; + u8 msg_flag; + u8 r3[6]; + __le16 adapter_status; + u8 r4[4]; +}; + +/** + * struct leapraid_adapter_log_req - Adapter log request + * + * @action: Action code. + * @type: Log type. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * r1: Reserved. + * @msg_flag: Message flags. + * r2: Reserved. + * @mbox: Mailbox for command-specific parameters. + * @sge: Scatter-gather entry for data buffer. + */ +struct leapraid_adapter_log_req { + u8 action; + u8 type; + u8 chain_offset; + u8 func; + u8 r1[3]; + u8 msg_flag; + u8 r2[4]; + union { + u8 b[12]; + __le16 s[6]; + __le32 w[3]; + } mbox; + struct leapraid_sge_simple64 sge; +} __packed __aligned(4); + +/** + * struct leapraid_adapter_log_rep - Adapter log reply + * + * @action: Action code echoed. + * @type: Log type echoed. + * @msg_len: Length of message. + * @func: Function code. + * @r1: Reserved. + * @msg_flag: Message flags. + * @r2: Reserved. + * @adapter_status: Status returned by adapter. + */ +struct leapraid_adapter_log_rep { + u8 action; + u8 type; + u8 msg_len; + u8 func; + u8 r1[3]; + u8 msg_flag; + u8 r2[6]; + __le16 adapter_status; +}; + +/** + * struct leapraid_adapter_features_req - Request adapter features + * + * @r1: Reserved. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @r2: Reserved. + * @msg_flag: Message flags. + * @r3: Reserved. + */ +struct leapraid_adapter_features_req { + u8 r1[2]; + u8 chain_offset; + u8 func; + u8 r2[3]; + u8 msg_flag; + u8 r3[4]; +}; + +/** + * struct leapraid_adapter_features_rep - Adapter features reply + * + * @msg_ver: Message version. + * @msg_len: Length of reply message. + * @func: Function code. + * @header_ver: Header version. + * @r1: Reserved. + * @msg_flag: Message flags. + * @r2: Reserved. + * @adapter_status: Adapter status. + * @r3: Reserved. + * @who_init: Who initialized the adapter. + * @r4: Reserved. + * @max_msix_vectors: Max MSI-X vectors supported. + * @req_slot: Number of request slots. + * @r5: Reserved. + * @adapter_caps: Adapter capabilities. + * @fw_version: Firmware version. + * @sas_wide_max_qdepth: Max wide SAS queue depth. + * @sas_narrow_max_qdepth: Max narrow SAS queue depth. + * @r6: Reserved. + * @hp_slot: Number of high-priority slots. + * @r7: Reserved. + * @max_volumes: Maximum supported volumes. + * @max_dev_hdl: Maximum device handle. + * @r8: Reserved. + * @min_dev_hdl: Minimum device handle. + * @r9: Reserved. + */ +struct leapraid_adapter_features_rep { + __le16 msg_ver; + u8 msg_len; + u8 func; + u16 header_ver; + u8 r1; + u8 msg_flag; + u8 r2[6]; + u16 adapter_status; + u8 r3[4]; + u8 sata_max_qdepth; + u8 who_init; + u8 r4; + u8 max_msix_vectors; + __le16 req_slot; + __le16 product_id; + __le32 adapter_caps; + __le32 fw_version; + __le16 sas_wide_max_qdepth; + __le16 sas_narrow_max_qdepth; + u8 r6[10]; + __le16 hp_slot; + u8 r7[3]; + u8 max_volumes; + __le16 max_dev_hdl; + u8 r8[2]; + __le16 min_dev_hdl; + u8 r9[6]; +}; + +/** + * struct leapraid_scan_dev_req - Request to scan devices + * + * @r1: Reserved. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @r2: Reserved. + * @msg_flag: Message flags. + * @r3: Reserved. + */ +struct leapraid_scan_dev_req { + u8 r1[2]; + u8 chain_offset; + u8 func; + u8 r2[3]; + u8 msg_flag; + u8 r3[4]; +}; + +/** + * struct leapraid_scan_dev_rep - Scan devices reply + * + * @r1: Reserved. + * @msg_len: Length of message. + * @func: Function code. + * @r2: Reserved. + * @msg_flag: Message flags. + * @r3: Reserved. + * @adapter_status: Adapter status. + * @r4: Reserved. + */ +struct leapraid_scan_dev_rep { + u8 r1[2]; + u8 msg_len; + u8 func; + u8 r2[3]; + u8 msg_flag; + u8 r3[6]; + __le16 adapter_status; + u8 r4[4]; +}; + +/** + * struct leapraid_evt_notify_req - Event notification request + * + * @r1: Reserved. + * @chain_offset: Offset for chained SGE. + * @func: Function code. + * @r2: Reserved. + * @msg_flag: Message flags. + * @r3: Reserved. + * @evt_masks: Event masks to enable notifications. + * @r4: Reserved. + */ +struct leapraid_evt_notify_req { + u8 r1[2]; + u8 chain_offset; + u8 func; + u8 r2[3]; + u8 msg_flag; + u8 r3[12]; + __le32 evt_masks[4]; + u8 r4[8]; +}; + +/** + * struct leapraid_evt_notify_rep - Event notification reply + * + * @evt_data_len: Length of event data. + * @msg_len: Length of message. + * @func: Function code. + * @r1: Reserved. + * @r2: Reserved. + * @msg_flag: Message flags. + * @r3: Reserved. + * @adapter_status: Adapter status. + * @r4: Reserved. + * @evt: Event code. + * @r5: Reserved. + * @evt_data: Event data array. + */ +struct leapraid_evt_notify_rep { + __le16 evt_data_len; + u8 msg_len; + u8 func; + u8 r1[2]; + u8 r2; + u8 msg_flag; + u8 r3[6]; + __le16 adapter_status; + u8 r4[4]; + __le16 evt; + u8 r5[6]; + __le32 evt_data[]; +}; + +/** + * struct leapraid_evt_data_sas_dev_status_change - SAS device status change + * + * @task_tag: Task identifier. + * @reason_code: Reason for status change. + * @physical_port: Physical port number. + * @r1: Reserved. + * @dev_hdl: Device handle. + * @r2: Reserved. + * @sas_address: SAS address of device. + * @lun: Logical unit Number. + */ +struct leapraid_evt_data_sas_dev_status_change { + __le16 task_tag; + u8 reason_code; + u8 physical_port; + u8 r1[2]; + __le16 dev_hdl; + u8 r2[4]; + __le64 sas_address; + u8 lun[8]; +} __packed __aligned(4); +/** + * struct leapraid_evt_data_ir_change - IR (Integrated RAID) change event data + * + * @r1: Reserved. + * @reason_code: Reason for IR change. + * @r2: Reserved. + * @vol_dev_hdl: Volume device handle. + * @phys_disk_dev_hdl: Physical disk device handle. + */ +struct leapraid_evt_data_ir_change { + u8 r1; + u8 reason_code; + u8 r2[2]; + __le16 vol_dev_hdl; + __le16 phys_disk_dev_hdl; +}; + +/** + * struct leapraid_evt_data_sas_disc - SAS discovery event data + * + * @r1: Reserved. + * @reason_code: Reason for discovery event. + * @physical_port: Physical port number where event occurred. + * @r2: Reserved. + */ +struct leapraid_evt_data_sas_disc { + u8 r1; + u8 reason_code; + u8 physical_port; + u8 r2[5]; +}; + +/** + * struct leapraid_evt_sas_topo_phy_entry - SAS topology PHY entry + * + * @attached_dev_hdl: Device handle attached to PHY. + * @link_rate: Current link rate. + * @phy_status: PHY status flags. + */ +struct leapraid_evt_sas_topo_phy_entry { + __le16 attached_dev_hdl; + u8 link_rate; + u8 phy_status; +}; + +/** + * struct leapraid_evt_data_sas_topo_change_list - SAS topology change list + * + * @encl_hdl: Enclosure handle. + * @exp_dev_hdl: Expander device handle. + * @num_phys: Number of PHYs in this entry. + * @r1: Reserved. + * @entry_num: Number of PHY elements. + * @start_phy_num: Start PHY number. + * @exp_status: Expander status. + * @physical_port: Physical port number. + * @phy: Array of SAS PHY entries. + */ +struct leapraid_evt_data_sas_topo_change_list { + __le16 encl_hdl; + __le16 exp_dev_hdl; + u8 num_phys; + u8 r1[3]; + u8 entry_num; + u8 start_phy_num; + u8 exp_status; + u8 physical_port; + struct leapraid_evt_sas_topo_phy_entry phy[]; +}; + +/** + * struct leapraid_evt_data_sas_enc_dev_status_change - + * SAS enclosure device status + * + * @enc_hdl: Enclosure handle. + * @reason_code: Reason code for status change. + * @physical_port: Physical port number. + * @encl_logical_id: Enclosure logical ID. + * @num_slots: Number of slots in enclosure. + * @start_slot: First affected slot. + * @phy_bits: Bitmap of affected PHYs. + */ +struct leapraid_evt_data_sas_enc_dev_status_change { + __le16 enc_hdl; + u8 reason_code; + u8 physical_port; + __le64 encl_logical_id; + __le16 num_slots; + __le16 start_slot; + __le32 phy_bits; +} __packed __aligned(4); + +/** + * struct leapraid_io_unit_ctrl_req - I/O unit control request + * + * @op: Operation code. + * @r1: Reserved. + * @chain_offset: SGE chain offset. + * @func: Function code. + * @dev_hdl: Device handle. + * @adapter_para: Adapter parameter selector. + * @msg_flag: Message flags. + * @r2: Reserved. + * @phy_num: PHY number. + * @r3: Reserved. + * @adapter_para_value: Value for adapter parameter. + * @adapter_para_value2: Optional second parameter value. + * @r4: Reserved. + */ +struct leapraid_io_unit_ctrl_req { + u8 op; + u8 r1; + u8 chain_offset; + u8 func; + __le16 dev_hdl; + u8 adapter_para; + u8 msg_flag; + u8 r2[6]; + u8 phy_num; + u8 r3[17]; + __le32 adapter_para_value; + __le32 adapter_para_value2; + u8 r4[4]; +}; + +/** + * struct leapraid_io_unit_ctrl_rep - I/O unit control reply + * + * @op: Operation code echoed. + * @r1: Reserved. + * @func: Function code. + * @dev_hdl: Device handle. + * @r2: Reserved. + */ +struct leapraid_io_unit_ctrl_rep { + u8 op; + u8 r1[2]; + u8 func; + __le16 dev_hdl; + u8 r2[14]; +}; + +/** + * struct leapraid_raid_act_req - RAID action request + * + * @act: RAID action code. + * @r1: Reserved. + * @func: Function code. + * @r2: Reserved. + * @phys_disk_num: Number of physical disks involved. + * @r3: Reserved. + * @action_data_sge: SGE describing action-specific data. + */ +struct leapraid_raid_act_req { + u8 act; + u8 r1[2]; + u8 func; + u8 r2[2]; + u8 phys_disk_num; + u8 r3[13]; + struct leapraid_sge_simple_union action_data_sge; +}; + +/** + * struct leapraid_raid_act_rep - RAID action reply + * + * @act: RAID action code echoed. + * @r1: Reserved. + * @func: Function code. + * @vol_dev_hdl: Volume device handle. + * @r2: Reserved + * @adapter_status: Status returned by adapter. + * @r3: Reserved. + */ +struct leapraid_raid_act_rep { + u8 act; + u8 r1[2]; + u8 func; + __le16 vol_dev_hdl; + u8 r2[8]; + __le16 adapter_status; + u8 r3[76]; +}; + +/** + * struct leapraid_smp_passthrough_req - SMP passthrough request + * + * @passthrough_flg: Passthrough flags. + * @physical_port: Target PHY port. + * @r1: Reserved. + * @func: Function code. + * @req_data_len: Request data length. + * @r2: Reserved. + * @sas_address: SAS address of target device. + * @r3: Reserved. + * @sgl: Scatter-gather list describing request buffer. + */ +struct leapraid_smp_passthrough_req { + u8 passthrough_flg; + u8 physical_port; + u8 r1; + u8 func; + __le16 req_data_len; + u8 r2[10]; + __le64 sas_address; + u8 r3[8]; + union leapraid_simple_sge_union sgl; +} __packed __aligned(4); + +/** + * struct leapraid_smp_passthrough_rep - SMP passthrough reply + * + * @passthrough_flg: Passthrough flags echoed. + * @physical_port: Target PHY port. + * @r1: Reserved. + * @func: Function code. + * @resp_data_len: Length of response data. + * @r2: Reserved. + * @adapter_status: Adapter status. + * @r3: Reserved. + */ +struct leapraid_smp_passthrough_rep { + u8 passthrough_flg; + u8 physical_port; + u8 r1; + u8 func; + __le16 resp_data_len; + u8 r2[8]; + __le16 adapter_status; + u8 r3[12]; +}; + +/** + * struct leapraid_sas_io_unit_ctrl_req - SAS I/O unit control request + * + * @op: Operation code. + * @r1: Reserved. + * @func: Function code. + * @dev_hdl: Device handle. + * @r2: Reserved. + */ +struct leapraid_sas_io_unit_ctrl_req { + u8 op; + u8 r1[2]; + u8 func; + __le16 dev_hdl; + u8 r2[38]; +}; + +#endif /* LEAPRAID_H */ diff --git a/drivers/scsi/leapraid/leapraid_app.c b/drivers/scsi/leapraid/leapraid_app.c new file mode 100644 index 000000000000..742f19c07fcb --- /dev/null +++ b/drivers/scsi/leapraid/leapraid_app.c @@ -0,0 +1,803 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 LeapIO Tech Inc. + * + * LeapRAID storage and RAID controller driver. + */ +#include +#include +#include + +#include "leapraid_func.h" + +/* IOCTL device file name. */ +#define LEAPRAID_DEV_NAME "leapraid_ctl" + +/* IOCTL version. */ +#define LEAPRAID_IOCTL_VERSION 0x07 + +/* IOCTL commands. */ +#define LEAPRAID_ADAPTER_INFO 17 +#define LEAPRAID_COMMAND 20 +#define LEAPRAID_EVENTQUERY 21 +#define LEAPRAID_EVENTREPORT 23 + +/** + * struct leapraid_ioctl_header - IOCTL command header + * + * @adapter_id : Adapter identifier. + * @port_number: Port identifier. + * @max_data_size: Maximum data size for transfer. + */ +struct leapraid_ioctl_header { + u32 adapter_id; + u32 port_number; + u32 max_data_size; +}; + +/** + * struct leapraid_ioctl_diag_reset - Diagnostic reset request + * + * @hdr: Common IOCTL header. + */ +struct leapraid_ioctl_diag_reset { + struct leapraid_ioctl_header hdr; +}; + +/** + * struct leapraid_ioctl_pci_info - PCI device information + * + * @u: Union holding PCI bus/device/function information. + * @u.bits.dev: PCI device number. + * @u.bits.func: PCI function number. + * @u.bits.bus: PCI bus number. + * @u.word: Combined representation of PCI BDF. + * @seg_id: PCI segment identifier. + */ +struct leapraid_ioctl_pci_info { + union { + struct { + u32 dev:5; + u32 func:3; + u32 bus:24; + } bits; + u32 word; + } u; + u32 seg_id; +}; + +/** + * struct leapraid_ioctl_adapter_info - Adapter information for IOCTL + * + * @hdr: IOCTL header. + * @adapter_type: Adapter type identifier. + * @port_number: Port number. + * @pci_id: PCI device ID. + * @revision: Revision number. + * @sub_dev: Subsystem device ID. + * @sub_vendor: Subsystem vendor ID. + * @r0: Reserved. + * @fw_ver: Firmware version. + * @bios_ver: BIOS version. + * @driver_ver: Driver version. + * @r1: Reserved. + * @scsi_id: SCSI ID. + * @r2: Reserved. + * @pci_info: PCI information structure. + */ +struct leapraid_ioctl_adapter_info { + struct leapraid_ioctl_header hdr; + u32 adapter_type; + u32 port_number; + u32 pci_id; + u32 revision; + u32 sub_dev; + u32 sub_vendor; + u32 r0; + u32 fw_ver; + u32 bios_ver; + u8 driver_ver[32]; + u8 r1; + u8 scsi_id; + u16 r2; + struct leapraid_ioctl_pci_info pci_info; +}; + +/** + * struct leapraid_ioctl_command - IOCTL command structure + * + * @hdr: IOCTL header. + * @timeout: Command timeout. + * @rep_msg_buf_ptr: User pointer to reply message buffer. + * @c2h_buf_ptr: User pointer to card-to-host data buffer. + * @h2c_buf_ptr: User pointer to host-to-card data buffer. + * @sense_data_ptr: User pointer to sense data buffer. + * @max_rep_bytes: Maximum reply bytes. + * @c2h_size: Card-to-host data size. + * @h2c_size: Host-to-card data size. + * @max_sense_bytes: Maximum sense data bytes. + * @data_sge_offset: Data SGE offset. + * @mf: Message frame data (flexible array). + */ +struct leapraid_ioctl_command { + struct leapraid_ioctl_header hdr; + u32 timeout; + void __user *rep_msg_buf_ptr; + void __user *c2h_buf_ptr; + void __user *h2c_buf_ptr; + void __user *sense_data_ptr; + u32 max_rep_bytes; + u32 c2h_size; + u32 h2c_size; + u32 max_sense_bytes; + u32 data_sge_offset; + u8 mf[]; +}; + +static int leapraid_ctl_validate_sge_offset(struct leapraid_adapter *adapter, + const struct leapraid_req *req, + u32 offset_bytes, + size_t h2c_size, + size_t c2h_size) +{ + size_t sge_bytes; + + switch (req->func) { + case LEAPRAID_FUNC_SCSIIO: + case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH: + case LEAPRAID_FUNC_SMP_PASSTHROUGH: + case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH: + case LEAPRAID_FUNC_FW_DOWNLOAD: + case LEAPRAID_FUNC_FW_UPLOAD: + sge_bytes = LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + break; + default: + sge_bytes = adapter->adapter_attr.use_32_dma_mask ? + sizeof(struct leapraid_sge_simple32) : + sizeof(struct leapraid_sge_simple64); + break; + } + + if (h2c_size && c2h_size) + sge_bytes *= 2; + + if (offset_bytes > LEAPRAID_REQUEST_SIZE - sge_bytes) { + dev_err(&adapter->pdev->dev, + "%s: Invalid offset_bytes=%u for func=0x%x\n", + __func__, offset_bytes, req->func); + return -EINVAL; + } + + return 0; +} + +static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id) +{ + struct leapraid_adapter *adapter; + struct Scsi_Host *shost; + + spin_lock(&leapraid_adapter_lock); + list_for_each_entry(adapter, &leapraid_adapter_list, list) { + if (adapter->adapter_attr.id == adapter_id) { + if (READ_ONCE(adapter->access_ctrl.host_removing)) + break; + shost = adapter->shost; + if (!shost || !scsi_host_get(shost)) + break; + spin_unlock(&leapraid_adapter_lock); + return adapter; + } + } + spin_unlock(&leapraid_adapter_lock); + + return NULL; +} + +static void leapraid_ctl_put_adapter(struct leapraid_adapter *adapter) +{ + if (adapter && adapter->shost) + scsi_host_put(adapter->shost); +} + +static void leapraid_ctl_scsiio_cmd(struct leapraid_adapter *adapter, + void *ctl_sp_mpi_req, + u16 taskid, + dma_addr_t h2c_dma_addr, size_t h2c_size, + dma_addr_t c2h_dma_addr, size_t c2h_size, + u16 dev_hdl, void *psge) +{ + struct leapraid_mpi_scsiio_req *scsiio_request = ctl_sp_mpi_req; + + scsiio_request->sense_buffer_len = SCSI_SENSE_BUFFERSIZE; + scsiio_request->sense_buffer_low_add = + leapraid_get_sense_buffer_dma(adapter, taskid); + memset(&adapter->driver_cmds.ctl_cmd.sense, + 0, SCSI_SENSE_BUFFERSIZE); + leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, + h2c_size, c2h_dma_addr, c2h_size); + if (scsiio_request->func == LEAPRAID_FUNC_SCSIIO) + leapraid_fire_scsi_io(adapter, taskid, dev_hdl); + else + leapraid_fire_task(adapter, taskid); +} + +static int leapraid_ctl_smp_passthrough_cmd(struct leapraid_adapter *adapter, + void *ctl_sp_req, + u16 taskid, + dma_addr_t h2c_dma_addr, + size_t h2c_size, + dma_addr_t c2h_dma_addr, + size_t c2h_size, + void *psge, void *h2c) +{ + struct leapraid_smp_passthrough_req *smp_pt_req = ctl_sp_req; + u8 *data; + + if (!adapter->adapter_attr.enable_mp) + smp_pt_req->physical_port = LEAPRAID_DISABLE_MP_PORT_ID; + if (smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR) + data = (u8 *)&smp_pt_req->sgl; + else + data = h2c; + + if (!(smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR) && + h2c_size <= 10) { + dev_err(&adapter->pdev->dev, + "%s: Invalid request size=%zu\n", + __func__, h2c_size); + return -EINVAL; + } + + if (data[1] == SMP_PHY_CONTROL && + (data[10] == SMP_PHY_CONTROL_LINK_RESET || + data[10] == SMP_PHY_CONTROL_HARD_RESET)) + adapter->reset_desc.adapter_link_resetting = 1; + + leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, + h2c_size, c2h_dma_addr, c2h_size); + leapraid_fire_task(adapter, taskid); + return 0; +} + +static void leapraid_ctl_sas_io_unit_ctrl_cmd(struct leapraid_adapter *adapter, + void *ctl_sp_req, + dma_addr_t h2c_dma_addr, + size_t h2c_size, + dma_addr_t c2h_dma_addr, + size_t c2h_size, + void *psge, u16 taskid) +{ + struct leapraid_sas_io_unit_ctrl_req *sas_io_unit_ctl_req = ctl_sp_req; + + if (sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_HARD_RESET || + sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_LINK_RESET) + adapter->reset_desc.adapter_link_resetting = 1; + leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr, + h2c_size, c2h_dma_addr, c2h_size); + leapraid_fire_task(adapter, taskid); +} + +static int leapraid_ctl_do_command(struct leapraid_adapter *adapter, + struct leapraid_ioctl_command *karg, + void __user *mf) +{ + struct leapraid_req *leap_mpi_req; + struct leapraid_req *ctl_sp_mpi_req; + u16 taskid; + void *h2c = NULL; + size_t h2c_size = 0; + dma_addr_t h2c_dma_addr = 0; + void *c2h = NULL; + size_t c2h_size = 0; + dma_addr_t c2h_dma_addr = 0; + void *psge; + unsigned long timeout; + u16 dev_hdl = LEAPRAID_INVALID_DEV_HANDLE; + bool issue_reset = false; + u32 data_sge_offset_bytes; + u32 sz; + int rc; + + rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, + __func__); + if (rc) + return rc; + + leap_mpi_req = kzalloc(LEAPRAID_REQUEST_SIZE, GFP_KERNEL); + if (!leap_mpi_req) + return -ENOMEM; + + if (karg->data_sge_offset > (UINT_MAX / LEAPRAID_SGE_OFFSET_SIZE)) { + dev_err(&adapter->pdev->dev, + "%s: Invalid data_sge_offset=%u\n", + __func__, karg->data_sge_offset); + rc = -EINVAL; + goto out_cleanup; + } + + data_sge_offset_bytes = karg->data_sge_offset * + LEAPRAID_SGE_OFFSET_SIZE; + if (data_sge_offset_bytes > LEAPRAID_REQUEST_SIZE) { + dev_err(&adapter->pdev->dev, + "%s: Invalid data_sge_offset=%u\n", + __func__, karg->data_sge_offset); + rc = -EINVAL; + goto out_cleanup; + } + + if (copy_from_user(leap_mpi_req, mf, data_sge_offset_bytes)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy request message from user\n", + __func__); + rc = -EFAULT; + goto out_cleanup; + } + + h2c_size = karg->h2c_size; + c2h_size = karg->c2h_size; + rc = leapraid_ctl_validate_sge_offset(adapter, leap_mpi_req, + data_sge_offset_bytes, + h2c_size, c2h_size); + if (rc) + goto out_cleanup; + + taskid = adapter->driver_cmds.ctl_cmd.taskid; + + adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_PENDING; + memset(&adapter->driver_cmds.ctl_cmd.reply, 0, LEAPRAID_REPLY_SIZE); + ctl_sp_mpi_req = leapraid_get_task_desc(adapter, taskid); + memset(ctl_sp_mpi_req, 0, LEAPRAID_REQUEST_SIZE); + memcpy(ctl_sp_mpi_req, leap_mpi_req, data_sge_offset_bytes); + + if (ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO || + ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH || + ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) { + dev_hdl = le16_to_cpu(ctl_sp_mpi_req->func_dep1); + if (!dev_hdl || + dev_hdl > adapter->adapter_attr.features.max_dev_handle) { + dev_err(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + rc = -EINVAL; + goto out_cleanup; + } + } + + if (WARN_ON(ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSI_TMF)) { + rc = -EINVAL; + goto out_cleanup; + } + + if (h2c_size) { + h2c = dma_alloc_coherent(&adapter->pdev->dev, h2c_size, + &h2c_dma_addr, GFP_KERNEL); + if (!h2c) { + rc = -ENOMEM; + goto out_cleanup; + } + if (copy_from_user(h2c, karg->h2c_buf_ptr, h2c_size)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy h2c from user\n", + __func__); + rc = -EFAULT; + goto out_cleanup; + } + } + if (c2h_size) { + c2h = dma_alloc_coherent(&adapter->pdev->dev, c2h_size, + &c2h_dma_addr, GFP_KERNEL); + if (!c2h) { + rc = -ENOMEM; + goto out_cleanup; + } + } + + psge = (void *)ctl_sp_mpi_req + data_sge_offset_bytes; + init_completion(&adapter->driver_cmds.ctl_cmd.done); + + switch (ctl_sp_mpi_req->func) { + case LEAPRAID_FUNC_SCSIIO: + case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH: + leapraid_ctl_scsiio_cmd(adapter, ctl_sp_mpi_req, taskid, + h2c_dma_addr, h2c_size, + c2h_dma_addr, c2h_size, + dev_hdl, psge); + break; + case LEAPRAID_FUNC_SMP_PASSTHROUGH: + if (!h2c) { + rc = -EINVAL; + goto out_cleanup; + } + rc = leapraid_ctl_smp_passthrough_cmd(adapter, + ctl_sp_mpi_req, taskid, + h2c_dma_addr, h2c_size, + c2h_dma_addr, c2h_size, + psge, h2c); + if (rc) + goto out_cleanup; + break; + case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH: + leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size, + c2h_dma_addr, c2h_size); + leapraid_fire_task(adapter, taskid); + break; + case LEAPRAID_FUNC_FW_DOWNLOAD: + case LEAPRAID_FUNC_FW_UPLOAD: + leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size, + c2h_dma_addr, c2h_size); + leapraid_fire_task(adapter, taskid); + break; + case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL: + leapraid_ctl_sas_io_unit_ctrl_cmd(adapter, ctl_sp_mpi_req, + h2c_dma_addr, h2c_size, + c2h_dma_addr, c2h_size, + psge, taskid); + break; + default: + leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr, + h2c_size, c2h_dma_addr, c2h_size); + leapraid_fire_task(adapter, taskid); + break; + } + + timeout = karg->timeout; + if (timeout < LEAPRAID_CTL_CMD_TIMEOUT) + timeout = LEAPRAID_CTL_CMD_TIMEOUT; + if (!wait_for_completion_timeout(&adapter->driver_cmds.ctl_cmd.done, + timeout * HZ)) { + dev_err(&adapter->pdev->dev, + "%s: ctl_cmd timeout, status=0x%x\n", + __func__, adapter->driver_cmds.ctl_cmd.status); + leapraid_log_req_context(adapter, taskid, ctl_sp_mpi_req); + } + + if ((leap_mpi_req->func == LEAPRAID_FUNC_SMP_PASSTHROUGH || + leap_mpi_req->func == LEAPRAID_FUNC_SAS_IO_UNIT_CTRL) && + adapter->reset_desc.adapter_link_resetting) + adapter->reset_desc.adapter_link_resetting = 0; + + if (!(adapter->driver_cmds.ctl_cmd.status & LEAPRAID_CMD_DONE)) { + issue_reset = + leapraid_check_reset( + adapter->driver_cmds.ctl_cmd.status); + goto reset; + } + + if (c2h_size && copy_to_user(karg->c2h_buf_ptr, c2h, c2h_size)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy c2h to user\n", __func__); + rc = -ENODATA; + goto out_cleanup; + } + if (karg->max_rep_bytes) { + sz = min_t(u32, karg->max_rep_bytes, LEAPRAID_REPLY_SIZE); + if (copy_to_user(karg->rep_msg_buf_ptr, + &adapter->driver_cmds.ctl_cmd.reply, + sz)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy reply to user\n", + __func__); + rc = -ENODATA; + goto out_cleanup; + } + } + + if (karg->max_sense_bytes && + (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO || + leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH)) { + if (!karg->sense_data_ptr) + goto out_cleanup; + + sz = min_t(u32, karg->max_sense_bytes, SCSI_SENSE_BUFFERSIZE); + if (copy_to_user(karg->sense_data_ptr, + &adapter->driver_cmds.ctl_cmd.sense, + sz)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy sense data to user\n", + __func__); + rc = -ENODATA; + goto out_cleanup; + } + } +reset: + if (issue_reset) { + rc = -ENODATA; + if (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO || + leap_mpi_req->func == + LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH || + leap_mpi_req->func == + LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) { + dev_err(&adapter->pdev->dev, + "Fire tgt reset, hdl=0x%04x\n", + le16_to_cpu(leap_mpi_req->func_dep1)); + leapraid_issue_locked_tm( + adapter, + le16_to_cpu(leap_mpi_req->func_dep1), 0, 0, 0, + LEAPRAID_TM_TASKTYPE_TARGET_RESET, taskid, + LEAPRAID_TM_MSGFLAGS_LINK_RESET); + } else { + dev_info(&adapter->pdev->dev, + "%s:%d: call hard_reset\n", + __func__, __LINE__); + leapraid_hard_reset_handler(adapter, FULL_RESET); + } + } +out_cleanup: + if (c2h) + dma_free_coherent(&adapter->pdev->dev, c2h_size, + c2h, c2h_dma_addr); + if (h2c) + dma_free_coherent(&adapter->pdev->dev, h2c_size, + h2c, h2c_dma_addr); + kfree(leap_mpi_req); + adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED; + return rc; +} + +static int leapraid_ctl_get_adapter_info(struct leapraid_adapter *adapter, + void __user *arg) +{ + struct leapraid_ioctl_adapter_info *karg; + u8 revision; + int rc = 0; + + karg = kzalloc_obj(*karg); + if (!karg) + return -ENOMEM; + + pci_read_config_byte(adapter->pdev, PCI_CLASS_REVISION, &revision); + karg->revision = revision; + karg->pci_id = adapter->pdev->device; + karg->sub_dev = adapter->pdev->subsystem_device; + karg->sub_vendor = adapter->pdev->subsystem_vendor; + karg->pci_info.u.bits.bus = adapter->pdev->bus->number; + karg->pci_info.u.bits.dev = PCI_SLOT(adapter->pdev->devfn); + karg->pci_info.u.bits.func = PCI_FUNC(adapter->pdev->devfn); + karg->pci_info.seg_id = pci_domain_nr(adapter->pdev->bus); + karg->fw_ver = adapter->adapter_attr.features.fw_version; + + snprintf(karg->driver_ver, sizeof(karg->driver_ver), "%s-%s", + LEAPRAID_DRIVER_NAME, LEAPRAID_DRIVER_VERSION); + + karg->adapter_type = LEAPRAID_IOCTL_VERSION; + karg->bios_ver = adapter->adapter_attr.bios_version; + if (copy_to_user(arg, karg, + sizeof(struct leapraid_ioctl_adapter_info))) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy info to user\n", __func__); + rc = -EFAULT; + goto free_buf; + } + +free_buf: + kfree(karg); + return rc; +} + +static int leapraid_ctl_ioctl_main(struct file *file, unsigned int cmd, + void __user *arg) +{ + struct leapraid_ioctl_header ioctl_header; + struct leapraid_adapter *adapter = NULL; + struct leapraid_ioctl_command __user *uarg; + struct leapraid_ioctl_command karg; + int rc = -ENOIOCTLCMD; + + if (copy_from_user(&ioctl_header, arg, + sizeof(struct leapraid_ioctl_header))) { + pr_err("%s:%s: Failed to copy header from user\n", + LEAPRAID_DRIVER_NAME, __func__); + return -EFAULT; + } + + adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id); + if (!adapter) + return -EFAULT; + + if (atomic_read(&adapter->overheat_desc.thermal_alert)) { + dev_warn(&adapter->pdev->dev, + "%s: Failed, thermal_alert=%d\n", + __func__, + atomic_read(&adapter->overheat_desc.thermal_alert)); + rc = -EFAULT; + goto out_put; + } + + mutex_lock(&adapter->access_ctrl.pci_access_lock); + + rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_LONG, + __func__); + if (rc) + goto unlock; + + if (!wait_event_timeout(adapter->access_ctrl.shost_recover_wq, + !adapter->access_ctrl.shost_recover_async, + LEAPRAID_WAIT_SHOST_RECOVERY * HZ)) { + dev_warn(&adapter->pdev->dev, + "Timeout waiting for shost recovery async\n"); + rc = -EAGAIN; + goto unlock; + } + + if (adapter->access_ctrl.pcie_recovering || + adapter->scan_dev_desc.driver_loading || + adapter->access_ctrl.host_removing) { + rc = -EAGAIN; + goto unlock; + } + + if (file->f_flags & O_NONBLOCK) { + if (!mutex_trylock(&adapter->driver_cmds.ctl_cmd.mutex)) { + rc = -EAGAIN; + goto unlock; + } + } else if (mutex_lock_interruptible(&adapter->driver_cmds + .ctl_cmd.mutex)) { + rc = -ERESTARTSYS; + goto unlock; + } + + switch (_IOC_NR(cmd)) { + case LEAPRAID_ADAPTER_INFO: + if (_IOC_SIZE(cmd) == + sizeof(struct leapraid_ioctl_adapter_info)) + rc = leapraid_ctl_get_adapter_info(adapter, arg); + break; + case LEAPRAID_COMMAND: + if (copy_from_user(&karg, arg, sizeof(karg))) { + dev_err(&adapter->pdev->dev, + "%s: Failed to copy data from user\n", + __func__); + rc = -EFAULT; + break; + } + + if (karg.hdr.adapter_id != ioctl_header.adapter_id) { + rc = -EINVAL; + break; + } + + if (_IOC_SIZE(cmd) == sizeof(struct leapraid_ioctl_command)) { + uarg = arg; + rc = leapraid_ctl_do_command(adapter, &karg, + &uarg->mf); + if (rc) + dev_warn(&adapter->pdev->dev, + "%s: IOCTL cmd failed rc=%d\n", + __func__, rc); + } + break; + case LEAPRAID_EVENTQUERY: + case LEAPRAID_EVENTREPORT: + rc = 0; + break; + default: + dev_err(&adapter->pdev->dev, + "Unknown IOCTL opcode=0x%08x\n", cmd); + break; + } + mutex_unlock(&adapter->driver_cmds.ctl_cmd.mutex); + +unlock: + mutex_unlock(&adapter->access_ctrl.pci_access_lock); +out_put: + leapraid_ctl_put_adapter(adapter); + return rc; +} + +static long leapraid_ctl_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + return leapraid_ctl_ioctl_main(file, cmd, (void __user *)arg); +} + +static void leapraid_fw_mmap_open(struct vm_area_struct *vma) +{ + struct leapraid_adapter *adapter = vma->vm_private_data; + + if (!adapter) + return; + + atomic_inc(&adapter->fw_log_desc.mmap_refcnt); +} + +static void leapraid_fw_mmap_close(struct vm_area_struct *vma) +{ + struct leapraid_adapter *adapter = vma->vm_private_data; + + if (!adapter) + return; + + if (atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt)) + wake_up(&adapter->fw_log_desc.mmap_waitq); + leapraid_ctl_put_adapter(adapter); +} + +static const struct vm_operations_struct leapraid_fw_mmap_vm_ops = { + .open = leapraid_fw_mmap_open, + .close = leapraid_fw_mmap_close, +}; + +static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct leapraid_adapter *adapter = NULL; + /* Userspace passes the adapter ID via vma->vm_pgoff. */ + u32 adapter_id = vma->vm_pgoff; + unsigned long length; + int rc = -EINVAL; + + length = vma->vm_end - vma->vm_start; + + adapter = leapraid_ctl_lookup_adapter(adapter_id); + if (!adapter) { + pr_err("%s: No adapter found!\n", __func__); + return -EINVAL; + } + + if (READ_ONCE(adapter->access_ctrl.host_removing)) { + rc = -EAGAIN; + goto out_put; + } + + if (length > (LEAPRAID_SYS_LOG_BUF_SIZE + + LEAPRAID_SYS_LOG_BUF_RESERVE)) { + dev_err(&adapter->pdev->dev, + "Requested mapping size is too large!\n"); + rc = -EINVAL; + goto out_put; + } + + if (!adapter->fw_log_desc.fw_log_buffer) { + dev_err(&adapter->pdev->dev, "No log buffer!\n"); + rc = -EINVAL; + goto out_put; + } + + vma->vm_pgoff = 0; + + rc = dma_mmap_coherent(&adapter->pdev->dev, vma, + adapter->fw_log_desc.fw_log_buffer, + adapter->fw_log_desc.fw_log_buffer_dma, + length); + if (rc) { + dev_err(&adapter->pdev->dev, + "Failed to map memory to user space!\n"); + goto out_put; + } + + vma->vm_private_data = adapter; + vma->vm_ops = &leapraid_fw_mmap_vm_ops; + leapraid_fw_mmap_open(vma); + adapter = NULL; + + rc = 0; +out_put: + leapraid_ctl_put_adapter(adapter); + return rc; +} + +static const struct file_operations leapraid_ctl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = leapraid_ctl_ioctl, + .mmap = leapraid_fw_mmap, +}; + +static struct miscdevice leapraid_ctl_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = LEAPRAID_DEV_NAME, + .fops = &leapraid_ctl_fops, +}; + +int leapraid_ctl_init(void) +{ + if (misc_register(&leapraid_ctl_dev) < 0) { + pr_err("%s Can't register misc device\n", + LEAPRAID_DRIVER_NAME); + return -ENODEV; + } + return 0; +} + +void leapraid_ctl_exit(void) +{ + misc_deregister(&leapraid_ctl_dev); +} diff --git a/drivers/scsi/leapraid/leapraid_func.c b/drivers/scsi/leapraid/leapraid_func.c new file mode 100644 index 000000000000..3e1aeceab994 --- /dev/null +++ b/drivers/scsi/leapraid/leapraid_func.c @@ -0,0 +1,8626 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 LeapIO Tech Inc. + * + * LeapRAID storage and RAID controller driver. + */ +#include + +#include "leapraid_func.h" + +static int poll_queues; +module_param(poll_queues, int, 0444); +MODULE_PARM_DESC(poll_queues, + "specifies the number of queues for io_uring poll mode."); + +static int max_msix_vectors = LEAPRAID_INVALID_MSIX_VECTORS; +module_param(max_msix_vectors, int, 0444); +MODULE_PARM_DESC(max_msix_vectors, "max msix vectors"); + +static void leapraid_remove_device(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev); +static void leapraid_set_led(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev, bool on); +static void leapraid_ublk_io_dev(struct leapraid_adapter *adapter, + u64 sas_address, + struct leapraid_card_port *port); +static void leapraid_clear_cached_boot_dev(struct leapraid_adapter *adapter, + void *dev, u32 chnl); +static int leapraid_make_adapter_available(struct leapraid_adapter *adapter); +static void leapraid_sync_irqs_for_cleanup(struct leapraid_adapter *adapter); +static int leapraid_fw_log_init(struct leapraid_adapter *adapter); +static bool leapraid_should_skip_poll_work(struct leapraid_adapter *adapter); +static int leapraid_make_adapter_ready(struct leapraid_adapter *adapter, + enum reset_type type); + +static noinline bool leapraid_shost_in_recovery(struct Scsi_Host *shost) +{ + enum scsi_host_state state; + + state = READ_ONCE(shost->shost_state); + return state == SHOST_RECOVERY || + state == SHOST_CANCEL_RECOVERY || + state == SHOST_DEL_RECOVERY || + shost->tmf_in_progress; +} + +static inline bool leapraid_is_end_dev(u32 dev_type) +{ + return (dev_type & LEAPRAID_DEVTYP_END_DEV) && + ((dev_type & LEAPRAID_DEVTYP_SSP_TGT) || + (dev_type & LEAPRAID_DEVTYP_STP_TGT) || + (dev_type & LEAPRAID_DEVTYP_SATA_DEV)); +} + +bool leapraid_pci_removed(struct leapraid_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + u32 vendor_id; + + if (pci_bus_read_config_dword(pdev->bus, + pdev->devfn, + PCI_VENDOR_ID, + &vendor_id)) + return true; + + return (vendor_id & LEAPRAID_PCI_VENDOR_ID_MASK) != + LEAPRAID_VENDOR_ID; +} + +static bool leapraid_pci_active(struct leapraid_adapter *adapter) +{ + return !(adapter->access_ctrl.pcie_recovering || + leapraid_pci_removed(adapter)); +} + +void *leapraid_get_reply_vaddr(struct leapraid_adapter *adapter, u32 rep_paddr) +{ + if (!rep_paddr) + return NULL; + + return adapter->mem_desc.rep_msg + + (rep_paddr - (u32)adapter->mem_desc.rep_msg_dma); +} + +void *leapraid_get_task_desc(struct leapraid_adapter *adapter, u16 taskid) +{ + return adapter->mem_desc.task_desc + + taskid * LEAPRAID_REQUEST_SIZE; +} + +void *leapraid_get_sense_buffer(struct leapraid_adapter *adapter, u16 taskid) +{ + return adapter->mem_desc.sense_data + + (taskid - 1) * SCSI_SENSE_BUFFERSIZE; +} + +__le32 leapraid_get_sense_buffer_dma(struct leapraid_adapter *adapter, + u16 taskid) +{ + return cpu_to_le32(adapter->mem_desc.sense_data_dma + + ((taskid - 1) * SCSI_SENSE_BUFFERSIZE)); +} + +void leapraid_mask_int(struct leapraid_adapter *adapter) +{ + u32 reg; + + adapter->mask_int = 1; + reg = leapraid_readl(&adapter->iomem_base->host_int_mask); + reg |= LEAPRAID_TO_SYS_DB_MASK + LEAPRAID_REPLY_INT_MASK + + LEAPRAID_RESET_IRQ_MASK; + writel(reg, &adapter->iomem_base->host_int_mask); + leapraid_readl(&adapter->iomem_base->host_int_mask); +} + +void leapraid_unmask_int(struct leapraid_adapter *adapter) +{ + u32 reg; + + reg = leapraid_readl(&adapter->iomem_base->host_int_mask); + reg &= ~LEAPRAID_REPLY_INT_MASK; + writel(reg, &adapter->iomem_base->host_int_mask); + adapter->mask_int = 0; +} + +static void leapraid_overheat_suspend(struct leapraid_adapter *adapter) +{ + struct workqueue_struct *wq; + struct Scsi_Host *shost; + struct pci_dev *pdev; + + if (!adapter) + return; + + pdev = adapter->pdev; + shost = pci_get_drvdata(pdev); + if (!shost) { + dev_warn(&pdev->dev, + "Overheat suspend failed, invalid host or adapter\n"); + return; + } + + wq = adapter->overheat_desc.fault_overheat_wq; + if (!wq) + return; + + if (atomic_cmpxchg(&adapter->overheat_desc.thermal_alert, 0, 1)) + return; + + queue_work(wq, &adapter->overheat_desc.fault_overheat_work); +} + +static void leapraid_stop_adapter_on_fault(struct leapraid_adapter *adapter, + u32 db) +{ + u32 adapter_state = db & LEAPRAID_DB_MASK; + bool fault_1 = false; + bool fault_2 = false; + + fault_1 = adapter_state == LEAPRAID_DB_MASK; + fault_2 = adapter_state == LEAPRAID_DB_FAULT && + (db & LEAPRAID_DB_DATA_MASK) == LEAPRAID_DB_OVER_TEMPERATURE; + + if (!fault_1 && !fault_2) + return; + + if (fault_1) + dev_err(&adapter->pdev->dev, + "%s: Doorbell status 0xFFFF!\n", __func__); + else + dev_err(&adapter->pdev->dev, + "%s: Adapter overheating detected!\n", __func__); + + leapraid_overheat_suspend(adapter); +} + +u32 leapraid_get_adapter_state(struct leapraid_adapter *adapter) +{ + u32 db; + u32 adapter_state; + + db = leapraid_readl(&adapter->iomem_base->db); + adapter_state = db & LEAPRAID_DB_MASK; + leapraid_stop_adapter_on_fault(adapter, db); + return adapter_state; +} + +static bool leapraid_wait_adapter_ready(struct leapraid_adapter *adapter) +{ + u32 cur_state; + u32 cnt; + + for (cnt = LEAPRAID_ADAPTER_READY_MAX_RETRY; cnt > 0; cnt--) { + cur_state = leapraid_get_adapter_state(adapter); + + if (cur_state == LEAPRAID_DB_READY) + return true; + if (cur_state == LEAPRAID_DB_FAULT) + break; + usleep_range(LEAPRAID_ADAPTER_READY_SLEEP_MIN_US, + LEAPRAID_ADAPTER_READY_SLEEP_MAX_US); + } + + return false; +} + +static int leapraid_db_wait_int_host(struct leapraid_adapter *adapter) +{ + u32 cnt; + + for (cnt = LEAPRAID_DB_WAIT_MAX_RETRY; cnt > 0; cnt--) { + if (leapraid_readl(&adapter->iomem_base->host_int_status) & + LEAPRAID_ADAPTER2HOST_DB_STATUS) + return 0; + + udelay(LEAPRAID_DB_WAIT_DELAY_US); + } + + return -EFAULT; +} + +static int leapraid_db_wait_ack_and_clear_int(struct leapraid_adapter *adapter) +{ + u32 adapter_state; + u32 int_status; + u32 cnt; + + for (cnt = LEAPRAID_ADAPTER_READY_MAX_RETRY; cnt > 0; cnt--) { + int_status = + leapraid_readl(&adapter->iomem_base->host_int_status); + + if (int_status == 0xFFFFFFFF) + return -EFAULT; + + if (!(int_status & LEAPRAID_HOST2ADAPTER_DB_STATUS)) + return 0; + + if (int_status & LEAPRAID_ADAPTER2HOST_DB_STATUS) { + adapter_state = leapraid_get_adapter_state(adapter); + if (adapter_state == LEAPRAID_DB_FAULT) + return -EFAULT; + } + + usleep_range(LEAPRAID_ADAPTER_READY_SLEEP_MIN_US, + LEAPRAID_ADAPTER_READY_SLEEP_MAX_US); + } + + return -EFAULT; +} + +static int leapraid_handshake_func(struct leapraid_adapter *adapter, + int req_bytes, u32 *req, + int rep_bytes, u16 *rep) +{ + int failed, i; + + if (leapraid_readl(&adapter->iomem_base->db) & LEAPRAID_DB_USED) { + dev_err(&adapter->pdev->dev, "doorbell used\n"); + return -EFAULT; + } + + if (leapraid_readl(&adapter->iomem_base->host_int_status) & + LEAPRAID_ADAPTER2HOST_DB_STATUS) + writel(0, &adapter->iomem_base->host_int_status); + + writel(((LEAPRAID_FUNC_HANDSHAKE << LEAPRAID_DB_FUNC_SHIFT) | + ((req_bytes / LEAPRAID_DWORDS_BYTE_SIZE) << + LEAPRAID_DB_ADD_DWORDS_SHIFT)), + &adapter->iomem_base->db); + + if (leapraid_db_wait_int_host(adapter)) { + dev_err(&adapter->pdev->dev, "%d: Wait db interrupt timeout\n", + __LINE__); + return -EFAULT; + } + + writel(0, &adapter->iomem_base->host_int_status); + + if (leapraid_db_wait_ack_and_clear_int(adapter)) { + dev_err(&adapter->pdev->dev, "%d: Wait ack failure\n", + __LINE__); + return -EFAULT; + } + + for (i = 0, failed = 0; + i < req_bytes / LEAPRAID_DWORDS_BYTE_SIZE && !failed; + i++) { + writel((u32)req[i], &adapter->iomem_base->db); + if (leapraid_db_wait_ack_and_clear_int(adapter)) + failed = 1; + } + if (failed) { + dev_err(&adapter->pdev->dev, "%d: Wait ack failure\n", + __LINE__); + return -EFAULT; + } + + for (i = 0; i < rep_bytes / LEAPRAID_WORD_BYTE_SIZE; i++) { + if (leapraid_db_wait_int_host(adapter)) { + dev_err(&adapter->pdev->dev, + "%d: Wait db interrupt timeout\n", __LINE__); + return -EFAULT; + } + rep[i] = (u16)(leapraid_readl(&adapter->iomem_base->db) + & LEAPRAID_DB_DATA_MASK); + writel(0, &adapter->iomem_base->host_int_status); + } + + if (leapraid_db_wait_int_host(adapter)) { + dev_err(&adapter->pdev->dev, "%d: Wait db interrupt timeout\n", + __LINE__); + return -EFAULT; + } + + writel(0, &adapter->iomem_base->host_int_status); + + return 0; +} + +int leapraid_check_adapter_is_op(struct leapraid_adapter *adapter, int wait, + const char *caller) +{ + int wait_count; + + for (wait_count = wait; wait_count > 0; wait_count--) { + if (READ_ONCE(adapter->access_ctrl.host_removing)) { + dev_warn(&adapter->pdev->dev, + "%s: Host is removing\n", caller); + return -EFAULT; + } + + if (leapraid_pci_removed(adapter)) { + dev_warn(&adapter->pdev->dev, + "%s: PCI device removed\n", __func__); + return -EFAULT; + } + + if (leapraid_get_adapter_state(adapter) == + LEAPRAID_DB_OPERATIONAL) + return 0; + + dev_dbg(&adapter->pdev->dev, + "%s: Wait for adapter to become op status(cnt=%d)\n", + caller, wait - wait_count); + + ssleep(1); + } + + dev_err(&adapter->pdev->dev, + "%s: Adapter failed to become op state, last state=%d\n", + caller, leapraid_get_adapter_state(adapter)); + + return -EFAULT; +} + +struct leapraid_io_req_tracker *leapraid_get_io_tracker_from_taskid( + struct leapraid_adapter *adapter, u16 taskid) +{ + struct scsi_cmnd *scmd; + + if (WARN_ON(!taskid)) + return NULL; + + if (WARN_ON(taskid > adapter->shost->can_queue)) + return NULL; + + scmd = leapraid_get_scmd_from_taskid(adapter, taskid); + if (scmd) + return scsi_cmd_priv(scmd); + + return NULL; +} + +static u8 leapraid_get_cb_idx(struct leapraid_adapter *adapter, u16 taskid) +{ + struct leapraid_driver_cmd *sp_cmd; + u8 cb_idx = 0xFF; + + if (WARN_ON(!taskid)) + return cb_idx; + + list_for_each_entry(sp_cmd, &adapter->driver_cmds.special_cmd_list, + list) + if (taskid == sp_cmd->taskid || + taskid == sp_cmd->hp_taskid || + taskid == sp_cmd->inter_taskid) + return sp_cmd->cb_idx; + + WARN_ON(cb_idx == 0xFF); + return cb_idx; +} + +struct scsi_cmnd *leapraid_get_scmd_from_taskid( + struct leapraid_adapter *adapter, + u16 taskid) +{ + struct leapraid_scsiio_req *leap_mpi_req; + struct leapraid_io_req_tracker *st; + struct scsi_cmnd *scmd; + u32 uniq_tag; + + if (taskid <= 0 || taskid > adapter->shost->can_queue) + return NULL; + + uniq_tag = adapter->mem_desc.taskid_to_uniq_tag[taskid - 1] << + BLK_MQ_UNIQUE_TAG_BITS | (taskid - 1); + leap_mpi_req = leapraid_get_task_desc(adapter, taskid); + if (!leap_mpi_req->dev_hdl) + return NULL; + + scmd = scsi_host_find_tag(adapter->shost, uniq_tag); + if (scmd) { + st = scsi_cmd_priv(scmd); + if (st && st->taskid == taskid) + return scmd; + } + + return NULL; +} + +u16 leapraid_alloc_scsiio_taskid(struct leapraid_adapter *adapter, + struct scsi_cmnd *scmd) +{ + struct leapraid_io_req_tracker *request; + u16 taskid; + u32 tag = scsi_cmd_to_rq(scmd)->tag; + u32 unique_tag; + + unique_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); + tag = blk_mq_unique_tag_to_tag(unique_tag); + adapter->mem_desc.taskid_to_uniq_tag[tag] = + blk_mq_unique_tag_to_hwq(unique_tag); + + request = scsi_cmd_priv(scmd); + taskid = tag + 1; + request->taskid = taskid; + request->scmd = scmd; + return taskid; +} + +static void leapraid_check_pending_io(struct leapraid_adapter *adapter) +{ + if (adapter->access_ctrl.shost_recovering && + adapter->reset_desc.pending_io_cnt) { + if (adapter->reset_desc.pending_io_cnt == 1) + wake_up(&adapter->reset_desc.reset_wait_queue); + adapter->reset_desc.pending_io_cnt--; + } +} + +static void leapraid_clear_io_tracker( + struct leapraid_adapter *adapter, + struct leapraid_io_req_tracker *io_tracker) +{ + if (!io_tracker) + return; + + if (WARN_ON(io_tracker->taskid == 0)) + return; + + io_tracker->scmd = NULL; +} + +static bool leapraid_is_fixed_taskid(struct leapraid_adapter *adapter, + u16 taskid) +{ + struct leapraid_driver_cmds *driver_cmds = &adapter->driver_cmds; + + return (taskid == driver_cmds->ctl_cmd.taskid || + taskid == driver_cmds->tm_cmd.hp_taskid || + taskid == driver_cmds->ctl_cmd.hp_taskid || + taskid == driver_cmds->scan_dev_cmd.inter_taskid || + taskid == driver_cmds->transport_cmd.inter_taskid || + taskid == driver_cmds->cfg_op_cmd.inter_taskid || + taskid == driver_cmds->enc_cmd.inter_taskid || + taskid == driver_cmds->notify_event_cmd.inter_taskid); +} + +void leapraid_free_taskid(struct leapraid_adapter *adapter, u16 taskid) +{ + struct leapraid_io_req_tracker *io_tracker; + void *task_desc; + + if (leapraid_is_fixed_taskid(adapter, taskid)) + return; + + if (taskid <= adapter->shost->can_queue) { + io_tracker = leapraid_get_io_tracker_from_taskid(adapter, + taskid); + if (!io_tracker) { + leapraid_check_pending_io(adapter); + return; + } + + task_desc = leapraid_get_task_desc(adapter, taskid); + memset(task_desc, 0, LEAPRAID_REQUEST_SIZE); + leapraid_clear_io_tracker(adapter, io_tracker); + leapraid_check_pending_io(adapter); + adapter->mem_desc.taskid_to_uniq_tag[taskid - 1] = 0xFFFF; + } +} + +static u8 leapraid_get_msix_idx(struct leapraid_adapter *adapter, + struct scsi_cmnd *scmd) +{ + if (scmd && adapter->shost->nr_hw_queues > 1) { + u32 tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); + + return blk_mq_unique_tag_to_hwq(tag); + } + return adapter->notification_desc.msix_cpu_map[raw_smp_processor_id()]; +} + +static u8 leapraid_get_and_set_msix_idx_from_taskid( + struct leapraid_adapter *adapter, u16 taskid) +{ + struct leapraid_io_req_tracker *io_tracker = NULL; + + if (taskid <= adapter->shost->can_queue) + io_tracker = leapraid_get_io_tracker_from_taskid(adapter, + taskid); + + if (!io_tracker) + return leapraid_get_msix_idx(adapter, NULL); + + io_tracker->msix_io = leapraid_get_msix_idx(adapter, io_tracker->scmd); + + return io_tracker->msix_io; +} + +void leapraid_fire_scsi_io(struct leapraid_adapter *adapter, u16 taskid, + u16 handle) +{ + struct leapraid_atomic_req_desc desc; + + desc.flg = LEAPRAID_REQ_DESC_FLG_SCSI_IO; + desc.msix_idx = leapraid_get_and_set_msix_idx_from_taskid(adapter, + taskid); + desc.taskid = cpu_to_le16(taskid); + writel((__force u32)cpu_to_le32(*((u32 *)&desc)), + &adapter->iomem_base->atomic_req_desc_post); +} + +void leapraid_fire_hpr_task(struct leapraid_adapter *adapter, u16 taskid, + u16 msix_task) +{ + struct leapraid_atomic_req_desc desc; + + desc.flg = LEAPRAID_REQ_DESC_FLG_HPR; + desc.msix_idx = msix_task; + desc.taskid = cpu_to_le16(taskid); + writel((__force u32)cpu_to_le32(*((u32 *)&desc)), + &adapter->iomem_base->atomic_req_desc_post); +} + +void leapraid_fire_task(struct leapraid_adapter *adapter, u16 taskid) +{ + struct leapraid_atomic_req_desc desc; + + desc.flg = LEAPRAID_REQ_DESC_FLG_DFLT_TYPE; + desc.msix_idx = leapraid_get_and_set_msix_idx_from_taskid(adapter, + taskid); + desc.taskid = cpu_to_le16(taskid); + writel((__force u32)cpu_to_le32(*((u32 *)&desc)), + &adapter->iomem_base->atomic_req_desc_post); +} + +void leapraid_clean_active_scsi_cmds(struct leapraid_adapter *adapter) +{ + struct leapraid_io_req_tracker *io_tracker; + struct scsi_cmnd *scmd; + void *task_desc; + u16 taskid; + + leapraid_sync_irqs_for_cleanup(adapter); + + for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) { + scmd = leapraid_get_scmd_from_taskid(adapter, taskid); + if (!scmd) + continue; + + io_tracker = scsi_cmd_priv(scmd); + if (io_tracker && io_tracker->taskid == 0) + continue; + + scsi_dma_unmap(scmd); + task_desc = leapraid_get_task_desc(adapter, taskid); + memset(task_desc, 0, LEAPRAID_REQUEST_SIZE); + leapraid_clear_io_tracker(adapter, io_tracker); + if (!leapraid_pci_active(adapter) || + adapter->reset_desc.adapter_reset_results != 0 || + atomic_read(&adapter->overheat_desc.thermal_alert) || + adapter->access_ctrl.host_removing) + scmd->result = DID_NO_CONNECT << 16; + else + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + scsi_done(scmd); + } +} + +static void leapraid_clean_active_driver_cmd(struct leapraid_driver_cmd *cmd) +{ + if (cmd->status & LEAPRAID_CMD_PENDING) { + cmd->status |= LEAPRAID_CMD_RESET; + complete(&cmd->done); + } +} + +static void leapraid_clean_active_driver_cmds(struct leapraid_adapter *adapter) +{ + struct leapraid_driver_cmds *driver_cmds; + + driver_cmds = &adapter->driver_cmds; + leapraid_clean_active_driver_cmd(&driver_cmds->tm_cmd); + leapraid_clean_active_driver_cmd(&driver_cmds->transport_cmd); + leapraid_clean_active_driver_cmd(&driver_cmds->enc_cmd); + leapraid_clean_active_driver_cmd(&driver_cmds->notify_event_cmd); + leapraid_clean_active_driver_cmd(&driver_cmds->cfg_op_cmd); + leapraid_clean_active_driver_cmd(&driver_cmds->ctl_cmd); + + if (driver_cmds->scan_dev_cmd.status & LEAPRAID_CMD_PENDING) { + adapter->scan_dev_desc.scan_dev_failed = 1; + driver_cmds->scan_dev_cmd.status |= LEAPRAID_CMD_RESET; + if (adapter->scan_dev_desc.driver_loading) { + adapter->scan_dev_desc.scan_start_failed = + LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR; + adapter->scan_dev_desc.scan_start = 0; + } else { + complete(&driver_cmds->scan_dev_cmd.done); + } + } +} + +static void leapraid_clean_active_cmds(struct leapraid_adapter *adapter) +{ + leapraid_clean_active_driver_cmds(adapter); + leapraid_clean_active_fw_evt(adapter); + leapraid_clean_active_scsi_cmds(adapter); +} + +static void leapraid_tgt_not_responding(struct leapraid_adapter *adapter, + u16 hdl) +{ + struct leapraid_starget_priv *starget_priv = NULL; + struct leapraid_sas_dev *sas_dev; + unsigned long flags = 0; + u32 adapter_state; + + if (adapter->access_ctrl.pcie_recovering) + return; + + adapter_state = leapraid_get_adapter_state(adapter); + if (adapter_state != LEAPRAID_DB_OPERATIONAL) + return; + + if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle || + test_bit(hdl, adapter->dev_topo.pd_hdls)) + return; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + if (sas_dev && sas_dev->starget && sas_dev->starget->hostdata) { + starget_priv = sas_dev->starget->hostdata; + starget_priv->deleted = 1; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (starget_priv) + starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; + + if (sas_dev) + leapraid_sdev_put(sas_dev); +} + +static void leapraid_tgt_rst_send(struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_starget_priv *starget_priv = NULL; + struct leapraid_sas_dev *sas_dev; + struct leapraid_card_port *port; + u64 sas_address; + unsigned long flags; + u32 adapter_state; + + if (adapter->access_ctrl.pcie_recovering) + return; + + adapter_state = leapraid_get_adapter_state(adapter); + if (adapter_state != LEAPRAID_DB_OPERATIONAL) + return; + + if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle || + test_bit(hdl, adapter->dev_topo.pd_hdls)) + return; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + if (sas_dev && sas_dev->starget && sas_dev->starget->hostdata) { + starget_priv = sas_dev->starget->hostdata; + starget_priv->deleted = 1; + sas_address = sas_dev->sas_addr; + port = sas_dev->card_port; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (starget_priv) { + leapraid_ublk_io_dev(adapter, sas_address, port); + starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; + } + if (sas_dev) + leapraid_sdev_put(sas_dev); +} + +static inline void leapraid_single_mpi_sg_append( + struct leapraid_adapter *adapter, + void *sge, + u32 flag_and_len, + dma_addr_t dma_addr) +{ + if (adapter->adapter_attr.use_32_dma_mask) { + struct leapraid_sge_simple32 *sge32 = sge; + + sge32->flg_and_len = + cpu_to_le32(flag_and_len | + (LEAPRAID_SGE_FLG_32 | + LEAPRAID_SGE_FLG_SYSTEM_ADDR) << + LEAPRAID_SGE_FLG_SHIFT); + sge32->addr = cpu_to_le32(dma_addr); + } else { + struct leapraid_sge_simple64 *sge64 = sge; + + sge64->flg_and_len = + cpu_to_le32(flag_and_len | + (LEAPRAID_SGE_FLG_64 | + LEAPRAID_SGE_FLG_SYSTEM_ADDR) << + LEAPRAID_SGE_FLG_SHIFT); + sge64->addr = cpu_to_le64(dma_addr); + } +} + +static inline void leapraid_single_ieee_sg_append(void *sge, u8 flag, + u8 next_chain_offset, + u32 len, + dma_addr_t dma_addr) +{ + struct leapraid_chain64_ieee_sg *ieee_sg = sge; + + ieee_sg->flg = flag; + ieee_sg->next_chain_offset = next_chain_offset; + ieee_sg->len = cpu_to_le32(len); + ieee_sg->addr = cpu_to_le64(dma_addr); +} + +static void leapraid_build_nodata_mpi_sg(struct leapraid_adapter *adapter, + void *sge) +{ + leapraid_single_mpi_sg_append(adapter, + sge, + (LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL | + LEAPRAID_SGE_FLG_SIMPLE_ONE) << + LEAPRAID_SGE_FLG_SHIFT, + LEAPRAID_SGE_NO_DATA_ADDR); +} + +void leapraid_build_mpi_sg(struct leapraid_adapter *adapter, void *sge, + dma_addr_t h2c_dma_addr, size_t h2c_size, + dma_addr_t c2h_dma_addr, size_t c2h_size) +{ + if (h2c_size && !c2h_size) { + leapraid_single_mpi_sg_append(adapter, + sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL | + LEAPRAID_SGE_FLG_H2C) << + LEAPRAID_SGE_FLG_SHIFT) | + h2c_size, + h2c_dma_addr); + } else if (!h2c_size && c2h_size) { + leapraid_single_mpi_sg_append(adapter, + sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL) << + LEAPRAID_SGE_FLG_SHIFT) | + c2h_size, + c2h_dma_addr); + } else if (h2c_size && c2h_size) { + leapraid_single_mpi_sg_append(adapter, + sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_H2C) << + LEAPRAID_SGE_FLG_SHIFT) | + h2c_size, + h2c_dma_addr); + if (adapter->adapter_attr.use_32_dma_mask) + sge += sizeof(struct leapraid_sge_simple32); + else + sge += sizeof(struct leapraid_sge_simple64); + leapraid_single_mpi_sg_append(adapter, + sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL) << + LEAPRAID_SGE_FLG_SHIFT) | + c2h_size, + c2h_dma_addr); + } else { + leapraid_build_nodata_mpi_sg(adapter, sge); + } +} + +void leapraid_build_ieee_nodata_sg(struct leapraid_adapter *adapter, void *sge) +{ + leapraid_single_ieee_sg_append(sge, + (LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | + LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR | + LEAPRAID_IEEE_SGE_FLG_EOL), + 0, + 0, + LEAPRAID_SGE_NO_DATA_ADDR); +} + +int leapraid_build_scmd_ieee_sg(struct leapraid_adapter *adapter, + struct scsi_cmnd *scmd, u16 taskid) +{ + struct leapraid_scsiio_req *scsiio_req; + struct leapraid_io_req_tracker *io_tracker; + struct scatterlist *scmd_sg_cur; + int sg_entries_left; + void *sg_entry_cur; + void *host_chain; + dma_addr_t host_chain_dma; + u8 host_chain_cursor; + u32 sg_entries_in_cur_seg; + u32 chain_offset_in_cur_seg; + u32 chain_len_in_cur_seg; + + io_tracker = scsi_cmd_priv(scmd); + scsiio_req = leapraid_get_task_desc(adapter, taskid); + scmd_sg_cur = scsi_sglist(scmd); + sg_entries_left = scsi_dma_map(scmd); + if (sg_entries_left < 0) + return -ENOMEM; + sg_entry_cur = &scsiio_req->sgl; + if (sg_entries_left <= LEAPRAID_SGL_INLINE_THRESHOLD) + goto fill_last_seg; + + scsiio_req->chain_offset = LEAPRAID_CHAIN_OFFSET_DWORDS; + leapraid_single_ieee_sg_append(sg_entry_cur, + LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | + LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR, + 0, sg_dma_len(scmd_sg_cur), + sg_dma_address(scmd_sg_cur)); + scmd_sg_cur = sg_next(scmd_sg_cur); + sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + sg_entries_left--; + + host_chain_cursor = 0; + host_chain = io_tracker->chain + + host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; + host_chain_dma = io_tracker->chain_dma + + host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; + host_chain_cursor += 1; + for (;;) { + sg_entries_in_cur_seg = + (sg_entries_left <= LEAPRAID_MAX_SGES_IN_CHAIN) ? + sg_entries_left : LEAPRAID_MAX_SGES_IN_CHAIN; + chain_offset_in_cur_seg = + (sg_entries_left == (int)sg_entries_in_cur_seg) ? + 0 : sg_entries_in_cur_seg; + chain_len_in_cur_seg = sg_entries_in_cur_seg * + LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + if (chain_offset_in_cur_seg) + chain_len_in_cur_seg += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + + leapraid_single_ieee_sg_append( + sg_entry_cur, + LEAPRAID_IEEE_SGE_FLG_CHAIN_ONE | + LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR, + chain_offset_in_cur_seg, chain_len_in_cur_seg, + host_chain_dma); + sg_entry_cur = host_chain; + if (!chain_offset_in_cur_seg) + goto fill_last_seg; + + while (sg_entries_in_cur_seg) { + leapraid_single_ieee_sg_append( + sg_entry_cur, + LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | + LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR, + 0, sg_dma_len(scmd_sg_cur), + sg_dma_address(scmd_sg_cur)); + scmd_sg_cur = sg_next(scmd_sg_cur); + sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + sg_entries_left--; + sg_entries_in_cur_seg--; + } + host_chain = io_tracker->chain + + host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; + host_chain_dma = io_tracker->chain_dma + + host_chain_cursor * LEAPRAID_CHAIN_SEG_SIZE; + host_chain_cursor += 1; + } + +fill_last_seg: + while (sg_entries_left > 0) { + u32 flags = LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | + LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR; + if (sg_entries_left == 1) + flags |= LEAPRAID_IEEE_SGE_FLG_EOL; + leapraid_single_ieee_sg_append(sg_entry_cur, flags, + 0, sg_dma_len(scmd_sg_cur), + sg_dma_address(scmd_sg_cur)); + scmd_sg_cur = sg_next(scmd_sg_cur); + sg_entry_cur += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + sg_entries_left--; + } + return 0; +} + +void leapraid_build_ieee_sg(struct leapraid_adapter *adapter, void *sge, + dma_addr_t h2c_dma_addr, size_t h2c_size, + dma_addr_t c2h_dma_addr, size_t c2h_size) +{ + u32 base_flag; + u32 flag; + + base_flag = LEAPRAID_IEEE_SGE_FLG_SIMPLE_ONE | + LEAPRAID_IEEE_SGE_FLG_SYSTEM_ADDR; + + if (h2c_size && !c2h_size) { + flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL; + + leapraid_single_ieee_sg_append(sge, + flag, + 0, + h2c_size, + h2c_dma_addr); + } else if (!h2c_size && c2h_size) { + flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL; + + leapraid_single_ieee_sg_append(sge, + flag, + 0, + c2h_size, + c2h_dma_addr); + } else if (h2c_size && c2h_size) { + leapraid_single_ieee_sg_append(sge, + base_flag, + 0, + h2c_size, + h2c_dma_addr); + sge += LEAPRAID_IEEE_SGE64_ENTRY_SIZE; + + flag = base_flag | LEAPRAID_IEEE_SGE_FLG_EOL; + leapraid_single_ieee_sg_append(sge, + flag, + 0, + c2h_size, + c2h_dma_addr); + } else { + leapraid_build_ieee_nodata_sg(adapter, sge); + } +} + +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_from_tgt( + struct leapraid_adapter *adapter, + struct leapraid_starget_priv *tgt_priv) +{ + assert_spin_locked(&adapter->dev_topo.sas_dev_lock); + if (tgt_priv->sas_dev) + leapraid_sdev_get(tgt_priv->sas_dev); + + return tgt_priv->sas_dev; +} + +struct leapraid_sas_dev *leapraid_get_sas_dev_from_tgt( + struct leapraid_adapter *adapter, + struct leapraid_starget_priv *tgt_priv) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter, tgt_priv); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return sas_dev; +} + +static struct leapraid_card_port *leapraid_get_port_by_id( + struct leapraid_adapter *adapter, + u8 port_id, bool skip_dirty) +{ + struct leapraid_card_port *port; + struct leapraid_card_port *dirty_port = NULL; + + if (!adapter->adapter_attr.enable_mp) + port_id = LEAPRAID_DISABLE_MP_PORT_ID; + + list_for_each_entry(port, &adapter->dev_topo.card_port_list, list) { + if (port->port_id != port_id) + continue; + + if (!(port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY)) + return port; + + if (skip_dirty && !dirty_port) + dirty_port = port; + } + + if (dirty_port) + return dirty_port; + + if (unlikely(!adapter->adapter_attr.enable_mp)) { + port = kzalloc_obj(*port, GFP_ATOMIC); + if (!port) { + dev_warn(&adapter->pdev->dev, + "%s: Failed to alloc port\n", __func__); + return NULL; + } + + port->port_id = LEAPRAID_DISABLE_MP_PORT_ID; + list_add_tail(&port->list, &adapter->dev_topo.card_port_list); + return port; + } + + return NULL; +} + +struct leapraid_vphy *leapraid_get_vphy_by_phy(struct leapraid_card_port *port, + u32 phy_seq_num) +{ + struct leapraid_vphy *vphy; + + if (!port || !port->vphys_mask) + return NULL; + + list_for_each_entry(vphy, &port->vphys_list, list) { + if (vphy->phy_mask & BIT(phy_seq_num)) + return vphy; + } + + return NULL; +} + +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr_and_rphy( + struct leapraid_adapter *adapter, + u64 sas_address, + struct sas_rphy *rphy) +{ + struct leapraid_sas_dev *sas_dev; + + assert_spin_locked(&adapter->dev_topo.sas_dev_lock); + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) + if (sas_dev->sas_addr == sas_address && + sas_dev->rphy == rphy) { + leapraid_sdev_get(sas_dev); + return sas_dev; + } + + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list, + list) + if (sas_dev->sas_addr == sas_address && + sas_dev->rphy == rphy) { + leapraid_sdev_get(sas_dev); + return sas_dev; + } + + return NULL; +} + +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr( + struct leapraid_adapter *adapter, + u64 sas_address, struct leapraid_card_port *port) +{ + struct leapraid_sas_dev *sas_dev; + + if (!port) { + dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__); + return NULL; + } + + assert_spin_locked(&adapter->dev_topo.sas_dev_lock); + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) + if (sas_dev->sas_addr == sas_address && + sas_dev->card_port == port) { + leapraid_sdev_get(sas_dev); + return sas_dev; + } + + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list, + list) + if (sas_dev->sas_addr == sas_address && + sas_dev->card_port == port) { + leapraid_sdev_get(sas_dev); + return sas_dev; + } + + return NULL; +} + +struct leapraid_sas_dev *leapraid_get_sas_dev_by_addr( + struct leapraid_adapter *adapter, + u64 sas_address, struct leapraid_card_port *port) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + + if (!port) { + dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__); + return NULL; + } + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(adapter, sas_address, + port); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return sas_dev; +} + +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_hdl( + struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_sas_dev *sas_dev; + + assert_spin_locked(&adapter->dev_topo.sas_dev_lock); + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) + if (sas_dev->hdl == hdl) { + leapraid_sdev_get(sas_dev); + return sas_dev; + } + + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_init_list, + list) + if (sas_dev->hdl == hdl) { + leapraid_sdev_get(sas_dev); + return sas_dev; + } + + return NULL; +} + +struct leapraid_sas_dev *leapraid_get_sas_dev_by_hdl( + struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return sas_dev; +} + +void leapraid_sas_dev_remove(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev) +{ + unsigned long flags; + bool del_from_list; + + if (!sas_dev) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid SAS device\n", __func__); + return; + } + + del_from_list = false; + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + if (!list_empty(&sas_dev->list)) { + list_del_init(&sas_dev->list); + del_from_list = true; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (del_from_list) { + leapraid_clear_cached_boot_dev(adapter, sas_dev, 0); + leapraid_sdev_put(sas_dev); + } +} + +static void leapraid_sas_dev_remove_by_hdl(struct leapraid_adapter *adapter, + u16 hdl) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + bool del_from_list; + + if (adapter->access_ctrl.shost_recovering) + return; + + del_from_list = false; + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + if (sas_dev && (!list_empty(&sas_dev->list))) { + list_del_init(&sas_dev->list); + del_from_list = true; + leapraid_sdev_put(sas_dev); + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (del_from_list) { + leapraid_remove_device(adapter, sas_dev); + leapraid_sdev_put(sas_dev); + } +} + +void leapraid_sas_dev_remove_by_sas_address(struct leapraid_adapter *adapter, + u64 sas_address, + struct leapraid_card_port *port) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + bool del_from_list; + + if (adapter->access_ctrl.shost_recovering) + return; + + del_from_list = false; + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr(adapter, sas_address, + port); + if (sas_dev && (!list_empty(&sas_dev->list))) { + list_del_init(&sas_dev->list); + del_from_list = true; + leapraid_sdev_put(sas_dev); + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (del_from_list) { + leapraid_remove_device(adapter, sas_dev); + leapraid_sdev_put(sas_dev); + } +} + +struct leapraid_raid_volume *leapraid_raid_volume_find_by_id( + struct leapraid_adapter *adapter, uint id, uint channel) +{ + struct leapraid_raid_volume *raid_volume; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list, + list) { + if (raid_volume->id == id && raid_volume->channel == channel) { + leapraid_raid_volume_get(raid_volume); + spin_unlock_irqrestore( + &adapter->dev_topo.raid_volume_lock, flags); + return raid_volume; + } + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + return NULL; +} + +struct leapraid_raid_volume *leapraid_raid_volume_find_by_hdl( + struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_raid_volume *raid_volume; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list, + list) { + if (raid_volume->hdl == hdl) { + leapraid_raid_volume_get(raid_volume); + spin_unlock_irqrestore( + &adapter->dev_topo.raid_volume_lock, flags); + return raid_volume; + } + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + return NULL; +} + +static struct leapraid_raid_volume *leapraid_raid_volume_find_by_wwid( + struct leapraid_adapter *adapter, u64 wwid) +{ + struct leapraid_raid_volume *raid_volume; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + list_for_each_entry(raid_volume, &adapter->dev_topo.raid_volume_list, + list) { + if (raid_volume->wwid == wwid) { + leapraid_raid_volume_get(raid_volume); + spin_unlock_irqrestore( + &adapter->dev_topo.raid_volume_lock, flags); + return raid_volume; + } + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + return NULL; +} + +static void leapraid_raid_volume_add(struct leapraid_adapter *adapter, + struct leapraid_raid_volume *raid_volume) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + leapraid_raid_volume_get(raid_volume); + list_add_tail(&raid_volume->list, &adapter->dev_topo.raid_volume_list); + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); +} + +void leapraid_raid_volume_remove(struct leapraid_adapter *adapter, + struct leapraid_raid_volume *raid_volume) +{ + unsigned long flags; + bool del_from_list = false; + + if (!raid_volume) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid RAID volume\n", __func__); + return; + } + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + if (!list_empty(&raid_volume->list)) { + list_del_init(&raid_volume->list); + del_from_list = true; + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + leapraid_clear_cached_boot_dev(adapter, raid_volume, RAID_CHANNEL); + if (del_from_list) + leapraid_raid_volume_put(raid_volume); +} + +static struct leapraid_enc_node *leapraid_enc_find_by_hdl( + struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_enc_node *enc_dev; + + list_for_each_entry(enc_dev, &adapter->dev_topo.enc_list, list) + if (le16_to_cpu(enc_dev->pg0.enc_hdl) == hdl) + return enc_dev; + + return NULL; +} + +struct leapraid_topo_node *leapraid_exp_find_by_sas_address( + struct leapraid_adapter *adapter, + u64 sas_address, struct leapraid_card_port *port) +{ + struct leapraid_topo_node *sas_exp; + + if (!port) { + dev_warn(&adapter->pdev->dev, "%s: Invalid port\n", __func__); + return NULL; + } + + list_for_each_entry(sas_exp, &adapter->dev_topo.exp_list, list) + if (sas_exp->sas_address == sas_address && + sas_exp->card_port == port) + return sas_exp; + + dev_warn(&adapter->pdev->dev, + "%s: No expander found for SAS addr=0x%016llx port=%p\n", + __func__, (unsigned long long)sas_address, port); + return NULL; +} + +bool leapraid_scmd_find_by_tgt(struct leapraid_adapter *adapter, uint id, + uint channel) +{ + struct scsi_cmnd *scmd; + int taskid; + + for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) { + scmd = leapraid_get_scmd_from_taskid(adapter, taskid); + if (!scmd) + continue; + + if (scmd->device->id == id && scmd->device->channel == channel) + return true; + } + + return false; +} + +bool leapraid_scmd_find_by_lun(struct leapraid_adapter *adapter, uint id, + unsigned int lun, uint channel) +{ + struct scsi_cmnd *scmd; + int taskid; + + for (taskid = 1; taskid <= adapter->shost->can_queue; taskid++) { + scmd = leapraid_get_scmd_from_taskid(adapter, taskid); + if (!scmd) + continue; + + if (scmd->device->id == id && + scmd->device->channel == channel && + scmd->device->lun == lun) + return true; + } + + return false; +} + +static struct leapraid_topo_node *leapraid_exp_find_by_hdl( + struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_topo_node *sas_exp; + + list_for_each_entry(sas_exp, &adapter->dev_topo.exp_list, list) + if (sas_exp->hdl == hdl) + return sas_exp; + + return NULL; +} + +static enum leapraid_card_port_checking_flg leapraid_get_card_port_feature( + struct leapraid_card_port *old_card_port, + struct leapraid_card_port *card_port, + struct leapraid_card_port_feature *feature) +{ + feature->dirty_flg = + old_card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY; + feature->same_addr = + old_card_port->sas_address == card_port->sas_address; + feature->exact_phy = + old_card_port->phy_mask == card_port->phy_mask; + feature->phy_overlap = + old_card_port->phy_mask & card_port->phy_mask; + feature->same_port = + old_card_port->port_id == card_port->port_id; + feature->cur_chking_old_port = old_card_port; + + if (!feature->dirty_flg || !feature->same_addr) + return CARD_PORT_SKIP_CHECKING; + + return CARD_PORT_FURTHER_CHECKING_NEEDED; +} + +static bool leapraid_process_card_port_feature( + struct leapraid_card_port_feature *feature) +{ + struct leapraid_card_port *old_card_port; + + old_card_port = feature->cur_chking_old_port; + if (feature->exact_phy) { + feature->checking_state = SAME_PORT_WITH_NOTHING_CHANGED; + feature->expected_old_port = old_card_port; + return true; + } + + if (feature->phy_overlap) { + if (feature->same_port) { + feature->checking_state = + SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS; + feature->expected_old_port = old_card_port; + } else if (feature->checking_state != + SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS) { + feature->checking_state = + SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS; + feature->expected_old_port = old_card_port; + } + } else if (feature->checking_state != + SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS && + feature->checking_state != + SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS) { + feature->checking_state = SAME_ADDR_ONLY; + feature->expected_old_port = old_card_port; + feature->same_addr_port_count++; + } + + return false; +} + +static int leapraid_check_card_port( + struct leapraid_adapter *adapter, + struct leapraid_card_port *card_port, + struct leapraid_card_port **expected_card_port, + int *count) +{ + struct leapraid_card_port *old_card_port; + struct leapraid_card_port_feature feature; + + *expected_card_port = NULL; + memset(&feature, 0, sizeof(struct leapraid_card_port_feature)); + feature.expected_old_port = NULL; + feature.same_addr_port_count = 0; + feature.checking_state = NEW_CARD_PORT; + + list_for_each_entry(old_card_port, &adapter->dev_topo.card_port_list, + list) { + if (leapraid_get_card_port_feature(old_card_port, card_port, + &feature)) + continue; + + if (leapraid_process_card_port_feature(&feature)) + break; + } + + if (feature.checking_state == SAME_ADDR_ONLY) + *count = feature.same_addr_port_count; + + *expected_card_port = feature.expected_old_port; + return feature.checking_state; +} + +static void leapraid_del_phy_part_of_anther_port( + struct leapraid_adapter *adapter, + struct leapraid_card_port *card_port_table, int index, + u8 port_count, int offset) +{ + struct leapraid_topo_node *card_topo_node; + bool found = false; + int i; + + card_topo_node = &adapter->dev_topo.card; + for (i = 0; i < port_count; i++) { + if (i == index) + continue; + + if (card_port_table[i].phy_mask & BIT(offset)) { + leapraid_transport_detach_phy_to_port( + adapter, + card_topo_node, + &card_topo_node->card_phy[offset]); + found = true; + break; + } + } + + if (!found) + card_port_table[index].phy_mask |= BIT(offset); +} + +static void leapraid_add_or_del_phys_from_existing_port( + struct leapraid_adapter *adapter, + struct leapraid_card_port *card_port, + struct leapraid_card_port *card_port_table, + int index, u8 port_count) +{ + struct leapraid_topo_node *card_topo_node; + u32 phy_mask_diff; + u32 offset; + + card_topo_node = &adapter->dev_topo.card; + phy_mask_diff = card_port->phy_mask ^ + card_port_table[index].phy_mask; + for (offset = 0; offset < adapter->dev_topo.card.phys_num; offset++) { + if (!(phy_mask_diff & BIT(offset))) + continue; + + if (!(card_port_table[index].phy_mask & BIT(offset))) { + leapraid_del_phy_part_of_anther_port(adapter, + card_port_table, + index, port_count, + offset); + continue; + } + + if (card_topo_node->card_phy[offset].phy_is_assigned) + leapraid_transport_detach_phy_to_port( + adapter, + card_topo_node, + &card_topo_node->card_phy[offset]); + + leapraid_transport_attach_phy_to_port( + adapter, + card_topo_node, + &card_topo_node->card_phy[offset], + card_port->sas_address, + card_port); + } +} + +struct leapraid_sas_dev *leapraid_get_next_sas_dev_from_init_list( + struct leapraid_adapter *adapter) +{ + struct leapraid_sas_dev *sas_dev = NULL; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + if (!list_empty(&adapter->dev_topo.sas_dev_init_list)) { + sas_dev = list_first_entry(&adapter->dev_topo.sas_dev_init_list, + struct leapraid_sas_dev, + list); + leapraid_sdev_get(sas_dev); + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return sas_dev; +} + +static bool leapraid_check_boot_dev_internal(u64 sas_address, u64 dev_name, + u64 enc_lid, u16 slot, + struct leapraid_boot_dev *boot_dev, + u8 form) +{ + struct leapraid_boot_dev_format_sas_wwid *wwid; + struct leapraid_boot_dev_format_enc_slot *es; + struct leapraid_boot_dev_format_dev_name *dn; + void *pg_dev; + + if (!boot_dev) + return false; + + pg_dev = boot_dev->pg_dev; + switch (form & LEAPRAID_BOOTDEV_FORM_MASK) { + case LEAPRAID_BOOTDEV_FORM_SAS_WWID: + wwid = pg_dev; + + if (!sas_address) + return false; + + return sas_address == le64_to_cpu(wwid->sas_addr); + case LEAPRAID_BOOTDEV_FORM_ENC_SLOT: + es = pg_dev; + + if (!enc_lid) + return false; + + return (enc_lid == le64_to_cpu(es->enc_lid) && + slot == le16_to_cpu(es->slot_num)); + case LEAPRAID_BOOTDEV_FORM_DEV_NAME: + dn = pg_dev; + + if (!dev_name) + return false; + + return dev_name == le64_to_cpu(dn->dev_name); + case LEAPRAID_BOOTDEV_FORM_NONE: + default: + return false; + } +} + +void leapraid_boot_dev_get(void *dev, u32 chnl) +{ + if (!dev) + return; + + if (chnl == RAID_CHANNEL) + leapraid_raid_volume_get((struct leapraid_raid_volume *)dev); + else + leapraid_sdev_get((struct leapraid_sas_dev *)dev); +} + +void leapraid_boot_dev_put(void *dev, u32 chnl) +{ + if (!dev) + return; + + if (chnl == RAID_CHANNEL) + leapraid_raid_volume_put((struct leapraid_raid_volume *)dev); + else + leapraid_sdev_put((struct leapraid_sas_dev *)dev); +} + +static void leapraid_try_set_boot_dev(struct leapraid_boot_dev *boot_dev, + u64 sas_addr, u64 dev_name, + u64 enc_lid, u16 slot, + void *dev, u32 chnl) +{ + bool matched = false; + + if (boot_dev->dev) + return; + + matched = leapraid_check_boot_dev_internal(sas_addr, dev_name, enc_lid, + slot, boot_dev, + boot_dev->form); + if (matched) { + leapraid_boot_dev_get(dev, chnl); + boot_dev->dev = dev; + boot_dev->chnl = chnl; + } +} + +static void leapraid_clear_boot_dev(struct leapraid_adapter *adapter, + struct leapraid_boot_dev *boot_dev, + void *dev, u32 chnl) +{ + void *cached_dev; + u32 cached_chnl; + unsigned long flags; + + spin_lock_irqsave(&adapter->boot_devs.lock, flags); + if (boot_dev->dev != dev || boot_dev->chnl != chnl) + goto out_unlock; + + cached_dev = boot_dev->dev; + cached_chnl = boot_dev->chnl; + boot_dev->dev = NULL; + boot_dev->chnl = 0; + spin_unlock_irqrestore(&adapter->boot_devs.lock, flags); + leapraid_boot_dev_put(cached_dev, cached_chnl); + return; + +out_unlock: + spin_unlock_irqrestore(&adapter->boot_devs.lock, flags); +} + +static void leapraid_clear_cached_boot_dev(struct leapraid_adapter *adapter, + void *dev, u32 chnl) +{ + leapraid_clear_boot_dev(adapter, + &adapter->boot_devs.requested_boot_dev, + dev, chnl); + leapraid_clear_boot_dev(adapter, + &adapter->boot_devs.requested_alt_boot_dev, + dev, chnl); + leapraid_clear_boot_dev(adapter, + &adapter->boot_devs.current_boot_dev, + dev, chnl); +} + +static void leapraid_check_boot_dev(struct leapraid_adapter *adapter, + void *dev, u32 chnl) +{ + struct leapraid_raid_volume *raid_volume; + struct leapraid_sas_dev *sas_dev; + u64 sas_addr; + u64 dev_name = 0; + u64 enc_lid = 0; + u16 slot = 0; + + if (!adapter->scan_dev_desc.driver_loading) + return; + + switch (chnl) { + case RAID_CHANNEL: + raid_volume = dev; + sas_addr = raid_volume->wwid; + break; + default: + sas_dev = dev; + sas_addr = sas_dev->sas_addr; + dev_name = sas_dev->dev_name; + enc_lid = sas_dev->enc_lid; + slot = sas_dev->slot; + break; + } + + leapraid_try_set_boot_dev(&adapter->boot_devs.requested_boot_dev, + sas_addr, dev_name, enc_lid, + slot, dev, chnl); + leapraid_try_set_boot_dev(&adapter->boot_devs.requested_alt_boot_dev, + sas_addr, dev_name, enc_lid, + slot, dev, chnl); + leapraid_try_set_boot_dev(&adapter->boot_devs.current_boot_dev, + sas_addr, dev_name, enc_lid, + slot, dev, chnl); +} + +static const char *leapraid_func_name(u8 func) +{ + switch (func) { + case LEAPRAID_FUNC_SCSIIO: + return "SCSIIO"; + case LEAPRAID_FUNC_SCSI_TMF: + return "SCSI_TMF"; + case LEAPRAID_FUNC_ADAPTER_INIT: + return "ADAPTER_INIT"; + case LEAPRAID_FUNC_GET_ADAPTER_FEATURES: + return "GET_ADAPTER_FEATURES"; + case LEAPRAID_FUNC_CONFIG_OP: + return "CONFIG_OP"; + case LEAPRAID_FUNC_SCAN_DEV: + return "SCAN_DEV"; + case LEAPRAID_FUNC_EVENT_NOTIFY: + return "EVENT_NOTIFY"; + case LEAPRAID_FUNC_FW_DOWNLOAD: + return "FW_DOWNLOAD"; + case LEAPRAID_FUNC_FW_UPLOAD: + return "FW_UPLOAD"; + case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH: + return "SCSIIO_RAID_PASSTHROUGH"; + case LEAPRAID_FUNC_SCSI_ENC_PROCESSOR: + return "SCSI_ENC_PROCESSOR"; + case LEAPRAID_FUNC_SMP_PASSTHROUGH: + return "SMP_PASSTHROUGH"; + case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL: + return "SAS_IO_UNIT_CTRL"; + case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH: + return "SCSIIO_SATA_PASSTHROUGH"; + case LEAPRAID_FUNC_ADAPTER_UNIT_RESET: + return "ADAPTER_UNIT_RESET"; + case LEAPRAID_FUNC_HANDSHAKE: + return "HANDSHAKE"; + case LEAPRAID_FUNC_LOGBUF_INIT: + return "LOGBUF_INIT"; + default: + return "UNKNOWN"; + } +} + +static const char *leapraid_cfg_action_name(u8 action) +{ + switch (action) { + case LEAPRAID_CFG_ACT_PAGE_HEADER: + return "PAGE_HEADER"; + case LEAPRAID_CFG_ACT_PAGE_READ_CUR: + return "PAGE_READ_CUR"; + case LEAPRAID_CFG_ACT_PAGE_WRITE_CUR: + return "PAGE_WRITE_CUR"; + default: + return "UNKNOWN"; + } +} + +static const char *leapraid_cfg_page_type_name(u8 page_type) +{ + switch (page_type) { + case LEAPRAID_CFG_PT_IO_UNIT: + return "IO_UNIT"; + case LEAPRAID_CFG_PT_ADAPTER: + return "ADAPTER"; + case LEAPRAID_CFG_PT_BIOS: + return "BIOS"; + case LEAPRAID_CFG_PT_RAID_VOLUME: + return "RAID_VOLUME"; + case LEAPRAID_CFG_PT_MANUFACTURING: + return "MANUFACTURING"; + case LEAPRAID_CFG_PT_RAID_PHYSDISK: + return "RAID_PHYSDISK"; + case LEAPRAID_CFG_PT_EXTENDED: + return "EXTENDED"; + default: + return "UNKNOWN"; + } +} + +static const char *leapraid_cfg_ext_page_type_name(u8 ext_page_type) +{ + switch (ext_page_type) { + case LEAPRAID_CFG_EXTPT_SAS_IO_UNIT: + return "SAS_IO_UNIT"; + case LEAPRAID_CFG_EXTPT_SAS_EXP: + return "SAS_EXPANDER"; + case LEAPRAID_CFG_EXTPT_SAS_DEV: + return "SAS_DEVICE"; + case LEAPRAID_CFG_EXTPT_SAS_PHY: + return "SAS_PHY"; + case LEAPRAID_CFG_EXTPT_ENC: + return "ENCLOSURE"; + case LEAPRAID_CFG_EXTPT_RAID_CONFIG: + return "RAID_CONFIG"; + default: + return "UNKNOWN"; + } +} + +static const char *leapraid_sep_action_name(u8 action) +{ + switch (action) { + case LEAPRAID_SEP_REQ_ACT_WRITE_STATUS: + return "WRITE_STATUS"; + default: + return "UNKNOWN"; + } +} + +static const char *leapraid_sas_op_name(u8 op) +{ + switch (op) { + case LEAPRAID_SAS_OP_PHY_LINK_RESET: + return "PHY_LINK_RESET"; + case LEAPRAID_SAS_OP_PHY_HARD_RESET: + return "PHY_HARD_RESET"; + case LEAPRAID_SAS_OP_SET_PARAMETER: + return "SET_PARAMETER"; + default: + return "UNKNOWN"; + } +} + +static const char *leapraid_tm_type_name(u8 task_type) +{ + switch (task_type) { + case LEAPRAID_TM_TASKTYPE_ABORT_TASK: + return "ABORT_TASK"; + case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET: + return "ABORT_TASK_SET"; + case LEAPRAID_TM_TASKTYPE_TARGET_RESET: + return "TARGET_RESET"; + case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET: + return "LOGICAL_UNIT_RESET"; + case LEAPRAID_TM_TASKTYPE_CLEAR_TASK_SET: + return "CLEAR_TASK_SET"; + case LEAPRAID_TM_TASKTYPE_QUERY_TASK: + return "QUERY_TASK"; + case LEAPRAID_TM_TASKTYPE_CLEAR_ACA: + return "CLEAR_ACA"; + case LEAPRAID_TM_TASKTYPE_QUERY_TASK_SET: + return "QUERY_TASK_SET"; + case LEAPRAID_TM_TASKTYPE_QUERY_ASYNC_EVENT: + return "QUERY_ASYNC_EVENT"; + default: + return "UNKNOWN"; + } +} + +void leapraid_log_req_context(struct leapraid_adapter *adapter, u16 smid, + const void *req_data) +{ + const struct leapraid_req *req = req_data; + + if (!adapter || !adapter->pdev || !req_data) + return; + + switch (req->func) { + case LEAPRAID_FUNC_CONFIG_OP: { + const struct leapraid_cfg_req *cfg_req = req_data; + + dev_err(&adapter->pdev->dev, + "cfg-req: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n", + smid, req->func, leapraid_func_name(req->func), + cfg_req->action, + leapraid_cfg_action_name(cfg_req->action)); + dev_err(&adapter->pdev->dev, + "cfg-req: page_type=0x%02x(%s) page_num=%u\n", + cfg_req->header.page_type, + leapraid_cfg_page_type_name(cfg_req->header.page_type), + cfg_req->header.page_num); + if (cfg_req->header.page_type == LEAPRAID_CFG_PT_EXTENDED) + dev_err(&adapter->pdev->dev, + "cfg-req: ext_page_type=0x%02x(%s)\n", + cfg_req->ext_page_type, + leapraid_cfg_ext_page_type_name( + cfg_req->ext_page_type)); + dev_err(&adapter->pdev->dev, "cfg-req: page_addr=0x%08x\n", + le32_to_cpu(cfg_req->page_addr)); + break; + } + case LEAPRAID_FUNC_SCSI_TMF: { + const struct leapraid_scsi_tm_req *tm_req = req_data; + + dev_err(&adapter->pdev->dev, + "scsi_tm:: smid=%u func=0x%02x(%s) task=0x%02x(%s)\n", + smid, req->func, leapraid_func_name(req->func), + tm_req->task_type, + leapraid_tm_type_name(tm_req->task_type)); + dev_err(&adapter->pdev->dev, + "scsi_tm:: dev_hdl=0x%04x task_mid=%u\n", + le16_to_cpu(tm_req->dev_hdl), + le16_to_cpu(tm_req->task_mid)); + break; + } + case LEAPRAID_FUNC_SCSI_ENC_PROCESSOR: { + const struct leapraid_sep_req *sep_req = req_data; + + dev_err(&adapter->pdev->dev, + "sep: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n", + smid, req->func, leapraid_func_name(req->func), + sep_req->act, + leapraid_sep_action_name(sep_req->act)); + dev_err(&adapter->pdev->dev, + "sep: dev_hdl=0x%04x slot=%u enc_hdl=0x%04x\n", + le16_to_cpu(sep_req->dev_hdl), + le16_to_cpu(sep_req->slot), + le16_to_cpu(sep_req->enc_hdl)); + break; + } + case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL: { + const struct leapraid_io_unit_ctrl_req *io_req = req_data; + + dev_err(&adapter->pdev->dev, + "ctl_cmd: smid=%u func=0x%02x(%s) action=0x%02x(%s)\n", + smid, req->func, leapraid_func_name(req->func), + io_req->op, leapraid_sas_op_name(io_req->op)); + dev_err(&adapter->pdev->dev, + "ctl_cmd: dev_hdl=0x%04x param=0x%02x\n", + le16_to_cpu(io_req->dev_hdl), + io_req->adapter_para); + break; + } + case LEAPRAID_FUNC_SMP_PASSTHROUGH: { + const struct leapraid_smp_passthrough_req *smp_req = req_data; + + dev_err(&adapter->pdev->dev, + "smp_cmd: smid=%u func=0x%02x(%s) action=0x%02x\n", + smid, req->func, leapraid_func_name(req->func), + smp_req->passthrough_flg); + dev_err(&adapter->pdev->dev, + "smp_cmd: port=%u req_len=%u\n", + smp_req->physical_port, + le16_to_cpu(smp_req->req_data_len)); + dev_err(&adapter->pdev->dev, "smp_cmd: sas_addr=0x%016llx\n", + (unsigned long long)le64_to_cpu(smp_req->sas_address)); + break; + } + default: + dev_err(&adapter->pdev->dev, + "cmd: smid=%u func=0x%02x(%s)\n", + smid, req->func, leapraid_func_name(req->func)); + break; + } +} + +static void leapraid_build_and_fire_cfg_req( + struct leapraid_adapter *adapter, + struct leapraid_cfg_req *leap_mpi_cfgp_req, + struct leapraid_cfg_rep *leap_mpi_cfgp_rep) +{ + struct leapraid_cfg_req *local_leap_cfg_req; + u16 smid; + + memset(leap_mpi_cfgp_rep, 0, sizeof(struct leapraid_cfg_rep)); + memset(&adapter->driver_cmds.cfg_op_cmd.reply, 0, + sizeof(struct leapraid_cfg_rep)); + adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_PENDING; + smid = adapter->driver_cmds.cfg_op_cmd.inter_taskid; + local_leap_cfg_req = leapraid_get_task_desc(adapter, smid); + memcpy(local_leap_cfg_req, leap_mpi_cfgp_req, + sizeof(struct leapraid_cfg_req)); + init_completion(&adapter->driver_cmds.cfg_op_cmd.done); + leapraid_fire_task(adapter, smid); + wait_for_completion_timeout(&adapter->driver_cmds.cfg_op_cmd.done, + LEAPRAID_CFG_OP_TIMEOUT * HZ); +} + +static int leapraid_req_cfg_func(struct leapraid_adapter *adapter, + struct leapraid_cfg_req *leap_mpi_cfgp_req, + struct leapraid_cfg_rep *leap_mpi_cfgp_rep, + void *target_cfg_pg, void *real_cfg_pg_addr, + u16 target_real_cfg_pg_sz) +{ + u32 adapter_status = UINT_MAX; + bool issue_reset = false; + u16 smid; + u8 retry_cnt; + int rc; + + retry_cnt = 0; + mutex_lock(&adapter->driver_cmds.cfg_op_cmd.mutex); + smid = adapter->driver_cmds.cfg_op_cmd.inter_taskid; +retry: + if (retry_cnt) { + if (retry_cnt > LEAPRAID_CFG_REQ_RETRY_TIMES) { + rc = -EFAULT; + goto out_cleanup; + } + dev_warn(&adapter->pdev->dev, + "cfg-req: Retry request, cnt=%u\n", retry_cnt); + } + + rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, + __func__); + if (rc) { + dev_err(&adapter->pdev->dev, + "cfg-req: Adapter not operational\n"); + goto out_cleanup; + } + + leapraid_build_and_fire_cfg_req(adapter, leap_mpi_cfgp_req, + leap_mpi_cfgp_rep); + if (!(adapter->driver_cmds.cfg_op_cmd.status & LEAPRAID_CMD_DONE)) { + retry_cnt++; + if (adapter->driver_cmds.cfg_op_cmd.status & + LEAPRAID_CMD_RESET) { + dev_warn(&adapter->pdev->dev, + "cfg-req: CMD fail due to hard reset\n"); + goto retry; + } + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.pcie_recovering) { + dev_err(&adapter->pdev->dev, + "cfg-req: pending in %s, status=0x%x\n", + adapter->access_ctrl.shost_recovering ? + "shost recovery" : "pcie recovery", + adapter->driver_cmds.cfg_op_cmd.status); + leapraid_log_req_context(adapter, smid, + leap_mpi_cfgp_req); + issue_reset = false; + rc = -EFAULT; + } else { + dev_err(&adapter->pdev->dev, + "cfg-req: timeout, status=0x%x, reset\n", + adapter->driver_cmds.cfg_op_cmd.status); + leapraid_log_req_context(adapter, smid, + leap_mpi_cfgp_req); + issue_reset = true; + } + + goto out_cleanup; + } + + if (adapter->driver_cmds.cfg_op_cmd.status & + LEAPRAID_CMD_REPLY_VALID) { + memcpy(leap_mpi_cfgp_rep, + &adapter->driver_cmds.cfg_op_cmd.reply, + sizeof(struct leapraid_cfg_rep)); + adapter_status = le16_to_cpu( + leap_mpi_cfgp_rep->adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (adapter_status == LEAPRAID_ADAPTER_STATUS_SUCCESS && + target_cfg_pg && real_cfg_pg_addr && + target_real_cfg_pg_sz && + leap_mpi_cfgp_req->action == + LEAPRAID_CFG_ACT_PAGE_READ_CUR) + memcpy(target_cfg_pg, real_cfg_pg_addr, + target_real_cfg_pg_sz); + if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { + if (adapter_status != + LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) + dev_err(&adapter->pdev->dev, + "cfg-rep: adapter_status=0x%x\n", + adapter_status); + rc = -EFAULT; + } + } else { + dev_err(&adapter->pdev->dev, "cfg-rep: Reply invalid\n"); + rc = -EFAULT; + } + +out_cleanup: + adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_NOT_USED; + mutex_unlock(&adapter->driver_cmds.cfg_op_cmd.mutex); + if (issue_reset) { + if (adapter->scan_dev_desc.first_scan_dev_fired) { + dev_warn(&adapter->pdev->dev, + "%s:%d cfg-req: Failure, issuing reset\n", + __func__, __LINE__); + leapraid_hard_reset_handler(adapter, FULL_RESET); + } else { + dev_warn(&adapter->pdev->dev, + "cfg-req: CMD fail in init, skip reset\n"); + } + rc = -EFAULT; + } + return rc; +} + +static int leapraid_request_cfg_pg_header( + struct leapraid_adapter *adapter, + struct leapraid_cfg_req *leap_mpi_cfgp_req, + struct leapraid_cfg_rep *leap_mpi_cfgp_rep) +{ + return leapraid_req_cfg_func(adapter, leap_mpi_cfgp_req, + leap_mpi_cfgp_rep, NULL, NULL, 0); +} + +static int leapraid_request_cfg_pg(struct leapraid_adapter *adapter, + struct leapraid_cfg_req *leap_mpi_cfgp_req, + struct leapraid_cfg_rep *leap_mpi_cfgp_rep, + void *target_cfg_pg, void *real_cfg_pg_addr, + u16 target_real_cfg_pg_sz) +{ + return leapraid_req_cfg_func(adapter, leap_mpi_cfgp_req, + leap_mpi_cfgp_rep, target_cfg_pg, + real_cfg_pg_addr, target_real_cfg_pg_sz); +} + +int leapraid_op_config_page(struct leapraid_adapter *adapter, + void *target_cfg_pg, union cfg_param_1 cfgp1, + union cfg_param_2 cfgp2, + enum config_page_action cfg_op) +{ + struct leapraid_cfg_req leap_mpi_cfgp_req; + struct leapraid_cfg_rep leap_mpi_cfgp_rep; + u16 real_cfg_pg_sz; + void *real_cfg_pg_addr; + dma_addr_t real_cfg_pg_dma = 0; + u32 __page_size; + int rc; + + memset(&leap_mpi_cfgp_req, 0, sizeof(struct leapraid_cfg_req)); + leap_mpi_cfgp_req.func = LEAPRAID_FUNC_CONFIG_OP; + leap_mpi_cfgp_req.action = LEAPRAID_CFG_ACT_PAGE_HEADER; + + switch (cfg_op) { + case GET_BIOS_PG3: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_BIOS; + leap_mpi_cfgp_req.header.page_num = + LEAPRAID_CFG_PAGE_NUM_BIOS3; + __page_size = sizeof(struct leapraid_bios_page3); + break; + case GET_BIOS_PG2: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_BIOS; + leap_mpi_cfgp_req.header.page_num = + LEAPRAID_CFG_PAGE_NUM_BIOS2; + __page_size = sizeof(struct leapraid_bios_page2); + break; + case GET_MANUFACTURING_PG0: + leap_mpi_cfgp_req.header.page_type = + LEAPRAID_CFG_PT_MANUFACTURING; + leap_mpi_cfgp_req.header.page_num = + LEAPRAID_CFG_PAGE_NUM_MANU0; + __page_size = sizeof(struct leapraid_manufacturing_p0); + break; + case GET_SAS_DEVICE_PG0: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_DEV; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_DEV0; + __page_size = sizeof(struct leapraid_sas_dev_p0); + break; + case GET_SAS_IOUNIT_PG0: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = + LEAPRAID_CFG_EXTPT_SAS_IO_UNIT; + leap_mpi_cfgp_req.header.page_num = + LEAPRAID_CFG_PAGE_NUM_IOUNIT0; + __page_size = cfgp1.size; + break; + case GET_SAS_IOUNIT_PG1: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = + LEAPRAID_CFG_EXTPT_SAS_IO_UNIT; + leap_mpi_cfgp_req.header.page_num = + LEAPRAID_CFG_PAGE_NUM_IOUNIT1; + __page_size = cfgp1.size; + break; + case GET_SAS_EXPANDER_PG0: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_EXP; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_EXP0; + __page_size = sizeof(struct leapraid_exp_p0); + break; + case GET_SAS_EXPANDER_PG1: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_EXP; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_EXP1; + __page_size = sizeof(struct leapraid_exp_p1); + break; + case GET_SAS_ENCLOSURE_PG0: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_ENC; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_ENC0; + __page_size = sizeof(struct leapraid_enc_p0); + break; + case GET_PHY_PG0: + leap_mpi_cfgp_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + leap_mpi_cfgp_req.ext_page_type = LEAPRAID_CFG_EXTPT_SAS_PHY; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_PHY0; + __page_size = sizeof(struct leapraid_sas_phy_p0); + break; + case GET_RAID_VOLUME_PG0: + leap_mpi_cfgp_req.header.page_type = + LEAPRAID_CFG_PT_RAID_VOLUME; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL0; + __page_size = cfgp1.size; + break; + case GET_RAID_VOLUME_PG1: + leap_mpi_cfgp_req.header.page_type = + LEAPRAID_CFG_PT_RAID_VOLUME; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL1; + __page_size = sizeof(struct leapraid_raidvol_p1); + break; + case GET_PHY_DISK_PG0: + leap_mpi_cfgp_req.header.page_type = + LEAPRAID_CFG_PT_RAID_PHYSDISK; + leap_mpi_cfgp_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_PD0; + __page_size = sizeof(struct leapraid_raidpd_p0); + break; + default: + dev_err(&adapter->pdev->dev, + "Unsupported config page action=%d!\n", cfg_op); + return -EINVAL; + } + + leapraid_build_nodata_mpi_sg(adapter, + &leap_mpi_cfgp_req.page_buf_sge); + rc = leapraid_request_cfg_pg_header(adapter, + &leap_mpi_cfgp_req, + &leap_mpi_cfgp_rep); + if (rc) { + dev_err(&adapter->pdev->dev, + "cfg-req: Header failed rc=%dn", rc); + return rc; + } + + if (cfg_op == GET_SAS_DEVICE_PG0 || + cfg_op == GET_SAS_EXPANDER_PG0 || + cfg_op == GET_SAS_ENCLOSURE_PG0 || + cfg_op == GET_RAID_VOLUME_PG1) + leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.form | + cfgp2.handle); + else if (cfg_op == GET_PHY_DISK_PG0) + leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.form | + cfgp2.form_specific); + else if (cfg_op == GET_RAID_VOLUME_PG0) + leap_mpi_cfgp_req.page_addr = + cpu_to_le32(cfgp2.handle | + LEAPRAID_RAID_VOL_CFG_PGAD_HDL); + else if (cfg_op == GET_SAS_EXPANDER_PG1) + leap_mpi_cfgp_req.page_addr = + cpu_to_le32(cfgp2.handle | + (cfgp1.phy_number << + LEAPRAID_SAS_EXP_CFG_PGAD_PHYNUM_SHIFT) | + LEAPRAID_SAS_EXP_CFG_PGAD_HDL_PHY_NUM); + else if (cfg_op == GET_PHY_PG0) + leap_mpi_cfgp_req.page_addr = cpu_to_le32(cfgp1.phy_number | + LEAPRAID_SAS_PHY_CFG_PGAD_PHY_NUMBER); + + leap_mpi_cfgp_req.action = LEAPRAID_CFG_ACT_PAGE_READ_CUR; + + leap_mpi_cfgp_req.header.page_num = leap_mpi_cfgp_rep.header.page_num; + leap_mpi_cfgp_req.header.page_type = + leap_mpi_cfgp_rep.header.page_type; + leap_mpi_cfgp_req.header.page_len = leap_mpi_cfgp_rep.header.page_len; + leap_mpi_cfgp_req.ext_page_len = leap_mpi_cfgp_rep.ext_page_len; + leap_mpi_cfgp_req.ext_page_type = leap_mpi_cfgp_rep.ext_page_type; + + real_cfg_pg_sz = (leap_mpi_cfgp_req.header.page_len) ? + leap_mpi_cfgp_req.header.page_len * sizeof(u32) : + le16_to_cpu(leap_mpi_cfgp_rep.ext_page_len) * sizeof(u32); + real_cfg_pg_addr = dma_alloc_coherent(&adapter->pdev->dev, + real_cfg_pg_sz, + &real_cfg_pg_dma, + GFP_KERNEL); + if (!real_cfg_pg_addr) + return -ENOMEM; + + if (leap_mpi_cfgp_req.action == LEAPRAID_CFG_ACT_PAGE_WRITE_CUR) { + leapraid_single_mpi_sg_append(adapter, + &leap_mpi_cfgp_req.page_buf_sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL | + LEAPRAID_SGE_FLG_H2C) << + LEAPRAID_SGE_FLG_SHIFT) | + real_cfg_pg_sz, + real_cfg_pg_dma); + memcpy(real_cfg_pg_addr, target_cfg_pg, + min_t(u16, real_cfg_pg_sz, __page_size)); + } else { + memset(target_cfg_pg, 0, __page_size); + leapraid_single_mpi_sg_append(adapter, + &leap_mpi_cfgp_req.page_buf_sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL) << + LEAPRAID_SGE_FLG_SHIFT) | + real_cfg_pg_sz, + real_cfg_pg_dma); + memset(real_cfg_pg_addr, 0, + min_t(u16, real_cfg_pg_sz, __page_size)); + } + + rc = leapraid_request_cfg_pg(adapter, + &leap_mpi_cfgp_req, + &leap_mpi_cfgp_rep, + target_cfg_pg, + real_cfg_pg_addr, + min_t(u16, real_cfg_pg_sz, __page_size)); + if (rc) { + u32 status; + + status = le16_to_cpu(leap_mpi_cfgp_rep.adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (status != LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) + dev_err(&adapter->pdev->dev, + "cfg-req: rc=%d, pg_info: 0x%x, 0x%x, %d\n", + rc, leap_mpi_cfgp_req.header.page_type, + leap_mpi_cfgp_req.ext_page_type, + leap_mpi_cfgp_req.header.page_num); + } + + if (real_cfg_pg_addr) + dma_free_coherent(&adapter->pdev->dev, + real_cfg_pg_sz, + real_cfg_pg_addr, + real_cfg_pg_dma); + + return rc; +} + +static int leapraid_cfg_find_vol_in_page( + struct leapraid_raid_cfg_p0 *raid_cfg_p0, + u16 pd_hdl, u16 *vol_hdl) +{ + u16 elements = raid_cfg_p0->elements_num; + int i; + + for (i = 0; i < elements; i++) { + struct leapraid_raid_cfg_p0_element *elem; + u16 type; + + elem = &raid_cfg_p0->cfg_element[i]; + + type = le16_to_cpu(elem->element_flg) & + LEAPRAID_RAIDCFG_P0_EFLG_MASK_ELEMENT_TYPE; + + switch (type) { + case LEAPRAID_RAIDCFG_P0_EFLG_VOL_PHYS_DISK_ELEMENT: + case LEAPRAID_RAIDCFG_P0_EFLG_OCE_ELEMENT: { + u16 phys_hdl; + + phys_hdl = le16_to_cpu(elem->phys_disk_dev_hdl); + if (phys_hdl == pd_hdl) { + *vol_hdl = le16_to_cpu(elem->vol_dev_hdl); + return 0; + } + break; + } + case LEAPRAID_RAIDCFG_P0_EFLG_HOT_SPARE_ELEMENT: + *vol_hdl = 0; + return 0; + + default: + break; + } + } + + return -ENOENT; +} + +static int leapraid_cfg_get_volume_hdl_dispatch( + struct leapraid_adapter *adapter, + struct leapraid_cfg_req *cfg_req, + struct leapraid_cfg_rep *cfg_rep, + struct leapraid_raid_cfg_p0 *raid_cfg_p0, + void *real_cfg_pg_addr, + u16 real_cfg_pg_sz, + u16 raid_cfg_p0_sz, + u16 pd_hdl, u16 *vol_hdl) +{ + u16 adapter_status; + int config_num; + int rc; + + config_num = 0xFF; + while (true) { + cfg_req->page_addr = + cpu_to_le32(config_num + + LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP); + rc = leapraid_request_cfg_pg( + adapter, cfg_req, cfg_rep, + raid_cfg_p0, real_cfg_pg_addr, + min_t(u16, real_cfg_pg_sz, raid_cfg_p0_sz)); + adapter_status = le16_to_cpu(cfg_rep->adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (rc) { + if (adapter_status == + LEAPRAID_ADAPTER_STATUS_CONFIG_INVALID_PAGE) { + *vol_hdl = 0; + return 0; + } + return rc; + } + + if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) + return LEAPRAID_OPERATION_FAILED; + + rc = leapraid_cfg_find_vol_in_page(raid_cfg_p0, + pd_hdl, + vol_hdl); + if (rc != -ENOENT) + return rc; + + config_num = raid_cfg_p0->cfg_num; + } + return 0; +} + +int leapraid_cfg_get_volume_hdl(struct leapraid_adapter *adapter, + u16 pd_hdl, u16 *vol_hdl) +{ + struct leapraid_raid_cfg_p0 *raid_cfg_p0; + struct leapraid_cfg_req cfg_req; + struct leapraid_cfg_rep cfg_rep; + dma_addr_t real_cfg_pg_dma = 0; + void *real_cfg_pg_addr; + u16 real_cfg_pg_sz; + int rc, raid_cfg_p0_sz; + + *vol_hdl = 0; + memset(&cfg_req, 0, sizeof(struct leapraid_cfg_req)); + cfg_req.func = LEAPRAID_FUNC_CONFIG_OP; + cfg_req.action = LEAPRAID_CFG_ACT_PAGE_HEADER; + cfg_req.header.page_type = LEAPRAID_CFG_PT_EXTENDED; + cfg_req.ext_page_type = LEAPRAID_CFG_EXTPT_RAID_CONFIG; + cfg_req.header.page_num = LEAPRAID_CFG_PAGE_NUM_VOL0; + + leapraid_build_nodata_mpi_sg(adapter, &cfg_req.page_buf_sge); + rc = leapraid_request_cfg_pg_header(adapter, &cfg_req, &cfg_rep); + if (rc) + return rc; + + cfg_req.action = LEAPRAID_CFG_ACT_PAGE_READ_CUR; + raid_cfg_p0_sz = le16_to_cpu(cfg_rep.ext_page_len) * + LEAPRAID_CFG_UNIT_SIZE; + raid_cfg_p0 = kmalloc(raid_cfg_p0_sz, GFP_KERNEL); + if (!raid_cfg_p0) + return -ENOMEM; + + real_cfg_pg_sz = (cfg_req.header.page_len) ? + cfg_req.header.page_len * LEAPRAID_CFG_UNIT_SIZE : + le16_to_cpu(cfg_rep.ext_page_len) * LEAPRAID_CFG_UNIT_SIZE; + + real_cfg_pg_addr = dma_alloc_coherent(&adapter->pdev->dev, + real_cfg_pg_sz, &real_cfg_pg_dma, + GFP_KERNEL); + if (!real_cfg_pg_addr) { + rc = -ENOMEM; + goto out_free; + } + + memset(raid_cfg_p0, 0, raid_cfg_p0_sz); + leapraid_single_mpi_sg_append(adapter, + &cfg_req.page_buf_sge, + ((LEAPRAID_SGE_FLG_SIMPLE_ONE | + LEAPRAID_SGE_FLG_LAST_ONE | + LEAPRAID_SGE_FLG_EOB | + LEAPRAID_SGE_FLG_EOL) << + LEAPRAID_SGE_FLG_SHIFT) | + real_cfg_pg_sz, + real_cfg_pg_dma); + memset(real_cfg_pg_addr, 0, + min_t(u16, real_cfg_pg_sz, raid_cfg_p0_sz)); + + rc = leapraid_cfg_get_volume_hdl_dispatch(adapter, + &cfg_req, &cfg_rep, + raid_cfg_p0, + real_cfg_pg_addr, + real_cfg_pg_sz, + raid_cfg_p0_sz, + pd_hdl, vol_hdl); + +out_free: + if (real_cfg_pg_addr) + dma_free_coherent(&adapter->pdev->dev, + real_cfg_pg_sz, real_cfg_pg_addr, + real_cfg_pg_dma); + kfree(raid_cfg_p0); + return rc; +} + +static int leapraid_get_adapter_phys(struct leapraid_adapter *adapter, + u8 *nr_phys) +{ + struct leapraid_sas_io_unit_p0 sas_io_unit_page0; + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + int rc; + + *nr_phys = 0; + cfgp1.size = sizeof(struct leapraid_sas_io_unit_p0); + rc = leapraid_op_config_page(adapter, &sas_io_unit_page0, cfgp1, + cfgp2, GET_SAS_IOUNIT_PG0); + if (rc) + return rc; + + *nr_phys = sas_io_unit_page0.phy_num; + + return 0; +} + +static int leapraid_cfg_get_number_pds(struct leapraid_adapter *adapter, + u16 hdl, u8 *num_pds) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_raidvol_p0 raidvol_p0; + int rc; + + *num_pds = 0; + cfgp1.size = sizeof(struct leapraid_raidvol_p0); + cfgp2.handle = hdl; + rc = leapraid_op_config_page(adapter, &raidvol_p0, cfgp1, + cfgp2, GET_RAID_VOLUME_PG0); + if (!rc) + *num_pds = raidvol_p0.num_phys_disks; + + return rc; +} + +int leapraid_cfg_get_volume_wwid(struct leapraid_adapter *adapter, + u16 vol_hdl, u64 *wwid) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_raidvol_p1 raidvol_p1; + int rc; + + *wwid = 0; + cfgp1.form = LEAPRAID_RAID_VOL_CFG_PGAD_HDL; + cfgp2.handle = vol_hdl; + rc = leapraid_op_config_page(adapter, &raidvol_p1, cfgp1, + cfgp2, GET_RAID_VOLUME_PG1); + if (!rc) + *wwid = le64_to_cpu(raidvol_p1.wwid); + + return rc; +} + +static int leapraid_get_sas_io_unit_page0( + struct leapraid_adapter *adapter, + struct leapraid_sas_io_unit_p0 *sas_io_unit_p0, + u16 sas_iou_pg0_sz) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + + cfgp1.size = sas_iou_pg0_sz; + return leapraid_op_config_page(adapter, sas_io_unit_p0, cfgp1, + cfgp2, GET_SAS_IOUNIT_PG0); +} + +static int leapraid_get_sas_address(struct leapraid_adapter *adapter, + u16 hdl, u64 *sas_address) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_p0; + + *sas_address = 0; + cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2.handle = hdl; + if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, + cfgp2, GET_SAS_DEVICE_PG0)) + return -ENXIO; + + if (hdl <= adapter->dev_topo.card.phys_num && + (!(le32_to_cpu(sas_dev_p0.dev_info) & LEAPRAID_DEVTYP_SEP))) + *sas_address = adapter->dev_topo.card.sas_address; + else + *sas_address = le64_to_cpu(sas_dev_p0.sas_address); + + return 0; +} + +int leapraid_get_volume_cap(struct leapraid_adapter *adapter, + struct leapraid_raid_volume *raid_volume) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_raidvol_p0 *raidvol_p0; + struct leapraid_sas_dev_p0 sas_dev_p0; + struct leapraid_raidpd_p0 raidpd_p0; + u8 num_pds; + u16 sz; + int rc = 0; + + if (leapraid_cfg_get_number_pds(adapter, raid_volume->hdl, + &num_pds) || !num_pds) + return -EFAULT; + + raid_volume->pd_num = num_pds; + sz = offsetof(struct leapraid_raidvol_p0, phys_disk) + + (num_pds * sizeof(struct leapraid_raidvol0_phys_disk)); + raidvol_p0 = kzalloc(sz, GFP_KERNEL); + if (!raidvol_p0) + return -ENOMEM; + + cfgp1.size = sz; + cfgp2.handle = raid_volume->hdl; + if (leapraid_op_config_page(adapter, raidvol_p0, cfgp1, cfgp2, + GET_RAID_VOLUME_PG0)) { + rc = -EFAULT; + goto out_cleanup; + } + + raid_volume->vol_type = raidvol_p0->volume_type; + cfgp1.form = LEAPRAID_PHYSDISK_CFG_PGAD_PHYSDISKNUM; + cfgp2.form_specific = raidvol_p0->phys_disk[0].phys_disk_num; + if (!(leapraid_op_config_page(adapter, &raidpd_p0, cfgp1, cfgp2, + GET_PHY_DISK_PG0))) { + cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2.handle = le16_to_cpu(raidpd_p0.dev_hdl); + if (!(leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, + cfgp2, GET_SAS_DEVICE_PG0))) + raid_volume->dev_info = + le32_to_cpu(sas_dev_p0.dev_info); + } + +out_cleanup: + kfree(raidvol_p0); + return rc; +} + +static bool leapraid_should_skip_poll_work(struct leapraid_adapter *adapter) +{ + unsigned long flags; + bool skip; + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + skip = adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.pcie_recovering || + adapter->access_ctrl.host_removing; + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); + + return skip; +} + +static void leapraid_fw_log_work(struct work_struct *work) +{ + struct leapraid_adapter *adapter = container_of(work, + struct leapraid_adapter, fw_log_desc.fw_log_work.work); + struct leapraid_fw_log_info *infom; + struct leapraid_reg_base __iomem *iomem_base; + unsigned long flags; + + if (leapraid_should_skip_poll_work(adapter)) + goto scheduled_timer; + + infom = (struct leapraid_fw_log_info *) + (adapter->fw_log_desc.fw_log_buffer + + LEAPRAID_SYS_LOG_BUF_SIZE); + + iomem_base = adapter->iomem_base; + if (adapter->fw_log_desc.fw_log_init_flag == 0) { + infom->user_position = + leapraid_readl(&iomem_base->host_log_buf_pos); + infom->adapter_position = + leapraid_readl(&iomem_base->adapter_log_buf_pos); + adapter->fw_log_desc.fw_log_init_flag++; + } + + writel(infom->user_position, &iomem_base->host_log_buf_pos); + infom->adapter_position = + leapraid_readl(&iomem_base->adapter_log_buf_pos); + +scheduled_timer: + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + if (adapter->fw_log_desc.fw_log_wq) + queue_delayed_work( + adapter->fw_log_desc.fw_log_wq, + &adapter->fw_log_desc.fw_log_work, + msecs_to_jiffies(LEAPRAID_PCIE_LOG_POLLING_INTERVAL)); + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); +} + +void leapraid_fw_log_stop(struct leapraid_adapter *adapter) +{ + struct workqueue_struct *wq; + unsigned long flags; + + if (!adapter->fw_log_desc.open_pcie_trace) + return; + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + wq = adapter->fw_log_desc.fw_log_wq; + adapter->fw_log_desc.fw_log_wq = NULL; + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); + if (wq) { + if (!cancel_delayed_work_sync(&adapter->fw_log_desc + .fw_log_work)) + flush_workqueue(wq); + destroy_workqueue(wq); + } +} + +void leapraid_fw_log_start(struct leapraid_adapter *adapter) +{ + unsigned long flags; + + if (!adapter->fw_log_desc.open_pcie_trace) + return; + + if (adapter->fw_log_desc.fw_log_wq) + return; + + INIT_DELAYED_WORK(&adapter->fw_log_desc.fw_log_work, + leapraid_fw_log_work); + snprintf(adapter->fw_log_desc.fw_log_wq_name, + sizeof(adapter->fw_log_desc.fw_log_wq_name), + "poll_%s%u_fw_log", + LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); + adapter->fw_log_desc.fw_log_wq = + create_singlethread_workqueue( + adapter->fw_log_desc.fw_log_wq_name); + if (!adapter->fw_log_desc.fw_log_wq) + return; + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + if (adapter->fw_log_desc.fw_log_wq) + queue_delayed_work( + adapter->fw_log_desc.fw_log_wq, + &adapter->fw_log_desc.fw_log_work, + msecs_to_jiffies(LEAPRAID_PCIE_LOG_POLLING_INTERVAL)); + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); +} + +static void leapraid_check_scheduled_fault_work(struct work_struct *work) +{ + struct leapraid_adapter *adapter; + unsigned long flags; + u32 adapter_state; + int rc; + + adapter = container_of(work, struct leapraid_adapter, + reset_desc.fault_reset_work.work); + + if (leapraid_should_skip_poll_work(adapter)) + goto scheduled_timer; + + adapter_state = leapraid_get_adapter_state(adapter); + if (adapter_state != LEAPRAID_DB_OPERATIONAL) { + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset 0x%x\n", + __func__, __LINE__, adapter_state); + rc = leapraid_hard_reset_handler(adapter, FULL_RESET); + adapter_state = leapraid_get_adapter_state(adapter); + if (rc && adapter_state != LEAPRAID_DB_OPERATIONAL) { + dev_err(&adapter->pdev->dev, + "%s: Hard reset failed, state=0x%x rc=%d\n", + __func__, adapter_state, rc); + return; + } + } + +scheduled_timer: + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + if (adapter->reset_desc.fault_reset_wq) + queue_delayed_work( + adapter->reset_desc.fault_reset_wq, + &adapter->reset_desc.fault_reset_work, + msecs_to_jiffies(LEAPRAID_FAULT_POLLING_INTERVAL)); + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); +} + +void leapraid_check_scheduled_fault_start(struct leapraid_adapter *adapter) +{ + unsigned long flags; + + if (adapter->reset_desc.fault_reset_wq) + return; + + INIT_DELAYED_WORK(&adapter->reset_desc.fault_reset_work, + leapraid_check_scheduled_fault_work); + snprintf(adapter->reset_desc.fault_reset_wq_name, + sizeof(adapter->reset_desc.fault_reset_wq_name), + "poll_%s%u_status", + LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); + adapter->reset_desc.fault_reset_wq = + create_singlethread_workqueue( + adapter->reset_desc.fault_reset_wq_name); + if (!adapter->reset_desc.fault_reset_wq) { + dev_err(&adapter->pdev->dev, + "Create single thread workqueue failed!\n"); + return; + } + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + if (adapter->reset_desc.fault_reset_wq) + queue_delayed_work( + adapter->reset_desc.fault_reset_wq, + &adapter->reset_desc.fault_reset_work, + msecs_to_jiffies(LEAPRAID_FAULT_POLLING_INTERVAL)); + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); +} + +void leapraid_check_scheduled_fault_stop(struct leapraid_adapter *adapter) +{ + struct workqueue_struct *wq; + unsigned long flags; + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + wq = adapter->reset_desc.fault_reset_wq; + adapter->reset_desc.fault_reset_wq = NULL; + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); + + if (!wq) + return; + + if (!cancel_delayed_work_sync(&adapter->reset_desc.fault_reset_work)) + flush_workqueue(wq); + destroy_workqueue(wq); +} + +static void leapraid_overheat_work(struct work_struct *work) +{ + struct leapraid_overheat_desc *desc; + struct leapraid_adapter *adapter; + struct workqueue_struct *wq; + struct Scsi_Host *shost; + struct pci_dev *pdev; + unsigned long flags; + + desc = container_of(work, struct leapraid_overheat_desc, + fault_overheat_work); + adapter = container_of(desc, struct leapraid_adapter, overheat_desc); + pdev = adapter->pdev; + shost = pci_get_drvdata(pdev); + + if (!shost) { + dev_err(&pdev->dev, + "Overheat processing failed: invalid host/adapter\n"); + atomic_set(&adapter->overheat_desc.thermal_alert, 0); + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + return; + } + + if (adapter->access_ctrl.host_removing) { + atomic_set(&adapter->overheat_desc.thermal_alert, 0); + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + return; + } + + adapter->access_ctrl.host_removing = 1; + adapter->access_ctrl.shost_recover_async = 0; + adapter->scan_dev_desc.scan_start = 0; + adapter->scan_dev_desc.wait_scan_dev_done = 0; + adapter->scan_dev_desc.driver_loading = 0; + wake_up(&adapter->access_ctrl.shost_recover_wq); + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + + leapraid_mask_int(adapter); + + leapraid_check_scheduled_fault_stop(adapter); + leapraid_fw_log_stop(adapter); + leapraid_mq_polling_pause(adapter); + + leapraid_clean_active_cmds(adapter); + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + wq = adapter->fw_evt_s.fw_evt_thread; + adapter->fw_evt_s.fw_evt_thread = NULL; + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + if (wq) + destroy_workqueue(wq); + + sas_remove_host(shost); + leapraid_cleanup_lists(adapter); + atomic_set(&adapter->overheat_desc.thermal_alert, 0); + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + dev_err(&pdev->dev, "%s: Suspend adapter due to overheat\n", __func__); +} + +static void leapraid_overheat_init(struct leapraid_adapter *adapter) +{ + if (adapter->overheat_desc.fault_overheat_wq) + return; + + atomic_set(&adapter->overheat_desc.thermal_alert, 0); + snprintf(adapter->overheat_desc.fault_overheat_wq_name, + sizeof(adapter->overheat_desc.fault_overheat_wq_name), + "driver_%s%u_overheat", + LEAPRAID_DRIVER_NAME, + adapter->adapter_attr.id); + adapter->overheat_desc.fault_overheat_wq = + create_singlethread_workqueue( + adapter->overheat_desc.fault_overheat_wq_name); + if (!adapter->overheat_desc.fault_overheat_wq) { + dev_err(&adapter->pdev->dev, + "Failed to create overheat workqueue\n"); + return; + } + + INIT_WORK(&adapter->overheat_desc.fault_overheat_work, + leapraid_overheat_work); +} + +void leapraid_overheat_cleanup(struct leapraid_adapter *adapter) +{ + struct workqueue_struct *wq; + + wq = xchg(&adapter->overheat_desc.fault_overheat_wq, NULL); + if (!wq) + return; + + cancel_work_sync(&adapter->overheat_desc.fault_overheat_work); + destroy_workqueue(wq); +} + +static void leapraid_fw_work(struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt); + +static void leapraid_fw_evt_free(struct kref *r) +{ + struct leapraid_fw_evt_work *fw_evt; + + fw_evt = container_of(r, struct leapraid_fw_evt_work, refcnt); + + kfree(fw_evt->evt_data); + kfree(fw_evt); +} + +static void leapraid_fw_evt_get(struct leapraid_fw_evt_work *fw_evt) +{ + kref_get(&fw_evt->refcnt); +} + +static void leapraid_fw_evt_put(struct leapraid_fw_evt_work *fw_work) +{ + kref_put(&fw_work->refcnt, leapraid_fw_evt_free); +} + +static struct leapraid_fw_evt_work *leapraid_alloc_fw_evt_work(void) +{ + struct leapraid_fw_evt_work *fw_evt = + kzalloc(sizeof(*fw_evt), GFP_ATOMIC); + + if (fw_evt) + kref_init(&fw_evt->refcnt); + + return fw_evt; +} + +static void leapraid_run_fw_evt_work(struct work_struct *work) +{ + struct leapraid_fw_evt_work *fw_evt = + container_of(work, struct leapraid_fw_evt_work, work); + + leapraid_fw_work(fw_evt->adapter, fw_evt); +} + +static void leapraid_fw_evt_add(struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + if (adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering || + !adapter->fw_evt_s.fw_evt_thread) { + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + return; + } + + leapraid_fw_evt_get(fw_evt); + INIT_LIST_HEAD(&fw_evt->list); + list_add_tail(&fw_evt->list, &adapter->fw_evt_s.fw_evt_list); + INIT_WORK(&fw_evt->work, leapraid_run_fw_evt_work); + leapraid_fw_evt_get(fw_evt); + queue_work(adapter->fw_evt_s.fw_evt_thread, &fw_evt->work); + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); +} + +static void leapraid_del_fw_evt_from_list(struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + if (!list_empty(&fw_evt->list)) { + list_del_init(&fw_evt->list); + leapraid_fw_evt_put(fw_evt); + } + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); +} + +static struct leapraid_fw_evt_work *leapraid_next_fw_evt( + struct leapraid_adapter *adapter) +{ + struct leapraid_fw_evt_work *fw_evt = NULL; + unsigned long flags; + + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + if (!list_empty(&adapter->fw_evt_s.fw_evt_list)) { + fw_evt = list_first_entry(&adapter->fw_evt_s.fw_evt_list, + struct leapraid_fw_evt_work, list); + list_del_init(&fw_evt->list); + } + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + return fw_evt; +} + +void leapraid_clean_active_fw_evt(struct leapraid_adapter *adapter) +{ + struct leapraid_fw_evt_work *fw_evt; + unsigned long flags; + bool in_fw_evt_context; + bool rc; + + if ((list_empty(&adapter->fw_evt_s.fw_evt_list) && + !adapter->fw_evt_s.cur_evt) || !adapter->fw_evt_s.fw_evt_thread) + return; + + adapter->fw_evt_s.fw_evt_cleanup = 1; + if (adapter->access_ctrl.shost_recovering && + adapter->fw_evt_s.cur_evt) + adapter->fw_evt_s.cur_evt->ignore = 1; + + while ((fw_evt = leapraid_next_fw_evt(adapter))) { + rc = cancel_work_sync(&fw_evt->work); + if (rc) + leapraid_fw_evt_put(fw_evt); + leapraid_fw_evt_put(fw_evt); + } + + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + fw_evt = adapter->fw_evt_s.cur_evt; + if (fw_evt) { + in_fw_evt_context = adapter->fw_evt_s.cur_evt_task == current; + leapraid_fw_evt_get(fw_evt); + } + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + if (fw_evt) { + if (!in_fw_evt_context) + cancel_work_sync(&fw_evt->work); + leapraid_fw_evt_put(fw_evt); + } + + adapter->fw_evt_s.fw_evt_cleanup = 0; +} + +static void leapraid_internal_dev_ublk(struct scsi_device *sdev, + struct leapraid_sdev_priv *sdev_priv) +{ + int rc; + + sdev_printk(KERN_WARNING, sdev, + "hdl 0x%04x: Now internal unblkg dev\n", + sdev_priv->starget_priv->hdl); + sdev_priv->block = 0; + rc = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING); + if (rc == -EINVAL) { + sdev_printk(KERN_WARNING, sdev, + "hdl 0x%04x: unblkg failed, rc=%d\n", + sdev_priv->starget_priv->hdl, rc); + sdev_priv->block = 1; + rc = scsi_internal_device_block_nowait(sdev); + if (rc) + sdev_printk(KERN_WARNING, sdev, + "hdl 0x%04x: Earlier ublkg err, rc=%d\n", + sdev_priv->starget_priv->hdl, rc); + + sdev_priv->block = 0; + rc = scsi_internal_device_unblock_nowait(sdev, SDEV_RUNNING); + if (rc) + sdev_printk(KERN_WARNING, sdev, + "hdl 0x%04x: ublkg failed again, rc=%d\n", + sdev_priv->starget_priv->hdl, rc); + } +} + +static void leapraid_ublk_io_dev(struct leapraid_adapter *adapter, + u64 sas_addr, + struct leapraid_card_port *card_port) +{ + struct leapraid_sdev_priv *sdev_priv; + struct scsi_device *sdev; + + shost_for_each_device(sdev, adapter->shost) { + sdev_priv = sdev->hostdata; + if (!sdev_priv || !sdev_priv->starget_priv) + continue; + + if (sdev_priv->starget_priv->sas_address != sas_addr) + continue; + + if (sdev_priv->starget_priv->card_port != card_port) + continue; + + if (sdev_priv->block) + leapraid_internal_dev_ublk(sdev, sdev_priv); + + scsi_device_set_state(sdev, SDEV_OFFLINE); + } +} + +static void leapraid_ublk_io_all_dev(struct leapraid_adapter *adapter) +{ + struct leapraid_sdev_priv *sdev_priv; + struct leapraid_starget_priv *stgt_priv; + struct scsi_device *sdev; + + shost_for_each_device(sdev, adapter->shost) { + sdev_priv = sdev->hostdata; + + if (!sdev_priv) + continue; + + stgt_priv = sdev_priv->starget_priv; + if (!stgt_priv || stgt_priv->deleted) + continue; + + if (!sdev_priv->block) + continue; + + sdev_printk(KERN_WARNING, sdev, "hdl 0x%04x: blkg...\n", + sdev_priv->starget_priv->hdl); + leapraid_internal_dev_ublk(sdev, sdev_priv); + continue; + } +} + +static void leapraid_internal_dev_blk( + struct scsi_device *sdev, + struct leapraid_sdev_priv *sdev_priv) +{ + int rc; + + sdev_printk(KERN_INFO, sdev, "Internal blkg hdl 0x%04x\n", + sdev_priv->starget_priv->hdl); + sdev_priv->block = 1; + rc = scsi_internal_device_block_nowait(sdev); + if (rc == -EINVAL) + sdev_printk(KERN_WARNING, sdev, + "hdl 0x%04x: blkg failed, rc=%d\n", + rc, sdev_priv->starget_priv->hdl); +} + +static void leapraid_imm_blkio_to_end_dev(struct leapraid_adapter *adapter, + struct leapraid_sas_port *sas_port) +{ + struct leapraid_sdev_priv *sdev_priv; + struct leapraid_sas_dev *sas_dev; + struct scsi_device *sdev; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr( + adapter, + sas_port->remote_identify.sas_address, + sas_port->card_port); + + if (sas_dev) { + shost_for_each_device(sdev, adapter->shost) { + sdev_priv = sdev->hostdata; + if (!sdev_priv) + continue; + + if (sdev_priv->starget_priv->hdl != sas_dev->hdl) + continue; + + if (sdev_priv->block) + continue; + + if (sas_dev && sas_dev->pend_sas_rphy_add) + continue; + + if (sdev_priv->sep) { + sdev_printk(KERN_INFO, sdev, + "skip dev blk: sep hdl 0x%04x\n", + sdev_priv->starget_priv->hdl); + continue; + } + + leapraid_internal_dev_blk(sdev, sdev_priv); + } + + leapraid_sdev_put(sas_dev); + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); +} + +static void leapraid_imm_blkio_set_end_dev_blk_hdls( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node_exp) +{ + struct leapraid_sas_port *sas_port; + + list_for_each_entry(sas_port, + &topo_node_exp->sas_port_list, port_list) { + if (sas_port->remote_identify.device_type == SAS_END_DEVICE) + leapraid_imm_blkio_to_end_dev(adapter, sas_port); + } +} + +static void leapraid_imm_blkio_to_kids_attchd_to_ex( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node_exp); + +static void leapraid_imm_blkio_to_sib_exp( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node_exp) +{ + struct leapraid_topo_node *topo_node_exp_sib; + struct leapraid_sas_port *sas_port; + + list_for_each_entry(sas_port, + &topo_node_exp->sas_port_list, port_list) { + if (sas_port->remote_identify.device_type == + SAS_EDGE_EXPANDER_DEVICE || + sas_port->remote_identify.device_type == + SAS_FANOUT_EXPANDER_DEVICE) { + topo_node_exp_sib = + leapraid_exp_find_by_sas_address( + adapter, + sas_port->remote_identify.sas_address, + sas_port->card_port); + leapraid_imm_blkio_to_kids_attchd_to_ex( + adapter, + topo_node_exp_sib); + } + } +} + +static void leapraid_imm_blkio_to_kids_attchd_to_ex( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node_exp) +{ + if (!topo_node_exp) + return; + + leapraid_imm_blkio_set_end_dev_blk_hdls(adapter, topo_node_exp); + + leapraid_imm_blkio_to_sib_exp(adapter, topo_node_exp); +} + +static void leapraid_report_sdev_directly(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev) +{ + struct leapraid_sas_port *sas_port; + + sas_port = leapraid_transport_port_add(adapter, + sas_dev->hdl, + sas_dev->parent_sas_addr, + sas_dev->card_port); + if (!sas_port) { + leapraid_sas_dev_remove(adapter, sas_dev); + return; + } + + if (!sas_dev->starget) { + if (!adapter->scan_dev_desc.driver_loading) { + leapraid_transport_port_remove( + adapter, + sas_dev->sas_addr, + sas_dev->parent_sas_addr, + sas_dev->card_port); + leapraid_sas_dev_remove(adapter, sas_dev); + } + return; + } +} + +static struct leapraid_sas_dev *leapraid_init_sas_dev( + struct leapraid_adapter *adapter, + struct leapraid_sas_dev_p0 *sas_dev_pg0, + struct leapraid_card_port *card_port, u16 hdl, + u64 parent_sas_addr, u64 sas_addr, u32 dev_info) +{ + struct leapraid_sas_dev *sas_dev; + struct leapraid_enc_node *enc_dev; + unsigned long flags; + + sas_dev = kzalloc_obj(*sas_dev); + if (!sas_dev) + return NULL; + + kref_init(&sas_dev->refcnt); + sas_dev->hdl = hdl; + sas_dev->dev_info = dev_info; + sas_dev->sas_addr = sas_addr; + sas_dev->card_port = card_port; + sas_dev->parent_sas_addr = parent_sas_addr; + sas_dev->phy = sas_dev_pg0->phy_num; + sas_dev->enc_hdl = le16_to_cpu(sas_dev_pg0->enc_hdl); + sas_dev->dev_name = le64_to_cpu(sas_dev_pg0->dev_name); + sas_dev->port_connection = sas_dev_pg0->max_port_connections; + sas_dev->slot = sas_dev->enc_hdl ? le16_to_cpu(sas_dev_pg0->slot) : 0; + if (le16_to_cpu(sas_dev_pg0->flg) & + LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID) { + sas_dev->enc_level = sas_dev_pg0->enc_level; + memcpy(sas_dev->connector_name, + sas_dev_pg0->connector_name, + LEAPRAID_SAS_DEV_P0_CON_NAME_LEN); + sas_dev->connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN] = + '\0'; + } else { + sas_dev->enc_level = 0; + sas_dev->connector_name[0] = '\0'; + } + if (sas_dev->enc_hdl) { + spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); + enc_dev = leapraid_enc_find_by_hdl(adapter, sas_dev->enc_hdl); + if (enc_dev) + sas_dev->enc_lid = le64_to_cpu(enc_dev->pg0.enc_lid); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); + } + dev_info(&adapter->pdev->dev, + "add dev: hdl=0x%x, SAS addr=0x%016llx, port connect=0x%x\n", + hdl, sas_dev->sas_addr, sas_dev->port_connection); + + return sas_dev; +} + +static void leapraid_add_dev(struct leapraid_adapter *adapter, u16 hdl) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_pg0; + struct leapraid_card_port *card_port; + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + u64 parent_sas_addr; + u32 dev_info; + u64 sas_addr; + u8 port_id; + + cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2.handle = hdl; + if (leapraid_op_config_page(adapter, &sas_dev_pg0, + cfgp1, cfgp2, GET_SAS_DEVICE_PG0)) + return; + + dev_info = le32_to_cpu(sas_dev_pg0.dev_info); + if (!(leapraid_is_end_dev(dev_info))) + return; + + sas_addr = le64_to_cpu(sas_dev_pg0.sas_address); + if (!(le16_to_cpu(sas_dev_pg0.flg) & + LEAPRAID_SAS_DEV_P0_FLG_DEV_PRESENT)) + return; + + port_id = sas_dev_pg0.physical_port; + card_port = leapraid_get_port_by_id(adapter, port_id, false); + if (!card_port) + return; + + sas_dev = leapraid_get_sas_dev_by_addr(adapter, sas_addr, card_port); + if (sas_dev) { + leapraid_sdev_put(sas_dev); + return; + } + + if (leapraid_get_sas_address(adapter, + le16_to_cpu(sas_dev_pg0.parent_dev_hdl), + &parent_sas_addr)) + return; + + sas_dev = leapraid_init_sas_dev(adapter, &sas_dev_pg0, card_port, + hdl, parent_sas_addr, sas_addr, + dev_info); + if (!sas_dev) + return; + + if (adapter->scan_dev_desc.wait_scan_dev_done) { + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + leapraid_sdev_get(sas_dev); + list_add_tail(&sas_dev->list, + &adapter->dev_topo.sas_dev_init_list); + leapraid_check_boot_dev(adapter, sas_dev, 0); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + } else { + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + leapraid_sdev_get(sas_dev); + list_add_tail(&sas_dev->list, &adapter->dev_topo.sas_dev_list); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + leapraid_report_sdev_directly(adapter, sas_dev); + } + leapraid_sdev_put(sas_dev); +} + +static void leapraid_remove_device(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev) +{ + struct leapraid_starget_priv *starget_priv; + + leapraid_clear_cached_boot_dev(adapter, sas_dev, 0); + if (sas_dev->led_on) { + leapraid_set_led(adapter, sas_dev, false); + sas_dev->led_on = 0; + } + + if (sas_dev->starget && sas_dev->starget->hostdata) { + starget_priv = sas_dev->starget->hostdata; + starget_priv->deleted = 1; + leapraid_ublk_io_dev(adapter, + sas_dev->sas_addr, sas_dev->card_port); + starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; + } + + leapraid_transport_port_remove(adapter, + sas_dev->sas_addr, + sas_dev->parent_sas_addr, + sas_dev->card_port); + + dev_info(&adapter->pdev->dev, + "remove dev: hdl=0x%04x, SAS addr=0x%016llx\n", + sas_dev->hdl, (unsigned long long)sas_dev->sas_addr); +} + +static struct leapraid_vphy *leapraid_alloc_vphy( + struct leapraid_adapter *adapter, + u8 port_id, u8 phy_num) +{ + struct leapraid_card_port *port; + struct leapraid_vphy *vphy; + + port = leapraid_get_port_by_id(adapter, port_id, false); + if (!port) + return NULL; + + vphy = leapraid_get_vphy_by_phy(port, phy_num); + if (vphy) + return vphy; + vphy = kzalloc_obj(*vphy); + if (!vphy) + return NULL; + + if (!port->vphys_mask) + INIT_LIST_HEAD(&port->vphys_list); + + port->vphys_mask |= BIT(phy_num); + vphy->phy_mask |= BIT(phy_num); + list_add_tail(&vphy->list, &port->vphys_list); + return vphy; +} + +static int leapraid_add_port_to_card_port_list( + struct leapraid_adapter *adapter, + u8 port_id, bool refresh) +{ + struct leapraid_card_port *card_port; + + card_port = leapraid_get_port_by_id(adapter, port_id, false); + if (card_port) + return 0; + card_port = kzalloc_obj(*card_port); + if (!card_port) + return -ENOMEM; + + card_port->port_id = port_id; + dev_dbg(&adapter->pdev->dev, + "port: %d is added to card_port list\n", + card_port->port_id); + + if (refresh && adapter->access_ctrl.shost_recovering) + card_port->flg = LEAPRAID_CARD_PORT_FLG_NEW; + list_add_tail(&card_port->list, &adapter->dev_topo.card_port_list); + return 0; +} + +static int leapraid_add_card_phy(struct leapraid_adapter *adapter, + struct leapraid_sas_io_unit_p0 *iou, + int i) +{ + struct leapraid_sas_phy_p0 phy_pg0; + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_topo_node *card = &adapter->dev_topo.card; + u8 port_id; + + cfgp1.phy_number = i; + + if (leapraid_op_config_page(adapter, &phy_pg0, + cfgp1, cfgp2, GET_PHY_PG0)) + return -EINVAL; + + port_id = iou->phy_info[i].port; + + if (leapraid_add_port_to_card_port_list(adapter, port_id, false)) + return -EINVAL; + + if ((le32_to_cpu(phy_pg0.phy_info) & + LEAPRAID_SAS_PHYINFO_VPHY) && + ((phy_pg0.neg_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) >= + LEAPRAID_SAS_NEG_LINK_RATE_1_5)) { + if (!leapraid_alloc_vphy(adapter, port_id, i)) + return -ENOMEM; + + card->card_phy[i].vphy = 1; + } + + card->card_phy[i].hdl = card->hdl; + card->card_phy[i].phy_id = i; + card->card_phy[i].card_port = + leapraid_get_port_by_id(adapter, port_id, false); + + leapraid_transport_add_card_phy(adapter, + &card->card_phy[i], + &phy_pg0, + card->parent_dev); + + return 0; +} + +static int leapraid_refresh_card_phy(struct leapraid_adapter *adapter, + struct leapraid_sas_io_unit_p0 *iou, + int i) +{ + struct leapraid_topo_node *card = &adapter->dev_topo.card; + struct leapraid_sas_phy_p0 phy_pg0; + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + u16 attached_hdl; + u32 dev_info; + u8 link_rate; + u8 port_id; + + link_rate = iou->phy_info[i].neg_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT; + port_id = iou->phy_info[i].port; + if (leapraid_add_port_to_card_port_list(adapter, port_id, true)) + return -EINVAL; + + dev_info = le32_to_cpu(iou->phy_info[i].controller_phy_dev_info); + if (dev_info & LEAPRAID_DEVTYP_SEP && + link_rate >= LEAPRAID_SAS_NEG_LINK_RATE_1_5) { + cfgp1.phy_number = i; + + if (leapraid_op_config_page(adapter, &phy_pg0, + cfgp1, cfgp2, GET_PHY_PG0)) + return 0; + + if (le32_to_cpu(phy_pg0.phy_info) & + LEAPRAID_SAS_PHYINFO_VPHY) { + if (!leapraid_alloc_vphy(adapter, port_id, i)) + return -ENOMEM; + card->card_phy[i].vphy = 1; + } + } + + card->card_phy[i].hdl = card->hdl; + + attached_hdl = le16_to_cpu(iou->phy_info[i].attached_dev_hdl); + if (attached_hdl && link_rate < LEAPRAID_SAS_NEG_LINK_RATE_1_5) + link_rate = LEAPRAID_SAS_NEG_LINK_RATE_1_5; + + card->card_phy[i].card_port = + leapraid_get_port_by_id(adapter, port_id, false); + + if (!card->card_phy[i].phy) { + cfgp1.phy_number = i; + if (leapraid_op_config_page(adapter, &phy_pg0, + cfgp1, cfgp2, GET_PHY_PG0)) + return 0; + + card->card_phy[i].phy_id = i; + + leapraid_transport_add_card_phy(adapter, + &card->card_phy[i], + &phy_pg0, + card->parent_dev); + return 0; + } + + leapraid_transport_update_links(adapter, + card->sas_address, + attached_hdl, + i, + link_rate, + card->card_phy[i].card_port); + + return 0; +} + +static void leapraid_sas_host_add(struct leapraid_adapter *adapter, + bool refresh) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_pg0; + struct leapraid_enc_p0 enc_pg0; + struct leapraid_sas_io_unit_p0 *sas_iou_pg0; + u16 sas_iou_pg0_sz; + u8 phys_num; + int i; + int rc; + + if (!refresh) { + if (leapraid_get_adapter_phys(adapter, &phys_num) || !phys_num) + return; + + adapter->dev_topo.card.card_phy = + kcalloc(phys_num, + sizeof(struct leapraid_card_phy), GFP_KERNEL); + if (!adapter->dev_topo.card.card_phy) + return; + + adapter->dev_topo.card.phys_num = phys_num; + } + + sas_iou_pg0_sz = + offsetof(struct leapraid_sas_io_unit_p0, phy_info) + + (adapter->dev_topo.card.phys_num * + sizeof(struct leapraid_sas_io_unit0_phy_info)); + sas_iou_pg0 = kzalloc(sas_iou_pg0_sz, GFP_KERNEL); + if (!sas_iou_pg0) + return; + + if (leapraid_get_sas_io_unit_page0(adapter, + sas_iou_pg0, + sas_iou_pg0_sz)) + goto out_free; + + adapter->dev_topo.card.parent_dev = &adapter->shost->shost_gendev; + adapter->dev_topo.card.hdl = + le16_to_cpu(sas_iou_pg0->phy_info[0].controller_dev_hdl); + for (i = 0; i < adapter->dev_topo.card.phys_num; i++) { + if (!refresh) /* add */ + rc = leapraid_add_card_phy(adapter, sas_iou_pg0, i); + else /* refresh */ + rc = leapraid_refresh_card_phy(adapter, + sas_iou_pg0, + i); + if (rc) + goto out_free; + } + + if (!refresh) { + cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2.handle = adapter->dev_topo.card.hdl; + if (leapraid_op_config_page(adapter, &sas_dev_pg0, cfgp1, + cfgp2, GET_SAS_DEVICE_PG0)) + goto out_free; + + adapter->dev_topo.card.enc_hdl = + le16_to_cpu(sas_dev_pg0.enc_hdl); + adapter->dev_topo.card.sas_address = + le64_to_cpu(sas_dev_pg0.sas_address); + dev_info(&adapter->pdev->dev, + "add host: hdl=0x%04x, SAS addr=0x%016llx, phy=%d\n", + adapter->dev_topo.card.hdl, + (unsigned long long)adapter->dev_topo.card.sas_address, + adapter->dev_topo.card.phys_num); + + if (adapter->dev_topo.card.enc_hdl) { + cfgp1.form = LEAPRAID_SAS_ENC_CFG_PGAD_HDL; + cfgp2.handle = adapter->dev_topo.card.enc_hdl; + if (!(leapraid_op_config_page(adapter, &enc_pg0, + cfgp1, cfgp2, + GET_SAS_ENCLOSURE_PG0))) + adapter->dev_topo.card.enc_lid = + le64_to_cpu(enc_pg0.enc_lid); + } + } +out_free: + kfree(sas_iou_pg0); +} + +static int leapraid_internal_exp_add(struct leapraid_adapter *adapter, + struct leapraid_exp_p0 *exp_pg0, + union cfg_param_1 *cfgp1, + union cfg_param_2 *cfgp2, + u16 hdl) +{ + struct leapraid_topo_node *topo_node_exp; + struct leapraid_sas_port *sas_port = NULL; + struct leapraid_enc_node *enc_dev; + struct leapraid_exp_p1 exp_pg1; + int ret; + int rc; + unsigned long flags; + u8 port_id; + u16 parent_handle; + u64 sas_addr_parent; + int i; + + port_id = exp_pg0->physical_port; + parent_handle = le16_to_cpu(exp_pg0->parent_dev_hdl); + + rc = leapraid_get_sas_address(adapter, + parent_handle, &sas_addr_parent); + if (rc) + return rc; + topo_node_exp = kzalloc_obj(*topo_node_exp); + if (!topo_node_exp) + return -ENOMEM; + + topo_node_exp->hdl = hdl; + topo_node_exp->phys_num = exp_pg0->phy_num; + topo_node_exp->sas_address_parent = sas_addr_parent; + topo_node_exp->sas_address = le64_to_cpu(exp_pg0->sas_address); + topo_node_exp->card_port = + leapraid_get_port_by_id(adapter, port_id, false); + if (!topo_node_exp->card_port) { + rc = -EPERM; + goto out_fail; + } + + dev_info(&adapter->pdev->dev, + "add exp: saddr=0x%016llx, hdl=0x%04x, phdl=0x%04x, phy=%d\n", + (unsigned long long)topo_node_exp->sas_address, + hdl, parent_handle, + topo_node_exp->phys_num); + if (!topo_node_exp->phys_num) { + dev_err(&adapter->pdev->dev, + "%s: Invalid PHY num from exp_pg0\n", __func__); + rc = -EPERM; + goto out_fail; + } + + topo_node_exp->card_phy = + kcalloc(topo_node_exp->phys_num, + sizeof(struct leapraid_card_phy), GFP_KERNEL); + if (!topo_node_exp->card_phy) { + dev_err(&adapter->pdev->dev, + "%s: Failed to alloc expander phy array, count=%u\n", + __func__, topo_node_exp->phys_num); + rc = -EPERM; + goto out_fail; + } + + INIT_LIST_HEAD(&topo_node_exp->sas_port_list); + sas_port = leapraid_transport_port_add(adapter, hdl, sas_addr_parent, + topo_node_exp->card_port); + if (!sas_port) { + rc = -EPERM; + goto out_fail; + } + + topo_node_exp->parent_dev = &sas_port->rphy->dev; + topo_node_exp->rphy = sas_port->rphy; + for (i = 0; i < topo_node_exp->phys_num; i++) { + cfgp1->phy_number = i; + cfgp2->handle = hdl; + if (leapraid_op_config_page(adapter, &exp_pg1, *cfgp1, *cfgp2, + GET_SAS_EXPANDER_PG1)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to get exp_pg1, phy=%d\n", + __func__, i); + rc = -EPERM; + goto out_fail; + } + + topo_node_exp->card_phy[i].hdl = hdl; + topo_node_exp->card_phy[i].phy_id = i; + topo_node_exp->card_phy[i].card_port = + leapraid_get_port_by_id(adapter, port_id, false); + ret = leapraid_transport_add_exp_phy( + adapter, + &topo_node_exp->card_phy[i], + &exp_pg1, + topo_node_exp->parent_dev); + if (ret) { + rc = -EPERM; + goto out_fail; + } + } + + if (topo_node_exp->enc_hdl) { + spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); + enc_dev = leapraid_enc_find_by_hdl(adapter, + topo_node_exp->enc_hdl); + if (enc_dev) + topo_node_exp->enc_lid = + le64_to_cpu(enc_dev->pg0.enc_lid); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); + } + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + list_add_tail(&topo_node_exp->list, &adapter->dev_topo.exp_list); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + return 0; + +out_fail: + if (sas_port) + leapraid_transport_port_remove(adapter, + topo_node_exp->sas_address, + sas_addr_parent, + topo_node_exp->card_port); + kfree(topo_node_exp->card_phy); + kfree(topo_node_exp); + return rc; +} + +static int leapraid_exp_add(struct leapraid_adapter *adapter, u16 hdl) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_topo_node *topo_node_exp; + struct leapraid_exp_p0 exp_pg0; + u16 parent_handle; + u64 sas_addr, sas_addr_parent; + unsigned long flags; + u8 port_id; + int rc; + + if (!hdl) { + dev_warn(&adapter->pdev->dev, "%s: Invalid hdl\n", __func__); + return -EPERM; + } + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.pcie_recovering) { + dev_warn(&adapter->pdev->dev, + "%s: Failed, shost_recovering=%d pcie_recovering=%d\n", + __func__, adapter->access_ctrl.shost_recovering, + adapter->access_ctrl.pcie_recovering); + return -EPERM; + } + + cfgp1.form = LEAPRAID_SAS_EXP_CFD_PGAD_HDL; + cfgp2.handle = hdl; + if (leapraid_op_config_page(adapter, &exp_pg0, cfgp1, cfgp2, + GET_SAS_EXPANDER_PG0)) + return -EPERM; + + parent_handle = le16_to_cpu(exp_pg0.parent_dev_hdl); + if (leapraid_get_sas_address(adapter, parent_handle, &sas_addr_parent)) + return -EPERM; + + port_id = exp_pg0.physical_port; + if (sas_addr_parent != adapter->dev_topo.card.sas_address) { + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + topo_node_exp = + leapraid_exp_find_by_sas_address( + adapter, + sas_addr_parent, + leapraid_get_port_by_id(adapter, + port_id, + false)); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, + flags); + if (!topo_node_exp) { + rc = leapraid_exp_add(adapter, parent_handle); + if (rc != 0) + return rc; + } + } + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + sas_addr = le64_to_cpu(exp_pg0.sas_address); + topo_node_exp = + leapraid_exp_find_by_sas_address( + adapter, + sas_addr, + leapraid_get_port_by_id(adapter, port_id, false)); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + if (topo_node_exp) + return 0; + + return leapraid_internal_exp_add(adapter, &exp_pg0, &cfgp1, + &cfgp2, hdl); +} + +static void leapraid_exp_node_rm(struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node_exp) +{ + struct leapraid_sas_port *sas_port, *sas_port_next; + unsigned long flags; + int port_id; + + list_for_each_entry_safe(sas_port, sas_port_next, + &topo_node_exp->sas_port_list, + port_list) { + if (adapter->access_ctrl.shost_recovering) + return; + + switch (sas_port->remote_identify.device_type) { + case SAS_END_DEVICE: + leapraid_sas_dev_remove_by_sas_address( + adapter, + sas_port->remote_identify.sas_address, + sas_port->card_port); + break; + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: + leapraid_exp_rm( + adapter, + sas_port->remote_identify.sas_address, + sas_port->card_port); + break; + default: + break; + } + } + + port_id = topo_node_exp->card_port->port_id; + leapraid_transport_port_remove(adapter, topo_node_exp->sas_address, + topo_node_exp->sas_address_parent, + topo_node_exp->card_port); + dev_info(&adapter->pdev->dev, + "removing exp: port=%d, SAS addr=0x%016llx, hdl=0x%04x\n", + port_id, (unsigned long long)topo_node_exp->sas_address, + topo_node_exp->hdl); + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + list_del(&topo_node_exp->list); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + kfree(topo_node_exp->card_phy); + kfree(topo_node_exp); +} + +void leapraid_exp_rm(struct leapraid_adapter *adapter, u64 sas_addr, + struct leapraid_card_port *port) +{ + struct leapraid_topo_node *topo_node_exp; + unsigned long flags; + + if (adapter->access_ctrl.shost_recovering) + return; + + if (!port) + return; + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + topo_node_exp = leapraid_exp_find_by_sas_address(adapter, + sas_addr, + port); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + if (topo_node_exp) + leapraid_exp_node_rm(adapter, topo_node_exp); +} + +static void leapraid_internal_sas_topo_chg_evt( + struct leapraid_adapter *adapter, + struct leapraid_card_port *card_port, + struct leapraid_topo_node *topo_node_exp, + struct leapraid_fw_evt_work *fw_evt, + u64 sas_addr, u8 max_phys) +{ + struct leapraid_evt_data_sas_topo_change_list *evt_data; + u8 phy_number; + u8 link_rate; + u16 reason_code; + u16 hdl; + int i; + + evt_data = fw_evt->evt_data; + for (i = 0; i < evt_data->entry_num; i++) { + if (fw_evt->ignore) + return; + + if (adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return; + + phy_number = evt_data->start_phy_num + i; + if (phy_number >= max_phys) + continue; + + reason_code = evt_data->phy[i].phy_status & + LEAPRAID_EVT_SAS_TOPO_RC_MASK; + + hdl = le16_to_cpu(evt_data->phy[i].attached_dev_hdl); + if (!hdl || + hdl > adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + continue; + } + link_rate = evt_data->phy[i].link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT; + switch (reason_code) { + case LEAPRAID_EVT_SAS_TOPO_RC_TARG_ADDED: + if (adapter->access_ctrl.shost_recovering) + break; + leapraid_transport_update_links(adapter, sas_addr, + hdl, phy_number, + link_rate, card_port); + if (link_rate < LEAPRAID_SAS_NEG_LINK_RATE_1_5) + break; + leapraid_add_dev(adapter, hdl); + break; + case LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING: + leapraid_sas_dev_remove_by_hdl(adapter, hdl); + break; + } + } + + if (evt_data->exp_status == LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING && + topo_node_exp) + leapraid_exp_rm(adapter, sas_addr, card_port); +} + +static void leapraid_sas_topo_chg_evt(struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt) +{ + struct leapraid_topo_node *topo_node_exp; + struct leapraid_card_port *card_port; + struct leapraid_evt_data_sas_topo_change_list *evt_data; + u16 phdl; + u8 max_phys; + u64 sas_addr; + unsigned long flags; + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return; + + evt_data = fw_evt->evt_data; + leapraid_sas_host_add(adapter, adapter->dev_topo.card.phys_num > 0); + + if (fw_evt->ignore) + return; + + phdl = le16_to_cpu(evt_data->exp_dev_hdl); + card_port = leapraid_get_port_by_id(adapter, + evt_data->physical_port, + false); + if (evt_data->exp_status == LEAPRAID_EVT_SAS_TOPO_ES_ADDED && + leapraid_exp_add(adapter, phdl) != 0) + return; + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + topo_node_exp = leapraid_exp_find_by_hdl(adapter, phdl); + if (topo_node_exp) { + sas_addr = topo_node_exp->sas_address; + max_phys = topo_node_exp->phys_num; + card_port = topo_node_exp->card_port; + } else if (phdl < adapter->dev_topo.card.phys_num) { + sas_addr = adapter->dev_topo.card.sas_address; + max_phys = adapter->dev_topo.card.phys_num; + } else { + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, + flags); + return; + } + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + leapraid_internal_sas_topo_chg_evt(adapter, card_port, + topo_node_exp, fw_evt, + sas_addr, max_phys); +} + +static void leapraid_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach) +{ + sdev->no_uld_attach = no_uld_attach ? 1 : 0; + sdev_printk(KERN_INFO, sdev, + "%s RAID component to upper layer\n", + sdev->no_uld_attach ? "hide" : "expose"); + WARN_ON(scsi_device_reprobe(sdev)); +} + +static void leapraid_sas_pd_add( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_p0; + struct leapraid_sas_dev *sas_dev; + u64 sas_address; + u16 parent_hdl; + u16 hdl; + + hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); + if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + return; + } + + set_bit(hdl, adapter->dev_topo.pd_hdls); + sas_dev = leapraid_get_sas_dev_by_hdl(adapter, hdl); + if (sas_dev) { + leapraid_sdev_put(sas_dev); + dev_warn(&adapter->pdev->dev, + "Dev handle 0x%x already exists\n", hdl); + return; + } + + cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2.handle = hdl; + if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, cfgp2, + GET_SAS_DEVICE_PG0)) { + dev_warn(&adapter->pdev->dev, "Failed to read dev page0\n"); + return; + } + + parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl); + if (!leapraid_get_sas_address(adapter, parent_hdl, &sas_address)) + leapraid_transport_update_links(adapter, sas_address, hdl, + sas_dev_p0.phy_num, + LEAPRAID_SAS_NEG_LINK_RATE_1_5, + leapraid_get_port_by_id(adapter, + sas_dev_p0.physical_port, + false)); + leapraid_add_dev(adapter, hdl); +} + +static void leapraid_sas_pd_delete( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + u16 hdl; + + hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); + leapraid_sas_dev_remove_by_hdl(adapter, hdl); +} + +static void leapraid_sas_pd_hide( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + struct leapraid_starget_priv *starget_priv; + struct scsi_target *starget = NULL; + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + u64 volume_wwid = 0; + u16 volume_hdl; + u16 hdl; + + hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); + if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + return; + } + leapraid_cfg_get_volume_hdl(adapter, hdl, &volume_hdl); + if (volume_hdl) + leapraid_cfg_get_volume_wwid(adapter, + volume_hdl, + &volume_wwid); + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + if (!sas_dev) { + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return; + } + + set_bit(hdl, adapter->dev_topo.pd_hdls); + if (sas_dev->starget && sas_dev->starget->hostdata) { + starget = sas_dev->starget; + starget_priv = starget->hostdata; + starget_priv->flg |= LEAPRAID_TGT_FLG_RAID_MEMBER; + sas_dev->volume_hdl = volume_hdl; + sas_dev->volume_wwid = volume_wwid; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + if (starget) { + starget_for_each_device(starget, + (void *)LEAPRAID_NO_ULD_ATTACH_FLAG, + leapraid_reprobe_lun); + } + + leapraid_sdev_put(sas_dev); +} + +static void leapraid_sas_pd_expose( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + struct leapraid_starget_priv *starget_priv; + struct scsi_target *starget = NULL; + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + u16 hdl; + + hdl = le16_to_cpu(evt_data->phys_disk_dev_hdl); + if (!hdl || hdl > adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + return; + } + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + if (!sas_dev) { + dev_warn(&adapter->pdev->dev, + "%s:%d: sas_dev not found, hdl=0x%x\n", + __func__, __LINE__, hdl); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return; + } + + sas_dev->volume_hdl = 0; + sas_dev->volume_wwid = 0; + clear_bit(hdl, adapter->dev_topo.pd_hdls); + if (sas_dev->starget && sas_dev->starget->hostdata) { + starget = sas_dev->starget; + starget_priv = starget->hostdata; + starget_priv->flg &= ~LEAPRAID_TGT_FLG_RAID_MEMBER; + sas_dev->led_on = 0; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (starget) { + starget_for_each_device(starget, + NULL, + leapraid_reprobe_lun); + } + + leapraid_sdev_put(sas_dev); +} + +static void leapraid_sas_vol_visibility( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + struct leapraid_raid_volume *raid_volume; + struct scsi_device *sdev; + bool reprobe_flg = false; + bool sdev_held = false; + unsigned long flags; + u16 hdl; + u8 rc; + + hdl = le16_to_cpu(evt_data->vol_dev_hdl); + rc = evt_data->reason_code; + raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl); + if (!raid_volume) { + dev_warn(&adapter->pdev->dev, + "%s:%d: Volume handle 0x%x not found\n", + __func__, __LINE__, hdl); + return; + } + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + sdev = raid_volume->sdev; + if (!sdev) { + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, + flags); + leapraid_raid_volume_put(raid_volume); + dev_warn(&adapter->pdev->dev, + "%s:%d: Volume handle 0x%x has no sdev\n", + __func__, __LINE__, hdl); + return; + } + + if (sdev->no_uld_attach && + rc == LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE) { + sdev->no_uld_attach = 0; + reprobe_flg = true; + } else if (!sdev->no_uld_attach && + rc == LEAPRAID_EVT_IR_RC_VOLUME_HIDE) { + sdev->no_uld_attach = 1; + reprobe_flg = true; + } + + if (reprobe_flg && !scsi_device_get(sdev)) + sdev_held = true; + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + leapraid_raid_volume_put(raid_volume); + + if (!reprobe_flg) { + dev_warn(&adapter->pdev->dev, + "%s:rc(0x%x): Request matches, skipping\n", + __func__, rc); + return; + } + + if (!sdev_held) { + dev_warn(&adapter->pdev->dev, + "%s: Failed to hold sdev for reprobe, hdl=0x%x\n", + __func__, hdl); + return; + } + + if (sdev->no_uld_attach) + sdev_printk(KERN_INFO, sdev, "hide vol\n"); + else + sdev_printk(KERN_INFO, sdev, "unhide vol\n"); + + WARN_ON(scsi_device_reprobe(sdev)); + scsi_device_put(sdev); +} + +static void leapraid_sas_volume_add( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + struct leapraid_raid_volume *raid_volume; + unsigned long flags; + u64 wwid; + u16 hdl; + + hdl = le16_to_cpu(evt_data->vol_dev_hdl); + + if (leapraid_cfg_get_volume_wwid(adapter, hdl, &wwid)) { + dev_warn(&adapter->pdev->dev, "Failed to read volume page1\n"); + return; + } + + if (!wwid) { + dev_warn(&adapter->pdev->dev, "Invalid WWID(handle=0x%x)\n", + hdl); + return; + } + + raid_volume = leapraid_raid_volume_find_by_wwid(adapter, wwid); + if (raid_volume) { + dev_warn(&adapter->pdev->dev, + "Volume handle 0x%x already exists\n", hdl); + leapraid_raid_volume_put(raid_volume); + return; + } + + raid_volume = kzalloc(sizeof(*raid_volume), GFP_KERNEL); + if (!raid_volume) + return; + + INIT_LIST_HEAD(&raid_volume->list); + kref_init(&raid_volume->refcnt); + raid_volume->id = adapter->dev_topo.sas_id++; + raid_volume->channel = RAID_CHANNEL; + raid_volume->hdl = hdl; + raid_volume->wwid = wwid; + leapraid_raid_volume_add(adapter, raid_volume); + if (!adapter->scan_dev_desc.wait_scan_dev_done) { + if (scsi_add_device(adapter->shost, RAID_CHANNEL, + raid_volume->id, 0)) + leapraid_raid_volume_remove(adapter, raid_volume); + dev_info(&adapter->pdev->dev, + "add RAID volume: hdl=0x%x, wwid=0x%llx\n", hdl, wwid); + } else { + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + leapraid_check_boot_dev(adapter, raid_volume, RAID_CHANNEL); + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, + flags); + } + leapraid_raid_volume_put(raid_volume); +} + +static void leapraid_sas_volume_delete_by_ptr( + struct leapraid_adapter *adapter, + struct leapraid_raid_volume *raid_volume) +{ + struct leapraid_starget_priv *starget_priv; + struct scsi_target *starget = NULL; + unsigned long flags; + bool in_list; + + if (!raid_volume) + return; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + in_list = !list_empty(&raid_volume->list); + if (in_list && raid_volume->starget) { + starget = raid_volume->starget; + starget_priv = starget->hostdata; + if (starget_priv) + starget_priv->deleted = 1; + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + if (!in_list) + return; + + dev_info(&adapter->pdev->dev, + "delete RAID volume: hdl=0x%x, wwid=0x%llx\n", + raid_volume->hdl, raid_volume->wwid); + leapraid_raid_volume_remove(adapter, raid_volume); + + if (starget) + scsi_remove_target(&starget->dev); +} + +static void leapraid_sas_volume_delete(struct leapraid_adapter *adapter, + u16 hdl) +{ + struct leapraid_raid_volume *raid_volume; + + raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl); + if (!raid_volume) { + dev_warn(&adapter->pdev->dev, + "%s:%d: Volume handle 0x%x not found\n", + __func__, __LINE__, hdl); + return; + } + + leapraid_sas_volume_delete_by_ptr(adapter, raid_volume); + leapraid_raid_volume_put(raid_volume); +} + +static void leapraid_sas_ir_chg_evt(struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt) +{ + struct leapraid_evt_data_ir_change *evt_data; + + evt_data = fw_evt->evt_data; + + switch (evt_data->reason_code) { + case LEAPRAID_EVT_IR_RC_VOLUME_ADD: + leapraid_sas_volume_add(adapter, evt_data); + break; + case LEAPRAID_EVT_IR_RC_VOLUME_DELETE: + leapraid_sas_volume_delete(adapter, + le16_to_cpu(evt_data->vol_dev_hdl)); + break; + case LEAPRAID_EVT_IR_RC_PD_HIDDEN_TO_ADD: + leapraid_sas_pd_add(adapter, evt_data); + break; + case LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE: + leapraid_sas_pd_delete(adapter, evt_data); + break; + case LEAPRAID_EVT_IR_RC_PD_CREATED_TO_HIDE: + leapraid_sas_pd_hide(adapter, evt_data); + break; + case LEAPRAID_EVT_IR_RC_PD_DELETED_TO_EXPOSE: + leapraid_sas_pd_expose(adapter, evt_data); + break; + case LEAPRAID_EVT_IR_RC_VOLUME_HIDE: + case LEAPRAID_EVT_IR_RC_VOLUME_UNHIDE: + leapraid_sas_vol_visibility(adapter, evt_data); + break; + default: + break; + } +} + +static void leapraid_sas_enc_dev_stat_add_node( + struct leapraid_adapter *adapter, u16 hdl) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_enc_node *enc_node; + struct leapraid_enc_node *enc_exist; + unsigned long flags; + int rc; + + enc_node = kzalloc_obj(*enc_node); + if (!enc_node) + return; + + cfgp1.form = LEAPRAID_SAS_ENC_CFG_PGAD_HDL; + cfgp2.handle = hdl; + rc = leapraid_op_config_page(adapter, &enc_node->pg0, cfgp1, cfgp2, + GET_SAS_ENCLOSURE_PG0); + if (rc) { + kfree(enc_node); + return; + } + + spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); + enc_exist = leapraid_enc_find_by_hdl(adapter, hdl); + if (enc_exist) { + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); + kfree(enc_node); + return; + } + list_add_tail(&enc_node->list, &adapter->dev_topo.enc_list); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); +} + +static void leapraid_sas_enc_dev_stat_del_node( + struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_enc_node *enc_node; + unsigned long flags; + + if (!hdl) + return; + + spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); + enc_node = leapraid_enc_find_by_hdl(adapter, hdl); + if (enc_node) + list_del(&enc_node->list); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); + + kfree(enc_node); +} + +static void leapraid_sas_enc_dev_stat_chg_evt( + struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt) +{ + struct leapraid_evt_data_sas_enc_dev_status_change *evt_data; + u16 enc_hdl; + + if (adapter->access_ctrl.shost_recovering) + return; + + evt_data = fw_evt->evt_data; + enc_hdl = le16_to_cpu(evt_data->enc_hdl); + switch (evt_data->reason_code) { + case LEAPRAID_EVT_SAS_ENCL_RC_ADDED: + if (enc_hdl) + leapraid_sas_enc_dev_stat_add_node(adapter, enc_hdl); + break; + case LEAPRAID_EVT_SAS_ENCL_RC_NOT_RESPONDING: + leapraid_sas_enc_dev_stat_del_node(adapter, enc_hdl); + break; + default: + break; + } +} + +static void leapraid_remove_unresp_sas_end_dev( + struct leapraid_adapter *adapter) +{ + struct leapraid_sas_dev *sas_dev, *sas_dev_next; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + list_for_each_entry_safe(sas_dev, sas_dev_next, + &adapter->dev_topo.sas_dev_init_list, list) { + if (sas_dev->rphy || sas_dev->pend_sas_rphy_add) + continue; + + list_del_init(&sas_dev->list); + leapraid_clear_cached_boot_dev(adapter, sas_dev, 0); + leapraid_sdev_put(sas_dev); + } + list_for_each_entry_safe(sas_dev, sas_dev_next, + &adapter->dev_topo.sas_dev_list, list) { + if (!sas_dev->resp) + list_move_tail(&sas_dev->list, &head); + else + sas_dev->resp = 0; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + list_for_each_entry_safe(sas_dev, sas_dev_next, &head, list) { + leapraid_remove_device(adapter, sas_dev); + list_del_init(&sas_dev->list); + leapraid_sdev_put(sas_dev); + } + + dev_warn(&adapter->pdev->dev, + "Unresponsive SAS end devices removed\n"); +} + +static void leapraid_remove_unresp_raid_volumes( + struct leapraid_adapter *adapter) +{ + unsigned long flags; + struct leapraid_raid_volume *raid_volume, *raid_volume_next; + LIST_HEAD(head); + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + list_for_each_entry_safe(raid_volume, raid_volume_next, + &adapter->dev_topo.raid_volume_list, list) { + if (!raid_volume->resp) + list_move_tail(&raid_volume->list, &head); + else + raid_volume->resp = 0; + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + list_for_each_entry_safe(raid_volume, raid_volume_next, &head, list) { + leapraid_sas_volume_delete_by_ptr(adapter, raid_volume); + } + + dev_warn(&adapter->pdev->dev, + "Unresponsive RAID volumes removed\n"); +} + +static void leapraid_remove_unresp_sas_exp(struct leapraid_adapter *adapter) +{ + struct leapraid_topo_node *topo_node_exp, *topo_node_exp_next; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + list_for_each_entry_safe(topo_node_exp, topo_node_exp_next, + &adapter->dev_topo.exp_list, list) { + if (!topo_node_exp->resp) + list_move_tail(&topo_node_exp->list, &head); + else + topo_node_exp->resp = 0; + } + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + list_for_each_entry_safe(topo_node_exp, topo_node_exp_next, + &head, list) + leapraid_exp_node_rm(adapter, topo_node_exp); + + dev_warn(&adapter->pdev->dev, + "Unresponsive SAS expanders removed\n"); +} + +static void leapraid_remove_unresp_dev(struct leapraid_adapter *adapter) +{ + leapraid_remove_unresp_sas_end_dev(adapter); + if (adapter->adapter_attr.raid_support) + leapraid_remove_unresp_raid_volumes(adapter); + leapraid_remove_unresp_sas_exp(adapter); + leapraid_ublk_io_all_dev(adapter); +} + +static void leapraid_del_dirty_vphy(struct leapraid_adapter *adapter) +{ + struct leapraid_card_port *card_port, *card_port_next; + struct leapraid_vphy *vphy, *vphy_next; + + list_for_each_entry_safe(card_port, card_port_next, + &adapter->dev_topo.card_port_list, list) { + if (!card_port->vphys_mask) + continue; + + list_for_each_entry_safe(vphy, vphy_next, + &card_port->vphys_list, list) { + if (!(vphy->flg & LEAPRAID_VPHY_FLG_DIRTY)) + continue; + + card_port->vphys_mask &= ~vphy->phy_mask; + list_del(&vphy->list); + kfree(vphy); + } + + if (!card_port->vphys_mask && !card_port->sas_address) + card_port->flg |= LEAPRAID_CARD_PORT_FLG_DIRTY; + } +} + +static void leapraid_del_dirty_card_port(struct leapraid_adapter *adapter) +{ + struct leapraid_card_port *card_port, *card_port_next; + + list_for_each_entry_safe(card_port, card_port_next, + &adapter->dev_topo.card_port_list, list) { + if (!(card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY) || + card_port->flg & LEAPRAID_CARD_PORT_FLG_NEW) + continue; + + list_del(&card_port->list); + kfree(card_port); + } +} + +static void leapraid_update_dev_qdepth(struct leapraid_adapter *adapter) +{ + struct leapraid_sdev_priv *sdev_priv; + struct leapraid_sas_dev *sas_dev; + struct leapraid_adapter_attr *attr; + struct scsi_device *sdev; + u16 qdepth; + + attr = &adapter->adapter_attr; + shost_for_each_device(sdev, adapter->shost) { + sdev_priv = sdev->hostdata; + if (!sdev_priv || !sdev_priv->starget_priv) + continue; + sas_dev = sdev_priv->starget_priv->sas_dev; + if (sas_dev && sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) + qdepth = (sas_dev->port_connection > 1) ? + attr->wideport_max_queue_depth : + attr->narrowport_max_queue_depth; + else if (sas_dev && sas_dev->dev_info & + LEAPRAID_DEVTYP_SATA_DEV) + qdepth = attr->sata_max_queue_depth; + else + continue; + + leapraid_change_queue_depth(sdev, qdepth); + } +} + +static void leapraid_update_exp_links(struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node_exp, + u16 hdl) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_exp_p1 exp_p1; + int i; + + cfgp2.handle = hdl; + for (i = 0; i < topo_node_exp->phys_num; i++) { + cfgp1.phy_number = i; + if (leapraid_op_config_page(adapter, &exp_p1, cfgp1, cfgp2, + GET_SAS_EXPANDER_PG1)) + return; + + leapraid_transport_update_links( + adapter, + topo_node_exp->sas_address, + le16_to_cpu(exp_p1.attached_dev_hdl), + i, + exp_p1.neg_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT, + topo_node_exp->card_port); + } +} + +static void leapraid_scan_exp_after_reset(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_topo_node *topo_node_exp; + struct leapraid_exp_p0 exp_p0; + unsigned long flags; + u16 hdl; + u8 port_id; + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (hdl = 0xFFFF, cfgp2.handle = hdl; + !leapraid_op_config_page(adapter, &exp_p0, cfgp1, cfgp2, + GET_SAS_EXPANDER_PG0); + cfgp2.handle = hdl) { + hdl = le16_to_cpu(exp_p0.dev_hdl); + port_id = exp_p0.physical_port; + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + topo_node_exp = + leapraid_exp_find_by_sas_address( + adapter, + le64_to_cpu(exp_p0.sas_address), + leapraid_get_port_by_id(adapter, + port_id, + false)); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, + flags); + + if (topo_node_exp) { + leapraid_update_exp_links(adapter, topo_node_exp, hdl); + } else { + leapraid_exp_add(adapter, hdl); + + dev_info(&adapter->pdev->dev, + "add exp: hdl=0x%04x, SAS addr=0x%016llx\n", + hdl, + (unsigned long long)le64_to_cpu( + exp_p0.sas_address)); + } + } +} + +static void leapraid_scan_phy_disks_after_reset( + struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + union cfg_param_1 cfgp1_extra = {0}; + union cfg_param_2 cfgp2_extra = {0}; + struct leapraid_sas_dev_p0 sas_dev_p0; + struct leapraid_raidpd_p0 raidpd_p0; + struct leapraid_sas_dev *sas_dev; + u8 phys_disk_num, port_id; + u16 hdl, parent_hdl; + u64 sas_addr; + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (phys_disk_num = 0xFF, cfgp2.form_specific = phys_disk_num; + !leapraid_op_config_page(adapter, &raidpd_p0, + cfgp1, cfgp2, GET_PHY_DISK_PG0); + cfgp2.form_specific = phys_disk_num) { + phys_disk_num = raidpd_p0.phys_disk_num; + hdl = le16_to_cpu(raidpd_p0.dev_hdl); + sas_dev = leapraid_get_sas_dev_by_hdl(adapter, hdl); + if (sas_dev) { + leapraid_sdev_put(sas_dev); + continue; + } + + cfgp1_extra.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2_extra.handle = hdl; + if (leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1_extra, + cfgp2_extra, GET_SAS_DEVICE_PG0) != + 0) + continue; + + parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl); + if (!leapraid_get_sas_address(adapter, + parent_hdl, + &sas_addr)) { + port_id = sas_dev_p0.physical_port; + leapraid_transport_update_links( + adapter, sas_addr, hdl, + sas_dev_p0.phy_num, + LEAPRAID_SAS_NEG_LINK_RATE_1_5, + leapraid_get_port_by_id( + adapter, port_id, false)); + if (!hdl || hdl > + adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", + __func__); + } else { + set_bit(hdl, adapter->dev_topo.pd_hdls); + leapraid_add_dev(adapter, hdl); + + dev_info(&adapter->pdev->dev, + "add pd hdl=0x%04x saddr=0x%016llx\n", + hdl, + (unsigned long long)le64_to_cpu( + sas_dev_p0.sas_address)); + } + } + } +} + +static void leapraid_scan_vol_after_reset(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + union cfg_param_1 cfgp1_extra = {0}; + union cfg_param_2 cfgp2_extra = {0}; + struct leapraid_evt_data_ir_change evt_data; + struct leapraid_raid_volume *raid_volume; + struct leapraid_raidvol_p1 *vol_p1; + struct leapraid_raidvol_p0 *vol_p0; + u16 hdl; + + vol_p0 = kzalloc_obj(*vol_p0); + if (!vol_p0) + return; + + vol_p1 = kzalloc_obj(*vol_p1); + if (!vol_p1) { + kfree(vol_p0); + return; + } + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (hdl = 0xFFFF, cfgp2.handle = hdl; + !leapraid_op_config_page(adapter, vol_p1, cfgp1, + cfgp2, GET_RAID_VOLUME_PG1); + cfgp2.handle = hdl) { + hdl = le16_to_cpu(vol_p1->dev_hdl); + raid_volume = leapraid_raid_volume_find_by_wwid( + adapter, + le64_to_cpu(vol_p1->wwid)); + if (raid_volume) { + leapraid_raid_volume_put(raid_volume); + continue; + } + + cfgp1_extra.size = sizeof(struct leapraid_raidvol_p0); + cfgp2_extra.handle = hdl; + if (leapraid_op_config_page(adapter, vol_p0, cfgp1_extra, + cfgp2_extra, GET_RAID_VOLUME_PG0)) + continue; + + if (vol_p0->volume_state == LEAPRAID_VOL_STATE_OPTIMAL || + vol_p0->volume_state == LEAPRAID_VOL_STATE_ONLINE || + vol_p0->volume_state == LEAPRAID_VOL_STATE_DEGRADED) { + memset(&evt_data, 0, + sizeof(struct leapraid_evt_data_ir_change)); + evt_data.reason_code = LEAPRAID_EVT_IR_RC_VOLUME_ADD; + evt_data.vol_dev_hdl = vol_p1->dev_hdl; + leapraid_sas_volume_add(adapter, &evt_data); + dev_info(&adapter->pdev->dev, + "add volume: hdl=0x%04x\n", + vol_p1->dev_hdl); + } + } + + kfree(vol_p0); + kfree(vol_p1); +} + +static void leapraid_scan_sas_dev_after_reset(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_p0; + struct leapraid_sas_dev *sas_dev; + u16 hdl, parent_hdl; + u64 sas_address; + u8 port_id; + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (hdl = 0xFFFF, cfgp2.handle = hdl; + !leapraid_op_config_page(adapter, &sas_dev_p0, cfgp1, cfgp2, + GET_SAS_DEVICE_PG0); + cfgp2.handle = hdl) { + hdl = le16_to_cpu(sas_dev_p0.dev_hdl); + if (!hdl || + hdl > adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + continue; + } + + if (!(leapraid_is_end_dev(le32_to_cpu(sas_dev_p0.dev_info)))) + continue; + + port_id = sas_dev_p0.physical_port; + sas_dev = leapraid_get_sas_dev_by_addr( + adapter, + le64_to_cpu(sas_dev_p0.sas_address), + leapraid_get_port_by_id( + adapter, + port_id, + false)); + if (sas_dev) { + leapraid_sdev_put(sas_dev); + continue; + } + + parent_hdl = le16_to_cpu(sas_dev_p0.parent_dev_hdl); + if (!leapraid_get_sas_address(adapter, parent_hdl, + &sas_address)) { + leapraid_transport_update_links( + adapter, + sas_address, + hdl, + sas_dev_p0.phy_num, + LEAPRAID_SAS_NEG_LINK_RATE_1_5, + leapraid_get_port_by_id(adapter, + port_id, + false)); + leapraid_add_dev(adapter, hdl); + dev_info(&adapter->pdev->dev, + "Add SAS dev: hdl=0x%04x, saddr=0x%016llx\n", + hdl, + (unsigned long long)le64_to_cpu( + sas_dev_p0.sas_address)); + } + } +} + +static void leapraid_scan_all_dev_after_reset(struct leapraid_adapter *adapter) +{ + leapraid_sas_host_add(adapter, adapter->dev_topo.card.phys_num > 0); + leapraid_scan_exp_after_reset(adapter); + if (adapter->adapter_attr.raid_support) { + leapraid_scan_phy_disks_after_reset(adapter); + leapraid_scan_vol_after_reset(adapter); + } + leapraid_scan_sas_dev_after_reset(adapter); +} + +static void leapraid_hardreset_async_logic(struct leapraid_adapter *adapter) +{ + unsigned long flags; + + leapraid_remove_unresp_dev(adapter); + leapraid_del_dirty_vphy(adapter); + leapraid_del_dirty_card_port(adapter); + leapraid_update_dev_qdepth(adapter); + leapraid_scan_all_dev_after_reset(adapter); + + if (adapter->scan_dev_desc.driver_loading) + leapraid_scan_dev_done(adapter); + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + adapter->access_ctrl.shost_recover_async = 0; + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); + wake_up(&adapter->access_ctrl.shost_recover_wq); +} + +static int leapraid_send_enc_cmd(struct leapraid_adapter *adapter, + struct leapraid_sep_rep *sep_rep, + struct leapraid_sep_req *sep_req) +{ + void *req; + bool reset_flg = false; + int rc; + u16 smid; + + mutex_lock(&adapter->driver_cmds.enc_cmd.mutex); + rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, + __func__); + if (rc) + goto unlock; + + adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_PENDING; + smid = adapter->driver_cmds.enc_cmd.inter_taskid; + req = leapraid_get_task_desc(adapter, smid); + memset(req, 0, LEAPRAID_REQUEST_SIZE); + memcpy(req, sep_req, sizeof(struct leapraid_sep_req)); + init_completion(&adapter->driver_cmds.enc_cmd.done); + leapraid_fire_task(adapter, smid); + wait_for_completion_timeout(&adapter->driver_cmds.enc_cmd.done, + LEAPRAID_ENC_CMD_TIMEOUT * HZ); + if (!(adapter->driver_cmds.enc_cmd.status & LEAPRAID_CMD_DONE)) { + dev_err(&adapter->pdev->dev, + "%s: SEP command timeout, status=0x%x\n", + __func__, adapter->driver_cmds.enc_cmd.status); + leapraid_log_req_context(adapter, smid, sep_req); + reset_flg = + leapraid_check_reset( + adapter->driver_cmds.enc_cmd.status); + rc = -EFAULT; + goto do_hard_reset; + } + + if (adapter->driver_cmds.enc_cmd.status & LEAPRAID_CMD_REPLY_VALID) + memcpy(sep_rep, &adapter->driver_cmds.enc_cmd.reply, + sizeof(struct leapraid_sep_rep)); +do_hard_reset: + if (reset_flg) { + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", + __func__, __LINE__); + leapraid_hard_reset_handler(adapter, FULL_RESET); + } + + adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_NOT_USED; +unlock: + mutex_unlock(&adapter->driver_cmds.enc_cmd.mutex); + return rc; +} + +static void leapraid_set_led(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev, bool on) +{ + struct leapraid_sep_rep sep_rep; + struct leapraid_sep_req sep_req; + + if (!sas_dev) + return; + + memset(&sep_req, 0, sizeof(struct leapraid_sep_req)); + memset(&sep_rep, 0, sizeof(struct leapraid_sep_rep)); + sep_req.func = LEAPRAID_FUNC_SCSI_ENC_PROCESSOR; + sep_req.act = LEAPRAID_SEP_REQ_ACT_WRITE_STATUS; + if (on) { + u32 status = LEAPRAID_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; + + sep_req.slot_status = cpu_to_le32(status); + sep_req.dev_hdl = cpu_to_le16(sas_dev->hdl); + sep_req.flg = LEAPRAID_SEP_REQ_FLG_DEVHDL_ADDRESS; + if (leapraid_send_enc_cmd(adapter, &sep_rep, &sep_req)) { + leapraid_sdev_put(sas_dev); + return; + } + + sas_dev->led_on = 1; + leapraid_sdev_put(sas_dev); + } else { + sep_req.slot_status = 0; + sep_req.slot = cpu_to_le16(sas_dev->slot); + sep_req.dev_hdl = 0; + sep_req.enc_hdl = cpu_to_le16(sas_dev->enc_hdl); + sep_req.flg = LEAPRAID_SEP_REQ_FLG_ENCLOSURE_SLOT_ADDRESS; + leapraid_send_enc_cmd(adapter, &sep_rep, &sep_req); + } +} + +static int leapraid_wait_adapter_recovery(struct leapraid_adapter *adapter) +{ + unsigned long flags; + + while (leapraid_shost_in_recovery(adapter->shost) || + READ_ONCE(adapter->access_ctrl.shost_recovering)) { + if (READ_ONCE(adapter->access_ctrl.host_removing) || + READ_ONCE(adapter->fw_evt_s.fw_evt_cleanup)) { + spin_lock_irqsave( + &adapter->reset_desc.adapter_reset_lock, + flags); + adapter->access_ctrl.shost_recover_async = 0; + spin_unlock_irqrestore( + &adapter->reset_desc.adapter_reset_lock, + flags); + + wake_up(&adapter->access_ctrl.shost_recover_wq); + + dev_warn(&adapter->pdev->dev, + "%s: Failed, shost %d, host %d\n", + __func__, + adapter->access_ctrl.shost_recovering, + adapter->access_ctrl.host_removing); + + return -EFAULT; + } + + wait_event_timeout( + adapter->access_ctrl.recovery_waitq, + (!leapraid_shost_in_recovery(adapter->shost) && + !READ_ONCE(adapter->access_ctrl.shost_recovering)), + msecs_to_jiffies(1000)); + } + + return 0; +} + +static void leapraid_fw_work(struct leapraid_adapter *adapter, + struct leapraid_fw_evt_work *fw_evt) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + adapter->fw_evt_s.cur_evt = fw_evt; + adapter->fw_evt_s.cur_evt_task = current; + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + leapraid_del_fw_evt_from_list(adapter, fw_evt); + if (adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) { + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + leapraid_fw_evt_put(fw_evt); + adapter->fw_evt_s.cur_evt = NULL; + adapter->fw_evt_s.cur_evt_task = NULL; + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + return; + } + switch (fw_evt->evt_type) { + case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST: + leapraid_sas_topo_chg_evt(adapter, fw_evt); + break; + case LEAPRAID_EVT_IR_CHANGE: + leapraid_sas_ir_chg_evt(adapter, fw_evt); + break; + case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE: + leapraid_sas_enc_dev_stat_chg_evt(adapter, fw_evt); + break; + case LEAPRAID_EVT_REMOVE_DEAD_DEV: + if (leapraid_wait_adapter_recovery(adapter)) + goto out_cleanup; + + leapraid_hardreset_async_logic(adapter); + break; + case LEAPRAID_EVT_TURN_ON_PFA_LED: + sas_dev = leapraid_get_sas_dev_by_hdl(adapter, + fw_evt->dev_handle); + leapraid_set_led(adapter, sas_dev, true); + break; + case LEAPRAID_EVT_SCAN_DEV_DONE: + adapter->scan_dev_desc.scan_start = 0; + break; + default: + break; + } +out_cleanup: + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + leapraid_fw_evt_put(fw_evt); + adapter->fw_evt_s.cur_evt = NULL; + adapter->fw_evt_s.cur_evt_task = NULL; + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); +} + +static void leapraid_sas_dev_stat_chg_evt( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_sas_dev_status_change *event_data) +{ + struct leapraid_starget_priv *starget_priv; + struct leapraid_sas_dev *sas_dev; + u64 sas_address; + unsigned long flags; + + switch (event_data->reason_code) { + case LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET: + case LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: + break; + default: + return; + } + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + + sas_address = le64_to_cpu(event_data->sas_address); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr( + adapter, + sas_address, + leapraid_get_port_by_id(adapter, + event_data->physical_port, + false)); + if (!sas_dev || !sas_dev->starget) + goto out_unlock; + + starget_priv = sas_dev->starget->hostdata; + if (starget_priv) { + switch (event_data->reason_code) { + case LEAPRAID_EVT_SAS_DEV_STAT_RC_INTERNAL_DEV_RESET: + starget_priv->tm_busy = 1; + break; + case LEAPRAID_EVT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET: + starget_priv->tm_busy = 0; + break; + } + } + +out_unlock: + if (sas_dev) + leapraid_sdev_put(sas_dev); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); +} + +static void leapraid_set_volume_delete_flag(struct leapraid_adapter *adapter, + u16 handle) +{ + struct leapraid_raid_volume *raid_volume; + struct leapraid_starget_priv *sas_target_priv_data; + unsigned long flags; + + raid_volume = leapraid_raid_volume_find_by_hdl(adapter, handle); + if (raid_volume) { + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + if (raid_volume->starget && + raid_volume->starget->hostdata) { + sas_target_priv_data = raid_volume->starget->hostdata; + sas_target_priv_data->deleted = 1; + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, + flags); + leapraid_raid_volume_put(raid_volume); + } +} + +static void leapraid_check_ir_change_evt( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_ir_change *evt_data) +{ + u16 phys_disk_dev_hdl; + + switch (evt_data->reason_code) { + case LEAPRAID_EVT_IR_RC_VOLUME_DELETE: + leapraid_set_volume_delete_flag( + adapter, + le16_to_cpu(evt_data->vol_dev_hdl)); + break; + case LEAPRAID_EVT_IR_RC_PD_UNHIDDEN_TO_DELETE: + phys_disk_dev_hdl = + le16_to_cpu(evt_data->phys_disk_dev_hdl); + if (!phys_disk_dev_hdl || + phys_disk_dev_hdl > + adapter->adapter_attr.features.max_dev_handle) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + } else { + clear_bit(phys_disk_dev_hdl, + adapter->dev_topo.pd_hdls); + leapraid_tgt_rst_send(adapter, phys_disk_dev_hdl); + } + break; + } +} + +static void leapraid_topo_del_evts_process_exp_status( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_sas_topo_change_list *evt_data) +{ + struct leapraid_fw_evt_work *fw_evt = NULL; + struct leapraid_evt_data_sas_topo_change_list *loc_evt_data; + unsigned long flags; + u16 exp_hdl; + + exp_hdl = le16_to_cpu(evt_data->exp_dev_hdl); + + switch (evt_data->exp_status) { + case LEAPRAID_EVT_SAS_TOPO_ES_NOT_RESPONDING: + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + list_for_each_entry(fw_evt, + &adapter->fw_evt_s.fw_evt_list, list) { + if (fw_evt->evt_type != + LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST || + fw_evt->ignore) + continue; + + loc_evt_data = fw_evt->evt_data; + if ((loc_evt_data->exp_status == + LEAPRAID_EVT_SAS_TOPO_ES_ADDED || + loc_evt_data->exp_status == + LEAPRAID_EVT_SAS_TOPO_ES_RESPONDING) && + le16_to_cpu(loc_evt_data->exp_dev_hdl) == exp_hdl) + fw_evt->ignore = 1; + } + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + break; + default: + break; + } +} + +static void leapraid_check_topo_del_evts( + struct leapraid_adapter *adapter, + struct leapraid_evt_data_sas_topo_change_list *evt_data) +{ + int reason_code; + u16 hdl; + int i; + + for (i = 0; i < evt_data->entry_num; i++) { + hdl = le16_to_cpu(evt_data->phy[i].attached_dev_hdl); + if (!hdl) + continue; + + reason_code = evt_data->phy[i].phy_status & + LEAPRAID_EVT_SAS_TOPO_RC_MASK; + if (reason_code == + LEAPRAID_EVT_SAS_TOPO_RC_TARG_NOT_RESPONDING) + leapraid_tgt_not_responding(adapter, hdl); + } + leapraid_topo_del_evts_process_exp_status(adapter, evt_data); +} + +static bool leapraid_async_evt_validate( + struct leapraid_adapter *adapter, + struct leapraid_evt_notify_rep *event_notify_rep) +{ + size_t msg_len; + size_t evt_sz; + size_t evt_avail; + u16 evt; + + msg_len = event_notify_rep->msg_len * sizeof(u32); + if (msg_len > LEAPRAID_REPLY_SIZE || + msg_len < offsetof(struct leapraid_evt_notify_rep, evt_data)) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid async event msg_len=%zu\n", + __func__, msg_len); + return false; + } + + evt_sz = le16_to_cpu(event_notify_rep->evt_data_len) * sizeof(u32); + evt_avail = msg_len - + offsetof(struct leapraid_evt_notify_rep, evt_data); + if (evt_sz > evt_avail) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid async event evt_data_len=%zu\n", + __func__, evt_sz); + return false; + } + + evt = le16_to_cpu(event_notify_rep->evt); + switch (evt) { + case LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE: + if (evt_sz < + sizeof(struct leapraid_evt_data_sas_dev_status_change)) + goto invalid_evt_sz; + break; + case LEAPRAID_EVT_IR_CHANGE: + if (evt_sz < sizeof(struct leapraid_evt_data_ir_change)) + goto invalid_evt_sz; + break; + case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST: + { + struct leapraid_evt_data_sas_topo_change_list *evt_data = + (void *)event_notify_rep->evt_data; + size_t hdr_sz; + + hdr_sz = + offsetof(struct leapraid_evt_data_sas_topo_change_list, + phy); + if (evt_sz < hdr_sz) + goto invalid_evt_sz; + + if (evt_data->entry_num > + (evt_sz - hdr_sz) / sizeof(evt_data->phy[0])) + goto invalid_evt_sz; + break; + } + case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE: + if (evt_sz < + sizeof(struct leapraid_evt_data_sas_enc_dev_status_change)) + goto invalid_evt_sz; + break; + default: + break; + } + + return true; + +invalid_evt_sz: + dev_warn(&adapter->pdev->dev, + "%s: Invalid async event size=%zu for evt=0x%x\n", + __func__, evt_sz, evt); + return false; +} + +static bool leapraid_async_process_evt( + struct leapraid_adapter *adapter, + struct leapraid_evt_notify_rep *event_notify_rep) +{ + u16 evt = le16_to_cpu(event_notify_rep->evt); + bool exit_flag = false; + + if (adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return true; + + switch (evt) { + case LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE: + leapraid_sas_dev_stat_chg_evt( + adapter, + (struct leapraid_evt_data_sas_dev_status_change + *)event_notify_rep->evt_data); + break; + case LEAPRAID_EVT_IR_CHANGE: + leapraid_check_ir_change_evt( + adapter, + (struct leapraid_evt_data_ir_change + *)event_notify_rep->evt_data); + break; + case LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST: + leapraid_check_topo_del_evts( + adapter, + (struct leapraid_evt_data_sas_topo_change_list + *)event_notify_rep->evt_data); + if (adapter->access_ctrl.shost_recovering) { + exit_flag = true; + return exit_flag; + } + break; + case LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE: + break; + default: + exit_flag = true; + return exit_flag; + } + + return exit_flag; +} + +static void leapraid_async_evt_cb_enqueue( + struct leapraid_adapter *adapter, + struct leapraid_evt_notify_rep *evt_notify_rep) +{ + struct leapraid_fw_evt_work *fw_evt; + u16 evt_sz; + + fw_evt = leapraid_alloc_fw_evt_work(); + if (!fw_evt) + return; + + evt_sz = le16_to_cpu(evt_notify_rep->evt_data_len) * sizeof(u32); + fw_evt->evt_data = kmemdup(evt_notify_rep->evt_data, + evt_sz, GFP_ATOMIC); + if (!fw_evt->evt_data) { + leapraid_fw_evt_put(fw_evt); + return; + } + fw_evt->adapter = adapter; + fw_evt->evt_type = le16_to_cpu(evt_notify_rep->evt); + leapraid_fw_evt_add(adapter, fw_evt); + leapraid_fw_evt_put(fw_evt); +} + +static void leapraid_async_evt_cb(struct leapraid_adapter *adapter, + u8 msix_index, u32 rep_paddr) +{ + struct leapraid_evt_notify_rep *evt_notify_rep; + + if (adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return; + + evt_notify_rep = leapraid_get_reply_vaddr(adapter, rep_paddr); + if (unlikely(!evt_notify_rep)) + return; + + if (!leapraid_async_evt_validate(adapter, evt_notify_rep)) + return; + + if (leapraid_async_process_evt(adapter, evt_notify_rep)) + return; + + leapraid_async_evt_cb_enqueue(adapter, evt_notify_rep); +} + +static void leapraid_handle_async_event(struct leapraid_adapter *adapter, + u8 msix_index, u32 reply) +{ + struct leapraid_evt_notify_rep *leap_mpi_rep = + leapraid_get_reply_vaddr(adapter, reply); + + if (!leap_mpi_rep) + return; + + if (leap_mpi_rep->func != LEAPRAID_FUNC_EVENT_NOTIFY) + return; + + leapraid_async_evt_cb(adapter, msix_index, reply); +} + +void leapraid_async_turn_on_led(struct leapraid_adapter *adapter, u16 handle) +{ + struct leapraid_fw_evt_work *fw_event; + + fw_event = leapraid_alloc_fw_evt_work(); + if (!fw_event) + return; + + fw_event->dev_handle = handle; + fw_event->adapter = adapter; + fw_event->evt_type = LEAPRAID_EVT_TURN_ON_PFA_LED; + leapraid_fw_evt_add(adapter, fw_event); + leapraid_fw_evt_put(fw_event); +} + +static void leapraid_hardreset_barrier(struct leapraid_adapter *adapter) +{ + struct leapraid_fw_evt_work *fw_event; + + fw_event = leapraid_alloc_fw_evt_work(); + if (!fw_event) + return; + + fw_event->adapter = adapter; + fw_event->evt_type = LEAPRAID_EVT_REMOVE_DEAD_DEV; + leapraid_fw_evt_add(adapter, fw_event); + leapraid_fw_evt_put(fw_event); +} + +static void leapraid_scan_dev_complete(struct leapraid_adapter *adapter) +{ + struct leapraid_fw_evt_work *fw_evt; + + fw_evt = leapraid_alloc_fw_evt_work(); + if (!fw_evt) + return; + + fw_evt->evt_type = LEAPRAID_EVT_SCAN_DEV_DONE; + fw_evt->adapter = adapter; + leapraid_fw_evt_add(adapter, fw_evt); + leapraid_fw_evt_put(fw_evt); +} + +static void leapraid_handle_scan_cb(struct leapraid_adapter *adapter, + struct leapraid_driver_cmd *cmd, + struct leapraid_rep *rep) +{ + u16 status; + + cmd->status &= ~LEAPRAID_CMD_PENDING; + + status = le16_to_cpu(rep->adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (status != LEAPRAID_ADAPTER_STATUS_SUCCESS) + adapter->scan_dev_desc.scan_dev_failed = 1; + + if (!cmd->async_scan_dev) { + complete(&cmd->done); + return; + } + + if (status == LEAPRAID_ADAPTER_STATUS_SUCCESS) + leapraid_scan_dev_complete(adapter); + else + adapter->scan_dev_desc.scan_start_failed = status; +} + +static void leapraid_handle_ctl_cb(struct leapraid_adapter *adapter, + struct leapraid_rep *rep, + u16 taskid) +{ + struct leapraid_scsiio_rep *scsiio_reply; + + if (rep->function != LEAPRAID_FUNC_SCSIIO && + rep->function != LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH) + return; + + scsiio_reply = (struct leapraid_scsiio_rep *)rep; + if (!(scsiio_reply->scsi_state & + LEAPRAID_SCSI_STATE_AUTOSENSE_VALID)) + return; + + memcpy(&adapter->driver_cmds.ctl_cmd.sense, + leapraid_get_sense_buffer(adapter, taskid), + min_t(u32, SCSI_SENSE_BUFFERSIZE, + le32_to_cpu(scsiio_reply->sense_count))); +} + +static bool leapraid_driver_cmds_done(struct leapraid_adapter *adapter, + u16 taskid, u8 msix_index, + u32 rep_paddr, u8 cb_idx) +{ + struct leapraid_rep *leap_mpi_rep = + leapraid_get_reply_vaddr(adapter, rep_paddr); + struct leapraid_driver_cmd *sp_cmd, *_sp_cmd = NULL; + u8 reply_len; + + list_for_each_entry(sp_cmd, &adapter->driver_cmds.special_cmd_list, + list) + if (cb_idx == sp_cmd->cb_idx) { + _sp_cmd = sp_cmd; + break; + } + + if (WARN_ON(!_sp_cmd)) + return true; + if (WARN_ON(_sp_cmd->status == LEAPRAID_CMD_NOT_USED)) + return true; + if (WARN_ON(taskid != _sp_cmd->hp_taskid && + taskid != _sp_cmd->taskid && + taskid != _sp_cmd->inter_taskid)) + return true; + _sp_cmd->status |= LEAPRAID_CMD_DONE; + if (leap_mpi_rep) { + reply_len = leap_mpi_rep->msg_len * sizeof(u32); + if (reply_len > LEAPRAID_REPLY_SIZE) + reply_len = LEAPRAID_REPLY_SIZE; + memcpy(&_sp_cmd->reply, leap_mpi_rep, reply_len); + _sp_cmd->status |= LEAPRAID_CMD_REPLY_VALID; + + if (_sp_cmd->cb_idx == LEAPRAID_SCAN_DEV_CB_IDX) { + leapraid_handle_scan_cb(adapter, _sp_cmd, + leap_mpi_rep); + return true; + } + + if (_sp_cmd->cb_idx == LEAPRAID_CTL_CB_IDX) + leapraid_handle_ctl_cb(adapter, leap_mpi_rep, taskid); + } + + _sp_cmd->status &= ~LEAPRAID_CMD_PENDING; + complete(&_sp_cmd->done); + + return true; +} + +static void leapraid_complete_task(struct leapraid_adapter *adapter, + u16 taskid, u8 msix_idx, u32 rep) +{ + bool scsiio_task = taskid <= adapter->shost->can_queue; + + if (scsiio_task) { + if (leapraid_scsiio_done(adapter, taskid, msix_idx, rep)) + leapraid_free_taskid(adapter, taskid); + return; + } + + if (leapraid_driver_cmds_done(adapter, taskid, msix_idx, rep, + leapraid_get_cb_idx(adapter, taskid))) + leapraid_free_taskid(adapter, taskid); +} + +static void leapraid_request_descript_handler( + struct leapraid_adapter *adapter, + union leapraid_rep_desc_union *rpf, + u8 req_desc_type, u8 msix_idx) +{ + u32 rep; + u16 taskid; + + rep = 0; + taskid = le16_to_cpu(rpf->dflt_rep.taskid); + switch (req_desc_type) { + case LEAPRAID_RPY_DESC_FLG_FP_SCSI_IO_SUCCESS: + case LEAPRAID_RPY_DESC_FLG_SCSI_IO_SUCCESS: + leapraid_complete_task(adapter, taskid, msix_idx, 0); + break; + case LEAPRAID_RPY_DESC_FLG_ADDRESS_REPLY: + rep = le32_to_cpu(rpf->addr_rep.rep_frame_addr); + if (rep > (u32)adapter->mem_desc.rep_msg_dma + + adapter->adapter_attr.rep_msg_qd * + LEAPRAID_REPLY_SIZE || + rep < (u32)adapter->mem_desc.rep_msg_dma) + rep = 0; + + if (taskid) + leapraid_complete_task(adapter, taskid, msix_idx, rep); + else + leapraid_handle_async_event(adapter, msix_idx, rep); + + if (!rep) + return; + + adapter->rep_msg_host_idx = + (adapter->rep_msg_host_idx == + (adapter->adapter_attr.rep_msg_qd - 1)) ? + 0 : adapter->rep_msg_host_idx + 1; + adapter->mem_desc.rep_msg_addr[adapter->rep_msg_host_idx] = + cpu_to_le32(rep); + wmb(); /* Make sure that all write ops are in order */ + writel(adapter->rep_msg_host_idx, + &adapter->iomem_base->rep_msg_host_idx); + + break; + default: + break; + } +} + +int leapraid_rep_queue_handler(struct leapraid_rq *rq) +{ + struct leapraid_adapter *adapter = rq->adapter; + union leapraid_rep_desc_union *rep_desc; + u8 req_desc_type; + u64 finish_cmds; + u8 msix_idx; + + msix_idx = rq->msix_idx; + finish_cmds = 0; + if (!atomic_add_unless(&rq->busy, LEAPRAID_BUSY_LIMIT, + LEAPRAID_BUSY_LIMIT)) + return finish_cmds; + + rep_desc = &rq->rep_desc[rq->rep_post_host_idx]; + req_desc_type = rep_desc->dflt_rep.rep_flg & + LEAPRAID_RPY_DESC_FLG_TYPE_MASK; + if (req_desc_type == LEAPRAID_RPY_DESC_FLG_UNUSED) { + atomic_dec(&rq->busy); + return finish_cmds; + } + + for (;;) { + if (rep_desc->u.low == UINT_MAX || + rep_desc->u.high == UINT_MAX) + break; + + leapraid_request_descript_handler(adapter, rep_desc, + req_desc_type, msix_idx); + dev_dbg(&adapter->pdev->dev, + "LEAPRAID_SCSIIO: Handled Desc taskid %d, msix %d\n", + rep_desc->dflt_rep.taskid, msix_idx); + rep_desc->words = cpu_to_le64(ULLONG_MAX); + rq->rep_post_host_idx = + (rq->rep_post_host_idx == + (adapter->adapter_attr.rep_desc_qd - + LEAPRAID_BUSY_LIMIT)) ? + 0 : rq->rep_post_host_idx + 1; + req_desc_type = + rq->rep_desc[rq->rep_post_host_idx].dflt_rep.rep_flg & + LEAPRAID_RPY_DESC_FLG_TYPE_MASK; + finish_cmds++; + if (req_desc_type == LEAPRAID_RPY_DESC_FLG_UNUSED) + break; + rep_desc = rq->rep_desc + rq->rep_post_host_idx; + } + + if (!finish_cmds) { + atomic_dec(&rq->busy); + return finish_cmds; + } + + wmb(); /* Make sure that all write ops are in order */ + writel(rq->rep_post_host_idx | ((msix_idx & LEAPRAID_MSIX_GROUP_MASK) << + LEAPRAID_RPHI_MSIX_IDX_SHIFT), + &adapter->iomem_base->rep_post_reg_idx[ + msix_idx / LEAPRAID_MSIX_GROUP_SIZE].idx); + atomic_dec(&rq->busy); + return finish_cmds; +} + +static irqreturn_t leapraid_irq_handler(int irq, void *bus_id) +{ + struct leapraid_rq *rq = bus_id; + struct leapraid_adapter *adapter = rq->adapter; + + dev_dbg(&adapter->pdev->dev, + "LEAPRAID_SCSIIO: Receive a interrupt, irq %d msix %d\n", + irq, rq->msix_idx); + + if (adapter->mask_int) + return IRQ_NONE; + + return (leapraid_rep_queue_handler(rq) > 0 ? + IRQ_HANDLED : IRQ_NONE); +} + +void leapraid_sync_irqs(struct leapraid_adapter *adapter, bool poll) +{ + struct leapraid_int_rq *int_rq; + struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; + unsigned int i; + + if (!adapter->notification_desc.msix_enable) + return; + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return; + + for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { + int_rq = &adapter->notification_desc.int_rqs[i]; + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return; + + if (int_rq->rq.msix_idx == 0) + continue; + + synchronize_irq(pci_irq_vector(adapter->pdev, + int_rq->rq.msix_idx)); + if (poll) + leapraid_rep_queue_handler(&int_rq->rq); + } + + for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { + blk_mq_poll_rq = + &adapter->notification_desc.blk_mq_poll_rqs[i]; + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) + return; + + if (blk_mq_poll_rq->rq.msix_idx == 0) + continue; + + leapraid_rep_queue_handler(&blk_mq_poll_rq->rq); + } +} + +static void leapraid_sync_irqs_for_cleanup(struct leapraid_adapter *adapter) +{ + struct leapraid_notification_desc *desc = &adapter->notification_desc; + struct leapraid_int_rq *int_rq; + unsigned int i; + + if (!adapter->mask_int && leapraid_pci_active(adapter)) + leapraid_mask_int(adapter); + + for (i = 0; i < desc->int_rqs_allocated; i++) { + int_rq = &desc->int_rqs[i]; + synchronize_irq(pci_irq_vector(adapter->pdev, + int_rq->rq.msix_idx)); + } +} + +void leapraid_mq_polling_pause(struct leapraid_adapter *adapter) +{ + struct leapraid_notification_desc *desc; + int iopoll_q_count; + int qid; + + desc = &adapter->notification_desc; + iopoll_q_count = adapter->adapter_attr.rq_cnt - desc->iopoll_qdex; + + for (qid = 0; qid < iopoll_q_count; qid++) + atomic_set(&desc->blk_mq_poll_rqs[qid].pause, 1); + + for (qid = 0; qid < iopoll_q_count; qid++) { + while (atomic_read(&desc->blk_mq_poll_rqs[qid].busy)) { + cpu_relax(); + udelay(LEAPRAID_IO_POLL_DELAY_US); + } + } +} + +void leapraid_mq_polling_resume(struct leapraid_adapter *adapter) +{ + struct leapraid_notification_desc *desc; + int iopoll_q_count; + int qid; + + desc = &adapter->notification_desc; + iopoll_q_count = adapter->adapter_attr.rq_cnt - desc->iopoll_qdex; + + for (qid = 0; qid < iopoll_q_count; qid++) + atomic_set(&desc->blk_mq_poll_rqs[qid].pause, 0); +} + +static int leapraid_unlock_host_diag(struct leapraid_adapter *adapter, + u32 *host_diag) +{ + const u32 unlock_seq[] = { + LEAPRAID_WRSEQ_FLUSH_KEY_VALUE, + LEAPRAID_WRSEQ_1ST_KEY_VALUE, + LEAPRAID_WRSEQ_2ND_KEY_VALUE, + LEAPRAID_WRSEQ_3RD_KEY_VALUE, + LEAPRAID_WRSEQ_4TH_KEY_VALUE, + LEAPRAID_WRSEQ_5TH_KEY_VALUE, + LEAPRAID_WRSEQ_6TH_KEY_VALUE + }; + + const int max_retries = LEAPRAID_UNLOCK_RETRY_LIMIT; + int retry = 0; + unsigned int i; + + *host_diag = 0; + while (retry++ <= max_retries) { + for (i = 0; i < ARRAY_SIZE(unlock_seq); i++) + writel(unlock_seq[i], &adapter->iomem_base->ws); + + msleep(LEAPRAID_UNLOCK_SLEEP_MS); + + *host_diag = leapraid_readl(&adapter->iomem_base->host_diag); + if (*host_diag & LEAPRAID_DIAG_WRITE_ENABLE) + return 0; + } + + dev_err(&adapter->pdev->dev, "Try host reset timeout!\n"); + return -EFAULT; +} + +static int leapraid_host_diag_reset(struct leapraid_adapter *adapter) +{ + u32 host_diag; + + pci_cfg_access_lock(adapter->pdev); + + mutex_lock(&adapter->reset_desc.host_diag_mutex); + if (leapraid_unlock_host_diag(adapter, &host_diag)) + goto out_cleanup; + + writel(host_diag | LEAPRAID_DIAG_RESET, + &adapter->iomem_base->host_diag); + + msleep(LEAPRAID_MSLEEP_EXTRA_LONG_MS); + msleep(LEAPRAID_MSLEEP_NORMAL_MS); + + host_diag = leapraid_readl(&adapter->iomem_base->host_diag); + if (host_diag == LEAPRAID_INVALID_HOST_DIAG_VAL || + host_diag & LEAPRAID_DIAG_RESET) + goto out_cleanup; + + writel(0x0, &adapter->iomem_base->ws); + mutex_unlock(&adapter->reset_desc.host_diag_mutex); + if (!leapraid_wait_adapter_ready(adapter)) + goto out_failed; + + pci_cfg_access_unlock(adapter->pdev); + return 0; + +out_cleanup: + mutex_unlock(&adapter->reset_desc.host_diag_mutex); +out_failed: + pci_cfg_access_unlock(adapter->pdev); + dev_err(&adapter->pdev->dev, "Host diag failed\n"); + return -EFAULT; +} + +static int leapraid_find_matching_port( + struct leapraid_card_port *card_port_table, + u8 count, u8 port_id, u64 sas_addr) +{ + int i; + + for (i = 0; i < count; i++) + if (card_port_table[i].port_id == port_id && + card_port_table[i].sas_address == sas_addr) + return i; + + return LEAPRAID_INVALID_INDEX; +} + +static u8 leapraid_fill_card_port_table( + struct leapraid_adapter *adapter, + struct leapraid_sas_io_unit_p0 *sas_iounit_p0, + struct leapraid_card_port *new_card_port_table) +{ + u8 port_entry_num = 0, port_id; + u16 attached_hdl; + u64 attached_sas_addr; + int i, idx; + + for (i = 0; i < adapter->dev_topo.card.phys_num; i++) { + if (sas_iounit_p0->phy_info[i].neg_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT < + LEAPRAID_SAS_NEG_LINK_RATE_1_5) + continue; + + attached_hdl = le16_to_cpu(sas_iounit_p0->phy_info[i] + .attached_dev_hdl); + if (leapraid_get_sas_address(adapter, + attached_hdl, + &attached_sas_addr) != 0) + continue; + + port_id = sas_iounit_p0->phy_info[i].port; + + idx = leapraid_find_matching_port(new_card_port_table, + port_entry_num, + port_id, + attached_sas_addr); + if (idx >= 0) { + new_card_port_table[idx].phy_mask |= BIT(i); + } else { + new_card_port_table[port_entry_num].port_id = port_id; + new_card_port_table[port_entry_num].phy_mask = BIT(i); + new_card_port_table[port_entry_num].sas_address = + attached_sas_addr; + port_entry_num++; + } + } + + return port_entry_num; +} + +static u8 leapraid_set_new_card_port_table_after_reset( + struct leapraid_adapter *adapter, + struct leapraid_card_port *new_card_port_table) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_io_unit_p0 *sas_iounit_p0; + u8 port_entry_num = 0; + u16 sz; + + sz = offsetof(struct leapraid_sas_io_unit_p0, phy_info) + + (adapter->dev_topo.card.phys_num * + sizeof(struct leapraid_sas_io_unit0_phy_info)); + sas_iounit_p0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_p0) + return port_entry_num; + + cfgp1.size = sz; + if (leapraid_op_config_page(adapter, sas_iounit_p0, cfgp1, cfgp2, + GET_SAS_IOUNIT_PG0) != 0) + goto out_free; + + port_entry_num = leapraid_fill_card_port_table(adapter, + sas_iounit_p0, + new_card_port_table); +out_free: + kfree(sas_iounit_p0); + return port_entry_num; +} + +static void leapraid_update_existing_port(struct leapraid_adapter *adapter, + struct leapraid_card_port *new_table, + int entry_idx, int port_entry_num) +{ + struct leapraid_card_port *matched_card_port; + int matched_code; + int count, lcount = 0; + u64 sas_addr; + int i; + + matched_code = leapraid_check_card_port(adapter, + &new_table[entry_idx], + &matched_card_port, + &count); + + if (!matched_card_port) + return; + + if (matched_code == SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS || + matched_code == SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS) { + leapraid_add_or_del_phys_from_existing_port(adapter, + matched_card_port, + new_table, + entry_idx, + port_entry_num); + } else if (matched_code == SAME_ADDR_ONLY) { + sas_addr = new_table[entry_idx].sas_address; + for (i = 0; i < port_entry_num; i++) + if (new_table[i].sas_address == sas_addr) + lcount++; + + if (count > 1 || lcount > 1) + return; + + leapraid_add_or_del_phys_from_existing_port(adapter, + matched_card_port, + new_table, + entry_idx, + port_entry_num); + } + + if (matched_card_port->port_id != new_table[entry_idx].port_id) + matched_card_port->port_id = new_table[entry_idx].port_id; + + matched_card_port->flg &= ~LEAPRAID_CARD_PORT_FLG_DIRTY; + matched_card_port->phy_mask = new_table[entry_idx].phy_mask; +} + +static void leapraid_update_card_port_after_reset( + struct leapraid_adapter *adapter) +{ + struct leapraid_card_port *new_card_port_table; + struct leapraid_card_port *matched_card_port; + u8 port_entry_num; + u8 nr_phys; + int i; + + if (leapraid_get_adapter_phys(adapter, &nr_phys) || !nr_phys) + return; + + if (!adapter->dev_topo.card.card_phy) { + adapter->dev_topo.card.card_phy = + kcalloc(nr_phys, sizeof(struct leapraid_card_phy), + GFP_KERNEL); + if (!adapter->dev_topo.card.card_phy) + return; + } + + adapter->dev_topo.card.phys_num = nr_phys; + + new_card_port_table = kcalloc(adapter->dev_topo.card.phys_num, + sizeof(struct leapraid_card_port), + GFP_KERNEL); + if (!new_card_port_table) + return; + + port_entry_num = + leapraid_set_new_card_port_table_after_reset( + adapter, + new_card_port_table); + if (!port_entry_num) + goto out_free_port_table; + + list_for_each_entry(matched_card_port, + &adapter->dev_topo.card_port_list, list) + matched_card_port->flg |= LEAPRAID_CARD_PORT_FLG_DIRTY; + + matched_card_port = NULL; + for (i = 0; i < port_entry_num; i++) + leapraid_update_existing_port(adapter, + new_card_port_table, + i, port_entry_num); +out_free_port_table: + kfree(new_card_port_table); +} + +static bool leapraid_is_valid_vphy( + struct leapraid_adapter *adapter, + struct leapraid_sas_io_unit_p0 *sas_io_unit_p0, + int phy_index) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_phy_p0 phy_p0; + u32 dev_info; + + if (sas_io_unit_p0->phy_info[phy_index].neg_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT < + LEAPRAID_SAS_NEG_LINK_RATE_1_5) + return false; + + dev_info = le32_to_cpu(sas_io_unit_p0->phy_info[phy_index] + .controller_phy_dev_info); + if (!(dev_info & LEAPRAID_DEVTYP_SEP)) + return false; + + cfgp1.phy_number = phy_index; + if (leapraid_op_config_page(adapter, &phy_p0, cfgp1, cfgp2, + GET_PHY_PG0)) + return false; + + if (!(le32_to_cpu(phy_p0.phy_info) & LEAPRAID_SAS_PHYINFO_VPHY)) + return false; + + return true; +} + +static void leapraid_update_vphy_binding(struct leapraid_adapter *adapter, + struct leapraid_card_port *card_port, + struct leapraid_vphy *vphy, + int phy_index, u8 may_new_port_id, + u64 attached_sas_addr) +{ + struct leapraid_card_port *may_new_card_port; + struct leapraid_sas_dev *sas_dev; + + may_new_card_port = leapraid_get_port_by_id(adapter, + may_new_port_id, + true); + if (!may_new_card_port) { + may_new_card_port = kzalloc_obj(*may_new_card_port); + if (!may_new_card_port) + return; + may_new_card_port->port_id = may_new_port_id; + dev_err(&adapter->pdev->dev, + "%s: New card port %p added, port=%d\n", + __func__, may_new_card_port, may_new_port_id); + list_add_tail(&may_new_card_port->list, + &adapter->dev_topo.card_port_list); + } + + if (card_port != may_new_card_port) { + if (!may_new_card_port->vphys_mask) + INIT_LIST_HEAD(&may_new_card_port->vphys_list); + may_new_card_port->vphys_mask |= BIT(phy_index); + card_port->vphys_mask &= ~BIT(phy_index); + list_move(&vphy->list, &may_new_card_port->vphys_list); + + sas_dev = leapraid_get_sas_dev_by_addr(adapter, + attached_sas_addr, + card_port); + if (sas_dev) { + sas_dev->card_port = may_new_card_port; + leapraid_sdev_put(sas_dev); + } + } + + if (may_new_card_port->flg & LEAPRAID_CARD_PORT_FLG_DIRTY) { + may_new_card_port->sas_address = 0; + may_new_card_port->phy_mask = 0; + may_new_card_port->flg &= ~LEAPRAID_CARD_PORT_FLG_DIRTY; + } + vphy->flg &= ~LEAPRAID_VPHY_FLG_DIRTY; +} + +static void leapraid_update_vphys_after_reset(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_io_unit_p0 *sas_iounit_p0; + struct leapraid_card_port *card_port, *card_port_next; + struct leapraid_vphy *vphy, *vphy_next; + u64 attached_sas_addr; + u16 sz; + u16 attached_hdl; + bool found = false; + u8 port_id; + int i; + + list_for_each_entry_safe(card_port, card_port_next, + &adapter->dev_topo.card_port_list, list) { + if (!card_port->vphys_mask) + continue; + + list_for_each_entry_safe(vphy, vphy_next, + &card_port->vphys_list, list) + vphy->flg |= LEAPRAID_VPHY_FLG_DIRTY; + } + + sz = offsetof(struct leapraid_sas_io_unit_p0, phy_info) + + (adapter->dev_topo.card.phys_num * + sizeof(struct leapraid_sas_io_unit0_phy_info)); + sas_iounit_p0 = kzalloc(sz, GFP_KERNEL); + if (!sas_iounit_p0) + return; + + cfgp1.size = sz; + if (leapraid_op_config_page(adapter, sas_iounit_p0, cfgp1, cfgp2, + GET_SAS_IOUNIT_PG0) != 0) + goto out_free; + + for (i = 0; i < adapter->dev_topo.card.phys_num; i++) { + if (!leapraid_is_valid_vphy(adapter, sas_iounit_p0, i)) + continue; + + attached_hdl = + le16_to_cpu(sas_iounit_p0->phy_info[i] + .attached_dev_hdl); + if (leapraid_get_sas_address(adapter, attached_hdl, + &attached_sas_addr) != 0) + continue; + + found = false; + card_port = NULL; + card_port_next = NULL; + list_for_each_entry_safe(card_port, card_port_next, + &adapter->dev_topo.card_port_list, + list) { + if (!card_port->vphys_mask) + continue; + + list_for_each_entry_safe(vphy, vphy_next, + &card_port->vphys_list, + list) { + if (!(vphy->flg & LEAPRAID_VPHY_FLG_DIRTY)) + continue; + + if (vphy->sas_address != attached_sas_addr) + continue; + + if (!(vphy->phy_mask & BIT(i))) + vphy->phy_mask = BIT(i); + + port_id = sas_iounit_p0->phy_info[i].port; + + leapraid_update_vphy_binding(adapter, + card_port, + vphy, + i, + port_id, + attached_sas_addr); + + found = true; + break; + } + if (found) + break; + } + } +out_free: + kfree(sas_iounit_p0); +} + +static void leapraid_mark_all_dev_deleted(struct leapraid_adapter *adapter) +{ + struct leapraid_sdev_priv *sdev_priv; + struct scsi_device *sdev; + + shost_for_each_device(sdev, adapter->shost) { + sdev_priv = sdev->hostdata; + if (sdev_priv && sdev_priv->starget_priv) + sdev_priv->starget_priv->deleted = 1; + } +} + +static void leapraid_free_enc_list(struct leapraid_adapter *adapter) +{ + struct leapraid_enc_node *enc_dev, *enc_dev_next; + LIST_HEAD(free_list); + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); + list_splice_init(&adapter->dev_topo.enc_list, &free_list); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); + + list_for_each_entry_safe(enc_dev, enc_dev_next, &free_list, list) { + list_del(&enc_dev->list); + kfree(enc_dev); + } +} + +static void leapraid_rebuild_enc_list_after_reset( + struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_enc_node *enc_node; + u16 enc_hdl; + unsigned long flags; + int rc; + + leapraid_free_enc_list(adapter); + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (enc_hdl = 0xFFFF; ; enc_hdl = le16_to_cpu(enc_node->pg0.enc_hdl)) { + enc_node = kzalloc_obj(*enc_node); + if (!enc_node) + return; + + cfgp2.handle = enc_hdl; + rc = leapraid_op_config_page(adapter, &enc_node->pg0, cfgp1, + cfgp2, GET_SAS_ENCLOSURE_PG0); + if (rc) { + kfree(enc_node); + return; + } + + spin_lock_irqsave(&adapter->dev_topo.enc_lock, flags); + list_add_tail(&enc_node->list, &adapter->dev_topo.enc_list); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, flags); + } +} + +static void leapraid_mark_resp_sas_dev(struct leapraid_adapter *adapter, + struct leapraid_sas_dev_p0 *sas_dev_p0) +{ + struct leapraid_starget_priv *starget_priv; + struct leapraid_enc_node *enc_node = NULL; + struct leapraid_card_port *card_port; + struct leapraid_sas_dev *sas_dev; + struct scsi_target *starget; + unsigned long flags; + unsigned long enc_flags = 0; + u16 enc_hdl; + u64 enc_lid = 0; + + card_port = leapraid_get_port_by_id(adapter, sas_dev_p0->physical_port, + false); + enc_hdl = le16_to_cpu(sas_dev_p0->enc_hdl); + if (enc_hdl) { + spin_lock_irqsave(&adapter->dev_topo.enc_lock, enc_flags); + enc_node = leapraid_enc_find_by_hdl(adapter, enc_hdl); + if (enc_node) + enc_lid = le64_to_cpu(enc_node->pg0.enc_lid); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, enc_flags); + if (!enc_node) + dev_info(&adapter->pdev->dev, + "enc hdl 0x%04x has no matched enc dev\n", + enc_hdl); + } + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + list_for_each_entry(sas_dev, &adapter->dev_topo.sas_dev_list, list) { + if (sas_dev->sas_addr != le64_to_cpu(sas_dev_p0->sas_address) || + sas_dev->slot != le16_to_cpu(sas_dev_p0->slot) || + sas_dev->card_port != card_port) + continue; + + sas_dev->resp = 1; + starget = sas_dev->starget; + if (starget && starget->hostdata) { + starget_priv = starget->hostdata; + starget_priv->tm_busy = 0; + starget_priv->deleted = 0; + } else { + starget_priv = NULL; + } + + if (starget) { + starget_printk( + KERN_INFO, starget, + "dev: hdl=0x%04x saddr=0x%016llx port_id=%d\n", + sas_dev->hdl, + (unsigned long long)sas_dev->sas_addr, + sas_dev->card_port->port_id); + if (sas_dev->enc_hdl != 0) + starget_printk( + KERN_INFO, + starget, + "enc info: enc_lid=0x%016llx slot=%d\n", + (unsigned long long)sas_dev->enc_lid, + sas_dev->slot); + } + + if (le16_to_cpu(sas_dev_p0->flg) & + LEAPRAID_SAS_DEV_P0_FLG_ENC_LEVEL_VALID) { + sas_dev->enc_level = sas_dev_p0->enc_level; + memcpy(sas_dev->connector_name, + sas_dev_p0->connector_name, + LEAPRAID_SAS_DEV_P0_CON_NAME_LEN); + sas_dev->connector_name[ + LEAPRAID_SAS_DEV_P0_CON_NAME_LEN] = '\0'; + } else { + sas_dev->enc_level = 0; + sas_dev->connector_name[0] = '\0'; + } + + sas_dev->enc_hdl = enc_hdl; + sas_dev->enc_lid = enc_lid; + + if (sas_dev->hdl == le16_to_cpu(sas_dev_p0->dev_hdl)) + goto unlock; + + dev_info(&adapter->pdev->dev, + "hdl changed: 0x%04x -> 0x%04x\n", + sas_dev->hdl, sas_dev_p0->dev_hdl); + sas_dev->hdl = le16_to_cpu(sas_dev_p0->dev_hdl); + if (starget_priv) + starget_priv->hdl = le16_to_cpu(sas_dev_p0->dev_hdl); + + goto unlock; + } +unlock: + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); +} + +static void leapraid_search_resp_sas_dev(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_p0; + u32 device_info; + + if (list_empty(&adapter->dev_topo.sas_dev_list)) + return; + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (cfgp2.handle = 0xFFFF; + !leapraid_op_config_page(adapter, &sas_dev_p0, + cfgp1, cfgp2, GET_SAS_DEVICE_PG0); + cfgp2.handle = le16_to_cpu(sas_dev_p0.dev_hdl)) { + device_info = le32_to_cpu(sas_dev_p0.dev_info); + if (!(leapraid_is_end_dev(device_info))) + continue; + + leapraid_mark_resp_sas_dev(adapter, &sas_dev_p0); + } +} + +static void leapraid_mark_resp_raid_volume(struct leapraid_adapter *adapter, + u64 wwid, u16 hdl) +{ + struct leapraid_raid_volume *raid_volume; + struct leapraid_starget_priv *starget_priv = NULL; + struct scsi_target *starget; + unsigned long flags; + u64 volume_wwid; + u16 old_hdl; + + raid_volume = leapraid_raid_volume_find_by_wwid(adapter, wwid); + if (!raid_volume) + return; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + if (!raid_volume->starget) { + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, + flags); + leapraid_raid_volume_put(raid_volume); + return; + } + + starget = raid_volume->starget; + if (starget->hostdata) { + starget_priv = starget->hostdata; + starget_priv->deleted = 0; + } + + raid_volume->resp = 1; + volume_wwid = raid_volume->wwid; + old_hdl = raid_volume->hdl; + if (old_hdl != hdl) { + raid_volume->hdl = hdl; + if (starget_priv) + starget_priv->hdl = hdl; + } + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + starget_printk(KERN_INFO, starget, + "raid volume: hdl=0x%04x, wwid=0x%016llx\n", + hdl, (unsigned long long)volume_wwid); + if (old_hdl != hdl) + dev_info(&adapter->pdev->dev, + "hdl changed: 0x%04x -> 0x%04x\n", + old_hdl, hdl); + + leapraid_raid_volume_put(raid_volume); +} + +static void leapraid_search_resp_raid_volume(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_1 cfgp1_extra = {0}; + union cfg_param_2 cfgp2 = {0}; + union cfg_param_2 cfgp2_extra = {0}; + struct leapraid_raidvol_p1 raidvol_p1; + struct leapraid_raidvol_p0 raidvol_p0; + struct leapraid_raidpd_p0 raidpd_p0; + unsigned long flags; + u16 hdl; + u8 phys_disk_num; + bool is_empty; + + if (!adapter->adapter_attr.raid_support) + return; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + is_empty = list_empty(&adapter->dev_topo.raid_volume_list); + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + if (is_empty) + return; + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (hdl = 0xFFFF, cfgp2.handle = hdl; + !leapraid_op_config_page(adapter, &raidvol_p1, cfgp1, cfgp2, + GET_RAID_VOLUME_PG1); + cfgp2.handle = hdl) { + hdl = le16_to_cpu(raidvol_p1.dev_hdl); + cfgp1_extra.size = sizeof(struct leapraid_raidvol_p0); + cfgp2_extra.handle = hdl; + if (leapraid_op_config_page(adapter, &raidvol_p0, cfgp1_extra, + cfgp2_extra, GET_RAID_VOLUME_PG0)) + continue; + + if (raidvol_p0.volume_state == LEAPRAID_VOL_STATE_OPTIMAL || + raidvol_p0.volume_state == LEAPRAID_VOL_STATE_ONLINE || + raidvol_p0.volume_state == LEAPRAID_VOL_STATE_DEGRADED) + leapraid_mark_resp_raid_volume( + adapter, + le64_to_cpu(raidvol_p1.wwid), + hdl); + } + + memset(adapter->dev_topo.pd_hdls, 0, adapter->dev_topo.pd_hdls_sz); + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (phys_disk_num = 0xFF, cfgp2.form_specific = phys_disk_num; + !leapraid_op_config_page(adapter, &raidpd_p0, cfgp1, cfgp2, + GET_PHY_DISK_PG0); + cfgp2.form_specific = phys_disk_num) { + phys_disk_num = raidpd_p0.phys_disk_num; + hdl = le16_to_cpu(raidpd_p0.dev_hdl); + if (!hdl || + hdl > adapter->adapter_attr.features.max_dev_handle) + dev_warn(&adapter->pdev->dev, + "%s: Invalid device handle\n", __func__); + else + set_bit(hdl, adapter->dev_topo.pd_hdls); + } +} + +static void leapraid_mark_resp_exp(struct leapraid_adapter *adapter, + struct leapraid_exp_p0 *exp_pg0) +{ + struct leapraid_enc_node *enc_node = NULL; + struct leapraid_topo_node *topo_node_exp; + u16 enc_hdl = le16_to_cpu(exp_pg0->enc_hdl); + u64 sas_address = le64_to_cpu(exp_pg0->sas_address); + u16 hdl = le16_to_cpu(exp_pg0->dev_hdl); + u8 port_id = exp_pg0->physical_port; + struct leapraid_card_port *card_port = leapraid_get_port_by_id(adapter, + port_id, + false); + unsigned long flags; + unsigned long enc_flags = 0; + u64 enc_lid = 0; + int i; + + if (enc_hdl) { + spin_lock_irqsave(&adapter->dev_topo.enc_lock, enc_flags); + enc_node = leapraid_enc_find_by_hdl(adapter, enc_hdl); + if (enc_node) + enc_lid = le64_to_cpu(enc_node->pg0.enc_lid); + spin_unlock_irqrestore(&adapter->dev_topo.enc_lock, enc_flags); + } + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + list_for_each_entry(topo_node_exp, &adapter->dev_topo.exp_list, list) { + if (topo_node_exp->sas_address != sas_address || + topo_node_exp->card_port != card_port) + continue; + + topo_node_exp->resp = 1; + topo_node_exp->enc_hdl = enc_hdl; + topo_node_exp->enc_lid = enc_lid; + if (topo_node_exp->hdl == hdl) + goto unlock; + + dev_info(&adapter->pdev->dev, + "hdl changed: 0x%04x -> 0x%04x\n", + topo_node_exp->hdl, hdl); + topo_node_exp->hdl = hdl; + for (i = 0; i < topo_node_exp->phys_num; i++) + topo_node_exp->card_phy[i].hdl = hdl; + goto unlock; + } +unlock: + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); +} + +static void leapraid_search_resp_exp(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_exp_p0 exp_p0; + u64 sas_address; + u16 hdl; + u8 port; + + if (list_empty(&adapter->dev_topo.exp_list)) + return; + + cfgp1.form = LEAPRAID_SAS_CFG_PGAD_GET_NEXT_LOOP; + for (hdl = 0xFFFF, cfgp2.handle = hdl; + !leapraid_op_config_page(adapter, &exp_p0, cfgp1, cfgp2, + GET_SAS_EXPANDER_PG0); + cfgp2.handle = hdl) { + hdl = le16_to_cpu(exp_p0.dev_hdl); + sas_address = le64_to_cpu(exp_p0.sas_address); + port = exp_p0.physical_port; + + dev_dbg(&adapter->pdev->dev, + "exp detected: hdl=0x%04x, sas=0x%016llx, port=%u", + hdl, (unsigned long long)sas_address, + adapter->adapter_attr.enable_mp ? port : + LEAPRAID_DISABLE_MP_PORT_ID); + leapraid_mark_resp_exp(adapter, &exp_p0); + } +} + +void leapraid_wait_cmds_done(struct leapraid_adapter *adapter) +{ + struct leapraid_io_req_tracker *io_req_tracker; + unsigned long flags; + u16 i; + + adapter->reset_desc.pending_io_cnt = 0; + if (!leapraid_pci_active(adapter)) { + dev_err(&adapter->pdev->dev, + "%s %s: PCI error, device reset or unplugged!\n", + adapter->adapter_attr.name, __func__); + return; + } + + if (leapraid_get_adapter_state(adapter) != LEAPRAID_DB_OPERATIONAL) + return; + + spin_lock_irqsave(&adapter->dynamic_task_desc.task_lock, flags); + for (i = 1; i <= adapter->shost->can_queue; i++) { + io_req_tracker = leapraid_get_io_tracker_from_taskid(adapter, + i); + if (io_req_tracker && io_req_tracker->taskid != 0 && + io_req_tracker->scmd) + adapter->reset_desc.pending_io_cnt++; + } + spin_unlock_irqrestore(&adapter->dynamic_task_desc.task_lock, flags); + + if (!adapter->reset_desc.pending_io_cnt) + return; + + wait_event_timeout(adapter->reset_desc.reset_wait_queue, + adapter->reset_desc.pending_io_cnt == 0, + LEAPRAID_IO_CMD_TIMEOUT * HZ); +} + +int leapraid_hard_reset_handler(struct leapraid_adapter *adapter, + enum reset_type type) +{ + unsigned long flags; + bool wake = false; + int rc; + + mutex_lock(&adapter->reset_desc.adapter_reset_mutex); + + if (adapter->access_ctrl.shost_recover_async) { + rc = adapter->reset_desc.adapter_reset_results; + dev_info(&adapter->pdev->dev, + "Skip nested hard reset, async evt running, rc=%d\n", + rc); + mutex_unlock(&adapter->reset_desc.adapter_reset_mutex); + return rc; + } + + if (!leapraid_pci_active(adapter)) { + if (leapraid_pci_removed(adapter)) { + dev_info(&adapter->pdev->dev, + "pci_dev removed, pause poll, clean cmds\n"); + leapraid_mq_polling_pause(adapter); + leapraid_clean_active_scsi_cmds(adapter); + leapraid_mq_polling_resume(adapter); + } + dev_err(&adapter->pdev->dev, "PCIe unavailable!\n"); + mutex_unlock(&adapter->reset_desc.adapter_reset_mutex); + return -ENXIO; + } + + dev_info(&adapter->pdev->dev, "Starting hard reset\n"); + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + adapter->access_ctrl.shost_recovering = 1; + adapter->access_ctrl.shost_recover_async = 1; + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); + + leapraid_wait_cmds_done(adapter); + leapraid_mask_int(adapter); + leapraid_mq_polling_pause(adapter); + rc = leapraid_make_adapter_ready(adapter, type); + if (rc) { + dev_err(&adapter->pdev->dev, + "Failed to make adapter ready, rc=%d\n", rc); + goto out_cleanup; + } + + rc = leapraid_fw_log_init(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, "Firmware log init failed\n"); + goto out_cleanup; + } + + leapraid_clean_active_cmds(adapter); + if (adapter->scan_dev_desc.driver_loading && + adapter->scan_dev_desc.scan_dev_failed) { + dev_err(&adapter->pdev->dev, + "Previous device scan failed or driver loading\n"); + adapter->access_ctrl.host_removing = 1; + rc = -EFAULT; + goto out_cleanup; + } + + rc = leapraid_make_adapter_available(adapter); + if (!rc) { + dev_info(&adapter->pdev->dev, + "Adapter is now available, rebuilding topology\n"); + if (adapter->adapter_attr.enable_mp) { + leapraid_update_card_port_after_reset(adapter); + leapraid_update_vphys_after_reset(adapter); + } + leapraid_mark_all_dev_deleted(adapter); + leapraid_rebuild_enc_list_after_reset(adapter); + leapraid_search_resp_sas_dev(adapter); + leapraid_search_resp_raid_volume(adapter); + leapraid_search_resp_exp(adapter); + leapraid_hardreset_barrier(adapter); + } +out_cleanup: + if (rc) + dev_err(&adapter->pdev->dev, "Hard reset failed\n"); + + spin_lock_irqsave(&adapter->reset_desc.adapter_reset_lock, flags); + adapter->reset_desc.adapter_reset_results = rc; + adapter->access_ctrl.shost_recovering = 0; + wake_up(&adapter->access_ctrl.recovery_waitq); + if (rc) { + adapter->access_ctrl.shost_recover_async = 0; + wake = true; + } + spin_unlock_irqrestore(&adapter->reset_desc.adapter_reset_lock, flags); + if (wake) + wake_up(&adapter->access_ctrl.shost_recover_wq); + + adapter->reset_desc.reset_cnt++; + mutex_unlock(&adapter->reset_desc.adapter_reset_mutex); + + if (rc) + leapraid_clean_active_scsi_cmds(adapter); + leapraid_mq_polling_resume(adapter); + + return rc; +} + +static int leapraid_get_adapter_features(struct leapraid_adapter *adapter) +{ + struct leapraid_adapter_features_req leap_mpi_req; + struct leapraid_adapter_features_rep leap_mpi_rep; + u8 fw_major, fw_minor, fw_build, fw_release; + u32 req_sz; + u32 rep_sz; + u32 db; + int r; + + db = leapraid_readl(&adapter->iomem_base->db); + if (db & LEAPRAID_DB_USED || + (db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) { + dev_err(&adapter->pdev->dev, + "%s: Doorbell used or fault\n", __func__); + return -EFAULT; + } + + if ((db & LEAPRAID_DB_MASK) != LEAPRAID_DB_READY && + (db & LEAPRAID_DB_MASK) != LEAPRAID_DB_OPERATIONAL && + !leapraid_wait_adapter_ready(adapter)) { + dev_err(&adapter->pdev->dev, + "%s: adapter not ready\n", __func__); + return -EFAULT; + } + + req_sz = sizeof(struct leapraid_adapter_features_req); + rep_sz = sizeof(struct leapraid_adapter_features_rep); + memset(&leap_mpi_req, 0, req_sz); + memset(&leap_mpi_rep, 0, rep_sz); + leap_mpi_req.func = LEAPRAID_FUNC_GET_ADAPTER_FEATURES; + r = leapraid_handshake_func(adapter, + req_sz, + (u32 *)&leap_mpi_req, + rep_sz, + (u16 *)&leap_mpi_rep); + if (r) { + dev_err(&adapter->pdev->dev, + "%s %s: Handshake failed, r=%d\n", + adapter->adapter_attr.name, __func__, r); + return r; + } + + memset(&adapter->adapter_attr.features, 0, + sizeof(struct leapraid_adapter_features)); + adapter->adapter_attr.features.req_slot = + le16_to_cpu(leap_mpi_rep.req_slot); + adapter->adapter_attr.features.hp_slot = + le16_to_cpu(leap_mpi_rep.hp_slot); + adapter->adapter_attr.features.adapter_caps = + le32_to_cpu(leap_mpi_rep.adapter_caps); + adapter->adapter_attr.features.max_msix_vectors = + leap_mpi_rep.max_msix_vectors; + adapter->adapter_attr.features.max_volumes = + leap_mpi_rep.max_volumes; + if (!adapter->adapter_attr.features.max_volumes) + adapter->adapter_attr.features.max_volumes = + LEAPRAID_MAX_VOLUMES_DEFAULT; + adapter->adapter_attr.features.max_dev_handle = + le16_to_cpu(leap_mpi_rep.max_dev_hdl); + if (!adapter->adapter_attr.features.max_dev_handle) + adapter->adapter_attr.features.max_dev_handle = + LEAPRAID_MAX_DEV_HANDLE_DEFAULT; + adapter->adapter_attr.features.min_dev_handle = + le16_to_cpu(leap_mpi_rep.min_dev_hdl); + if (adapter->adapter_attr.features.adapter_caps & + LEAPRAID_ADAPTER_FEATURES_CAP_INTEGRATED_RAID) + adapter->adapter_attr.raid_support = 1; + adapter->adapter_attr.wideport_max_queue_depth = + le16_to_cpu(leap_mpi_rep.sas_wide_max_qdepth) ? + le16_to_cpu(leap_mpi_rep.sas_wide_max_qdepth) : + LEAPRAID_SAS_QUEUE_DEPTH; + adapter->adapter_attr.narrowport_max_queue_depth = + le16_to_cpu(leap_mpi_rep.sas_narrow_max_qdepth) ? + le16_to_cpu(leap_mpi_rep.sas_narrow_max_qdepth) : + LEAPRAID_SAS_QUEUE_DEPTH; + adapter->adapter_attr.sata_max_queue_depth = + leap_mpi_rep.sata_max_qdepth ? + leap_mpi_rep.sata_max_qdepth : + LEAPRAID_SATA_QUEUE_DEPTH; + adapter->adapter_attr.raid_volume_max_queue_depth = + LEAPRAID_RAID_QUEUE_DEPTH; + dev_info(&adapter->pdev->dev, + "max: wp qd=%d, np qd=%d, SATA qd=%d, raid qd=%d\n", + adapter->adapter_attr.wideport_max_queue_depth, + adapter->adapter_attr.narrowport_max_queue_depth, + adapter->adapter_attr.sata_max_queue_depth, + adapter->adapter_attr.raid_volume_max_queue_depth); + if (WARN_ON(!(adapter->adapter_attr.features.adapter_caps & + LEAPRAID_ADAPTER_FEATURES_CAP_ATOMIC_REQ))) + return -EFAULT; + adapter->adapter_attr.features.fw_version = + le32_to_cpu(leap_mpi_rep.fw_version); + + fw_major = (adapter->adapter_attr.features.fw_version >> + LEAPRAID_VER_MAJOR_SHIFT) & LEAPRAID_VER_MASK; + fw_minor = (adapter->adapter_attr.features.fw_version >> + LEAPRAID_VER_MINOR_SHIFT) & LEAPRAID_VER_MASK; + fw_build = (adapter->adapter_attr.features.fw_version >> + LEAPRAID_VER_BUILD_SHIFT) & LEAPRAID_VER_MASK; + fw_release = + adapter->adapter_attr.features.fw_version & LEAPRAID_VER_MASK; + + dev_info(&adapter->pdev->dev, + "Firmware version: %u.%u.%u.%u (0x%08x)\n", + fw_major, fw_minor, fw_build, fw_release, + adapter->adapter_attr.features.fw_version); + + adapter->adapter_attr.features.msg_ver = + le16_to_cpu(leap_mpi_rep.msg_ver); + adapter->adapter_attr.features.product_id = + le16_to_cpu(leap_mpi_rep.product_id); + dev_info(&adapter->pdev->dev, + "message version: 0x%x, product id 0x%x\n", + adapter->adapter_attr.features.msg_ver, + adapter->adapter_attr.features.product_id); + + if (adapter->adapter_attr.features.msg_ver < 0x1000) { + dev_err(&adapter->pdev->dev, "Device not supported\n"); + return -EFAULT; + } + adapter->shost->max_id = LEAPRAID_INVALID_INITIAL_VALUE; + + return 0; +} + +static inline void leapraid_disable_pcie(struct leapraid_adapter *adapter) +{ + mutex_lock(&adapter->access_ctrl.pci_access_lock); + if (adapter->iomem_base) { + iounmap(adapter->iomem_base); + adapter->iomem_base = NULL; + } + if (pci_is_enabled(adapter->pdev)) { + pci_release_regions(adapter->pdev); + pci_disable_device(adapter->pdev); + } + mutex_unlock(&adapter->access_ctrl.pci_access_lock); +} + +static int leapraid_enable_pcie(struct leapraid_adapter *adapter) +{ + u64 dma_mask; + int rc; + + rc = pci_enable_device(adapter->pdev); + if (rc) { + dev_err(&adapter->pdev->dev, "Failed to enable PCI device\n"); + return rc; + } + + rc = pci_request_regions(adapter->pdev, LEAPRAID_DRIVER_NAME); + if (rc) { + dev_err(&adapter->pdev->dev, + "Failed to obtain PCI resources\n"); + return rc; + } + + if (sizeof(dma_addr_t) > 4) { + dma_mask = DMA_BIT_MASK(DMA_64_BITS); + adapter->adapter_attr.use_32_dma_mask = 0; + } else { + dma_mask = DMA_BIT_MASK(DMA_32_BITS); + adapter->adapter_attr.use_32_dma_mask = 1; + } + + rc = dma_set_mask_and_coherent(&adapter->pdev->dev, dma_mask); + if (rc) { + dev_err(&adapter->pdev->dev, + "Failed to set %lld DMA mask\n", dma_mask); + return rc; + } + adapter->iomem_base = ioremap(pci_resource_start(adapter->pdev, 0), + sizeof(struct leapraid_reg_base)); + if (!adapter->iomem_base) { + dev_err(&adapter->pdev->dev, + "Failed to map memory for controller registers\n"); + return -ENOMEM; + } + + pci_set_master(adapter->pdev); + + return 0; +} + +static void leapraid_cpus_on_irq(struct leapraid_adapter *adapter) +{ + struct leapraid_int_rq *int_rq; + unsigned int i, base_group, this_group; + unsigned int cpu, nr_cpus, total_msix, index; + + total_msix = adapter->notification_desc.iopoll_qdex; + nr_cpus = num_online_cpus(); + + if (!nr_cpus || !total_msix) + return; + base_group = nr_cpus / total_msix; + + cpu = cpumask_first(cpu_online_mask); + for (index = 0; index < adapter->notification_desc.iopoll_qdex; + index++) { + int_rq = &adapter->notification_desc.int_rqs[index]; + + if (cpu >= adapter->notification_desc.msix_cpu_map_sz) + break; + + this_group = base_group + + (index < (nr_cpus % total_msix) ? 1 : 0); + + for (i = 0 ; i < this_group ; i++) { + if (cpu >= adapter->notification_desc.msix_cpu_map_sz) + break; + + adapter->notification_desc.msix_cpu_map[cpu] = + int_rq->rq.msix_idx; + cpu = cpumask_next(cpu, cpu_online_mask); + } + } +} + +static void leapraid_map_msix_to_cpu(struct leapraid_adapter *adapter) +{ + struct leapraid_int_rq *int_rq; + const cpumask_t *affinity_mask; + int cpu; + u32 i; + + if (!adapter->adapter_attr.rq_cnt) + return; + + for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { + int_rq = &adapter->notification_desc.int_rqs[i]; + affinity_mask = pci_irq_get_affinity(adapter->pdev, + int_rq->rq.msix_idx); + if (!affinity_mask) { + dev_warn(&adapter->pdev->dev, + "%s: IRQ affinity NULL, msix_idx=%d\n", + __func__, int_rq->rq.msix_idx); + goto out_apply_irq_affinity; + } + + for_each_cpu_and(cpu, affinity_mask, cpu_online_mask) { + if (cpu >= adapter->notification_desc.msix_cpu_map_sz) + continue; + + adapter->notification_desc.msix_cpu_map[cpu] = + int_rq->rq.msix_idx; + } + } + return; +out_apply_irq_affinity: + leapraid_cpus_on_irq(adapter); +} + +static int leapraid_alloc_msix_cpu_map(struct leapraid_adapter *adapter) +{ + adapter->notification_desc.msix_cpu_map_sz = nr_cpu_ids; + adapter->notification_desc.msix_cpu_map = + kzalloc(adapter->notification_desc.msix_cpu_map_sz, + GFP_KERNEL); + if (!adapter->notification_desc.msix_cpu_map) + return -ENOMEM; + + return 0; +} + +static void leapraid_configure_reply_queue_affinity( + struct leapraid_adapter *adapter) +{ + if (!adapter || !adapter->notification_desc.msix_enable) + return; + + leapraid_map_msix_to_cpu(adapter); +} + +static void leapraid_free_irq(struct leapraid_adapter *adapter) +{ + struct leapraid_int_rq *int_rq; + unsigned int i; + int irq; + + for (i = 0; adapter->notification_desc.int_rqs && + i < adapter->notification_desc.int_rqs_allocated; i++) { + int_rq = &adapter->notification_desc.int_rqs[i]; + if (!int_rq) + continue; + + irq = pci_irq_vector(adapter->pdev, int_rq->rq.msix_idx); + irq_set_affinity_hint(irq, NULL); + free_irq(irq, &int_rq->rq); + } + adapter->notification_desc.int_rqs_allocated = 0; + + if (adapter->notification_desc.irq_vectors_allocated) { + pci_free_irq_vectors(adapter->pdev); + adapter->notification_desc.irq_vectors_allocated = 0; + } + + adapter->notification_desc.msix_enable = 0; + + kfree(adapter->notification_desc.blk_mq_poll_rqs); + adapter->notification_desc.blk_mq_poll_rqs = NULL; + + kfree(adapter->notification_desc.int_rqs); + adapter->notification_desc.int_rqs = NULL; + + kfree(adapter->notification_desc.msix_cpu_map); + adapter->notification_desc.msix_cpu_map = NULL; + adapter->notification_desc.msix_cpu_map_sz = 0; + adapter->notification_desc.iopoll_qdex = 0; + adapter->notification_desc.iopoll_qcnt = 0; +} + +static int leapraid_setup_irqs(struct leapraid_adapter *adapter) +{ + int irq_mode = adapter->notification_desc.irq_mode; + unsigned int i; + int rc = 0; + + if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSIX) { + rc = pci_alloc_irq_vectors_affinity( + adapter->pdev, + adapter->notification_desc.iopoll_qdex, + adapter->notification_desc.iopoll_qdex, + PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, NULL); + if (rc < 0) { + dev_err(&adapter->pdev->dev, + "%d MSI/MSIX vectors allocated failed!\n", + adapter->notification_desc.iopoll_qdex); + return rc; + } + + adapter->notification_desc.irq_vectors_allocated = 1; + } + + for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { + adapter->notification_desc.int_rqs[i].rq.adapter = adapter; + adapter->notification_desc.int_rqs[i].rq.msix_idx = i; + atomic_set(&adapter->notification_desc.int_rqs[i].rq.busy, 0); + if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSIX) + snprintf(adapter->notification_desc.int_rqs[i].rq.name, + LEAPRAID_NAME_LENGTH, "%s%u-MSIx%u", + LEAPRAID_DRIVER_NAME, + adapter->adapter_attr.id, i); + else if (irq_mode == LEAPRAID_INTERRUPT_MODE_MSI) + snprintf(adapter->notification_desc.int_rqs[i].rq.name, + LEAPRAID_NAME_LENGTH, "%s%u-MSI%u", + LEAPRAID_DRIVER_NAME, + adapter->adapter_attr.id, i); + + rc = request_irq(pci_irq_vector(adapter->pdev, i), + leapraid_irq_handler, + IRQF_SHARED, + adapter->notification_desc.int_rqs[i].rq.name, + &adapter->notification_desc.int_rqs[i].rq); + if (rc) { + dev_err(&adapter->pdev->dev, + "MSI/MSIx: request_irq %s failed!\n", + adapter->notification_desc.int_rqs[i].rq.name); + return rc; + } + adapter->notification_desc.int_rqs_allocated++; + } + + return 0; +} + +static int leapraid_setup_legacy_int(struct leapraid_adapter *adapter) +{ + int rc; + + adapter->notification_desc.int_rqs[0].rq.adapter = adapter; + adapter->notification_desc.int_rqs[0].rq.msix_idx = 0; + atomic_set(&adapter->notification_desc.int_rqs[0].rq.busy, 0); + snprintf(adapter->notification_desc.int_rqs[0].rq.name, + LEAPRAID_NAME_LENGTH, "%s%d-LegacyInt", + LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); + + rc = pci_alloc_irq_vectors_affinity( + adapter->pdev, + adapter->notification_desc.iopoll_qdex, + adapter->notification_desc.iopoll_qdex, + PCI_IRQ_INTX | PCI_IRQ_AFFINITY, + NULL); + if (rc < 0) { + dev_err(&adapter->pdev->dev, + "Legacy irq allocated failed!\n"); + return rc; + } + + adapter->notification_desc.irq_vectors_allocated = 1; + adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_LEGACY; + rc = request_irq(pci_irq_vector(adapter->pdev, 0), + leapraid_irq_handler, + IRQF_SHARED, + adapter->notification_desc.int_rqs[0].rq.name, + &adapter->notification_desc.int_rqs[0].rq); + if (rc) { + irq_set_affinity_hint(pci_irq_vector(adapter->pdev, 0), NULL); + pci_free_irq_vectors(adapter->pdev); + adapter->notification_desc.irq_vectors_allocated = 0; + dev_err(&adapter->pdev->dev, + "Legacy Int: request_irq %s failed!\n", + adapter->notification_desc.int_rqs[0].rq.name); + return -EBUSY; + } + adapter->notification_desc.int_rqs_allocated = 1; + return rc; +} + +static int leapraid_set_legacy_int(struct leapraid_adapter *adapter) +{ + int rc; + + rc = leapraid_alloc_msix_cpu_map(adapter); + if (rc) + return rc; + + adapter->adapter_attr.rq_cnt = 1; + adapter->notification_desc.iopoll_qdex = + adapter->adapter_attr.rq_cnt; + adapter->notification_desc.iopoll_qcnt = 0; + dev_info(&adapter->pdev->dev, + "Legacy Intr: req queue cnt=%d intr=%d/poll=%d rep queues!\n", + adapter->adapter_attr.rq_cnt, + adapter->notification_desc.iopoll_qdex, + adapter->notification_desc.iopoll_qcnt); + adapter->notification_desc.int_rqs = + kcalloc(adapter->notification_desc.iopoll_qdex, + sizeof(struct leapraid_int_rq), GFP_KERNEL); + if (!adapter->notification_desc.int_rqs) + return -ENOMEM; + + return leapraid_setup_legacy_int(adapter); +} + +static int leapraid_set_msix(struct leapraid_adapter *adapter) +{ + int iopoll_qcnt = 0; + unsigned int i; + int rc, msix_cnt; + + msix_cnt = pci_msix_vec_count(adapter->pdev); + if (msix_cnt <= 0 || + adapter->adapter_attr.features.max_msix_vectors == 0) { + dev_info(&adapter->pdev->dev, "MSIX unsupported!\n"); + return -EOPNOTSUPP; + } + + msix_cnt = min_t(int, msix_cnt, + adapter->adapter_attr.features.max_msix_vectors); + + if (reset_devices) + adapter->adapter_attr.rq_cnt = 1; + else + adapter->adapter_attr.rq_cnt = min_t(int, + num_online_cpus(), + msix_cnt); + + if (max_msix_vectors > 0) + adapter->adapter_attr.rq_cnt = min_t( + int, max_msix_vectors, adapter->adapter_attr.rq_cnt); + + if (adapter->adapter_attr.rq_cnt <= 1) + adapter->shost->host_tagset = 0; + if (adapter->shost->host_tagset) { + iopoll_qcnt = poll_queues; + if (iopoll_qcnt >= adapter->adapter_attr.rq_cnt) + iopoll_qcnt = 0; + } + if (iopoll_qcnt) { + adapter->notification_desc.blk_mq_poll_rqs = + kcalloc(iopoll_qcnt, + sizeof(struct leapraid_blk_mq_poll_rq), + GFP_KERNEL); + if (!adapter->notification_desc.blk_mq_poll_rqs) + return -ENOMEM; + adapter->adapter_attr.rq_cnt = + min(adapter->adapter_attr.rq_cnt + iopoll_qcnt, + msix_cnt); + } + + adapter->notification_desc.iopoll_qdex = + adapter->adapter_attr.rq_cnt - iopoll_qcnt; + + adapter->notification_desc.iopoll_qcnt = iopoll_qcnt; + dev_info(&adapter->pdev->dev, + "MSIx: req queue cnt=%d, intr=%d/poll=%d rep queues!\n", + adapter->adapter_attr.rq_cnt, + adapter->notification_desc.iopoll_qdex, + adapter->notification_desc.iopoll_qcnt); + + adapter->notification_desc.int_rqs = + kcalloc(adapter->notification_desc.iopoll_qdex, + sizeof(struct leapraid_int_rq), GFP_KERNEL); + if (!adapter->notification_desc.int_rqs) + return -ENOMEM; + + for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { + adapter->notification_desc.blk_mq_poll_rqs[i].rq.adapter = + adapter; + adapter->notification_desc.blk_mq_poll_rqs[i].rq.msix_idx = + i + adapter->notification_desc.iopoll_qdex; + atomic_set( + &adapter->notification_desc.blk_mq_poll_rqs[i].rq.busy, + 0); + snprintf(adapter->notification_desc.blk_mq_poll_rqs[i].rq.name, + LEAPRAID_NAME_LENGTH, + "%s%u-MQ-Poll%u", LEAPRAID_DRIVER_NAME, + adapter->adapter_attr.id, i); + atomic_set(&adapter->notification_desc.blk_mq_poll_rqs[i].busy, + 0); + atomic_set(&adapter->notification_desc.blk_mq_poll_rqs[i].pause, + 0); + } + + rc = leapraid_alloc_msix_cpu_map(adapter); + if (rc) + return rc; + + adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_MSIX; + adapter->notification_desc.msix_enable = 1; + rc = leapraid_setup_irqs(adapter); + if (rc) { + leapraid_free_irq(adapter); + adapter->notification_desc.msix_enable = 0; + return rc; + } + + return 0; +} + +static int leapraid_set_msi(struct leapraid_adapter *adapter) +{ + int iopoll_qcnt = 0; + unsigned int i; + int rc, msi_cnt; + + msi_cnt = pci_msi_vec_count(adapter->pdev); + if (msi_cnt <= 0 || + adapter->adapter_attr.features.max_msix_vectors == 0) { + dev_info(&adapter->pdev->dev, "MSI unsupported!\n"); + return -EOPNOTSUPP; + } + + msi_cnt = min_t(int, msi_cnt, + adapter->adapter_attr.features.max_msix_vectors); + + if (reset_devices) + adapter->adapter_attr.rq_cnt = 1; + else + adapter->adapter_attr.rq_cnt = min_t(int, + num_online_cpus(), + msi_cnt); + + if (max_msix_vectors > 0) + adapter->adapter_attr.rq_cnt = min_t( + int, max_msix_vectors, adapter->adapter_attr.rq_cnt); + + if (adapter->adapter_attr.rq_cnt <= 1) + adapter->shost->host_tagset = 0; + if (adapter->shost->host_tagset) { + iopoll_qcnt = poll_queues; + if (iopoll_qcnt >= adapter->adapter_attr.rq_cnt) + iopoll_qcnt = 0; + } + + if (iopoll_qcnt) { + adapter->notification_desc.blk_mq_poll_rqs = + kcalloc(iopoll_qcnt, + sizeof(struct leapraid_blk_mq_poll_rq), + GFP_KERNEL); + if (!adapter->notification_desc.blk_mq_poll_rqs) + return -ENOMEM; + + adapter->adapter_attr.rq_cnt = + min(adapter->adapter_attr.rq_cnt + iopoll_qcnt, + msi_cnt); + } + + adapter->notification_desc.iopoll_qdex = + adapter->adapter_attr.rq_cnt - iopoll_qcnt; + rc = pci_alloc_irq_vectors_affinity( + adapter->pdev, + 1, + adapter->notification_desc.iopoll_qdex, + PCI_IRQ_MSI | PCI_IRQ_AFFINITY, NULL); + if (rc < 0) { + dev_err(&adapter->pdev->dev, + "%d MSI vectors allocated failed!\n", + adapter->notification_desc.iopoll_qdex); + leapraid_free_irq(adapter); + return rc; + } + adapter->notification_desc.irq_vectors_allocated = 1; + if (rc != adapter->notification_desc.iopoll_qdex) { + adapter->notification_desc.iopoll_qdex = rc; + adapter->adapter_attr.rq_cnt = + adapter->notification_desc.iopoll_qdex + iopoll_qcnt; + } + adapter->notification_desc.iopoll_qcnt = iopoll_qcnt; + dev_info(&adapter->pdev->dev, + "MSI: req queue cnt=%d, intr=%d/poll=%d rep queues!\n", + adapter->adapter_attr.rq_cnt, + adapter->notification_desc.iopoll_qdex, + adapter->notification_desc.iopoll_qcnt); + + adapter->notification_desc.int_rqs = + kcalloc(adapter->notification_desc.iopoll_qdex, + sizeof(struct leapraid_int_rq), + GFP_KERNEL); + if (!adapter->notification_desc.int_rqs) + return -ENOMEM; + + for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { + adapter->notification_desc.blk_mq_poll_rqs[i].rq.adapter = + adapter; + adapter->notification_desc.blk_mq_poll_rqs[i].rq.msix_idx = + i + adapter->notification_desc.iopoll_qdex; + atomic_set( + &adapter->notification_desc.blk_mq_poll_rqs[i].rq.busy, + 0); + snprintf(adapter->notification_desc.blk_mq_poll_rqs[i].rq.name, + LEAPRAID_NAME_LENGTH, + "%s%u-MQ-Poll%u", LEAPRAID_DRIVER_NAME, + adapter->adapter_attr.id, i); + atomic_set( + &adapter->notification_desc.blk_mq_poll_rqs[i].busy, + 0); + atomic_set( + &adapter->notification_desc.blk_mq_poll_rqs[i].pause, + 0); + } + + rc = leapraid_alloc_msix_cpu_map(adapter); + if (rc) + return rc; + + adapter->notification_desc.irq_mode = LEAPRAID_INTERRUPT_MODE_MSI; + adapter->notification_desc.msix_enable = 1; + rc = leapraid_setup_irqs(adapter); + if (rc) { + leapraid_free_irq(adapter); + adapter->notification_desc.msix_enable = 0; + return rc; + } + + return 0; +} + +static int leapraid_set_notification_auto(struct leapraid_adapter *adapter) +{ + int rc; + + rc = leapraid_set_msix(adapter); + if (!rc) + return 0; + + leapraid_free_irq(adapter); + dev_info(&adapter->pdev->dev, + "MSI-X setup failed (%d), trying MSI\n", rc); + + rc = leapraid_set_msi(adapter); + if (!rc) + return 0; + + leapraid_free_irq(adapter); + dev_info(&adapter->pdev->dev, + "MSI setup failed (%d), back to legacy INTx\n", rc); + + rc = leapraid_set_legacy_int(adapter); + if (rc) + dev_err(&adapter->pdev->dev, + "%s: Enable legacy irq failed!\n", __func__); + + return rc; +} + +int leapraid_set_pcie_and_notification(struct leapraid_adapter *adapter) +{ + int rc; + + rc = leapraid_enable_pcie(adapter); + if (rc) + goto out_fail; + + leapraid_mask_int(adapter); + + rc = leapraid_make_adapter_ready(adapter, PART_RESET); + if (rc) { + dev_err(&adapter->pdev->dev, "Make adapter ready failure\n"); + goto out_fail; + } + + rc = leapraid_get_adapter_features(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, "Get adapter feature failure\n"); + goto out_fail; + } + + rc = leapraid_set_notification_auto(adapter); + if (rc) + goto out_fail; + + pci_save_state(adapter->pdev); + + return 0; + +out_fail: + leapraid_free_irq(adapter); + leapraid_disable_pcie(adapter); + return rc; +} + +void leapraid_disable_controller(struct leapraid_adapter *adapter) +{ + if (!adapter->iomem_base) + return; + + leapraid_mask_int(adapter); + + adapter->access_ctrl.shost_recovering = 1; + leapraid_make_adapter_ready(adapter, PART_RESET); + adapter->access_ctrl.shost_recovering = 0; + wake_up(&adapter->access_ctrl.recovery_waitq); + + leapraid_free_irq(adapter); + leapraid_disable_pcie(adapter); +} + +static int leapraid_adapter_unit_reset(struct leapraid_adapter *adapter) +{ + int rc = 0; + + writel(LEAPRAID_FUNC_ADAPTER_UNIT_RESET << LEAPRAID_DB_FUNC_SHIFT, + &adapter->iomem_base->db); + if (leapraid_db_wait_ack_and_clear_int(adapter)) + rc = -EFAULT; + + if (!leapraid_wait_adapter_ready(adapter)) { + dev_err(&adapter->pdev->dev, "unit reset failed\n"); + return -EFAULT; + } + + return rc; +} + +static int leapraid_make_adapter_ready(struct leapraid_adapter *adapter, + enum reset_type type) +{ + u32 db; + int count; + + if (!leapraid_pci_active(adapter)) + return 0; + + count = 0; + db = leapraid_readl(&adapter->iomem_base->db); + if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_RESET) { + while ((db & LEAPRAID_DB_MASK) != LEAPRAID_DB_READY) { + if (count++ == LEAPRAID_DB_RETRY_COUNT_MAX) { + dev_err(&adapter->pdev->dev, + "Wait adapter ready timeout\n"); + return -EFAULT; + } + ssleep(1); + db = leapraid_readl(&adapter->iomem_base->db); + dev_info(&adapter->pdev->dev, + "Wait adapter ready, count=%d, db=0x%x\n", + count, db); + } + } + if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_READY) + return 0; + + if (db & LEAPRAID_DB_USED) + goto full_reset; + + if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) + goto full_reset; + + if (type == FULL_RESET) + goto full_reset; + + if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_OPERATIONAL && + !leapraid_adapter_unit_reset(adapter)) + return 0; + +full_reset: + return leapraid_host_diag_reset(adapter); +} + +static void leapraid_fw_log_exit(struct leapraid_adapter *adapter) +{ + if (!adapter->fw_log_desc.open_pcie_trace) + return; + + if (adapter->fw_log_desc.fw_log_buffer) { + wait_event(adapter->fw_log_desc.mmap_waitq, + !atomic_read(&adapter->fw_log_desc.mmap_refcnt)); + dma_free_coherent(&adapter->pdev->dev, + (LEAPRAID_SYS_LOG_BUF_SIZE + + LEAPRAID_SYS_LOG_BUF_RESERVE), + adapter->fw_log_desc.fw_log_buffer, + adapter->fw_log_desc.fw_log_buffer_dma); + adapter->fw_log_desc.fw_log_buffer = NULL; + } +} + +static int leapraid_fw_log_init(struct leapraid_adapter *adapter) +{ + struct leapraid_adapter_log_req adapter_log_req; + struct leapraid_adapter_log_rep adapter_log_rep; + u16 adapter_status; + u64 buf_addr; + u32 rc; + + if (!adapter->fw_log_desc.open_pcie_trace) + return 0; + + if (!adapter->fw_log_desc.fw_log_buffer) { + adapter->fw_log_desc.fw_log_buffer = + dma_alloc_coherent( + &adapter->pdev->dev, + (LEAPRAID_SYS_LOG_BUF_SIZE + + LEAPRAID_SYS_LOG_BUF_RESERVE), + &adapter->fw_log_desc.fw_log_buffer_dma, + GFP_KERNEL); + if (!adapter->fw_log_desc.fw_log_buffer) + return -ENOMEM; + } + + memset(&adapter_log_req, 0, sizeof(struct leapraid_adapter_log_req)); + adapter_log_req.func = LEAPRAID_FUNC_LOGBUF_INIT; + buf_addr = adapter->fw_log_desc.fw_log_buffer_dma; + + adapter_log_req.mbox.w[0] = + cpu_to_le32((u32)(buf_addr & 0xFFFFFFFF)); + adapter_log_req.mbox.w[1] = + cpu_to_le32((u32)((buf_addr >> 32) & 0xFFFFFFFF)); + adapter_log_req.mbox.w[2] = + cpu_to_le32(LEAPRAID_SYS_LOG_BUF_SIZE); + rc = leapraid_handshake_func(adapter, + sizeof(struct leapraid_adapter_log_req), + (u32 *)&adapter_log_req, + sizeof(struct leapraid_adapter_log_rep), + (u16 *)&adapter_log_rep); + if (rc != 0) { + dev_err(&adapter->pdev->dev, "%s: Handshake failed, rc=%d\n", + __func__, rc); + return rc; + } + + adapter_status = le16_to_cpu(adapter_log_rep.adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { + dev_err(&adapter->pdev->dev, "%s: failed!\n", __func__); + rc = -EIO; + } + + return rc; +} + +static void leapraid_free_host_memory(struct leapraid_adapter *adapter) +{ + unsigned int i; + + if (adapter->mem_desc.task_desc) { + dma_free_coherent(&adapter->pdev->dev, + adapter->adapter_attr.task_desc_dma_size, + adapter->mem_desc.task_desc, + adapter->mem_desc.task_desc_dma); + adapter->mem_desc.task_desc = NULL; + } + + if (adapter->mem_desc.sense_data) { + dma_free_coherent( + &adapter->pdev->dev, + adapter->adapter_attr.io_qd * SCSI_SENSE_BUFFERSIZE, + adapter->mem_desc.sense_data, + adapter->mem_desc.sense_data_dma); + adapter->mem_desc.sense_data = NULL; + } + + if (adapter->mem_desc.rep_msg) { + dma_free_coherent( + &adapter->pdev->dev, + adapter->adapter_attr.rep_msg_qd * LEAPRAID_REPLY_SIZE, + adapter->mem_desc.rep_msg, + adapter->mem_desc.rep_msg_dma); + adapter->mem_desc.rep_msg = NULL; + } + + if (adapter->mem_desc.rep_msg_addr) { + dma_free_coherent(&adapter->pdev->dev, + adapter->adapter_attr.rep_msg_qd * + LEAPRAID_REP_MSG_ADDR_SIZE, + adapter->mem_desc.rep_msg_addr, + adapter->mem_desc.rep_msg_addr_dma); + adapter->mem_desc.rep_msg_addr = NULL; + } + + if (adapter->mem_desc.rep_desc_seg_maint) { + for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; + i++) { + if (adapter->mem_desc.rep_desc_seg_maint[i] + .rep_desc_seg) { + dma_free_coherent( + &adapter->pdev->dev, + (adapter->adapter_attr.rep_desc_qd * + LEAPRAID_REP_DESC_ENTRY_SIZE) * + LEAPRAID_REP_DESC_CHUNK_SIZE, + adapter->mem_desc.rep_desc_seg_maint[i] + .rep_desc_seg, + adapter->mem_desc.rep_desc_seg_maint[i] + .rep_desc_seg_dma); + adapter->mem_desc.rep_desc_seg_maint[i] + .rep_desc_seg = NULL; + } + } + + if (adapter->mem_desc.rep_desc_q_arr) { + dma_free_coherent( + &adapter->pdev->dev, + adapter->adapter_attr.rq_cnt * + LEAPRAID_REP_RQ_CNT_SIZE, + adapter->mem_desc.rep_desc_q_arr, + adapter->mem_desc.rep_desc_q_arr_dma); + adapter->mem_desc.rep_desc_q_arr = NULL; + } + + for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) { + struct leapraid_mem_desc *mem_desc = + &adapter->mem_desc; + kfree(adapter->mem_desc.rep_desc_seg_maint[i] + .rep_desc_maint); + mem_desc->rep_desc_seg_maint[i].rep_desc_maint = NULL; + } + kfree(adapter->mem_desc.rep_desc_seg_maint); + adapter->mem_desc.rep_desc_seg_maint = NULL; + } + + kfree(adapter->mem_desc.taskid_to_uniq_tag); + adapter->mem_desc.taskid_to_uniq_tag = NULL; + + dma_pool_destroy(adapter->mem_desc.sg_chain_pool); + adapter->mem_desc.sg_chain_pool = NULL; +} + +static inline bool leapraid_is_in_same_4g_seg(dma_addr_t start, u32 size) +{ + return upper_32_bits(start) == upper_32_bits(start + size - 1); +} + +int leapraid_internal_init_cmd_priv(struct leapraid_adapter *adapter, + struct leapraid_io_req_tracker *io_tracker) +{ + io_tracker->chain = + dma_pool_alloc(adapter->mem_desc.sg_chain_pool, + GFP_KERNEL, + &io_tracker->chain_dma); + + if (!io_tracker->chain) + return -ENOMEM; + + return 0; +} + +void leapraid_internal_exit_cmd_priv(struct leapraid_adapter *adapter, + struct leapraid_io_req_tracker *io_tracker) +{ + if (io_tracker && io_tracker->chain) + dma_pool_free(adapter->mem_desc.sg_chain_pool, + io_tracker->chain, + io_tracker->chain_dma); +} + +static int leapraid_request_host_memory(struct leapraid_adapter *adapter) +{ + struct leapraid_adapter_features *facts = + &adapter->adapter_attr.features; + u16 rep_desc_q_cnt_allocated; + unsigned int i, j; + int rc; + + /* Scatter-gather table size. */ + adapter->shost->sg_tablesize = LEAPRAID_SG_DEPTH; + if (reset_devices) + adapter->shost->sg_tablesize = + LEAPRAID_KDUMP_MIN_PHYS_SEGMENTS; + /* High-priority commands queue depth. */ + adapter->dynamic_task_desc.hp_cmd_qd = LEAPRAID_FIXED_HP_CMDS; + /* Internal commands queue depth. */ + adapter->dynamic_task_desc.inter_cmd_qd = LEAPRAID_FIXED_INTER_CMDS; + /* Adapter commands total queue depth. */ + if (reset_devices) + adapter->adapter_attr.adapter_total_qd = + LEAPRAID_DEFAULT_CMD_QD_OFFSET + + adapter->dynamic_task_desc.inter_cmd_qd + + adapter->dynamic_task_desc.hp_cmd_qd; + else + adapter->adapter_attr.adapter_total_qd = facts->req_slot + + adapter->dynamic_task_desc.hp_cmd_qd; + /* Reply message queue depth. */ + adapter->adapter_attr.rep_msg_qd = + adapter->adapter_attr.adapter_total_qd + + LEAPRAID_DEFAULT_CMD_QD_OFFSET; + /* Reply descriptor queue depth. */ + adapter->adapter_attr.rep_desc_qd = + round_up(adapter->adapter_attr.adapter_total_qd + + adapter->adapter_attr.rep_msg_qd + + LEAPRAID_TASKID_OFFSET_CTRL_CMD, + LEAPRAID_REPLY_QD_ALIGNMENT); + /* SCSI command I/O depth. */ + adapter->adapter_attr.io_qd = + adapter->adapter_attr.adapter_total_qd - + adapter->dynamic_task_desc.hp_cmd_qd - + adapter->dynamic_task_desc.inter_cmd_qd; + /* SCSI host can queue. */ + adapter->shost->can_queue = adapter->adapter_attr.io_qd - + LEAPRAID_TASKID_OFFSET_CTRL_CMD; + adapter->driver_cmds.ctl_cmd.taskid = adapter->shost->can_queue + + LEAPRAID_TASKID_OFFSET_CTRL_CMD; + + /* Allocate task descriptor. */ +try_again: + adapter->adapter_attr.task_desc_dma_size = + (adapter->adapter_attr.adapter_total_qd + + LEAPRAID_TASKID_OFFSET_CTRL_CMD) * + LEAPRAID_REQUEST_SIZE; + adapter->mem_desc.task_desc = + dma_alloc_coherent(&adapter->pdev->dev, + adapter->adapter_attr.task_desc_dma_size, + &adapter->mem_desc.task_desc_dma, + GFP_KERNEL); + if (!adapter->mem_desc.task_desc) + return -ENOMEM; + + /* Allocate chain message pool. */ + adapter->mem_desc.sg_chain_pool_size = + LEAPRAID_DEFAULT_CHAINS_PER_IO * LEAPRAID_CHAIN_SEG_SIZE; + adapter->mem_desc.sg_chain_pool = + dma_pool_create("leapraid chain pool", + &adapter->pdev->dev, + adapter->mem_desc.sg_chain_pool_size, + LEAPRAID_DMA_ALIGN, 0); + if (!adapter->mem_desc.sg_chain_pool) + return -ENOMEM; + + /* Allocate I/O tracker to SCSI I/O. */ + adapter->mem_desc.taskid_to_uniq_tag = + kcalloc(adapter->shost->can_queue, sizeof(u16), GFP_KERNEL); + if (!adapter->mem_desc.taskid_to_uniq_tag) + return -ENOMEM; + + adapter->dynamic_task_desc.hp_taskid = + adapter->adapter_attr.io_qd + + LEAPRAID_HP_TASKID_OFFSET_CTL_CMD; + /* Allocate static high-priority task ID. */ + adapter->driver_cmds.ctl_cmd.hp_taskid = + adapter->dynamic_task_desc.hp_taskid; + adapter->driver_cmds.tm_cmd.hp_taskid = + adapter->dynamic_task_desc.hp_taskid + + LEAPRAID_HP_TASKID_OFFSET_TM_CMD; + + adapter->dynamic_task_desc.inter_taskid = + adapter->dynamic_task_desc.hp_taskid + + adapter->dynamic_task_desc.hp_cmd_qd; + adapter->driver_cmds.scan_dev_cmd.inter_taskid = + adapter->dynamic_task_desc.inter_taskid; + adapter->driver_cmds.cfg_op_cmd.inter_taskid = + adapter->dynamic_task_desc.inter_taskid + + LEAPRAID_TASKID_OFFSET_CFG_OP_CMD; + adapter->driver_cmds.transport_cmd.inter_taskid = + adapter->dynamic_task_desc.inter_taskid + + LEAPRAID_TASKID_OFFSET_TRANSPORT_CMD; + adapter->driver_cmds.enc_cmd.inter_taskid = + adapter->dynamic_task_desc.inter_taskid + + LEAPRAID_TASKID_OFFSET_ENC_CMD; + adapter->driver_cmds.notify_event_cmd.inter_taskid = + adapter->dynamic_task_desc.inter_taskid + + LEAPRAID_TASKID_OFFSET_NOTIFY_EVENT_CMD; + dev_info(&adapter->pdev->dev, "queue depth:\n"); + dev_info(&adapter->pdev->dev, " host->can_queue: %d\n", + adapter->shost->can_queue); + dev_info(&adapter->pdev->dev, " io_qd: %d\n", + adapter->adapter_attr.io_qd); + dev_info(&adapter->pdev->dev, " hpr_cmd_qd: %d\n", + adapter->dynamic_task_desc.hp_cmd_qd); + dev_info(&adapter->pdev->dev, " inter_cmd_qd: %d\n", + adapter->dynamic_task_desc.inter_cmd_qd); + dev_info(&adapter->pdev->dev, " adapter_total_qd: %d\n", + adapter->adapter_attr.adapter_total_qd); + + dev_info(&adapter->pdev->dev, "taskid range:\n"); + dev_info(&adapter->pdev->dev, + " adapter->dynamic_task_desc.hp_taskid: %d\n", + adapter->dynamic_task_desc.hp_taskid); + dev_info(&adapter->pdev->dev, + " adapter->dynamic_task_desc.inter_taskid: %d\n", + adapter->dynamic_task_desc.inter_taskid); + + /* + * Allocate sense data DMA buffer. + * Constraint: Must reside within the same 4GB segment + * (driver-maintained). + */ + adapter->mem_desc.sense_data = + dma_alloc_coherent( + &adapter->pdev->dev, + adapter->adapter_attr.io_qd * SCSI_SENSE_BUFFERSIZE, + &adapter->mem_desc.sense_data_dma, + GFP_KERNEL); + if (!adapter->mem_desc.sense_data) + return -ENOMEM; + + if (!leapraid_is_in_same_4g_seg(adapter->mem_desc.sense_data_dma, + adapter->adapter_attr.io_qd * + SCSI_SENSE_BUFFERSIZE)) { + dev_warn(&adapter->pdev->dev, + "Try 32bit DMA: Sense not in same 4G\n"); + rc = -EAGAIN; + goto out_fail; + } + + /* + * Allocate reply frame buffer. + * Constraint: Must reside in the same 4GB segment as the sense DMA. + */ + adapter->mem_desc.rep_msg = + dma_alloc_coherent(&adapter->pdev->dev, + adapter->adapter_attr.rep_msg_qd * + LEAPRAID_REPLY_SIZE, + &adapter->mem_desc.rep_msg_dma, + GFP_KERNEL); + if (!adapter->mem_desc.rep_msg) { + rc = -ENOMEM; + goto out_fail; + } + + if (!leapraid_is_in_same_4g_seg(adapter->mem_desc.rep_msg_dma, + adapter->adapter_attr.rep_msg_qd * + LEAPRAID_REPLY_SIZE)) { + dev_warn(&adapter->pdev->dev, + "Use 32 bit DMA due to rep msg is not in same 4g!\n"); + rc = -EAGAIN; + goto out_fail; + } + + /* Address of reply frame. */ + adapter->mem_desc.rep_msg_addr = + dma_alloc_coherent(&adapter->pdev->dev, + adapter->adapter_attr.rep_msg_qd * + LEAPRAID_REP_MSG_ADDR_SIZE, + &adapter->mem_desc.rep_msg_addr_dma, + GFP_KERNEL); + if (!adapter->mem_desc.rep_msg_addr) + return -ENOMEM; + adapter->adapter_attr.rep_desc_q_seg_cnt = + DIV_ROUND_UP(adapter->adapter_attr.rq_cnt, + LEAPRAID_REP_DESC_CHUNK_SIZE); + adapter->mem_desc.rep_desc_seg_maint = + kcalloc(adapter->adapter_attr.rep_desc_q_seg_cnt, + sizeof(struct leapraid_rep_desc_seg_maint), + GFP_KERNEL); + if (!adapter->mem_desc.rep_desc_seg_maint) + return -ENOMEM; + + rep_desc_q_cnt_allocated = 0; + for (i = 0; i < adapter->adapter_attr.rep_desc_q_seg_cnt; i++) { + adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint = + kcalloc(LEAPRAID_REP_DESC_CHUNK_SIZE, + sizeof(struct leapraid_rep_desc_maint), + GFP_KERNEL); + if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_maint) + return -ENOMEM; + + adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_seg = + dma_alloc_coherent( + &adapter->pdev->dev, + (adapter->adapter_attr.rep_desc_qd * + LEAPRAID_REP_DESC_ENTRY_SIZE) * + LEAPRAID_REP_DESC_CHUNK_SIZE, + &adapter->mem_desc.rep_desc_seg_maint[i] + .rep_desc_seg_dma, + GFP_KERNEL); + if (!adapter->mem_desc.rep_desc_seg_maint[i].rep_desc_seg) + return -ENOMEM; + + for (j = 0; j < LEAPRAID_REP_DESC_CHUNK_SIZE; j++) { + if (rep_desc_q_cnt_allocated >= + adapter->adapter_attr.rq_cnt) + break; + adapter->mem_desc + .rep_desc_seg_maint[i] + .rep_desc_maint[j] + .rep_desc = + (void *)((u8 *)( + adapter->mem_desc + .rep_desc_seg_maint[i] + .rep_desc_seg) + + j * + (adapter->adapter_attr.rep_desc_qd * + LEAPRAID_REP_DESC_ENTRY_SIZE)); + adapter->mem_desc + .rep_desc_seg_maint[i] + .rep_desc_maint[j] + .rep_desc_dma = + adapter->mem_desc + .rep_desc_seg_maint[i] + .rep_desc_seg_dma + + j * + (adapter->adapter_attr.rep_desc_qd * + LEAPRAID_REP_DESC_ENTRY_SIZE); + rep_desc_q_cnt_allocated++; + } + } + + if (!reset_devices) { + adapter->mem_desc.rep_desc_q_arr = + dma_alloc_coherent( + &adapter->pdev->dev, + adapter->adapter_attr.rq_cnt * + LEAPRAID_REP_RQ_CNT_SIZE, + &adapter->mem_desc.rep_desc_q_arr_dma, + GFP_KERNEL); + if (!adapter->mem_desc.rep_desc_q_arr) + return -ENOMEM; + } + + return 0; +out_fail: + if (rc == -EAGAIN) { + leapraid_free_host_memory(adapter); + adapter->adapter_attr.use_32_dma_mask = 1; + rc = dma_set_mask_and_coherent(&adapter->pdev->dev, + DMA_BIT_MASK(DMA_32_BITS)); + if (rc) { + dev_err(&adapter->pdev->dev, + "Failed to set 32 DMA mask\n"); + return rc; + } + goto try_again; + } + return rc; +} + +static int leapraid_alloc_dev_topo_bitmaps(struct leapraid_adapter *adapter) +{ + u16 pd_hdls_sz; + + pd_hdls_sz = + BITS_TO_LONGS( + adapter->adapter_attr.features.max_dev_handle + 1) * + sizeof(unsigned long); + + adapter->dev_topo.pd_hdls_sz = pd_hdls_sz; + adapter->dev_topo.pd_hdls = + kzalloc(adapter->dev_topo.pd_hdls_sz, GFP_KERNEL); + if (!adapter->dev_topo.pd_hdls) + return -ENOMEM; + + adapter->dev_topo.blocking_hdls = + kzalloc(adapter->dev_topo.pd_hdls_sz, GFP_KERNEL); + if (!adapter->dev_topo.blocking_hdls) + return -ENOMEM; + + return 0; +} + +static void leapraid_free_dev_topo_bitmaps(struct leapraid_adapter *adapter) +{ + kfree(adapter->dev_topo.pd_hdls); + kfree(adapter->dev_topo.blocking_hdls); +} + +static int leapraid_init_driver_cmds(struct leapraid_adapter *adapter) +{ + INIT_LIST_HEAD(&adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.scan_dev_cmd.cb_idx = LEAPRAID_SCAN_DEV_CB_IDX; + list_add_tail(&adapter->driver_cmds.scan_dev_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.cfg_op_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.cfg_op_cmd.cb_idx = LEAPRAID_CONFIG_CB_IDX; + mutex_init(&adapter->driver_cmds.cfg_op_cmd.mutex); + list_add_tail(&adapter->driver_cmds.cfg_op_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.transport_cmd.cb_idx = LEAPRAID_TRANSPORT_CB_IDX; + mutex_init(&adapter->driver_cmds.transport_cmd.mutex); + list_add_tail(&adapter->driver_cmds.transport_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.enc_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.enc_cmd.cb_idx = LEAPRAID_ENC_CB_IDX; + mutex_init(&adapter->driver_cmds.enc_cmd.mutex); + list_add_tail(&adapter->driver_cmds.enc_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.notify_event_cmd.cb_idx = + LEAPRAID_NOTIFY_EVENT_CB_IDX; + mutex_init(&adapter->driver_cmds.notify_event_cmd.mutex); + list_add_tail(&adapter->driver_cmds.notify_event_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.ctl_cmd.cb_idx = LEAPRAID_CTL_CB_IDX; + mutex_init(&adapter->driver_cmds.ctl_cmd.mutex); + list_add_tail(&adapter->driver_cmds.ctl_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_NOT_USED; + adapter->driver_cmds.tm_cmd.cb_idx = LEAPRAID_TM_CB_IDX; + mutex_init(&adapter->driver_cmds.tm_cmd.mutex); + list_add_tail(&adapter->driver_cmds.tm_cmd.list, + &adapter->driver_cmds.special_cmd_list); + + return 0; +} + +static void leapraid_unmask_evts(struct leapraid_adapter *adapter, u16 evt) +{ + if (evt >= LEAPRAID_MAX_EVENT_NUM) + return; + + clear_bit(evt, (unsigned long *)adapter->fw_evt_s.leapraid_evt_masks); +} + +static void leapraid_init_event_mask(struct leapraid_adapter *adapter) +{ + int i; + + for (i = 0; i < LEAPRAID_EVT_MASK_COUNT; i++) + adapter->fw_evt_s.leapraid_evt_masks[i] = + LEAPRAID_INVALID_INITIAL_VALUE; + + leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_TOPO_CHANGE_LIST); + leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_ENCL_DEV_STATUS_CHANGE); + leapraid_unmask_evts(adapter, LEAPRAID_EVT_SAS_DEV_STATUS_CHANGE); + leapraid_unmask_evts(adapter, LEAPRAID_EVT_IR_CHANGE); +} + +static void leapraid_prepare_adapter_init_req( + struct leapraid_adapter *adapter, + struct leapraid_adapter_init_req *init_req) +{ + ktime_t cur_time; + int i, chunk; + u32 reply_post_free_ary_sz; + + memset(init_req, 0, sizeof(struct leapraid_adapter_init_req)); + init_req->func = LEAPRAID_FUNC_ADAPTER_INIT; + init_req->who_init = LEAPRAID_WHOINIT_LINUX_DRIVER; + init_req->msg_ver = cpu_to_le16(LEAPRAID_MSG_VERSION); + init_req->header_ver = cpu_to_le16(LEAPRAID_HEADER_VERSION); + init_req->driver_ver = + cpu_to_le32((LEAPRAID_MAJOR_VERSION << + LEAPRAID_VER_MAJOR_SHIFT) | + (LEAPRAID_MINOR_VERSION << LEAPRAID_VER_MINOR_SHIFT) | + (LEAPRAID_BUILD_VERSION << LEAPRAID_VER_BUILD_SHIFT) | + LEAPRAID_RELEASE_VERSION); + if (adapter->notification_desc.msix_enable) + init_req->host_msix_vectors = adapter->adapter_attr.rq_cnt; + + init_req->req_frame_size = + cpu_to_le16(LEAPRAID_REQUEST_SIZE / LEAPRAID_DWORDS_BYTE_SIZE); + init_req->rep_desc_qd = + cpu_to_le16(adapter->adapter_attr.rep_desc_qd); + init_req->rep_msg_qd = + cpu_to_le16(adapter->adapter_attr.rep_msg_qd); + init_req->sense_buffer_add_high = + cpu_to_le32((u64)adapter->mem_desc.sense_data_dma >> 32); + init_req->rep_msg_dma_high = + cpu_to_le32((u64)adapter->mem_desc.rep_msg_dma >> 32); + init_req->task_desc_base_addr = + cpu_to_le64((u64)adapter->mem_desc.task_desc_dma); + init_req->rep_msg_addr_dma = + cpu_to_le64((u64)adapter->mem_desc.rep_msg_addr_dma); + if (!reset_devices) { + reply_post_free_ary_sz = + adapter->adapter_attr.rq_cnt * LEAPRAID_REP_RQ_CNT_SIZE; + memset(adapter->mem_desc.rep_desc_q_arr, 0, + reply_post_free_ary_sz); + + chunk = LEAPRAID_REP_DESC_CHUNK_SIZE; + for (i = 0; i < adapter->adapter_attr.rq_cnt; i++) + adapter->mem_desc.rep_desc_q_arr[i].rep_desc_base_addr = + cpu_to_le64 ((u64)adapter->mem_desc + .rep_desc_seg_maint[i / chunk] + .rep_desc_maint[i % chunk] + .rep_desc_dma); + + init_req->msg_flg = + LEAPRAID_ADAPTER_INIT_MSGFLG_RDPQ_ARRAY_MODE; + init_req->rep_desc_q_arr_addr = + cpu_to_le64((u64)adapter->mem_desc.rep_desc_q_arr_dma); + } else { + init_req->rep_desc_q_arr_addr = + cpu_to_le64((u64)adapter->mem_desc + .rep_desc_seg_maint[0] + .rep_desc_maint[0] + .rep_desc_dma); + } + cur_time = ktime_get_real(); + init_req->time_stamp = cpu_to_le64(ktime_to_ms(cur_time)); +} + +static int leapraid_send_adapter_init(struct leapraid_adapter *adapter) +{ + struct leapraid_adapter_init_req init_req; + struct leapraid_adapter_init_rep init_rep; + u16 adapter_status; + int rc; + + leapraid_prepare_adapter_init_req(adapter, &init_req); + + rc = leapraid_handshake_func(adapter, + sizeof(struct leapraid_adapter_init_req), + (u32 *)&init_req, + sizeof(struct leapraid_adapter_init_rep), + (u16 *)&init_rep); + if (rc != 0) { + dev_err(&adapter->pdev->dev, "%s: Handshake failed, rc=%d\n", + __func__, rc); + return rc; + } + + adapter_status = + le16_to_cpu(init_rep.adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { + dev_err(&adapter->pdev->dev, "%s: failed\n", __func__); + rc = -EIO; + } + + return rc; +} + +static int leapraid_cfg_pages(struct leapraid_adapter *adapter) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + union { + struct leapraid_manufacturing_p0 manufacturing_page0; + struct leapraid_bios_page3 bios_page3; + struct leapraid_bios_page2 bios_page2; + } cfg_page; + int rc; + + rc = leapraid_op_config_page(adapter, &cfg_page.bios_page3, cfgp1, + cfgp2, GET_BIOS_PG3); + if (rc) + return rc; + + adapter->adapter_attr.bios_version = + le32_to_cpu(cfg_page.bios_page3.bios_version); + + rc = leapraid_op_config_page(adapter, &cfg_page.bios_page2, cfgp1, + cfgp2, GET_BIOS_PG2); + if (rc) + return rc; + + adapter->boot_devs.requested_boot_dev.form = + cfg_page.bios_page2.requested_boot_dev_form; + memcpy(adapter->boot_devs.requested_boot_dev.pg_dev, + &cfg_page.bios_page2.requested_boot_dev, + LEAPRAID_BOOT_DEV_SIZE); + adapter->boot_devs.requested_alt_boot_dev.form = + cfg_page.bios_page2.requested_alt_boot_dev_form; + memcpy(adapter->boot_devs.requested_alt_boot_dev.pg_dev, + &cfg_page.bios_page2.requested_alt_boot_dev, + LEAPRAID_BOOT_DEV_SIZE); + adapter->boot_devs.current_boot_dev.form = + cfg_page.bios_page2.current_boot_dev_form; + memcpy(adapter->boot_devs.current_boot_dev.pg_dev, + &cfg_page.bios_page2.current_boot_dev, + LEAPRAID_BOOT_DEV_SIZE); + + rc = leapraid_op_config_page(adapter, &cfg_page.manufacturing_page0, + cfgp1, cfgp2, GET_MANUFACTURING_PG0); + if (rc) + return rc; + + snprintf(adapter->adapter_attr.board_name, + sizeof(adapter->adapter_attr.board_name), + "%.*s", + (int)sizeof(cfg_page.manufacturing_page0.board_name), + cfg_page.manufacturing_page0.board_name); + return rc; +} + +static int leapraid_evt_notify(struct leapraid_adapter *adapter) +{ + struct leapraid_evt_notify_req *evt_notify_req; + struct leapraid_evt_notify_rep *evt_notify_rep; + u16 adapter_status; + int rc = 0; + int i; + + mutex_lock(&adapter->driver_cmds.notify_event_cmd.mutex); + adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_PENDING; + evt_notify_req = + leapraid_get_task_desc( + adapter, + adapter->driver_cmds.notify_event_cmd.inter_taskid); + memset(evt_notify_req, 0, sizeof(struct leapraid_evt_notify_req)); + evt_notify_req->func = LEAPRAID_FUNC_EVENT_NOTIFY; + for (i = 0; i < LEAPRAID_EVT_MASK_COUNT; i++) + evt_notify_req->evt_masks[i] = + cpu_to_le32(adapter->fw_evt_s.leapraid_evt_masks[i]); + init_completion(&adapter->driver_cmds.notify_event_cmd.done); + leapraid_fire_task(adapter, + adapter->driver_cmds.notify_event_cmd.inter_taskid); + wait_for_completion_timeout( + &adapter->driver_cmds.notify_event_cmd.done, + LEAPRAID_NOTIFY_EVENT_CMD_TIMEOUT * HZ); + + if (!(adapter->driver_cmds.notify_event_cmd.status & + LEAPRAID_CMD_DONE)) { + rc = -EFAULT; + goto out_cleanup; + } + + if (!(adapter->driver_cmds.notify_event_cmd.status & + LEAPRAID_CMD_REPLY_VALID)) { + rc = -EFAULT; + goto out_cleanup; + } + + evt_notify_rep = (void *)&adapter->driver_cmds.notify_event_cmd.reply; + adapter_status = le16_to_cpu(evt_notify_rep->adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) + rc = -EFAULT; + +out_cleanup: + adapter->driver_cmds.notify_event_cmd.status = LEAPRAID_CMD_NOT_USED; + mutex_unlock(&adapter->driver_cmds.notify_event_cmd.mutex); + + return rc; +} + +int leapraid_scan_dev(struct leapraid_adapter *adapter, bool async_scan_dev) +{ + struct leapraid_scan_dev_req *scan_dev_req; + struct leapraid_scan_dev_rep *scan_dev_rep; + u16 adapter_status; + int rc = 0; + + dev_info(&adapter->pdev->dev, + "Send device scan, async_scan_dev=%d!\n", async_scan_dev); + + adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_PENDING; + adapter->driver_cmds.scan_dev_cmd.async_scan_dev = async_scan_dev; + scan_dev_req = + leapraid_get_task_desc( + adapter, + adapter->driver_cmds.scan_dev_cmd.inter_taskid); + memset(scan_dev_req, 0, sizeof(struct leapraid_scan_dev_req)); + scan_dev_req->func = LEAPRAID_FUNC_SCAN_DEV; + + if (async_scan_dev) { + adapter->scan_dev_desc.first_scan_dev_fired = 1; + leapraid_fire_task( + adapter, + adapter->driver_cmds.scan_dev_cmd.inter_taskid); + return 0; + } + + init_completion(&adapter->driver_cmds.scan_dev_cmd.done); + leapraid_fire_task(adapter, + adapter->driver_cmds.scan_dev_cmd.inter_taskid); + wait_for_completion_timeout(&adapter->driver_cmds.scan_dev_cmd.done, + LEAPRAID_SCAN_DEV_CMD_TIMEOUT * HZ); + if (!(adapter->driver_cmds.scan_dev_cmd.status & LEAPRAID_CMD_DONE)) { + dev_err(&adapter->pdev->dev, "Device scan timeout!\n"); + if (adapter->driver_cmds.scan_dev_cmd.status & + LEAPRAID_CMD_RESET) + rc = -EFAULT; + else + rc = -ETIME; + goto out_cleanup; + } + + scan_dev_rep = (void *)&adapter->driver_cmds.scan_dev_cmd.reply; + adapter_status = + le16_to_cpu(scan_dev_rep->adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + if (adapter_status != LEAPRAID_ADAPTER_STATUS_SUCCESS) { + dev_err(&adapter->pdev->dev, "Device scan failure!\n"); + rc = -EFAULT; + goto out_cleanup; + } + +out_cleanup: + adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED; + return rc; +} + +static void leapraid_init_task_tracker(struct leapraid_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->dynamic_task_desc.task_lock, flags); + + spin_unlock_irqrestore(&adapter->dynamic_task_desc.task_lock, flags); +} + +static void leapraid_init_rep_msg_addr(struct leapraid_adapter *adapter) +{ + u32 reply_address; + unsigned int i; + + for (i = 0, reply_address = (u32)adapter->mem_desc.rep_msg_dma; + i < adapter->adapter_attr.rep_msg_qd; + i++, reply_address += LEAPRAID_REPLY_SIZE) + adapter->mem_desc.rep_msg_addr[i] = cpu_to_le32(reply_address); +} + +static void init_rep_desc( + struct leapraid_rq *rq, + int index, + union leapraid_rep_desc_union *reply_post_free_contig) +{ + struct leapraid_adapter *adapter = rq->adapter; + unsigned int i; + + if (!reset_devices) + rq->rep_desc = + adapter->mem_desc + .rep_desc_seg_maint[index / + LEAPRAID_REP_DESC_CHUNK_SIZE] + .rep_desc_maint[index % + LEAPRAID_REP_DESC_CHUNK_SIZE] + .rep_desc; + else + rq->rep_desc = reply_post_free_contig; + + rq->rep_post_host_idx = 0; + for (i = 0; i < adapter->adapter_attr.rep_desc_qd; i++) + rq->rep_desc[i].words = cpu_to_le64(ULLONG_MAX); +} + +static void leapraid_init_rep_desc(struct leapraid_adapter *adapter) +{ + union leapraid_rep_desc_union *reply_post_free_contig; + struct leapraid_int_rq *int_rq; + struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; + unsigned int i; + int index; + + index = 0; + reply_post_free_contig = adapter->mem_desc + .rep_desc_seg_maint[0] + .rep_desc_maint[0] + .rep_desc; + + for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { + int_rq = &adapter->notification_desc.int_rqs[i]; + init_rep_desc(&int_rq->rq, index, reply_post_free_contig); + if (!reset_devices) + index++; + else + reply_post_free_contig += + adapter->adapter_attr.rep_desc_qd; + } + + for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { + blk_mq_poll_rq = &adapter->notification_desc.blk_mq_poll_rqs[i]; + init_rep_desc(&blk_mq_poll_rq->rq, + index, reply_post_free_contig); + if (!reset_devices) + index++; + else + reply_post_free_contig += + adapter->adapter_attr.rep_desc_qd; + } +} + +static void leapraid_init_bar_idx_regs(struct leapraid_adapter *adapter) +{ + struct leapraid_int_rq *int_rq; + struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; + unsigned int i, j; + + adapter->rep_msg_host_idx = adapter->adapter_attr.rep_msg_qd - 1; + writel(adapter->rep_msg_host_idx, + &adapter->iomem_base->rep_msg_host_idx); + + for (i = 0; i < adapter->notification_desc.iopoll_qdex; i++) { + int_rq = &adapter->notification_desc.int_rqs[i]; + for (j = 0; j < REP_POST_HOST_IDX_REG_CNT; j++) + writel((int_rq->rq.msix_idx & 7) << + LEAPRAID_RPHI_MSIX_IDX_SHIFT, + &adapter->iomem_base->rep_post_reg_idx[j].idx); + } + + for (i = 0; i < adapter->notification_desc.iopoll_qcnt; i++) { + blk_mq_poll_rq = + &adapter->notification_desc.blk_mq_poll_rqs[i]; + for (j = 0; j < REP_POST_HOST_IDX_REG_CNT; j++) + writel((blk_mq_poll_rq->rq.msix_idx & 7) << + LEAPRAID_RPHI_MSIX_IDX_SHIFT, + &adapter->iomem_base->rep_post_reg_idx[j].idx); + } +} + +static int leapraid_make_adapter_available(struct leapraid_adapter *adapter) +{ + int rc; + + leapraid_init_task_tracker(adapter); + leapraid_init_rep_msg_addr(adapter); + + if (adapter->scan_dev_desc.driver_loading) + leapraid_configure_reply_queue_affinity(adapter); + + leapraid_init_rep_desc(adapter); + rc = leapraid_send_adapter_init(adapter); + if (rc) + return rc; + + leapraid_init_bar_idx_regs(adapter); + leapraid_unmask_int(adapter); + rc = leapraid_cfg_pages(adapter); + if (rc) + return rc; + + rc = leapraid_evt_notify(adapter); + if (rc) + return rc; + + if (!adapter->access_ctrl.shost_recovering) { + adapter->scan_dev_desc.wait_scan_dev_done = 1; + return 0; + } + + return leapraid_scan_dev(adapter, false); +} + +int leapraid_ctrl_init(struct leapraid_adapter *adapter) +{ + u32 cap; + int rc; + + rc = leapraid_init_driver_cmds(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, "Init driver cmds failure\n"); + goto out_free; + } + + rc = leapraid_set_pcie_and_notification(adapter); + if (rc) + goto out_free; + + pci_set_drvdata(adapter->pdev, adapter->shost); + + pcie_capability_read_dword(adapter->pdev, PCI_EXP_DEVCAP, &cap); + + if (cap & PCI_EXP_DEVCAP_EXT_TAG) + pcie_capability_set_word(adapter->pdev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_EXT_TAG); + + rc = leapraid_fw_log_init(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, "FW log init failure\n"); + goto out_free; + } + + rc = leapraid_request_host_memory(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, "Request host memory failure\n"); + goto out_free; + } + + init_waitqueue_head(&adapter->reset_desc.reset_wait_queue); + init_waitqueue_head(&adapter->access_ctrl.shost_recover_wq); + + rc = leapraid_alloc_dev_topo_bitmaps(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, "Alloc topo bitmaps failure\n"); + goto out_free; + } + + leapraid_init_event_mask(adapter); + + rc = leapraid_make_adapter_available(adapter); + if (rc) { + dev_err(&adapter->pdev->dev, + "Make adapter available failure\n"); + goto out_free; + } + + leapraid_overheat_init(adapter); + if (!adapter->overheat_desc.fault_overheat_wq) { + rc = -ENOMEM; + goto out_free; + } + + return 0; + +out_free: + adapter->access_ctrl.host_removing = 1; + leapraid_fw_log_exit(adapter); + leapraid_disable_controller(adapter); + leapraid_free_host_memory(adapter); + leapraid_free_dev_topo_bitmaps(adapter); + pci_set_drvdata(adapter->pdev, NULL); + return rc; +} + +void leapraid_remove_ctrl(struct leapraid_adapter *adapter) +{ + leapraid_overheat_cleanup(adapter); + leapraid_check_scheduled_fault_stop(adapter); + leapraid_fw_log_stop(adapter); + leapraid_fw_log_exit(adapter); + leapraid_disable_controller(adapter); + leapraid_free_host_memory(adapter); + leapraid_free_dev_topo_bitmaps(adapter); + leapraid_free_enc_list(adapter); + pci_set_drvdata(adapter->pdev, NULL); +} diff --git a/drivers/scsi/leapraid/leapraid_func.h b/drivers/scsi/leapraid/leapraid_func.h new file mode 100644 index 000000000000..923596211aa1 --- /dev/null +++ b/drivers/scsi/leapraid/leapraid_func.h @@ -0,0 +1,1486 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 LeapIO Tech Inc. + * + * LeapRAID storage and RAID controller driver. + */ +#ifndef LEAPRAID_FUNC_H_INCLUDED +#define LEAPRAID_FUNC_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leapraid.h" + +/* Request and reply buffer size. */ +#define LEAPRAID_REQUEST_SIZE 128 +#define LEAPRAID_REPLY_SIZE 128 +#define LEAPRAID_CHAIN_SEG_SIZE 128 +#define LEAPRAID_MAX_SGES_IN_CHAIN 7 +#define LEAPRAID_DEFAULT_CHAINS_PER_IO 19 +#define LEAPRAID_DEFAULT_DIX_CHAINS_PER_IO \ + (2 * LEAPRAID_DEFAULT_CHAINS_PER_IO) /* TODO DIX */ +#define LEAPRAID_IEEE_SGE64_ENTRY_SIZE 16 +#define LEAPRAID_REP_DESC_CHUNK_SIZE 16 +#define LEAPRAID_REP_DESC_ENTRY_SIZE 8 +#define LEAPRAID_REP_MSG_ADDR_SIZE 4 +#define LEAPRAID_REP_RQ_CNT_SIZE 16 + +#define LEAPRAID_SYS_LOG_BUF_SIZE 0x200000 +#define LEAPRAID_SYS_LOG_BUF_RESERVE 0x1000 + +/* Driver version and name. */ +#define LEAPRAID_DRIVER_NAME "LeapRAID" +#define LEAPRAID_NAME_LENGTH 48 +#define LEAPRAID_BOARD_NAME_LENGTH 17 +#define LEAPRAID_AUTHOR "LeapIO Inc." +#define LEAPRAID_DESCRIPTION "LeapRAID Driver" +#define LEAPRAID_DRIVER_VERSION "2.00.01.09" +#define LEAPRAID_MAJOR_VERSION 2 +#define LEAPRAID_MINOR_VERSION 0 +#define LEAPRAID_BUILD_VERSION 1 +#define LEAPRAID_RELEASE_VERSION 9 +#define LEAPRAID_MSG_VERSION 0x1021 +#define LEAPRAID_HEADER_VERSION 0x0000 + +/* Device ID. */ +#define LEAPRAID_VENDOR_ID 0xD405 +#define LEAPRAID_DEVID_HBA 0x8200 +#define LEAPRAID_SUBVENDOR_ID 0xD405 +#define LEAPRAID_SUBDEVID_HBA 0x8200 + +#define LEAPRAID_PCI_VENDOR_ID_MASK 0xFFFF + + /* RAID virtual channel ID. */ +#define RAID_CHANNEL 1 + +/* Scatter-gather (SG) segment limits. */ +#define LEAPRAID_MAX_PHYS_SEGMENTS SG_CHUNK_SIZE + +#define LEAPRAID_KDUMP_MIN_PHYS_SEGMENTS 32 +#define LEAPRAID_SG_DEPTH LEAPRAID_MAX_PHYS_SEGMENTS + +/* Firmware and config page operations. */ +#define LEAPRAID_CFG_REQ_RETRY_TIMES 2 + +/* Hardware access helpers. */ +#define leapraid_readl(addr) readl(addr) +#define leapraid_check_reset(status) \ + (!((status) & LEAPRAID_CMD_RESET)) + +/* Polling intervals. */ +#define LEAPRAID_PCIE_LOG_POLLING_INTERVAL 1 +#define LEAPRAID_FAULT_POLLING_INTERVAL 1000 + +/* Init mask. */ +#define LEAPRAID_RESET_IRQ_MASK 0x40000000 +#define LEAPRAID_REPLY_INT_MASK 0x00000008 +#define LEAPRAID_TO_SYS_DB_MASK 0x00000001 + +/* Queue depth. */ +#define LEAPRAID_SATA_QUEUE_DEPTH 32 +#define LEAPRAID_SAS_QUEUE_DEPTH 64 +#define LEAPRAID_RAID_QUEUE_DEPTH 64 + +/* Target probe flag. */ +#define LEAPRAID_NO_ULD_ATTACH_FLAG 1 + +/* SCSI device and queue limits. */ +#define LEAPRAID_MAX_SECTORS 2048 +#define LEAPRAID_MAX_CDB_LEN 32 +#define LEAPRAID_MAX_LUNS 16384 +#define LEAPRAID_CAN_QUEUE_MIN 1 +#define LEAPRAID_THIS_ID_NONE -1 +#define LEAPRAID_CMD_PER_LUN 128 +#define LEAPRAID_MAX_SEGMENT_SIZE 0xffffffff + +/* SCSI sense and ASC/ASCQ and disk geometry configuration. */ +#define DESC_FORMAT_THRESHOLD 0x72 +#define SENSE_KEY_MASK 0x0F +#define SCSI_SENSE_RESPONSE_CODE_MASK 0x7F +#define ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED 0x5D +#define LEAPRAID_LARGE_DISK_THRESHOLD 0x200000UL +#define LEAPRAID_LARGE_DISK_HEADS 255 +#define LEAPRAID_LARGE_DISK_SECTORS 63 +#define LEAPRAID_SMALL_DISK_HEADS 64 +#define LEAPRAID_SMALL_DISK_SECTORS 32 + +/* SMP (Serial Management Protocol). */ +#define LEAPRAID_SMP_PT_FLAG_SGL_PTR 0x80 +#define LEAPRAID_SMP_FRAME_HEADER_SIZE 4 +#define LEAPRAID_SCSI_HOST_SHIFT 16 +#define LEAPRAID_SCSI_DRIVER_SHIFT 24 + +/* SCSI ASC/ASCQ definitions. */ +#define LEAPRAID_SCSI_ASCQ_DEFAULT 0x00 +#define LEAPRAID_SCSI_ASC_POWER_ON_RESET 0x29 +#define LEAPRAID_SCSI_ASC_INVALID_CMD_CODE 0x20 +#define LEAPRAID_SCSI_ASCQ_POWER_ON_RESET 0x07 + +/* VPD Page 0x89 (ATA Information). */ +#define LEAPRAID_VPD_PAGE_ATA_INFO 0x89 +#define LEAPRAID_VPD_PG89_MAX_LEN 255 +#define LEAPRAID_VPD_PG89_MIN_LEN 214 + +/* Byte index for NCQ support flag in VPD page 0x89. */ +#define LEAPRAID_VPD_PG89_NCQ_BYTE_IDX 213 +#define LEAPRAID_VPD_PG89_NCQ_BIT_SHIFT 4 +#define LEAPRAID_VPD_PG89_NCQ_BIT_MASK 0x1 + +/* Readiness polling: max retries, sleep µs between. */ +#define LEAPRAID_ADAPTER_READY_MAX_RETRY 15000 +#define LEAPRAID_ADAPTER_READY_SLEEP_MIN_US 1000 +#define LEAPRAID_ADAPTER_READY_SLEEP_MAX_US 1100 + +/* Doorbell wait parameters. */ +#define LEAPRAID_DB_WAIT_MAX_RETRY 20000 +#define LEAPRAID_DB_WAIT_DELAY_US 500 + +/* Basic data size definitions. */ +#define LEAPRAID_DWORDS_BYTE_SIZE 4 +#define LEAPRAID_WORD_BYTE_SIZE 2 + +/* SGL threshold and chain offset. */ +#define LEAPRAID_SGL_INLINE_THRESHOLD 2 +#define LEAPRAID_CHAIN_OFFSET_DWORDS 7 + +/* MSI-X group size and mask. */ +#define LEAPRAID_MSIX_GROUP_SIZE 8 +#define LEAPRAID_MSIX_GROUP_MASK 7 + +/* Basic constants and limits. */ +#define LEAPRAID_BUSY_LIMIT 1 +#define LEAPRAID_INVALID_HOST_DIAG_VAL 0xFFFFFFFF + +/* Retry/Sleep configuration. */ +#define LEAPRAID_WRSEQ_FLUSH_KEY_VALUE 0x0 +#define LEAPRAID_WRSEQ_1ST_KEY_VALUE 0xF +#define LEAPRAID_WRSEQ_2ND_KEY_VALUE 0x4 +#define LEAPRAID_WRSEQ_3RD_KEY_VALUE 0xB +#define LEAPRAID_WRSEQ_4TH_KEY_VALUE 0x2 +#define LEAPRAID_WRSEQ_5TH_KEY_VALUE 0x7 +#define LEAPRAID_WRSEQ_6TH_KEY_VALUE 0xD +#define LEAPRAID_UNLOCK_RETRY_LIMIT 20 +#define LEAPRAID_UNLOCK_SLEEP_MS 100 +#define LEAPRAID_MSLEEP_NORMAL_MS 100 +#define LEAPRAID_MSLEEP_EXTRA_LONG_MS 500 +#define LEAPRAID_IO_POLL_DELAY_US 500 + +/* Device/Volume configuration. */ +#define LEAPRAID_MAX_VOLUMES_DEFAULT 32 +#define LEAPRAID_MAX_DEV_HANDLE_DEFAULT 2048 +#define LEAPRAID_INVALID_DEV_HANDLE 0xFFFF + +/* Commands queue depth. */ +#define LEAPRAID_DEFAULT_CMD_QD_OFFSET 64 +#define LEAPRAID_REPLY_QD_ALIGNMENT 16 +/* Task ID offset. */ +#define LEAPRAID_TASKID_OFFSET_CTRL_CMD 1 +#define LEAPRAID_TASKID_OFFSET_CFG_OP_CMD 1 +#define LEAPRAID_TASKID_OFFSET_TRANSPORT_CMD 2 +#define LEAPRAID_TASKID_OFFSET_ENC_CMD 3 +#define LEAPRAID_TASKID_OFFSET_NOTIFY_EVENT_CMD 4 + +/* Task ID offset for high-priority. */ +#define LEAPRAID_HP_TASKID_OFFSET_CTL_CMD 0 +#define LEAPRAID_HP_TASKID_OFFSET_TM_CMD 1 + +/* Event/Boot configuration. */ +#define LEAPRAID_EVT_MASK_COUNT 4 +#define LEAPRAID_BOOT_DEV_SIZE 24 + +/* Commands timeout. */ +#define LEAPRAID_UNIFIED_TIMEOUT 30 +#define LEAPRAID_CFG_OP_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT +#define LEAPRAID_CTL_CMD_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT +#define LEAPRAID_SCAN_DEV_CMD_TIMEOUT 300 +#define LEAPRAID_ENC_CMD_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT +#define LEAPRAID_IO_CMD_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT +#define LEAPRAID_NOTIFY_EVENT_CMD_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT +#define LEAPRAID_TM_CMD_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT +#define LEAPRAID_TRANSPORT_CMD_TIMEOUT LEAPRAID_UNIFIED_TIMEOUT + +/* Host DMA cap. */ +#define DMA_32_BITS 32 +#define DMA_64_BITS 64 +#define LEAPRAID_DMA_ALIGN 16 + +/* Values used to represent an invalid. */ +#define LEAPRAID_INVALID_INDEX -1 +#define LEAPRAID_INVALID_MSIX_VECTORS -1 +#define LEAPRAID_INVALID_INITIAL_VALUE -1 +#define LEAPRAID_OPERATION_FAILED -1 + +/* Version shift and mask. */ +#define LEAPRAID_VER_MAJOR_SHIFT 24 +#define LEAPRAID_VER_MINOR_SHIFT 16 +#define LEAPRAID_VER_BUILD_SHIFT 8 +#define LEAPRAID_VER_MASK 0xFF + +/* Interrupt type. */ +#define LEAPRAID_INTERRUPT_MODE_MSIX 0 +#define LEAPRAID_INTERRUPT_MODE_MSI 1 +#define LEAPRAID_INTERRUPT_MODE_LEGACY 2 + +/* SMP link reset. */ +#define SMP_PHY_CONTROL_LINK_RESET 0x01 +#define SMP_PHY_CONTROL_HARD_RESET 0x02 +#define SMP_PHY_CONTROL_DISABLE 0x03 + +/** + * struct leapraid_adapter_features - Adapter features/capabilities. + * + * @req_slot: Number of request slots supported by the adapter. + * @hp_slot: Number of high-priority slots supported by the adapter. + * @adapter_caps: Adapter capabilities. + * @fw_version: Firmware version of the adapter. + * @max_dev_handle: Maximum device handle supported by the adapter. + * @min_dev_handle: Minimum device handle supported by the adapter. + */ +struct leapraid_adapter_features { + u16 req_slot; + u16 hp_slot; + u32 adapter_caps; + u32 fw_version; + u8 max_msix_vectors; + u8 max_volumes; + u16 max_dev_handle; + u16 min_dev_handle; + u16 msg_ver; + u16 product_id; +}; + +/** + * struct leapraid_adapter_attr - Adapter attributes and capabilities + * + * @id: Adapter identifier. + * @raid_support: Indicates if RAID is supported. + * @bios_version: Version of the adapter BIOS. + * @enable_mp: Indicates if multipath (MP) support is enabled. + * @wideport_max_queue_depth: Maximum queue depth for wide ports. + * @narrowport_max_queue_depth: Maximum queue depth for narrow ports. + * @sata_max_queue_depth: Maximum queue depth for SATA. + * @raid_volume_max_queue_depth: Maximum queue depth for RAID volumes. + * @features: Detailed features of the adapter. + * @adapter_total_qd: Total queue depth available on the adapter. + * @io_qd: Queue depth allocated for I/O operations. + * @rep_msg_qd: Queue depth for reply messages. + * @rep_desc_qd: Queue depth for reply descriptors. + * @rep_desc_q_seg_cnt: Number of segments in a reply descriptor queue. + * @rq_cnt: Number of request queues. + * @task_desc_dma_size: Size of task descriptor DMA memory. + * @use_32_dma_mask: Indicates if 32-bit DMA mask is used. + * @name: Adapter name string. + * @board_name: Board name retrieved from manufacturing page 0. + */ +struct leapraid_adapter_attr { + u8 id; + u8 raid_support; + u32 bios_version; + u8 enable_mp; + u32 wideport_max_queue_depth; + u32 narrowport_max_queue_depth; + u32 sata_max_queue_depth; + u32 raid_volume_max_queue_depth; + struct leapraid_adapter_features features; + u32 adapter_total_qd; + u32 io_qd; + u32 rep_msg_qd; + u32 rep_desc_qd; + u32 rep_desc_q_seg_cnt; + u16 rq_cnt; + u32 task_desc_dma_size; + u8 use_32_dma_mask; + char name[LEAPRAID_NAME_LENGTH]; + char board_name[LEAPRAID_BOARD_NAME_LENGTH]; +}; + +/** + * struct leapraid_io_req_tracker - Track a SCSI I/O request for the adapter + * + * @taskid: Unique task ID for this I/O request. + * @scmd: Pointer to the associated SCSI command. + * @chain_list: List of chain frames associated with this request. + * @msix_io: MSI-X vector assigned to this I/O request. + * @chain: Pointer to the chain memory for this request. + * @chain_dma: DMA address of the chain memory. + */ +struct leapraid_io_req_tracker { + u16 taskid; + struct scsi_cmnd *scmd; + struct list_head chain_list; + u16 msix_io; + void *chain; + dma_addr_t chain_dma; +}; + +/** + * struct leapraid_task_tracker - Tracks a task in the adapter + * + * @taskid: Unique task ID for this tracker. + * @cb_idx: Callback index associated with this task. + * @tracker_list: Linked list node to chain this tracker in lists. + */ +struct leapraid_task_tracker { + u16 taskid; + u8 cb_idx; + struct list_head tracker_list; +}; + +/** + * struct leapraid_rep_desc_maint - Maintains reply descriptor memory + * + * @rep_desc: Pointer to the reply descriptor. + * @rep_desc_dma: DMA address of the reply descriptor. + */ +struct leapraid_rep_desc_maint { + union leapraid_rep_desc_union *rep_desc; + dma_addr_t rep_desc_dma; +}; + +/** + * struct leapraid_rep_desc_seg_maint - + * Maintains reply descriptor segment memory + * + * @rep_desc_seg: Pointer to the reply descriptor segment. + * @rep_desc_seg_dma: DMA address of the reply descriptor segment. + * @rep_desc_maint: Pointer to the main reply descriptor structure. + */ +struct leapraid_rep_desc_seg_maint { + void *rep_desc_seg; + dma_addr_t rep_desc_seg_dma; + struct leapraid_rep_desc_maint *rep_desc_maint; +}; + +/** + * struct leapraid_mem_desc - Memory descriptor for LeapRAID adapter + * + * @task_desc: Pointer to task descriptor. + * @task_desc_dma: DMA address of task descriptor. + * @sg_chain_pool: DMA pool for SGL chain allocations. + * @sg_chain_pool_size: Size of the sg_chain_pool. + * @taskid_to_uniq_tag: Mapping from task ID to unique tag. + * @sense_data: Buffer for SCSI sense data. + * @sense_data_dma: DMA address of sense_data buffer. + * @rep_msg: Buffer for reply message. + * @rep_msg_dma: DMA address of reply message buffer. + * @rep_msg_addr: Pointer to reply message address. + * @rep_msg_addr_dma: DMA address of reply message address. + * @rep_desc_seg_maint: Pointer to reply descriptor segment. + * @rep_desc_q_arr: Pointer to reply descriptor queue array. + * @rep_desc_q_arr_dma: DMA address of reply descriptor queue array. + */ +struct leapraid_mem_desc { + void *task_desc; + dma_addr_t task_desc_dma; + struct dma_pool *sg_chain_pool; + u16 sg_chain_pool_size; + u16 *taskid_to_uniq_tag; + u8 *sense_data; + dma_addr_t sense_data_dma; + u8 *rep_msg; + dma_addr_t rep_msg_dma; + __le32 *rep_msg_addr; + dma_addr_t rep_msg_addr_dma; + struct leapraid_rep_desc_seg_maint *rep_desc_seg_maint; + struct leapraid_rep_desc_q_arr *rep_desc_q_arr; + dma_addr_t rep_desc_q_arr_dma; +}; + +/* internal cmd description */ +#define LEAPRAID_FIXED_INTER_CMDS 5 +#define LEAPRAID_FIXED_HP_CMDS 2 + +#define LEAPRAID_CMD_NOT_USED 0x8000 +#define LEAPRAID_CMD_DONE 0x0001 +#define LEAPRAID_CMD_PENDING 0x0002 +#define LEAPRAID_CMD_REPLY_VALID 0x0004 +#define LEAPRAID_CMD_RESET 0x0008 + +/** + * enum LEAPRAID_CB_INDEX - Callback index for LeapRAID driver + * + * @LEAPRAID_SCAN_DEV_CB_IDX: Scan device callback index. + * @LEAPRAID_CONFIG_CB_IDX: Configuration callback index. + * @LEAPRAID_TRANSPORT_CB_IDX: Transport callback index. + * @LEAPRAID_SAS_CTRL_CB_IDX: SAS controller callback index. + * @LEAPRAID_ENC_CB_IDX: Encryption callback index. + * @LEAPRAID_NOTIFY_EVENT_CB_IDX: Notify event callback index. + * @LEAPRAID_CTL_CB_IDX: Control callback index. + * @LEAPRAID_TM_CB_IDX: Task management callback index. + */ +enum LEAPRAID_CB_INDEX { + LEAPRAID_SCAN_DEV_CB_IDX = 0x1, + LEAPRAID_CONFIG_CB_IDX = 0x2, + LEAPRAID_TRANSPORT_CB_IDX = 0x3, + LEAPRAID_SAS_CTRL_CB_IDX = 0x5, + LEAPRAID_ENC_CB_IDX = 0x6, + LEAPRAID_NOTIFY_EVENT_CB_IDX = 0x7, + LEAPRAID_CTL_CB_IDX = 0x8, + LEAPRAID_TM_CB_IDX = 0x9, + LEAPRAID_NUM_CB_IDXS +}; + +/** + * struct leapraid_default_reply - Default reply frame buffer + * @pad: Raw reply data buffer returned by firmware. + * + * This structure represents a generic reply frame used by the firmware + * when no specific reply format is required. The buffer size is defined + * by LEAPRAID_REPLY_SIZE and stores the raw reply data. + */ +struct leapraid_default_reply { + u8 pad[LEAPRAID_REPLY_SIZE]; +}; + +/** + * struct leapraid_sense_buffer - SCSI sense data buffer + * @pad: Buffer used to store sense data returned by a SCSI command. + * + * This buffer holds the sense data provided by a device.The size is + * defined by SCSI_SENSE_BUFFERSIZE. + */ +struct leapraid_sense_buffer { + u8 pad[SCSI_SENSE_BUFFERSIZE]; +}; + +/** + * struct leapraid_driver_cmd - Driver command tracking structure + * + * @reply: Default reply structure returned by the adapter. + * @done: Completion object used to signal command completion. + * @status: Status code returned by the firmware. + * @taskid: Unique task identifier for this command. + * @hp_taskid: Task identifier for high-priority commands. + * @inter_taskid: Task identifier for internal commands. + * @cb_idx: Callback index used to identify completion context. + * @async_scan_dev: True if this command is for asynchronous device scan. + * @sense: Sense buffer holding error information from device. + * @mutex: Mutex to protect access to this command structure. + * @list: List node for linking driver commands into lists. + */ +struct leapraid_driver_cmd { + struct leapraid_default_reply reply; + struct completion done; + u16 status; + u16 taskid; + u16 hp_taskid; + u16 inter_taskid; + u8 cb_idx; + u8 async_scan_dev; + struct leapraid_sense_buffer sense; + struct mutex mutex; /* Mutex for driver cmd */ + struct list_head list; +}; + +/** + * struct leapraid_driver_cmds - Collection of driver command objects + * + * @special_cmd_list: List head for tracking special driver commands. + * @scan_dev_cmd: Command used for asynchronous device scan operations. + * @cfg_op_cmd: Command for configuration operations. + * @transport_cmd: Command for transport-level operations. + * @enc_cmd: Command for enclosure management operations. + * @notify_event_cmd: Command for asynchronous event notification handling. + * @ctl_cmd: Command for generic control or maintenance operations. + * @tm_cmd: Task management command. + */ +struct leapraid_driver_cmds { + struct list_head special_cmd_list; + struct leapraid_driver_cmd scan_dev_cmd; + struct leapraid_driver_cmd cfg_op_cmd; + struct leapraid_driver_cmd transport_cmd; + struct leapraid_driver_cmd enc_cmd; + struct leapraid_driver_cmd notify_event_cmd; + struct leapraid_driver_cmd ctl_cmd; + struct leapraid_driver_cmd tm_cmd; +}; + +/** + * struct leapraid_dynamic_task_desc - Dynamic task descriptor + * + * @task_lock: Spinlock to protect concurrent access. + * @hp_taskid: Current high-priority task ID. + * @hp_cmd_qd: Fixed command queue depth for high-priority tasks. + * @inter_taskid: Current internal task ID. + * @inter_cmd_qd: Fixed command queue depth for internal tasks. + */ +struct leapraid_dynamic_task_desc { + spinlock_t task_lock; /* protects dynamic task */ + u16 hp_taskid; + u16 hp_cmd_qd; + u16 inter_taskid; + u16 inter_cmd_qd; +}; + +/** + * struct leapraid_fw_evt_work - Firmware event work structure + * + * @list: Linked list node for queuing the work. + * @adapter: Pointer to the associated LeapRAID adapter. + * @work: Work structure used by the kernel workqueue. + * @refcnt: Reference counter for managing the lifetime of this work. + * @evt_data: Pointer to firmware event data. + * @dev_handle: Device handle associated with the event. + * @evt_type: Type of firmware event. + * @ignore: Flag indicating whether the event should be ignored. + */ +struct leapraid_fw_evt_work { + struct list_head list; + struct leapraid_adapter *adapter; + struct work_struct work; + struct kref refcnt; + void *evt_data; + u16 dev_handle; + u16 evt_type; + u8 ignore; +}; + +/** + * struct leapraid_fw_evt_struct - Firmware event handling structure + * + * @fw_evt_name: Name of the firmware event. + * @fw_evt_thread: Workqueue used for processing firmware events. + * @fw_evt_lock: Spinlock protecting access to the firmware event list. + * @fw_evt_list: Linked list of pending firmware events. + * @cur_evt: Pointer to the currently processing firmware event. + * @cur_evt_task: Task currently executing @cur_evt work. + * @fw_evt_cleanup: Flag indicating whether cleanup of events is in progress. + * @leapraid_evt_masks: Array of event masks for filtering firmware events. + */ +struct leapraid_fw_evt_struct { + u32 leapraid_evt_masks[4]; + char fw_evt_name[48]; + struct workqueue_struct *fw_evt_thread; + spinlock_t fw_evt_lock; /* protects firmware event */ + struct list_head fw_evt_list; + struct leapraid_fw_evt_work *cur_evt; + struct task_struct *cur_evt_task; + u8 fw_evt_cleanup; +}; + +/** + * struct leapraid_rq - Represents a LeapRAID request queue + * + * @adapter: Pointer to the associated LeapRAID adapter. + * @msix_idx: MSI-X vector index used by this queue. + * @rep_post_host_idx: Index of the last processed reply descriptor. + * @rep_desc: Pointer to the reply descriptor associated with this queue. + * @name: Name of the request queue. + * @busy: Atomic counter indicating if the queue is busy. + */ +struct leapraid_rq { + struct leapraid_adapter *adapter; + u8 msix_idx; + u32 rep_post_host_idx; + union leapraid_rep_desc_union *rep_desc; + char name[LEAPRAID_NAME_LENGTH]; + atomic_t busy; +}; + +/** + * struct leapraid_int_rq - Internal request queue for a CPU + * + * @affinity_hint: CPU affinity mask for the queue. + * @rq: Underlying LeapRAID request queue structure. + */ +struct leapraid_int_rq { + cpumask_var_t affinity_hint; + struct leapraid_rq rq; +}; + +/** + * struct leapraid_blk_mq_poll_rq - Polling request for LeapRAID blk-mq + * + * @busy: Atomic flag indicating request is being processed. + * @pause: Atomic flag to temporarily suspend polling. + * @rq: The underlying LeapRAID request structure. + */ +struct leapraid_blk_mq_poll_rq { + atomic_t busy; + atomic_t pause; + struct leapraid_rq rq; +}; + +/** + * struct leapraid_notification_desc - Notification descriptor for LeapRAID + * + * @iopoll_qdex: Index of the I/O polling queue. + * @iopoll_qcnt: Count of I/O polling queues. + * @msix_enable: Flag indicating MSI-X is enabled. + * @irq_vectors_allocated: Flag indicating PCI IRQ vectors are allocated. + * @irq_mode: Actual interrupt mode used for allocated PCI IRQ vectors. + * @msix_cpu_map: CPU-ID indexed MSI-X queue map. + * @msix_cpu_map_sz: Number of CPU-ID slots allocated in @msix_cpu_map. + * @int_rqs: Array of interrupt request queues. + * @int_rqs_allocated: Count of allocated interrupt request queues. + * @blk_mq_poll_rqs: Array of blk-mq polling requests. + */ +struct leapraid_notification_desc { + u32 iopoll_qdex; + u32 iopoll_qcnt; + u8 msix_enable; + u8 irq_vectors_allocated; + u8 irq_mode; + u8 *msix_cpu_map; + u32 msix_cpu_map_sz; + struct leapraid_int_rq *int_rqs; + u32 int_rqs_allocated; + struct leapraid_blk_mq_poll_rq *blk_mq_poll_rqs; +}; + +/** + * struct leapraid_overheat_desc - Overheat descriptor for LeapRAID + * + * @fault_overheat_wq: Workqueue for adapter thermal overheat operations. + * @fault_overheat_work: Work structure for thermal overheat. + * @thermal_alert: Flag indicating if adapter thermal alert is active. + * @fault_overheat_wq_name: Name of the fault overheat workqueue. + */ +struct leapraid_overheat_desc { + struct workqueue_struct *fault_overheat_wq; + struct work_struct fault_overheat_work; + atomic_t thermal_alert; + char fault_overheat_wq_name[48]; +}; + +/** + * struct leapraid_reset_desc - Reset descriptor for LeapRAID + * + * @fault_reset_wq: Workqueue for fault reset operations. + * @fault_reset_work: Delayed work structure for fault reset. + * @fault_reset_wq_name: Name of the fault reset workqueue. + * @host_diag_mutex: Mutex for host diagnostic operations. + * @adapter_reset_lock: Spinlock for adapter reset operations. + * @adapter_reset_mutex: Mutex for adapter reset operations. + * @adapter_link_resetting: Flag indicating if adapter link is resetting. + * @adapter_reset_results: Results of the adapter reset operation. + * @pending_io_cnt: Count of pending I/O operations. + * @reset_wait_queue: Wait queue for reset operations. + * @reset_cnt: Counter for reset operations. + */ +struct leapraid_reset_desc { + struct workqueue_struct *fault_reset_wq; + struct delayed_work fault_reset_work; + char fault_reset_wq_name[48]; + struct mutex host_diag_mutex; /* Mutex for operations. */ + spinlock_t adapter_reset_lock; /* Protects adapter reset state. */ + struct mutex adapter_reset_mutex; /* Serializes adapter reset. */ + u8 adapter_link_resetting; + int adapter_reset_results; + int pending_io_cnt; + wait_queue_head_t reset_wait_queue; + u32 reset_cnt; +}; + +/** + * struct leapraid_scan_dev_desc - Scan device descriptor for LeapRAID + * + * @wait_scan_dev_done: Flag indicating if scan device operation is done. + * @driver_loading: Flag indicating if driver is loading. + * @first_scan_dev_fired: Flag indicating if first scan device operation fired. + * @scan_dev_failed: Flag indicating if scan device operation failed. + * @scan_start: Flag indicating if scan operation started. + * @scan_start_failed: Count of failed scan start operations. + */ +struct leapraid_scan_dev_desc { + u8 wait_scan_dev_done; + u8 driver_loading; + wait_queue_head_t wait_driver_loading; + u8 first_scan_dev_fired; + u8 scan_dev_failed; + u8 scan_start; + u16 scan_start_failed; +}; + +/** + * struct leapraid_access_ctrl - Access control structure for LeapRAID + * + * @pci_access_lock: Mutex for PCI access control. + * @shost_recovering: Flag indicating if host is recovering. + * @shost_recover_async: Flag indicating if host is recovering + * and the 0xFFFF event. + * @shost_recover_wq: Wait queue for threads waiting on shost_recover_async. + * @host_removing: Flag indicating if host is being removed. + * @pcie_recovering: Flag indicating if PCIe is recovering. + */ +struct leapraid_access_ctrl { + struct mutex pci_access_lock; /* serializes PCI register access. */ + u8 shost_recovering; + wait_queue_head_t recovery_waitq; + u8 shost_recover_async; + wait_queue_head_t shost_recover_wq; + u8 host_removing; + u8 pcie_recovering; +}; + +/** + * struct leapraid_fw_log_desc - Firmware log descriptor for LeapRAID + * + * @fw_log_buffer: Buffer for firmware log data. + * @fw_log_buffer_dma: DMA address of the firmware log buffer. + * @fw_log_wq_name: Name of the firmware log workqueue. + * @fw_log_wq: Workqueue for firmware log operations. + * @fw_log_work: Delayed work structure for firmware log. + * @open_pcie_trace: Flag indicating if PCIe tracing is open. + * @fw_log_init_flag: Flag indicating if firmware log is initialized. + * @mmap_refcnt: Number of active user VMAs on fw_log_buffer. + * @mmap_waitq: Waitqueue for fw_log_buffer VMA teardown. + */ +struct leapraid_fw_log_desc { + u8 *fw_log_buffer; + dma_addr_t fw_log_buffer_dma; + char fw_log_wq_name[48]; + struct workqueue_struct *fw_log_wq; + struct delayed_work fw_log_work; + int open_pcie_trace; + int fw_log_init_flag; + atomic_t mmap_refcnt; + wait_queue_head_t mmap_waitq; +}; + +#define LEAPRAID_CARD_PORT_FLG_DIRTY 0x01 +#define LEAPRAID_CARD_PORT_FLG_NEW 0x02 +#define LEAPRAID_DISABLE_MP_PORT_ID 0xFF +/** + * struct leapraid_card_port - Card port structure for LeapRAID + * + * @list: List head for card port. + * @vphys_list: List head for virtual PHY list. + * @port_id: Port ID. + * @sas_address: SAS address. + * @phy_mask: Mask of PHY. + * @vphys_mask: Mask of virtual PHY. + * @flg: Flags for the port. + */ +struct leapraid_card_port { + struct list_head list; + struct list_head vphys_list; + u8 port_id; + u64 sas_address; + u32 phy_mask; + u32 vphys_mask; + u8 flg; +}; + +/** + * struct leapraid_card_phy - Card PHY structure for LeapRAID + * + * @port_siblings: List head for port siblings. + * @card_port: Pointer to the card port. + * @identify: SAS identify structure. + * @remote_identify: Remote SAS identify structure. + * @phy: SAS PHY structure. + * @phy_id: PHY ID. + * @hdl: Handle for the port. + * @attached_hdl: Handle for the attached port. + * @phy_is_assigned: Flag indicating if PHY is assigned. + * @vphy: Flag indicating if virtual PHY. + */ +struct leapraid_card_phy { + struct list_head port_siblings; + struct leapraid_card_port *card_port; + struct sas_identify identify; + struct sas_identify remote_identify; + struct sas_phy *phy; + u8 phy_id; + u16 hdl; + u16 attached_hdl; + u8 phy_is_assigned; + u8 vphy; +}; + +/** + * struct leapraid_topo_node - SAS topology node for LeapRAID + * + * @list: List head for linking nodes. + * @sas_port_list: List of SAS ports. + * @card_port: Associated card port. + * @card_phy: Associated card PHY. + * @rphy: SAS remote PHY device. + * @parent_dev: Parent device pointer. + * @sas_address: SAS address of this node. + * @sas_address_parent: Parent node's SAS address. + * @phys_num: Number of physical links. + * @hdl: Handle identifier. + * @enc_hdl: Enclosure handle. + * @enc_lid: Enclosure logical identifier. + * @resp: Response status flag. + */ +struct leapraid_topo_node { + struct list_head list; + struct list_head sas_port_list; + struct leapraid_card_port *card_port; + struct leapraid_card_phy *card_phy; + struct sas_rphy *rphy; + struct device *parent_dev; + u64 sas_address; + u64 sas_address_parent; + u8 phys_num; + u16 hdl; + u16 enc_hdl; + u64 enc_lid; + u8 resp; +}; + +/** + * struct leapraid_dev_topo - LeapRAID device topology management structure + * + * @topo_node_lock: Spinlock for protecting topology node operations. + * @sas_dev_lock: Spinlock for SAS device list access. + * @raid_volume_lock: Spinlock for RAID volume list access. + * @enc_lock: Spinlock for enclosure device list access. + * @sas_id: SAS domain identifier. + * @card: Main card topology node. + * @exp_list: List of expander devices. + * @enc_list: List of enclosure devices. + * @sas_dev_list: List of SAS devices. + * @sas_dev_init_list: List of SAS devices being initialized. + * @raid_volume_list: List of RAID volumes. + * @card_port_list: List of card ports. + * @pd_hdls: Array of physical disk handles. + * @blocking_hdls: Array of blocking handles. + */ +struct leapraid_dev_topo { + spinlock_t topo_node_lock; /* Protects topology node. */ + spinlock_t sas_dev_lock; /* Protects SAS device list access. */ + spinlock_t raid_volume_lock; /* Protects RAID volume list access. */ + spinlock_t enc_lock; /* Protects enclosure device list access. */ + int sas_id; + struct leapraid_topo_node card; + struct list_head exp_list; + struct list_head enc_list; + struct list_head sas_dev_list; + struct list_head sas_dev_init_list; + struct list_head raid_volume_list; + struct list_head card_port_list; + u16 pd_hdls_sz; + unsigned long *pd_hdls; + unsigned long *blocking_hdls; +}; + +/** + * struct leapraid_boot_dev - Boot device structure for LeapRAID + * + * @dev: Device pointer. + * @chnl: Channel number. + * @form: Form factor. + * @pg_dev: Config page device content. + */ +struct leapraid_boot_dev { + void *dev; + u8 chnl; + u8 form; + u8 pg_dev[24]; +}; + +/** + * struct leapraid_boot_devs - Boot device management structure + * + * @lock: Spinlock protecting boot device cache access. + * @requested_boot_dev: Requested primary boot device. + * @requested_alt_boot_dev: Requested alternate boot device. + * @current_boot_dev: Currently active boot device. + */ +struct leapraid_boot_devs { + spinlock_t lock; /* protects boot dev */ + struct leapraid_boot_dev requested_boot_dev; + struct leapraid_boot_dev requested_alt_boot_dev; + struct leapraid_boot_dev current_boot_dev; +}; + +/** + * struct leapraid_adapter - Main LeapRAID adapter structure + * + * @list: List head for adapter management. + * @shost: SCSI host structure. + * @pdev: PCI device structure. + * @iomem_base: I/O memory mapped base address. + * @rep_msg_host_idx: Host index for reply messages. + * @mask_int: Interrupt masking flag. + * @adapter_attr: Adapter attributes. + * @mem_desc: Memory descriptor. + * @driver_cmds: Driver commands. + * @dynamic_task_desc: Dynamic task descriptor. + * @fw_evt_s: Firmware event structure. + * @notification_desc: Notification descriptor. + * @reset_desc: Reset descriptor. + * @scan_dev_desc: Device scan descriptor. + * @access_ctrl: Access control. + * @fw_log_desc: Firmware log descriptor. + * @dev_topo: Device topology. + * @boot_devs: Boot devices. + * @overheat_desc: Overheat processing descriptor. + */ +struct leapraid_adapter { + struct list_head list; + struct Scsi_Host *shost; + struct pci_dev *pdev; + struct leapraid_reg_base __iomem *iomem_base; + u32 rep_msg_host_idx; + u8 mask_int; + + struct leapraid_adapter_attr adapter_attr; + struct leapraid_mem_desc mem_desc; + struct leapraid_driver_cmds driver_cmds; + struct leapraid_dynamic_task_desc dynamic_task_desc; + struct leapraid_fw_evt_struct fw_evt_s; + struct leapraid_notification_desc notification_desc; + struct leapraid_reset_desc reset_desc; + struct leapraid_scan_dev_desc scan_dev_desc; + struct leapraid_access_ctrl access_ctrl; + struct leapraid_fw_log_desc fw_log_desc; + struct leapraid_dev_topo dev_topo; + struct leapraid_boot_devs boot_devs; + struct leapraid_overheat_desc overheat_desc; +}; + +union cfg_param_1 { + u32 form; + u32 size; + u32 phy_number; +}; + +union cfg_param_2 { + u32 handle; + u32 form_specific; +}; + +enum config_page_action { + GET_BIOS_PG2, + GET_BIOS_PG3, + GET_MANUFACTURING_PG0, + GET_SAS_DEVICE_PG0, + GET_SAS_IOUNIT_PG0, + GET_SAS_IOUNIT_PG1, + GET_SAS_EXPANDER_PG0, + GET_SAS_EXPANDER_PG1, + GET_SAS_ENCLOSURE_PG0, + GET_PHY_PG0, + GET_RAID_VOLUME_PG0, + GET_RAID_VOLUME_PG1, + GET_PHY_DISK_PG0, +}; + +/** + * struct leapraid_enc_node - Enclosure node structure + * + * @list: List head for enclosure management. + * @pg0: Enclosure page 0 data. + */ +struct leapraid_enc_node { + struct list_head list; + struct leapraid_enc_p0 pg0; +}; + +/** + * struct leapraid_raid_volume - RAID volume structure + * + * @list: List head for volume management. + * @refcnt: Reference count. + * @starget: SCSI target structure. + * @sdev: SCSI device structure. + * @id: Volume ID. + * @channel: SCSI channel. + * @wwid: World wide Identifier. + * @hdl: Volume handle. + * @vol_type: Volume type. + * @pd_num: Number of physical disks. + * @resp: Response status. + * @dev_info: Device information. + */ +struct leapraid_raid_volume { + struct list_head list; + struct kref refcnt; + struct scsi_target *starget; + struct scsi_device *sdev; + unsigned int id; + unsigned int channel; + u64 wwid; + u16 hdl; + u8 vol_type; + u8 pd_num; + u8 resp; + u32 dev_info; +}; + +static inline void leapraid_raid_volume_free(struct kref *ref) +{ + kfree(container_of(ref, struct leapraid_raid_volume, refcnt)); +} + +#define leapraid_raid_volume_get(volume) \ + kref_get(&(volume)->refcnt) +#define leapraid_raid_volume_put(volume) \ + kref_put(&(volume)->refcnt, leapraid_raid_volume_free) + +#define LEAPRAID_TGT_FLG_RAID_MEMBER 0x01 +#define LEAPRAID_TGT_FLG_VOLUME 0x02 +#define LEAPRAID_NO_ULD_ATTACH 1 +/** + * struct leapraid_starget_priv - SCSI target private data + * + * @starget: SCSI target structure. + * @sas_address: SAS address. + * @hdl: Device handle. + * @num_luns: Number of LUNs. + * @flg: Flags. + * @deleted: Deletion flag. + * @tm_busy: Task management busy flag. + * @card_port: Associated card port. + * @sas_dev: SAS device structure. + */ +struct leapraid_starget_priv { + struct scsi_target *starget; + u64 sas_address; + u16 hdl; + int num_luns; + u32 flg; + u8 deleted; + u8 tm_busy; + struct leapraid_card_port *card_port; + struct leapraid_sas_dev *sas_dev; +}; + +#define LEAPRAID_DEVICE_FLG_INIT 0x01 +/** + * struct leapraid_sdev_priv - SCSI device private data + * + * @starget_priv: Associated target private data. + * @lun: Logical Unit Number. + * @flg: Flags. + * @ncq_cmd_prio_enable: Enables NCQ command priority for RT I/O. + * @block: Block flag. + * @deleted: Deletion flag. + * @sep: SEP flag. + */ +struct leapraid_sdev_priv { + struct leapraid_starget_priv *starget_priv; + unsigned int lun; + u32 flg; + u8 ncq_cmd_prio_enable; + u8 block; + u8 deleted; + u8 sep; +}; + +/** + * struct leapraid_sas_dev - SAS device structure + * + * @list: List head for device management. + * @starget: SCSI target structure. + * @card_port: Associated card port. + * @rphy: SAS remote PHY. + * @refcnt: Reference count. + * @id: Device ID. + * @channel: SCSI channel. + * @slot: Slot number. + * @phy: PHY identifier. + * @resp: Response status. + * @led_on: LED state. + * @sas_addr: SAS address. + * @dev_name: Device name. + * @hdl: Device handle. + * @parent_sas_addr: Parent SAS address. + * @enc_hdl: Enclosure handle. + * @enc_lid: Enclosure logical ID. + * @volume_hdl: Volume handle. + * @volume_wwid: Volume WWID. + * @dev_info: Device information. + * @pend_sas_rphy_add: Pending SAS remote PHY addition flag. + * @enc_level: Enclosure level. + * @port_connection: Port connection. + * @connector_name: Connector name. + */ +struct leapraid_sas_dev { + struct list_head list; + struct scsi_target *starget; + struct leapraid_card_port *card_port; + struct sas_rphy *rphy; + struct kref refcnt; + unsigned int id; + unsigned int channel; + u16 slot; + u8 phy; + u8 resp; + u8 led_on; + u64 sas_addr; + u64 dev_name; + u16 hdl; + u64 parent_sas_addr; + u16 enc_hdl; + u64 enc_lid; + u16 volume_hdl; + u64 volume_wwid; + u32 dev_info; + u8 pend_sas_rphy_add; + u8 enc_level; + u8 port_connection; + u8 connector_name[LEAPRAID_SAS_DEV_P0_CON_NAME_LEN + 1]; +}; + +static inline void leapraid_sdev_free(struct kref *ref) +{ + kfree(container_of(ref, struct leapraid_sas_dev, refcnt)); +} + +#define leapraid_sdev_get(sdev) kref_get(&(sdev)->refcnt) +#define leapraid_sdev_put(sdev) kref_put(&(sdev)->refcnt, leapraid_sdev_free) + +void leapraid_boot_dev_get(void *dev, u32 chnl); +void leapraid_boot_dev_put(void *dev, u32 chnl); + +/** + * struct leapraid_sas_port - SAS port structure + * + * @port_list: List head for port management. + * @phy_list: List of PHYs in this port. + * @port: SAS port structure. + * @card_port: Associated card port. + * @remote_identify: Remote device identification. + * @rphy: SAS remote PHY. + * @phys_num: Number of PHYs in this port. + */ +struct leapraid_sas_port { + struct list_head port_list; + struct list_head phy_list; + struct sas_port *port; + struct leapraid_card_port *card_port; + struct sas_identify remote_identify; + struct sas_rphy *rphy; + u8 phys_num; +}; + +#define LEAPRAID_VPHY_FLG_DIRTY 0x01 +/** + * struct leapraid_vphy - Virtual PHY structure + * + * @list: List head for PHY management. + * @sas_address: SAS address. + * @phy_mask: PHY mask. + * @flg: Flags. + */ +struct leapraid_vphy { + struct list_head list; + u64 sas_address; + u32 phy_mask; + u8 flg; +}; + +/** + * struct leapraid_tgt_rst_list - Target reset tracking entry + * @list: List node used to link reset entries. + * @handle: Device handle of the target being reset. + * @state: Current state of the target reset operation. + * + * Each entry represents a target device that is undergoing or has + * undergone a target reset operation. + */ +struct leapraid_tgt_rst_list { + struct list_head list; + u16 handle; + u16 state; +}; + +/** + * struct sense_info - Parsed SCSI sense information + * @sense_key: Sense key extracted from sense data. + * @asc: Additional Sense Code (ASC). + * @ascq: Additional Sense Code Qualifier (ASCQ). + * + * This structure stores key fields parsed from SCSI sense data to + * simplify error handling and reporting. + */ +struct sense_info { + u8 sense_key; + u8 asc; + u8 ascq; +}; + +/** + * struct leapraid_fw_log_info - Firmware log position information + * @user_position: Current log read position used by the driver. + * @adapter_position: Current log write position maintained by firmware. + * + * This structure tracks firmware log buffer positions used for retrieving + * log entries from the adapter. + */ +struct leapraid_fw_log_info { + u32 user_position; + u32 adapter_position; +}; + +/** + * enum reset_type - Reset type enumeration + * + * @FULL_RESET: Full hardware reset. + * @PART_RESET: Partial reset. + */ +enum reset_type { + FULL_RESET, + PART_RESET, +}; + +enum leapraid_card_port_checking_flg { + CARD_PORT_FURTHER_CHECKING_NEEDED = 0, + CARD_PORT_SKIP_CHECKING, +}; + +enum leapraid_port_checking_state { + NEW_CARD_PORT = 0, + SAME_PORT_WITH_NOTHING_CHANGED, + SAME_PORT_WITH_PARTIALLY_CHANGED_PHYS, + SAME_ADDR_WITH_PARTIALLY_CHANGED_PHYS, + SAME_ADDR_ONLY, +}; + +/** + * struct leapraid_card_port_feature - Card port feature + * + * @dirty_flg: Dirty flag indicator. + * @same_addr: Same address flag. + * @exact_phy: Exact PHY match flag. + * @phy_overlap: PHY overlap bitmap. + * @same_port: Same port flag. + * @cur_chking_old_port: Current checking old port. + * @expected_old_port: Expected old port. + * @same_addr_port_count: Same address port count. + * @checking_state: Port checking state. + */ +struct leapraid_card_port_feature { + u8 dirty_flg; + u8 same_addr; + u8 exact_phy; + u32 phy_overlap; + u8 same_port; + struct leapraid_card_port *cur_chking_old_port; + struct leapraid_card_port *expected_old_port; + int same_addr_port_count; + enum leapraid_port_checking_state checking_state; +}; + +#define SMP_REPORT_MANUFACTURER_INFORMATION_FRAME_TYPE 0x40 +#define SMP_REPORT_MANUFACTURER_INFORMATION_FUNC 0x01 + +/** + * ref: SAS-2(INCITS 457-2010) 10.4.3.5 + */ +struct leapraid_rep_manu_request { + u8 smp_frame_type; + u8 function; + u8 allocated_response_length; + u8 request_length; +}; + +/** + * ref: SAS-2(INCITS 457-2010) 10.4.3.5 + */ +struct leapraid_rep_manu_reply { + u8 smp_frame_type; + u8 function; + u8 function_result; + u8 response_length; + u16 expander_change_count; + u8 r1[2]; + u8 sas_format; + u8 r2[3]; + u8 vendor_identification[SAS_EXPANDER_VENDOR_ID_LEN]; + u8 product_identification[SAS_EXPANDER_PRODUCT_ID_LEN]; + u8 product_revision_level[SAS_EXPANDER_PRODUCT_REV_LEN]; + u8 comp_vendor_identification[SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN]; + u16 component_id; + u8 component_revision_level; + u8 r3; + u8 vendor_specific[8]; +}; + +extern struct list_head leapraid_adapter_list; +extern spinlock_t leapraid_adapter_lock; +extern char driver_name[LEAPRAID_NAME_LENGTH]; + +int leapraid_ctrl_init(struct leapraid_adapter *adapter); +void leapraid_remove_ctrl(struct leapraid_adapter *adapter); +void leapraid_check_scheduled_fault_start(struct leapraid_adapter *adapter); +void leapraid_check_scheduled_fault_stop(struct leapraid_adapter *adapter); +void leapraid_fw_log_start(struct leapraid_adapter *adapter); +void leapraid_fw_log_stop(struct leapraid_adapter *adapter); +int leapraid_set_pcie_and_notification(struct leapraid_adapter *adapter); +void leapraid_disable_controller(struct leapraid_adapter *adapter); +int leapraid_hard_reset_handler(struct leapraid_adapter *adapter, + enum reset_type type); +void leapraid_mask_int(struct leapraid_adapter *adapter); +void leapraid_unmask_int(struct leapraid_adapter *adapter); +u32 leapraid_get_adapter_state(struct leapraid_adapter *adapter); +bool leapraid_pci_removed(struct leapraid_adapter *adapter); +int leapraid_check_adapter_is_op(struct leapraid_adapter *adapter, int wait, + const char *caller); +void *leapraid_get_task_desc(struct leapraid_adapter *adapter, u16 taskid); +void *leapraid_get_sense_buffer(struct leapraid_adapter *adapter, u16 taskid); +__le32 leapraid_get_sense_buffer_dma(struct leapraid_adapter *adapter, + u16 taskid); +void *leapraid_get_reply_vaddr(struct leapraid_adapter *adapter, + u32 phys_addr); +u16 leapraid_alloc_scsiio_taskid(struct leapraid_adapter *adapter, + struct scsi_cmnd *scmd); +void leapraid_free_taskid(struct leapraid_adapter *adapter, u16 taskid); +struct leapraid_io_req_tracker *leapraid_get_io_tracker_from_taskid( + struct leapraid_adapter *adapter, u16 taskid); +struct scsi_cmnd *leapraid_get_scmd_from_taskid( + struct leapraid_adapter *adapter, u16 taskid); +int leapraid_scan_dev(struct leapraid_adapter *adapter, bool async_scan_dev); +void leapraid_scan_dev_done(struct leapraid_adapter *adapter); +void leapraid_cleanup_lists(struct leapraid_adapter *adapter); +void leapraid_wait_cmds_done(struct leapraid_adapter *adapter); +void leapraid_clean_active_scsi_cmds(struct leapraid_adapter *adapter); +void leapraid_sync_irqs(struct leapraid_adapter *adapter, bool poll); +int leapraid_rep_queue_handler(struct leapraid_rq *rq); +int leapraid_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); +void leapraid_mq_polling_pause(struct leapraid_adapter *adapter); +void leapraid_mq_polling_resume(struct leapraid_adapter *adapter); +void leapraid_set_tm_flg(struct leapraid_adapter *adapter, u16 handle); +void leapraid_clear_tm_flg(struct leapraid_adapter *adapter, u16 handle); +void leapraid_async_turn_on_led(struct leapraid_adapter *adapter, u16 handle); +int leapraid_issue_locked_tm(struct leapraid_adapter *adapter, u16 handle, + uint channel, uint id, uint lun, u8 type, + u16 taskid_task, u8 tr_method); +int leapraid_issue_tm(struct leapraid_adapter *adapter, u16 handle, + uint channel, uint id, uint lun, u8 type, + u16 taskid_task, u8 tr_method); +bool leapraid_scsiio_done(struct leapraid_adapter *adapter, u16 taskid, + u8 msix_index, u32 rep); +int leapraid_get_volume_cap(struct leapraid_adapter *adapter, + struct leapraid_raid_volume *raid_volume); +int leapraid_internal_init_cmd_priv( + struct leapraid_adapter *adapter, + struct leapraid_io_req_tracker *io_tracker); +void leapraid_internal_exit_cmd_priv( + struct leapraid_adapter *adapter, + struct leapraid_io_req_tracker *io_tracker); +void leapraid_clean_active_fw_evt(struct leapraid_adapter *adapter); +bool leapraid_scmd_find_by_lun(struct leapraid_adapter *adapter, + uint id, unsigned int lun, uint channel); +bool leapraid_scmd_find_by_tgt(struct leapraid_adapter *adapter, + uint id, uint channel); +struct leapraid_vphy *leapraid_get_vphy_by_phy(struct leapraid_card_port *port, + u32 phy); +struct leapraid_raid_volume *leapraid_raid_volume_find_by_id( + struct leapraid_adapter *adapter, uint id, uint channel); +struct leapraid_raid_volume *leapraid_raid_volume_find_by_hdl( + struct leapraid_adapter *adapter, u16 handle); +struct leapraid_topo_node *leapraid_exp_find_by_sas_address( + struct leapraid_adapter *adapter, u64 sas_address, + struct leapraid_card_port *port); +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr_and_rphy( + struct leapraid_adapter *adapter, + u64 sas_address, struct sas_rphy *rphy); +struct leapraid_sas_dev *leapraid_get_sas_dev_by_addr( + struct leapraid_adapter *adapter, u64 sas_address, + struct leapraid_card_port *port); +struct leapraid_sas_dev *leapraid_get_sas_dev_by_hdl( + struct leapraid_adapter *adapter, u16 handle); +struct leapraid_sas_dev *leapraid_get_sas_dev_from_tgt( + struct leapraid_adapter *adapter, + struct leapraid_starget_priv *tgt_priv); +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_from_tgt( + struct leapraid_adapter *adapter, + struct leapraid_starget_priv *tgt_priv); +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_hdl( + struct leapraid_adapter *adapter, u16 handle); +struct leapraid_sas_dev *leapraid_hold_lock_get_sas_dev_by_addr( + struct leapraid_adapter *adapter, u64 sas_address, + struct leapraid_card_port *port); +struct leapraid_sas_dev *leapraid_get_next_sas_dev_from_init_list( + struct leapraid_adapter *adapter); +void leapraid_sas_dev_remove_by_sas_address( + struct leapraid_adapter *adapter, + u64 sas_address, struct leapraid_card_port *port); +void leapraid_sas_dev_remove(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev); +void leapraid_raid_volume_remove(struct leapraid_adapter *adapter, + struct leapraid_raid_volume *raid_volume); +void leapraid_exp_rm(struct leapraid_adapter *adapter, + u64 sas_address, struct leapraid_card_port *port); +void leapraid_build_mpi_sg(struct leapraid_adapter *adapter, + void *sge, dma_addr_t h2c_dma_addr, size_t h2c_size, + dma_addr_t c2h_dma_addr, size_t c2h_size); +void leapraid_build_ieee_nodata_sg(struct leapraid_adapter *adapter, + void *sge); +void leapraid_build_ieee_sg(struct leapraid_adapter *adapter, + void *psge, dma_addr_t h2c_dma_addr, + size_t h2c_size, dma_addr_t c2h_dma_addr, + size_t c2h_size); +int leapraid_build_scmd_ieee_sg(struct leapraid_adapter *adapter, + struct scsi_cmnd *scmd, u16 taskid); +void leapraid_fire_scsi_io(struct leapraid_adapter *adapter, + u16 taskid, u16 handle); +void leapraid_fire_hpr_task(struct leapraid_adapter *adapter, u16 taskid, + u16 msix_task); +void leapraid_fire_task(struct leapraid_adapter *adapter, u16 taskid); +int leapraid_cfg_get_volume_hdl(struct leapraid_adapter *adapter, + u16 pd_handle, u16 *volume_handle); +int leapraid_cfg_get_volume_wwid(struct leapraid_adapter *adapter, + u16 volume_handle, u64 *wwid); +int leapraid_op_config_page(struct leapraid_adapter *adapter, + void *cfgp, union cfg_param_1 cfgp1, + union cfg_param_2 cfgp2, + enum config_page_action cfg_op); +void leapraid_log_req_context(struct leapraid_adapter *adapter, u16 smid, + const void *req_data); + +int leapraid_change_queue_depth(struct scsi_device *sdev, int qdepth); + +int leapraid_ctl_release(struct inode *inode, struct file *filep); +int leapraid_ctl_init(void); +void leapraid_ctl_exit(void); + +extern struct sas_function_template leapraid_transport_functions; +extern struct scsi_transport_template *leapraid_transport_template; +struct leapraid_sas_port *leapraid_transport_port_add( + struct leapraid_adapter *adapter, u16 handle, u64 sas_address, + struct leapraid_card_port *card_port); +void leapraid_transport_port_remove(struct leapraid_adapter *adapter, + u64 sas_address, u64 sas_address_parent, + struct leapraid_card_port *card_port); +void leapraid_transport_add_card_phy(struct leapraid_adapter *adapter, + struct leapraid_card_phy *card_phy, + struct leapraid_sas_phy_p0 *phy_pg0, + struct device *parent_dev); +int leapraid_transport_add_exp_phy(struct leapraid_adapter *adapter, + struct leapraid_card_phy *card_phy, + struct leapraid_exp_p1 *exp_pg1, + struct device *parent_dev); +void leapraid_transport_update_links(struct leapraid_adapter *adapter, + u64 sas_address, u16 handle, + u8 phy_number, u8 link_rate, + struct leapraid_card_port *card_port); +void leapraid_transport_detach_phy_to_port( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node, + struct leapraid_card_phy *card_phy); +void leapraid_transport_attach_phy_to_port( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *sas_node, + struct leapraid_card_phy *card_phy, + u64 sas_address, + struct leapraid_card_port *card_port); +enum scsi_qc_status leapraid_queuecommand(struct Scsi_Host *shost, + struct scsi_cmnd *scmd); +void leapraid_overheat_cleanup(struct leapraid_adapter *adapter); +void leapraid_smart_fault_detect(struct leapraid_adapter *adapter, u16 hdl); + +#endif /* LEAPRAID_FUNC_H_INCLUDED */ diff --git a/drivers/scsi/leapraid/leapraid_os.c b/drivers/scsi/leapraid/leapraid_os.c new file mode 100644 index 000000000000..2f3d4ac7490c --- /dev/null +++ b/drivers/scsi/leapraid/leapraid_os.c @@ -0,0 +1,2468 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 LeapIO Tech Inc. + * + * LeapRAID storage and RAID controller driver. + */ +#include + +#include "leapraid_func.h" +#include "leapraid.h" + +LIST_HEAD(leapraid_adapter_list); +DEFINE_SPINLOCK(leapraid_adapter_lock); + +MODULE_AUTHOR(LEAPRAID_AUTHOR); +MODULE_DESCRIPTION(LEAPRAID_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_VERSION(LEAPRAID_DRIVER_VERSION); + +static atomic_t leapraid_ids = ATOMIC_INIT(0); + +static int open_pcie_trace = 1; +module_param(open_pcie_trace, int, 0644); +MODULE_PARM_DESC(open_pcie_trace, "Default=1(open)/0(close)"); + +static int enable_mp = 1; +module_param(enable_mp, int, 0444); +MODULE_PARM_DESC(enable_mp, + "Enable multipath on target device. default=1(enable)"); + +static inline void leapraid_get_sense_data(char *sense, + struct sense_info *data) +{ + bool desc_format = (sense[0] & SCSI_SENSE_RESPONSE_CODE_MASK) >= + DESC_FORMAT_THRESHOLD; + + if (desc_format) { + data->sense_key = sense[1] & SENSE_KEY_MASK; + data->asc = sense[2]; + data->ascq = sense[3]; + } else { + data->sense_key = sense[2] & SENSE_KEY_MASK; + data->asc = sense[12]; + data->ascq = sense[13]; + } +} + +static struct leapraid_adapter *pdev_to_adapter(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + + if (!shost) + return NULL; + + return shost_priv(shost); +} + +void leapraid_set_tm_flg(struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_sdev_priv *sdev_priv; + struct scsi_device *sdev; + bool skip = false; + + /* Iterate over all devices. */ + shost_for_each_device(sdev, adapter->shost) { + if (skip) + continue; + + sdev_priv = sdev->hostdata; + if (!sdev_priv) + continue; + + if (sdev_priv->starget_priv->hdl == hdl) { + sdev_priv->starget_priv->tm_busy = 1; + skip = true; + } + } +} + +void leapraid_clear_tm_flg(struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_sdev_priv *sdev_priv; + struct scsi_device *sdev; + bool skip = false; + + /* Iterate over all devices. */ + shost_for_each_device(sdev, adapter->shost) { + if (skip) + continue; + + sdev_priv = sdev->hostdata; + if (!sdev_priv) + continue; + + if (sdev_priv->starget_priv->hdl == hdl) { + sdev_priv->starget_priv->tm_busy = 0; + skip = true; + } + } +} + +static int leapraid_tm_cmd_map_status(struct leapraid_adapter *adapter, + uint channel, + uint id, + uint lun, + u8 type, + u16 taskid_task) +{ + int rc = FAILED; + + if (taskid_task <= adapter->shost->can_queue) { + switch (type) { + case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET: + case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET: + if (!leapraid_scmd_find_by_lun(adapter, id, lun, + channel)) + rc = SUCCESS; + break; + case LEAPRAID_TM_TASKTYPE_TARGET_RESET: + if (!leapraid_scmd_find_by_tgt(adapter, id, channel)) + rc = SUCCESS; + break; + default: + rc = SUCCESS; + } + } + + if (taskid_task == adapter->driver_cmds.ctl_cmd.hp_taskid && + (adapter->driver_cmds.ctl_cmd.status & + LEAPRAID_CMD_DONE || + adapter->driver_cmds.ctl_cmd.status & + LEAPRAID_CMD_NOT_USED)) + rc = SUCCESS; + + return rc; +} + +static int leapraid_tm_post_processing(struct leapraid_adapter *adapter, + u16 hdl, uint channel, uint id, + uint lun, u8 type, u16 taskid_task) +{ + int rc; + + rc = leapraid_tm_cmd_map_status(adapter, channel, id, lun, + type, taskid_task); + if (rc == SUCCESS) + return rc; + + leapraid_mask_int(adapter); + leapraid_sync_irqs(adapter, true); + leapraid_unmask_int(adapter); + + return leapraid_tm_cmd_map_status(adapter, channel, id, lun, type, + taskid_task); +} + +static void leapraid_build_tm_req(struct leapraid_scsi_tm_req *scsi_tm_req, + u16 hdl, uint lun, u8 type, u8 tr_method, + u16 target_taskid) +{ + memset(scsi_tm_req, 0, sizeof(*scsi_tm_req)); + scsi_tm_req->func = LEAPRAID_FUNC_SCSI_TMF; + scsi_tm_req->dev_hdl = cpu_to_le16(hdl); + scsi_tm_req->task_type = type; + scsi_tm_req->msg_flg = tr_method; + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK || + type == LEAPRAID_TM_TASKTYPE_QUERY_TASK) + scsi_tm_req->task_mid = cpu_to_le16(target_taskid); + int_to_scsilun(lun, (struct scsi_lun *)scsi_tm_req->lun); +} + +int leapraid_issue_tm(struct leapraid_adapter *adapter, u16 hdl, uint channel, + uint id, uint lun, u8 type, + u16 target_taskid, u8 tr_method) +{ + struct leapraid_scsi_tm_req *scsi_tm_req; + struct leapraid_scsiio_req *scsiio_req; + struct leapraid_io_req_tracker *io_req_tracker = NULL; + u16 msix_task; + u16 taskid; + bool issue_reset = false; + u32 db; + int rc; + + lockdep_assert_held(&adapter->driver_cmds.tm_cmd.mutex); + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.host_removing || + adapter->access_ctrl.pcie_recovering) { + dev_info(&adapter->pdev->dev, + "%s %s: Host is recovering, skip tm command!\n", + __func__, adapter->adapter_attr.name); + return FAILED; + } + + db = leapraid_readl(&adapter->iomem_base->db); + if (db & LEAPRAID_DB_USED) { + dev_info(&adapter->pdev->dev, + "%s Unexpected db status, issuing hard reset!\n", + adapter->adapter_attr.name); + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", + __func__, __LINE__); + rc = leapraid_hard_reset_handler(adapter, FULL_RESET); + return !rc ? SUCCESS : FAILED; + } + + if ((db & LEAPRAID_DB_MASK) == LEAPRAID_DB_FAULT) { + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", + __func__, __LINE__); + rc = leapraid_hard_reset_handler(adapter, FULL_RESET); + return !rc ? SUCCESS : FAILED; + } + + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) + io_req_tracker = + leapraid_get_io_tracker_from_taskid(adapter, + target_taskid); + + adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_PENDING; + scsi_tm_req = + leapraid_get_task_desc(adapter, + adapter->driver_cmds.tm_cmd.hp_taskid); + leapraid_build_tm_req(scsi_tm_req, hdl, lun, type, tr_method, + target_taskid); + memset(&adapter->driver_cmds.tm_cmd.reply, 0, + sizeof(struct leapraid_scsi_tm_rep)); + leapraid_set_tm_flg(adapter, hdl); + init_completion(&adapter->driver_cmds.tm_cmd.done); + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK && + io_req_tracker && + io_req_tracker->msix_io < adapter->adapter_attr.rq_cnt) + msix_task = io_req_tracker->msix_io; + else + msix_task = 0; + taskid = adapter->driver_cmds.tm_cmd.hp_taskid; + leapraid_fire_hpr_task(adapter, taskid, msix_task); + wait_for_completion_timeout(&adapter->driver_cmds.tm_cmd.done, + LEAPRAID_TM_CMD_TIMEOUT * HZ); + if (!(adapter->driver_cmds.tm_cmd.status & LEAPRAID_CMD_DONE)) { + dev_err(&adapter->pdev->dev, + "%s: TM cmd timeout, status=0x%x\n", + __func__, adapter->driver_cmds.tm_cmd.status); + leapraid_log_req_context(adapter, taskid, scsi_tm_req); + issue_reset = + leapraid_check_reset( + adapter->driver_cmds.tm_cmd.status); + if (issue_reset) { + dev_info(&adapter->pdev->dev, + "%s:%d: call hard_reset\n", + __func__, __LINE__); + rc = leapraid_hard_reset_handler(adapter, FULL_RESET); + rc = !rc ? SUCCESS : FAILED; + goto out_cleanup; + } + } + + leapraid_sync_irqs(adapter, false); + + switch (type) { + case LEAPRAID_TM_TASKTYPE_TARGET_RESET: + case LEAPRAID_TM_TASKTYPE_ABRT_TASK_SET: + case LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET: + rc = leapraid_tm_post_processing(adapter, hdl, channel, id, + lun, type, target_taskid); + break; + case LEAPRAID_TM_TASKTYPE_ABORT_TASK: + rc = SUCCESS; + scsiio_req = leapraid_get_task_desc(adapter, target_taskid); + if (le16_to_cpu(scsiio_req->dev_hdl) != hdl) + break; + dev_err(&adapter->pdev->dev, "%s: Abort failed, hdl=0x%04x\n", + adapter->adapter_attr.name, hdl); + rc = FAILED; + break; + case LEAPRAID_TM_TASKTYPE_QUERY_TASK: + rc = SUCCESS; + break; + default: + rc = FAILED; + break; + } + +out_cleanup: + leapraid_clear_tm_flg(adapter, hdl); + adapter->driver_cmds.tm_cmd.status = LEAPRAID_CMD_NOT_USED; + return rc; +} + +int leapraid_issue_locked_tm(struct leapraid_adapter *adapter, u16 hdl, + uint channel, uint id, uint lun, u8 type, + u16 target_taskid, u8 tr_method) +{ + int rc; + + mutex_lock(&adapter->driver_cmds.tm_cmd.mutex); + rc = leapraid_issue_tm(adapter, hdl, channel, id, lun, type, + target_taskid, tr_method); + mutex_unlock(&adapter->driver_cmds.tm_cmd.mutex); + + return rc; +} + +void leapraid_smart_fault_detect(struct leapraid_adapter *adapter, u16 hdl) +{ + struct leapraid_starget_priv *starget_priv; + struct leapraid_sas_dev *sas_dev; + struct scsi_target *starget; + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_hdl(adapter, hdl); + if (!sas_dev) { + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return; + } + + starget = sas_dev->starget; + starget_priv = starget ? starget->hostdata : NULL; + if (!starget_priv) { + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + goto release_sdev; + } + + if (starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER || + starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) { + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + goto release_sdev; + } + + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + leapraid_async_turn_on_led(adapter, hdl); +release_sdev: + if (sas_dev) + leapraid_sdev_put(sas_dev); +} + +static void leapraid_process_sense_data(struct leapraid_adapter *adapter, + struct leapraid_scsiio_rep *scsiio_rep, + struct scsi_cmnd *scmd, u16 taskid) +{ + struct sense_info data; + const void *sense_data; + u32 sz; + + if (!(scsiio_rep->scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID)) + return; + + sense_data = leapraid_get_sense_buffer(adapter, taskid); + sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, + le32_to_cpu(scsiio_rep->sense_count)); + + memcpy(scmd->sense_buffer, sense_data, sz); + leapraid_get_sense_data(scmd->sense_buffer, &data); + if (data.asc == ASC_FAILURE_PREDICTION_THRESHOLD_EXCEEDED) + leapraid_smart_fault_detect(adapter, + le16_to_cpu(scsiio_rep->dev_hdl)); +} + +static void leapraid_handle_data_underrun( + struct leapraid_scsiio_rep *scsiio_rep, + struct scsi_cmnd *scmd, u32 xfer_cnt) +{ + u8 scsi_status = scsiio_rep->scsi_status; + u8 scsi_state = scsiio_rep->scsi_state; + + scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | scsi_status; + + if (scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID) + return; + + if (xfer_cnt < scmd->underflow) { + if (scsi_status == SAM_STAT_BUSY) + scmd->result = SAM_STAT_BUSY; + else + scmd->result = DID_SOFT_ERROR << + LEAPRAID_SCSI_HOST_SHIFT; + } else if (scsi_state & (LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED | + LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)) { + scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT; + } else if (scsi_state & LEAPRAID_SCSI_STATE_TERMINATED) { + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + } else if (!xfer_cnt && scmd->cmnd[0] == REPORT_LUNS) { + scsiio_rep->scsi_state = LEAPRAID_SCSI_STATE_AUTOSENSE_VALID; + scsiio_rep->scsi_status = SAM_STAT_CHECK_CONDITION; + scsi_build_sense(scmd, 0, ILLEGAL_REQUEST, + LEAPRAID_SCSI_ASC_INVALID_CMD_CODE, + LEAPRAID_SCSI_ASCQ_DEFAULT); + } +} + +static void leapraid_handle_success_status( + struct leapraid_scsiio_rep *scsiio_rep, + struct scsi_cmnd *scmd, + u32 response_code) +{ + u8 scsi_status = scsiio_rep->scsi_status; + u8 scsi_state = scsiio_rep->scsi_state; + + scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | scsi_status; + + if (response_code == LEAPRAID_TM_RSP_INVALID_FRAME || + (scsi_state & (LEAPRAID_SCSI_STATE_AUTOSENSE_FAILED | + LEAPRAID_SCSI_STATE_NO_SCSI_STATUS))) + scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT; + else if (scsi_state & LEAPRAID_SCSI_STATE_TERMINATED) + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; +} + +static void leapraid_scsiio_done_dispatch( + struct leapraid_adapter *adapter, + struct leapraid_scsiio_rep *scsiio_rep, + struct leapraid_sdev_priv *sdev_priv, + struct scsi_cmnd *scmd, + u16 taskid, u32 response_code) +{ + u8 scsi_status = scsiio_rep->scsi_status; + u8 scsi_state = scsiio_rep->scsi_state; + u16 adapter_status; + u32 xfer_cnt; + u32 sz; + + adapter_status = le16_to_cpu(scsiio_rep->adapter_status) & + LEAPRAID_ADAPTER_STATUS_MASK; + + xfer_cnt = le32_to_cpu(scsiio_rep->transfer_count); + scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); + + if (adapter_status == LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN && + xfer_cnt == 0 && + (scsi_status == LEAPRAID_SCSI_STATUS_BUSY || + scsi_status == LEAPRAID_SCSI_STATUS_RESERVATION_CONFLICT || + scsi_status == LEAPRAID_SCSI_STATUS_TASK_SET_FULL)) + adapter_status = LEAPRAID_ADAPTER_STATUS_SUCCESS; + + switch (adapter_status) { + case LEAPRAID_ADAPTER_STATUS_SCSI_DEVICE_NOT_THERE: + scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT; + break; + + case LEAPRAID_ADAPTER_STATUS_BUSY: + case LEAPRAID_ADAPTER_STATUS_INSUFFICIENT_RESOURCES: + scmd->result = SAM_STAT_BUSY; + break; + + case LEAPRAID_ADAPTER_STATUS_SCSI_RESIDUAL_MISMATCH: + if (xfer_cnt == 0 || scmd->underflow > xfer_cnt) + scmd->result = DID_SOFT_ERROR << + LEAPRAID_SCSI_HOST_SHIFT; + else + scmd->result = (DID_OK << LEAPRAID_SCSI_HOST_SHIFT) | + scsi_status; + break; + + case LEAPRAID_ADAPTER_STATUS_SCSI_ADAPTER_TERMINATED: + if (sdev_priv->block) { + scmd->result = DID_TRANSPORT_DISRUPTED << + LEAPRAID_SCSI_HOST_SHIFT; + return; + } + + if (scmd->device->channel == RAID_CHANNEL && + scsi_state == (LEAPRAID_SCSI_STATE_TERMINATED | + LEAPRAID_SCSI_STATE_NO_SCSI_STATUS)) { + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + break; + } + + scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT; + break; + + case LEAPRAID_ADAPTER_STATUS_SCSI_TASK_TERMINATED: + case LEAPRAID_ADAPTER_STATUS_SCSI_EXT_TERMINATED: + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + break; + + case LEAPRAID_ADAPTER_STATUS_SCSI_DATA_UNDERRUN: + leapraid_handle_data_underrun(scsiio_rep, scmd, xfer_cnt); + break; + + case LEAPRAID_ADAPTER_STATUS_SCSI_DATA_OVERRUN: + scsi_set_resid(scmd, 0); + leapraid_handle_success_status(scsiio_rep, scmd, + response_code); + break; + case LEAPRAID_ADAPTER_STATUS_SCSI_RECOVERED_ERROR: + case LEAPRAID_ADAPTER_STATUS_SUCCESS: + leapraid_handle_success_status(scsiio_rep, scmd, + response_code); + break; + + case LEAPRAID_ADAPTER_STATUS_SCSI_PROTOCOL_ERROR: + case LEAPRAID_ADAPTER_STATUS_INTERNAL_ERROR: + case LEAPRAID_ADAPTER_STATUS_SCSI_IO_DATA_ERROR: + case LEAPRAID_ADAPTER_STATUS_SCSI_TASK_MGMT_FAILED: + default: + scmd->result = DID_SOFT_ERROR << LEAPRAID_SCSI_HOST_SHIFT; + break; + } + + if (!scmd->result) + return; + + scsi_print_command(scmd); + dev_warn(&adapter->pdev->dev, + "SCSI I/O: hdl=0x%x, status: 0x%x, 0x%x, 0x%x\n", + le16_to_cpu(scsiio_rep->dev_hdl), adapter_status, + scsi_status, scsi_state); + + if (scsi_state & LEAPRAID_SCSI_STATE_AUTOSENSE_VALID) { + struct scsi_sense_hdr sshdr; + + sz = min_t(u32, SCSI_SENSE_BUFFERSIZE, + le32_to_cpu(scsiio_rep->sense_count)); + if (scsi_normalize_sense(scmd->sense_buffer, sz, + &sshdr)) + dev_warn(&adapter->pdev->dev, + "Sense: key=0x%x asc=0x%x ascq=0x%x\n", + sshdr.sense_key, sshdr.asc, + sshdr.ascq); + else + dev_warn(&adapter->pdev->dev, + "Sense: Invalid sense data\n"); + } +} + +bool leapraid_scsiio_done(struct leapraid_adapter *adapter, u16 taskid, + u8 msix_index, u32 rep) +{ + struct leapraid_scsiio_rep *scsiio_rep; + struct leapraid_sdev_priv *sdev_priv; + struct scsi_cmnd *scmd; + u32 response_code = 0; + + scmd = leapraid_get_scmd_from_taskid(adapter, taskid); + if (!scmd) + return true; + + scsiio_rep = leapraid_get_reply_vaddr(adapter, rep); + if (!scsiio_rep) { + scmd->result = DID_OK << LEAPRAID_SCSI_HOST_SHIFT; + goto out_scsiio_done; + } + + sdev_priv = scmd->device->hostdata; + if (!sdev_priv || + !sdev_priv->starget_priv || + sdev_priv->starget_priv->deleted) { + scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT; + goto out_scsiio_done; + } + + if (scsiio_rep->scsi_state & LEAPRAID_SCSI_STATE_RESPONSE_INFO_VALID) + response_code = le32_to_cpu(scsiio_rep->resp_info) & 0xFF; + + leapraid_process_sense_data(adapter, scsiio_rep, scmd, taskid); + leapraid_scsiio_done_dispatch(adapter, scsiio_rep, sdev_priv, scmd, + taskid, response_code); + +out_scsiio_done: + scsi_dma_unmap(scmd); + leapraid_free_taskid(adapter, taskid); + scsi_done(scmd); + return false; +} + +static void leapraid_probe_raid(struct leapraid_adapter *adapter) +{ + struct leapraid_raid_volume *raid_volume, *next_raid_volume; + unsigned long flags; + LIST_HEAD(head); + int rc; + + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + list_splice_init(&adapter->dev_topo.raid_volume_list, &head); + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, flags); + + list_for_each_entry_safe(raid_volume, next_raid_volume, &head, list) { + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + list_move_tail(&raid_volume->list, + &adapter->dev_topo.raid_volume_list); + if (raid_volume->starget) { + spin_unlock_irqrestore( + &adapter->dev_topo.raid_volume_lock, flags); + continue; + } + + leapraid_raid_volume_get(raid_volume); + spin_unlock_irqrestore( + &adapter->dev_topo.raid_volume_lock, flags); + + rc = scsi_add_device(adapter->shost, RAID_CHANNEL, + raid_volume->id, 0); + if (rc) + leapraid_raid_volume_remove(adapter, raid_volume); + + leapraid_raid_volume_put(raid_volume); + } +} + +static void leapraid_sas_dev_make_active(struct leapraid_adapter *adapter, + struct leapraid_sas_dev *sas_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + if (!list_empty(&sas_dev->list)) { + list_del_init(&sas_dev->list); + leapraid_sdev_put(sas_dev); + } + + leapraid_sdev_get(sas_dev); + list_add_tail(&sas_dev->list, &adapter->dev_topo.sas_dev_list); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); +} + +static void leapraid_probe_sas(struct leapraid_adapter *adapter) +{ + struct leapraid_sas_dev *sas_dev; + bool added; + + for (;;) { + sas_dev = leapraid_get_next_sas_dev_from_init_list(adapter); + if (!sas_dev) + break; + added = leapraid_transport_port_add(adapter, + sas_dev->hdl, + sas_dev->parent_sas_addr, + sas_dev->card_port); + if (!added) + goto remove_dev; + + if (!sas_dev->starget && + !adapter->scan_dev_desc.driver_loading) { + leapraid_transport_port_remove(adapter, + sas_dev->sas_addr, + sas_dev->parent_sas_addr, + sas_dev->card_port); + goto remove_dev; + } + + leapraid_sas_dev_make_active(adapter, sas_dev); + leapraid_sdev_put(sas_dev); + continue; + +remove_dev: + leapraid_sas_dev_remove(adapter, sas_dev); + leapraid_sdev_put(sas_dev); + } +} + +static bool leapraid_get_boot_dev(struct leapraid_adapter *adapter, + struct leapraid_boot_dev *boot_dev, + void **pdev, u32 *pchnl) +{ + unsigned long flags; + void *dev; + u32 chnl; + + spin_lock_irqsave(&adapter->boot_devs.lock, flags); + if (!boot_dev->dev) { + spin_unlock_irqrestore(&adapter->boot_devs.lock, flags); + return false; + } + + dev = boot_dev->dev; + chnl = boot_dev->chnl; + leapraid_boot_dev_get(dev, chnl); + spin_unlock_irqrestore(&adapter->boot_devs.lock, flags); + + *pdev = dev; + *pchnl = chnl; + return true; +} + +static void leapraid_probe_boot_dev(struct leapraid_adapter *adapter) +{ + struct leapraid_raid_volume *raid_volume; + struct leapraid_sas_dev *sas_dev; + struct leapraid_sas_port *sport; + unsigned long flags; + void *dev = NULL; + u32 chnl; + + if (leapraid_get_boot_dev(adapter, + &adapter->boot_devs.requested_boot_dev, + &dev, &chnl)) + goto boot_dev_found; + + if (leapraid_get_boot_dev(adapter, + &adapter->boot_devs.requested_alt_boot_dev, + &dev, &chnl)) + goto boot_dev_found; + + if (leapraid_get_boot_dev(adapter, + &adapter->boot_devs.current_boot_dev, + &dev, &chnl)) + goto boot_dev_found; + + return; + +boot_dev_found: + switch (chnl) { + case RAID_CHANNEL: + raid_volume = dev; + + if (raid_volume->starget) + break; + + /* TODO eedp */ + + if (scsi_add_device(adapter->shost, RAID_CHANNEL, + raid_volume->id, 0)) + leapraid_raid_volume_remove(adapter, raid_volume); + break; + default: + sas_dev = dev; + + if (sas_dev->starget) + break; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + list_move_tail(&sas_dev->list, + &adapter->dev_topo.sas_dev_list); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + if (!sas_dev->card_port) + break; + + sport = leapraid_transport_port_add(adapter, sas_dev->hdl, + sas_dev->parent_sas_addr, + sas_dev->card_port); + if (!sport) + leapraid_sas_dev_remove(adapter, sas_dev); + break; + } + + leapraid_boot_dev_put(dev, chnl); +} + +static void leapraid_probe_devices(struct leapraid_adapter *adapter) +{ + leapraid_probe_boot_dev(adapter); + + if (adapter->adapter_attr.raid_support) { + leapraid_probe_raid(adapter); + leapraid_probe_sas(adapter); + } else { + leapraid_probe_sas(adapter); + } +} + +void leapraid_scan_dev_done(struct leapraid_adapter *adapter) +{ + if (adapter->scan_dev_desc.wait_scan_dev_done) { + adapter->scan_dev_desc.wait_scan_dev_done = 0; + leapraid_probe_devices(adapter); + } + + adapter->scan_dev_desc.scan_start = 0; + + leapraid_check_scheduled_fault_start(adapter); + leapraid_fw_log_start(adapter); + adapter->scan_dev_desc.driver_loading = 0; + wake_up(&adapter->scan_dev_desc.wait_driver_loading); +} + +static const struct pci_device_id leapraid_pci_table[] = { + { PCI_DEVICE_SUB(LEAPRAID_VENDOR_ID, LEAPRAID_DEVID_HBA, + LEAPRAID_SUBVENDOR_ID, + LEAPRAID_SUBDEVID_HBA) }, + { 0, } +}; + +static inline bool leapraid_is_scmd_permitted(struct leapraid_adapter *adapter, + struct scsi_cmnd *scmd) +{ + u8 opcode; + + if (adapter->access_ctrl.pcie_recovering || + atomic_read(&adapter->overheat_desc.thermal_alert)) + return false; + + if (adapter->access_ctrl.host_removing) { + if (leapraid_pci_removed(adapter)) + return false; + + opcode = scmd->cmnd[0]; + return opcode == SYNCHRONIZE_CACHE || opcode == START_STOP; + } + return true; +} + +static bool leapraid_should_queuecommand(struct leapraid_adapter *adapter, + struct leapraid_sdev_priv *sdev_priv, + struct scsi_cmnd *scmd, + enum scsi_qc_status *rc) +{ + struct leapraid_starget_priv *starget_priv; + + if (!sdev_priv || !sdev_priv->starget_priv) + goto no_connect; + + if (!leapraid_is_scmd_permitted(adapter, scmd)) + goto no_connect; + + starget_priv = sdev_priv->starget_priv; + if (starget_priv->hdl == LEAPRAID_INVALID_DEV_HANDLE) + goto no_connect; + + if (sdev_priv->block && + scmd->device->host->shost_state == SHOST_RECOVERY && + scmd->cmnd[0] == TEST_UNIT_READY) { + scsi_build_sense(scmd, 0, UNIT_ATTENTION, + LEAPRAID_SCSI_ASC_POWER_ON_RESET, + LEAPRAID_SCSI_ASCQ_POWER_ON_RESET); + goto scsiio_done; + } + + if (adapter->access_ctrl.shost_recovering || + adapter->reset_desc.adapter_link_resetting) { + *rc = SCSI_MLQUEUE_HOST_BUSY; + return false; + } + + if (starget_priv->deleted || sdev_priv->deleted) + goto no_connect; + + if (starget_priv->tm_busy || sdev_priv->block) { + *rc = SCSI_MLQUEUE_DEVICE_BUSY; + return false; + } + + return true; + +no_connect: + scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT; +scsiio_done: + scsi_done(scmd); + + return false; +} + +static u32 build_scsiio_req_control(struct scsi_cmnd *scmd, + struct leapraid_sdev_priv *sdev_priv) +{ + u32 control; + + switch (scmd->sc_data_direction) { + case DMA_FROM_DEVICE: + control = LEAPRAID_SCSIIO_CTRL_READ; + break; + case DMA_TO_DEVICE: + control = LEAPRAID_SCSIIO_CTRL_WRITE; + break; + default: + control = LEAPRAID_SCSIIO_CTRL_NODATATRANSFER; + break; + } + + control |= LEAPRAID_SCSIIO_CTRL_SIMPLEQ; + + if (sdev_priv->ncq_cmd_prio_enable && + (IOPRIO_PRIO_CLASS(req_get_ioprio(scsi_cmd_to_rq(scmd))) == + IOPRIO_CLASS_RT)) + control |= LEAPRAID_SCSIIO_CTRL_CMDPRI; + if (scmd->cmd_len == 32) + control |= LEAPRAID_SCSIIO_CTRL_CDB_32BYTE << + LEAPRAID_SCSIIO_CTRL_CDB_LEN_SHIFT; + + return control; +} + +enum scsi_qc_status leapraid_queuecommand(struct Scsi_Host *shost, + struct scsi_cmnd *scmd) +{ + struct leapraid_adapter *adapter = shost_priv(scmd->device->host); + struct leapraid_sdev_priv *sdev_priv = scmd->device->hostdata; + struct leapraid_starget_priv *starget_priv; + struct leapraid_scsiio_req *scsiio_req; + u32 control; + u16 taskid; + u16 hdl; + enum scsi_qc_status rc = 0; + + if (!leapraid_should_queuecommand(adapter, sdev_priv, scmd, &rc)) + return rc; + + starget_priv = sdev_priv->starget_priv; + hdl = starget_priv->hdl; + control = build_scsiio_req_control(scmd, sdev_priv); + + taskid = leapraid_alloc_scsiio_taskid(adapter, scmd); + scsiio_req = leapraid_get_task_desc(adapter, taskid); + + scsiio_req->func = LEAPRAID_FUNC_SCSIIO; + if (sdev_priv->starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER) + scsiio_req->func = LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH; + else + scsiio_req->func = LEAPRAID_FUNC_SCSIIO; + + scsiio_req->dev_hdl = cpu_to_le16(hdl); + scsiio_req->data_len = cpu_to_le32(scsi_bufflen(scmd)); + scsiio_req->ctrl = cpu_to_le32(control); + scsiio_req->io_flg = cpu_to_le16(scmd->cmd_len); + scsiio_req->msg_flg = 0; + scsiio_req->sense_buffer_len = SCSI_SENSE_BUFFERSIZE; + scsiio_req->sense_buffer_low_add = + leapraid_get_sense_buffer_dma(adapter, taskid); + scsiio_req->sgl_offset0 = + offsetof(struct leapraid_scsiio_req, sgl) / + LEAPRAID_DWORDS_BYTE_SIZE; + int_to_scsilun(sdev_priv->lun, (struct scsi_lun *)scsiio_req->lun); + memcpy(scsiio_req->cdb.cdb32, scmd->cmnd, scmd->cmd_len); + if (scsiio_req->data_len) { + if (leapraid_build_scmd_ieee_sg(adapter, scmd, taskid)) { + leapraid_free_taskid(adapter, taskid); + return SCSI_MLQUEUE_HOST_BUSY; + } + } else { + leapraid_build_ieee_nodata_sg(adapter, &scsiio_req->sgl); + } + + if (likely(scsiio_req->func == LEAPRAID_FUNC_SCSIIO)) + leapraid_fire_scsi_io(adapter, taskid, + le16_to_cpu(scsiio_req->dev_hdl)); + else + leapraid_fire_task(adapter, taskid); + + dev_dbg(&adapter->pdev->dev, + "LEAPRAID_SCSIIO: Send Descriptor taskid %d, req type 0x%x\n", + taskid, scsiio_req->func); + return rc; +} + +static int leapraid_init_cmd_priv(struct Scsi_Host *shost, + struct scsi_cmnd *scmd) +{ + struct leapraid_adapter *adapter = shost_priv(shost); + struct leapraid_io_req_tracker *io_tracker; + + io_tracker = scsi_cmd_priv(scmd); + leapraid_internal_init_cmd_priv(adapter, io_tracker); + + return 0; +} + +static int leapraid_exit_cmd_priv(struct Scsi_Host *shost, + struct scsi_cmnd *scmd) +{ + struct leapraid_adapter *adapter = shost_priv(shost); + struct leapraid_io_req_tracker *io_tracker; + + io_tracker = scsi_cmd_priv(scmd); + leapraid_internal_exit_cmd_priv(adapter, io_tracker); + + return 0; +} + +static int leapraid_error_handler(struct scsi_cmnd *scmd, + const char *str, u8 type) +{ + struct leapraid_adapter *adapter = shost_priv(scmd->device->host); + struct scsi_target *starget = scmd->device->sdev_target; + struct leapraid_starget_priv *starget_priv = starget->hostdata; + struct leapraid_io_req_tracker *io_req_tracker = NULL; + struct leapraid_sdev_priv *sdev_priv; + struct leapraid_sas_dev *sas_dev = NULL; + u16 hdl; + int rc; + + dev_info(&adapter->pdev->dev, + "EH enter: type=%s, scmd=0x%p, req tag=%d\n", str, scmd, + scsi_cmd_to_rq(scmd)->tag); + scsi_print_command(scmd); + + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) { + io_req_tracker = scsi_cmd_priv(scmd); + dev_info(&adapter->pdev->dev, + "EH ABORT: scmd=0x%p, pend=%ums, tout=%ums, tag=%d\n", + scmd, + jiffies_to_msecs(jiffies - scmd->jiffies_at_alloc), + (scsi_cmd_to_rq(scmd)->timeout / HZ) * 1000, + scsi_cmd_to_rq(scmd)->tag); + } + + if (leapraid_pci_removed(adapter) || + adapter->access_ctrl.host_removing) { + dev_err(&adapter->pdev->dev, + "EH %s failed: %s scmd=0x%p\n", str, + (adapter->access_ctrl.host_removing ? + "shost removing!" : "pci_dev removed!"), scmd); + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK && + io_req_tracker && io_req_tracker->taskid) + leapraid_free_taskid(adapter, io_req_tracker->taskid); + scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT; +#ifdef FAST_IO_FAIL + rc = FAST_IO_FAIL; +#else + rc = FAILED; +#endif + goto out_eh_done; + } + + sdev_priv = scmd->device->hostdata; + if (!sdev_priv || !sdev_priv->starget_priv) { + dev_warn(&adapter->pdev->dev, + "EH %s: SAS dev or starget gone, scmd=0x%p\n", + str, scmd); + scmd->result = DID_NO_CONNECT << LEAPRAID_SCSI_HOST_SHIFT; + scsi_done(scmd); + rc = SUCCESS; + goto out_eh_done; + } + + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) { + if (!io_req_tracker) { + dev_warn(&adapter->pdev->dev, + "EH ABORT: No I/O tracker, scmd 0x%p\n", scmd); + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + rc = SUCCESS; + goto out_eh_done; + } + + if (sdev_priv->starget_priv->flg & + LEAPRAID_TGT_FLG_RAID_MEMBER || + sdev_priv->starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) { + dev_err(&adapter->pdev->dev, + "EH ABORT: Skip RAID/VOLUME, scmd=0x%p\n", + scmd); + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + rc = FAILED; + goto out_eh_done; + } + + hdl = sdev_priv->starget_priv->hdl; + } else { + hdl = 0; + if (sdev_priv->starget_priv->flg & + LEAPRAID_TGT_FLG_RAID_MEMBER) { + sas_dev = leapraid_get_sas_dev_from_tgt(adapter, + starget_priv); + if (sas_dev) + hdl = sas_dev->volume_hdl; + } else { + hdl = sdev_priv->starget_priv->hdl; + } + + if (!hdl) { + dev_err(&adapter->pdev->dev, + "EH %s failed: Target handle 0, scmd=0x%p\n", + str, scmd); + scmd->result = DID_RESET << LEAPRAID_SCSI_HOST_SHIFT; + rc = FAILED; + goto out_eh_done; + } + } + + dev_info(&adapter->pdev->dev, + "EH issue TM: type=%s, scmd=0x%p, hdl=0x%x\n", + str, scmd, hdl); + + rc = leapraid_issue_locked_tm( + adapter, + hdl, + scmd->device->channel, + scmd->device->id, + (type == LEAPRAID_TM_TASKTYPE_TARGET_RESET ? + 0 : scmd->device->lun), + type, + (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK ? + io_req_tracker->taskid : 0), + LEAPRAID_TM_MSGFLAGS_LINK_RESET); + +out_eh_done: + if (type == LEAPRAID_TM_TASKTYPE_ABORT_TASK) { + if (rc != SUCCESS) + dev_err(&adapter->pdev->dev, + "EH ABORT result: failed, scmd=0x%p\n", + scmd); + } else { + if (rc != SUCCESS) + dev_err(&adapter->pdev->dev, + "EH %s result: failed, scmd=0x%p\n", + str, scmd); + if (sas_dev) + leapraid_sdev_put(sas_dev); + } + return rc; +} + +static int leapraid_eh_abort_handler(struct scsi_cmnd *scmd) +{ + return leapraid_error_handler(scmd, "ABORT TASK", + LEAPRAID_TM_TASKTYPE_ABORT_TASK); +} + +static int leapraid_eh_device_reset_handler(struct scsi_cmnd *scmd) +{ + return leapraid_error_handler(scmd, "UNIT RESET", + LEAPRAID_TM_TASKTYPE_LOGICAL_UNIT_RESET); +} + +static int leapraid_eh_target_reset_handler(struct scsi_cmnd *scmd) +{ + return leapraid_error_handler(scmd, "TARGET RESET", + LEAPRAID_TM_TASKTYPE_TARGET_RESET); +} + +static int leapraid_eh_host_reset_handler(struct scsi_cmnd *scmd) +{ + struct leapraid_adapter *adapter = shost_priv(scmd->device->host); + int rc; + + dev_info(&adapter->pdev->dev, + "EH HOST RESET enter: scmd=%p, req tag=%d\n", + scmd, + scsi_cmd_to_rq(scmd)->tag); + scsi_print_command(scmd); + + if (adapter->scan_dev_desc.driver_loading || + adapter->access_ctrl.host_removing) { + dev_err(&adapter->pdev->dev, + "EH HOST RESET failed: %s scmd=0x%p\n", + (adapter->access_ctrl.host_removing ? + "shost removing!" : "driver loading!"), scmd); + rc = FAILED; + goto out_host_reset_done; + } + + dev_info(&adapter->pdev->dev, "%s:%d: Issuing hard reset\n", + __func__, __LINE__); + if (leapraid_hard_reset_handler(adapter, FULL_RESET) < 0) + rc = FAILED; + else + rc = SUCCESS; + +out_host_reset_done: + if (rc != SUCCESS) + dev_err(&adapter->pdev->dev, + "EH HOST RESET result: failed, scmd=0x%p\n", + scmd); + return rc; +} + +static int leapraid_sdev_init(struct scsi_device *sdev) +{ + struct leapraid_raid_volume *raid_volume; + struct leapraid_starget_priv *stgt_priv; + struct leapraid_sdev_priv *sdev_priv; + struct leapraid_adapter *adapter; + struct leapraid_sas_dev *sas_dev; + struct scsi_target *tgt; + struct Scsi_Host *shost; + unsigned long flags; + + sdev_priv = kzalloc_obj(*sdev_priv); + if (!sdev_priv) + return -ENOMEM; + + sdev_priv->lun = sdev->lun; + sdev_priv->flg = LEAPRAID_DEVICE_FLG_INIT; + tgt = scsi_target(sdev); + stgt_priv = tgt->hostdata; + stgt_priv->num_luns++; + sdev_priv->starget_priv = stgt_priv; + sdev->hostdata = sdev_priv; + if (stgt_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER) + sdev->no_uld_attach = LEAPRAID_NO_ULD_ATTACH; + + shost = dev_to_shost(&tgt->dev); + adapter = shost_priv(shost); + if (tgt->channel == RAID_CHANNEL) { + raid_volume = leapraid_raid_volume_find_by_id(adapter, + tgt->id, + tgt->channel); + if (raid_volume) { + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, + flags); + raid_volume->sdev = sdev; + spin_unlock_irqrestore( + &adapter->dev_topo.raid_volume_lock, flags); + leapraid_raid_volume_put(raid_volume); + } + } + + if (!(stgt_priv->flg & LEAPRAID_TGT_FLG_VOLUME)) { + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr( + adapter, + stgt_priv->sas_address, + stgt_priv->card_port); + if (sas_dev && !sas_dev->starget) { + sdev_printk(KERN_INFO, sdev, + "%s: Assign starget to sas_dev\n", + __func__); + sas_dev->starget = tgt; + } + + if (sas_dev) + leapraid_sdev_put(sas_dev); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + } + return 0; +} + +static bool leapraid_slave_cfg_volume(struct scsi_device *sdev, + struct queue_limits *lim) +{ + struct Scsi_Host *shost = sdev->host; + struct leapraid_adapter *adapter = shost_priv(shost); + struct leapraid_raid_volume *raid_volume; + struct leapraid_starget_priv *starget_priv; + struct leapraid_sdev_priv *sdev_priv; + int qd; + u16 hdl; + + sdev_priv = sdev->hostdata; + starget_priv = sdev_priv->starget_priv; + hdl = starget_priv->hdl; + + raid_volume = leapraid_raid_volume_find_by_hdl(adapter, hdl); + if (!raid_volume) { + sdev_printk(KERN_WARNING, sdev, + "%s: RAID volume not found, hdl=0x%x\n", + __func__, hdl); + return 1; + } + + if (leapraid_get_volume_cap(adapter, raid_volume)) { + sdev_printk(KERN_ERR, sdev, + "%s: Failed to get volume cap, hdl=0x%x\n", + __func__, hdl); + leapraid_raid_volume_put(raid_volume); + return 1; + } + + qd = (raid_volume->dev_info & LEAPRAID_DEVTYP_SSP_TGT) ? + adapter->adapter_attr.narrowport_max_queue_depth : + adapter->adapter_attr.sata_max_queue_depth; + if (raid_volume->vol_type != LEAPRAID_VOL_TYPE_RAID0) + qd = adapter->adapter_attr.raid_volume_max_queue_depth; + + sdev_printk(KERN_INFO, sdev, + "RAID volume: hdl=0x%04x, wwid=0x%016llx\n", + raid_volume->hdl, (unsigned long long)raid_volume->wwid); + + if (shost->max_sectors > LEAPRAID_MAX_SECTORS) + lim->max_hw_sectors = LEAPRAID_MAX_SECTORS; + + leapraid_change_queue_depth(sdev, qd); + leapraid_raid_volume_put(raid_volume); + return 0; +} + +static bool leapraid_slave_configure_extra(struct scsi_device *sdev, + struct leapraid_sas_dev **psas_dev, + u16 vol_hdl, u64 volume_wwid, + bool *is_target_ssp, int *qd) +{ + struct leapraid_sas_dev *sas_dev; + struct leapraid_sdev_priv *sdev_priv; + struct Scsi_Host *shost = sdev->host; + struct leapraid_adapter *adapter = shost_priv(shost); + unsigned long flags; + + sdev_priv = sdev->hostdata; + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + *is_target_ssp = false; + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr( + adapter, + sdev_priv->starget_priv->sas_address, + sdev_priv->starget_priv->card_port); + if (!sas_dev) { + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + sdev_printk(KERN_WARNING, sdev, + "%s: SAS dev not found, sas=0x%llx\n", + __func__, sdev_priv->starget_priv->sas_address); + return 1; + } + + *psas_dev = sas_dev; + sas_dev->volume_hdl = vol_hdl; + sas_dev->volume_wwid = volume_wwid; + if (sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) { + *qd = (sas_dev->port_connection > 1) ? + adapter->adapter_attr.wideport_max_queue_depth : + adapter->adapter_attr.narrowport_max_queue_depth; + *is_target_ssp = true; + if (sas_dev->dev_info & LEAPRAID_DEVTYP_SEP) + sdev_priv->sep = 1; + } else { + *qd = adapter->adapter_attr.sata_max_queue_depth; + } + + sdev_printk(KERN_INFO, sdev, + "device name=0x%016llx, SAS addr=0x%016llx\n", + (unsigned long long)sas_dev->dev_name, + (unsigned long long)sas_dev->sas_addr); + leapraid_sdev_put(sas_dev); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + return 0; +} + +static int leapraid_sdev_configure(struct scsi_device *sdev, + struct queue_limits *lim) +{ + struct leapraid_sas_dev *sas_dev; + struct leapraid_sdev_priv *sdev_priv; + struct Scsi_Host *shost = sdev->host; + struct leapraid_starget_priv *starget_priv; + struct leapraid_adapter *adapter; + u16 hdl, vol_hdl = 0; + bool is_target_ssp = false; + u64 volume_wwid = 0; + int qd = 1; + + adapter = shost_priv(shost); + sdev_priv = sdev->hostdata; + sdev_priv->flg &= ~LEAPRAID_DEVICE_FLG_INIT; + starget_priv = sdev_priv->starget_priv; + hdl = starget_priv->hdl; + if (starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) + return leapraid_slave_cfg_volume(sdev, lim); + + if (starget_priv->flg & LEAPRAID_TGT_FLG_RAID_MEMBER) { + if (leapraid_cfg_get_volume_hdl(adapter, hdl, &vol_hdl)) { + sdev_printk(KERN_WARNING, sdev, + "%s: Get volume hdl failed, hdl=0x%x\n", + __func__, hdl); + return 1; + } + + if (vol_hdl && leapraid_cfg_get_volume_wwid(adapter, vol_hdl, + &volume_wwid)) { + sdev_printk(KERN_WARNING, sdev, + "%s: Get wwid failed, volume_hdl=0x%x\n", + __func__, vol_hdl); + return 1; + } + } + + if (leapraid_slave_configure_extra(sdev, &sas_dev, vol_hdl, + volume_wwid, &is_target_ssp, &qd)) { + sdev_printk(KERN_WARNING, sdev, + "%s: slave_configure_extra failed\n", __func__); + return 1; + } + + leapraid_change_queue_depth(sdev, qd); + if (is_target_ssp) + sas_read_port_mode_page(sdev); + + return 0; +} + +static void leapraid_sdev_destroy(struct scsi_device *sdev) +{ + struct leapraid_adapter *adapter; + struct Scsi_Host *shost; + struct leapraid_sas_dev *sas_dev; + struct leapraid_starget_priv *starget_priv; + struct scsi_target *stgt; + unsigned long flags; + + if (!sdev->hostdata) + return; + + stgt = scsi_target(sdev); + starget_priv = stgt->hostdata; + starget_priv->num_luns--; + shost = dev_to_shost(&stgt->dev); + adapter = shost_priv(shost); + if (!(starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME)) { + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter, + starget_priv); + if (sas_dev && !starget_priv->num_luns) + sas_dev->starget = NULL; + if (sas_dev) + leapraid_sdev_put(sas_dev); + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + } + + kfree(sdev->hostdata); + sdev->hostdata = NULL; +} + +static int leapraid_target_alloc_raid(struct scsi_target *tgt) +{ + struct leapraid_starget_priv *starget_priv; + struct leapraid_raid_volume *raid_volume; + struct Scsi_Host *shost = dev_to_shost(&tgt->dev); + struct leapraid_adapter *adapter = shost_priv(shost); + unsigned long flags; + + starget_priv = tgt->hostdata; + raid_volume = leapraid_raid_volume_find_by_id(adapter, tgt->id, + tgt->channel); + if (raid_volume) { + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + starget_priv->hdl = raid_volume->hdl; + starget_priv->sas_address = raid_volume->wwid; + starget_priv->flg |= LEAPRAID_TGT_FLG_VOLUME; + raid_volume->starget = tgt; + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, + flags); + leapraid_raid_volume_put(raid_volume); + } + return 0; +} + +static int leapraid_target_alloc_sas(struct scsi_target *tgt) +{ + struct sas_rphy *rphy; + struct Scsi_Host *shost; + struct leapraid_sas_dev *sas_dev; + struct leapraid_adapter *adapter; + struct leapraid_starget_priv *starget_priv; + unsigned long flags; + + shost = dev_to_shost(&tgt->dev); + adapter = shost_priv(shost); + starget_priv = tgt->hostdata; + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + rphy = dev_to_rphy(tgt->dev.parent); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr_and_rphy( + adapter, + rphy->identify.sas_address, + rphy); + if (sas_dev) { + starget_priv->sas_dev = sas_dev; + starget_priv->card_port = sas_dev->card_port; + starget_priv->sas_address = sas_dev->sas_addr; + starget_priv->hdl = sas_dev->hdl; + sas_dev->channel = tgt->channel; + sas_dev->id = tgt->id; + sas_dev->starget = tgt; + if (sas_dev->hdl && + sas_dev->hdl <= + adapter->adapter_attr.features.max_dev_handle && + test_bit(sas_dev->hdl, adapter->dev_topo.pd_hdls)) + starget_priv->flg |= LEAPRAID_TGT_FLG_RAID_MEMBER; + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + return 0; +} + +static int leapraid_target_alloc(struct scsi_target *tgt) +{ + struct leapraid_starget_priv *starget_priv; + + starget_priv = kzalloc_obj(*starget_priv); + if (!starget_priv) + return -ENOMEM; + + tgt->hostdata = starget_priv; + starget_priv->starget = tgt; + starget_priv->hdl = LEAPRAID_INVALID_DEV_HANDLE; + if (tgt->channel == RAID_CHANNEL) + return leapraid_target_alloc_raid(tgt); + + return leapraid_target_alloc_sas(tgt); +} + +static void leapraid_target_destroy_raid(struct scsi_target *tgt) +{ + struct leapraid_raid_volume *raid_volume; + struct Scsi_Host *shost = dev_to_shost(&tgt->dev); + struct leapraid_adapter *adapter = shost_priv(shost); + unsigned long flags; + + raid_volume = leapraid_raid_volume_find_by_id(adapter, tgt->id, + tgt->channel); + if (raid_volume) { + spin_lock_irqsave(&adapter->dev_topo.raid_volume_lock, flags); + raid_volume->starget = NULL; + raid_volume->sdev = NULL; + spin_unlock_irqrestore(&adapter->dev_topo.raid_volume_lock, + flags); + leapraid_raid_volume_put(raid_volume); + } +} + +static void leapraid_target_destroy_sas(struct scsi_target *tgt) +{ + struct leapraid_adapter *adapter; + struct leapraid_sas_dev *sas_dev; + struct leapraid_starget_priv *starget_priv; + struct Scsi_Host *shost; + unsigned long flags; + + shost = dev_to_shost(&tgt->dev); + adapter = shost_priv(shost); + starget_priv = tgt->hostdata; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_from_tgt(adapter, + starget_priv); + if (sas_dev && + sas_dev->starget == tgt && + sas_dev->id == tgt->id && + sas_dev->channel == tgt->channel) + sas_dev->starget = NULL; + + if (sas_dev) { + starget_priv->sas_dev = NULL; + leapraid_sdev_put(sas_dev); + leapraid_sdev_put(sas_dev); + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); +} + +static void leapraid_target_destroy(struct scsi_target *tgt) +{ + struct leapraid_starget_priv *starget_priv; + + starget_priv = tgt->hostdata; + if (!starget_priv) + return; + + if (tgt->channel == RAID_CHANNEL) { + leapraid_target_destroy_raid(tgt); + goto out_free; + } + + leapraid_target_destroy_sas(tgt); + +out_free: + kfree(starget_priv); + tgt->hostdata = NULL; +} + +static bool leapraid_scan_check_status(struct leapraid_adapter *adapter, + bool *need_hard_reset) +{ + u32 adapter_state; + + if (adapter->scan_dev_desc.scan_start) { + adapter_state = leapraid_get_adapter_state(adapter); + if (adapter_state == LEAPRAID_DB_FAULT) { + *need_hard_reset = true; + return true; + } + return false; + } + + if (adapter->driver_cmds.scan_dev_cmd.status & LEAPRAID_CMD_RESET) { + dev_err(&adapter->pdev->dev, + "Device scan: Aborted due to reset\n"); + adapter->driver_cmds.scan_dev_cmd.status = + LEAPRAID_CMD_NOT_USED; + adapter->scan_dev_desc.driver_loading = 0; + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + return true; + } + + if (adapter->scan_dev_desc.scan_start_failed) { + dev_err(&adapter->pdev->dev, + "Device scan: Failed with adapter_status=0x%08x\n", + adapter->scan_dev_desc.scan_start_failed); + adapter->scan_dev_desc.driver_loading = 0; + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + adapter->scan_dev_desc.wait_scan_dev_done = 0; + adapter->access_ctrl.host_removing = 1; + return true; + } + + adapter->driver_cmds.scan_dev_cmd.status = LEAPRAID_CMD_NOT_USED; + leapraid_scan_dev_done(adapter); + return true; +} + +static int leapraid_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ + struct leapraid_adapter *adapter = shost_priv(shost); + bool need_hard_reset = false; + + if (time >= (LEAPRAID_SCAN_DEV_CMD_TIMEOUT * HZ)) { + adapter->driver_cmds.scan_dev_cmd.status = + LEAPRAID_CMD_NOT_USED; + dev_err(&adapter->pdev->dev, + "Device scan: Failed with timeout 300s\n"); + adapter->scan_dev_desc.driver_loading = 0; + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + return 1; + } + + if (!leapraid_scan_check_status(adapter, &need_hard_reset)) + return 0; + + if (need_hard_reset) { + adapter->driver_cmds.scan_dev_cmd.status = + LEAPRAID_CMD_NOT_USED; + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", + __func__, __LINE__); + if (leapraid_hard_reset_handler(adapter, PART_RESET)) { + adapter->scan_dev_desc.driver_loading = 0; + wake_up(&adapter->scan_dev_desc.wait_driver_loading); + } + } + + return 1; +} + +static void leapraid_scan_start(struct Scsi_Host *shost) +{ + struct leapraid_adapter *adapter = shost_priv(shost); + + adapter->scan_dev_desc.scan_start = 1; + leapraid_scan_dev(adapter, true); +} + +static u32 leapraid_get_raid_qd(struct leapraid_adapter *adapter, + const struct leapraid_raid_volume *raid_volume, + bool default_qd) +{ + const struct leapraid_adapter_attr *attr = &adapter->adapter_attr; + + if (raid_volume->vol_type != LEAPRAID_VOL_TYPE_RAID0) + return default_qd ? LEAPRAID_RAID_QUEUE_DEPTH : + attr->raid_volume_max_queue_depth; + + if (raid_volume->dev_info & LEAPRAID_DEVTYP_SSP_TGT) + return default_qd ? LEAPRAID_SAS_QUEUE_DEPTH : + attr->narrowport_max_queue_depth; + + return default_qd ? LEAPRAID_SATA_QUEUE_DEPTH : + attr->sata_max_queue_depth; +} + +static int leapraid_calc_max_queue_depth(struct scsi_device *sdev, int qdepth) +{ + struct Scsi_Host *shost; + struct leapraid_adapter *adapter; + struct leapraid_starget_priv *starget_priv; + struct leapraid_sdev_priv *sdev_priv; + struct leapraid_raid_volume *raid_volume; + struct leapraid_sas_dev *sas_dev; + int max_depth; + u32 default_qdepth = 0; + u32 fw_qdepth = 0; + + shost = sdev->host; + adapter = shost_priv(shost); + max_depth = shost->can_queue; + + sdev_priv = sdev->hostdata; + if (!sdev_priv) + goto out_tag_check; + + starget_priv = sdev_priv->starget_priv; + if (!starget_priv) + goto out_tag_check; + + if (starget_priv->flg & LEAPRAID_TGT_FLG_VOLUME) { + raid_volume = leapraid_raid_volume_find_by_hdl( + adapter, starget_priv->hdl); + if (raid_volume) { + default_qdepth = leapraid_get_raid_qd( + adapter, raid_volume, true); + fw_qdepth = leapraid_get_raid_qd( + adapter, raid_volume, false); + leapraid_raid_volume_put(raid_volume); + } + goto out_limit_check; + } + + sas_dev = leapraid_get_sas_dev_from_tgt(adapter, starget_priv); + if (sas_dev) { + if (sas_dev->dev_info & LEAPRAID_DEVTYP_SSP_TGT) { + default_qdepth = LEAPRAID_SAS_QUEUE_DEPTH; + fw_qdepth = (sas_dev->port_connection > 1) ? + adapter->adapter_attr.wideport_max_queue_depth : + adapter->adapter_attr.narrowport_max_queue_depth; + } + if (sas_dev->dev_info & LEAPRAID_DEVTYP_SATA_DEV) { + default_qdepth = LEAPRAID_SATA_QUEUE_DEPTH; + fw_qdepth = adapter->adapter_attr.sata_max_queue_depth; + } + leapraid_sdev_put(sas_dev); + } + +out_limit_check: + if (fw_qdepth > shost->can_queue && default_qdepth) + fw_qdepth = default_qdepth; + + if (fw_qdepth) + max_depth = min_t(int, max_depth, fw_qdepth); + +out_tag_check: + if (!sdev->tagged_supported) + max_depth = 1; + + if (qdepth > max_depth) + qdepth = max_depth; + + return qdepth; +} + +int leapraid_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + qdepth = leapraid_calc_max_queue_depth(sdev, qdepth); + scsi_change_queue_depth(sdev, qdepth); + return sdev->queue_depth; +} + +static void leapraid_map_queues(struct Scsi_Host *shost) +{ + struct leapraid_adapter *adapter; + struct blk_mq_queue_map *queue_map; + int msix_queue_count; + int poll_queue_count; + int queue_offset; + int map_index; + + adapter = (struct leapraid_adapter *)shost->hostdata; + if (shost->nr_hw_queues == 1) + return; + + msix_queue_count = adapter->notification_desc.iopoll_qdex; + poll_queue_count = adapter->adapter_attr.rq_cnt - msix_queue_count; + + queue_offset = 0; + for (map_index = 0; map_index < shost->nr_maps; map_index++) { + queue_map = &shost->tag_set.map[map_index]; + queue_map->nr_queues = 0; + + switch (map_index) { + case HCTX_TYPE_DEFAULT: + queue_map->nr_queues = msix_queue_count; + queue_map->queue_offset = queue_offset; + WARN_ON_ONCE(!queue_map->nr_queues); + blk_mq_map_hw_queues(queue_map, + &adapter->pdev->dev, 0); + break; + case HCTX_TYPE_POLL: + queue_map->nr_queues = poll_queue_count; + queue_map->queue_offset = queue_offset; + blk_mq_map_queues(queue_map); + break; + default: + queue_map->queue_offset = queue_offset; + blk_mq_map_hw_queues(queue_map, + &adapter->pdev->dev, 0); + break; + } + queue_offset += queue_map->nr_queues; + } +} + +int leapraid_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num) +{ + struct leapraid_adapter *adapter = + (struct leapraid_adapter *)shost->hostdata; + struct leapraid_blk_mq_poll_rq *blk_mq_poll_rq; + int num_entries; + int qid = queue_num - adapter->notification_desc.iopoll_qdex; + + blk_mq_poll_rq = &adapter->notification_desc.blk_mq_poll_rqs[qid]; + if (atomic_read(&blk_mq_poll_rq->pause) || + !atomic_add_unless(&blk_mq_poll_rq->busy, 1, 1)) + return 0; + + num_entries = leapraid_rep_queue_handler(&blk_mq_poll_rq->rq); + atomic_dec(&blk_mq_poll_rq->busy); + return num_entries; +} + +static int leapraid_bios_param(struct scsi_device *sdev, + struct gendisk *disk, sector_t capacity, + int geom[]) +{ + int heads; + int sectors; + sector_t cylinders; + + if (scsi_partsize(disk, capacity, geom)) + return 0; + + if ((ulong)capacity >= LEAPRAID_LARGE_DISK_THRESHOLD) { + heads = LEAPRAID_LARGE_DISK_HEADS; + sectors = LEAPRAID_LARGE_DISK_SECTORS; + } else { + heads = LEAPRAID_SMALL_DISK_HEADS; + sectors = LEAPRAID_SMALL_DISK_SECTORS; + } + + cylinders = capacity; + sector_div(cylinders, heads * sectors); + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + return 0; +} + +static ssize_t fw_queue_depth_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct leapraid_adapter *adapter = shost_priv(shost); + + return sysfs_emit(buf, "%02d\n", + adapter->adapter_attr.features.req_slot); +} + +static ssize_t host_sas_address_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct leapraid_adapter *adapter = shost_priv(shost); + + return sysfs_emit(buf, "0x%016llx\n", + (unsigned long long)adapter->dev_topo.card.sas_address); +} + +static ssize_t board_name_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct leapraid_adapter *adapter = shost_priv(shost); + + return sysfs_emit(buf, "%s\n", adapter->adapter_attr.board_name); +} + +static DEVICE_ATTR_RO(fw_queue_depth); +static DEVICE_ATTR_RO(host_sas_address); +static DEVICE_ATTR_RO(board_name); + +static struct attribute *leapraid_shost_attrs[] = { + &dev_attr_fw_queue_depth.attr, + &dev_attr_host_sas_address.attr, + &dev_attr_board_name.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(leapraid_shost); + +static ssize_t sas_device_handle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata; + + if (!sas_device_priv_data || !sas_device_priv_data->starget_priv) { + dev_err(&sdev->sdev_gendev, + "%s: Invalid sdev_priv or starget_priv\n", __func__); + return -EINVAL; + } + + return sysfs_emit(buf, "0x%04x\n", + sas_device_priv_data->starget_priv->hdl); +} + +static ssize_t ncq_cmd_prio_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata; + + if (!sas_device_priv_data) { + dev_err(&sdev->sdev_gendev, + "%s: Invalid sdev_priv\n", __func__); + return -EINVAL; + } + + return sysfs_emit(buf, "%d\n", + sas_device_priv_data->ncq_cmd_prio_enable); +} + +static ssize_t ncq_cmd_prio_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct scsi_device *sdev = to_scsi_device(dev); + struct leapraid_sdev_priv *sas_device_priv_data = sdev->hostdata; + struct scsi_vpd *vpd_pg89; + int ncq_cmd_prio_enable; + bool ncq_supported; + + if (!sas_device_priv_data) { + dev_err(&sdev->sdev_gendev, + "%s: Invalid sdev_priv\n", __func__); + return -EINVAL; + } + + if (kstrtoint(buf, 0, &ncq_cmd_prio_enable)) + return -EINVAL; + + if (ncq_cmd_prio_enable != 0 && ncq_cmd_prio_enable != 1) { + dev_err(&sdev->sdev_gendev, + "%s: Invalid NCQ cmd prio %d (0/1 only)\n", + __func__, ncq_cmd_prio_enable); + return -EINVAL; + } + + rcu_read_lock(); + vpd_pg89 = rcu_dereference(sdev->vpd_pg89); + if (!vpd_pg89 || vpd_pg89->len < LEAPRAID_VPD_PG89_MIN_LEN) { + rcu_read_unlock(); + return -EINVAL; + } + + ncq_supported = (vpd_pg89->data[LEAPRAID_VPD_PG89_NCQ_BYTE_IDX] >> + LEAPRAID_VPD_PG89_NCQ_BIT_SHIFT) & + LEAPRAID_VPD_PG89_NCQ_BIT_MASK; + rcu_read_unlock(); + if (ncq_supported) + sas_device_priv_data->ncq_cmd_prio_enable = + ncq_cmd_prio_enable; + return count; +} + +static DEVICE_ATTR_RO(sas_device_handle); + +static DEVICE_ATTR_RW(ncq_cmd_prio_enable); + +static struct attribute *leapraid_sdev_attrs[] = { + &dev_attr_sas_device_handle.attr, + &dev_attr_ncq_cmd_prio_enable.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(leapraid_sdev); + +static struct scsi_host_template leapraid_driver_template = { + .module = THIS_MODULE, + .name = "LEAPIO RAID Host", + .proc_name = LEAPRAID_DRIVER_NAME, + .queuecommand = leapraid_queuecommand, + .cmd_size = sizeof(struct leapraid_io_req_tracker), + .init_cmd_priv = leapraid_init_cmd_priv, + .exit_cmd_priv = leapraid_exit_cmd_priv, + .eh_abort_handler = leapraid_eh_abort_handler, + .eh_device_reset_handler = leapraid_eh_device_reset_handler, + .eh_target_reset_handler = leapraid_eh_target_reset_handler, + .eh_host_reset_handler = leapraid_eh_host_reset_handler, + .sdev_init = leapraid_sdev_init, + .sdev_destroy = leapraid_sdev_destroy, + .sdev_configure = leapraid_sdev_configure, + .target_alloc = leapraid_target_alloc, + .target_destroy = leapraid_target_destroy, + .scan_finished = leapraid_scan_finished, + .scan_start = leapraid_scan_start, + .change_queue_depth = leapraid_change_queue_depth, + .map_queues = leapraid_map_queues, + .mq_poll = leapraid_blk_mq_poll, + .bios_param = leapraid_bios_param, + .can_queue = LEAPRAID_CAN_QUEUE_MIN, + .this_id = LEAPRAID_THIS_ID_NONE, + .sg_tablesize = LEAPRAID_SG_DEPTH, + .max_sectors = LEAPRAID_MAX_SECTORS, + .max_segment_size = LEAPRAID_MAX_SEGMENT_SIZE, + .cmd_per_lun = LEAPRAID_CMD_PER_LUN, + .shost_groups = leapraid_shost_groups, + .sdev_groups = leapraid_sdev_groups, + .track_queue_depth = 1, +}; + +static void leapraid_lock_init(struct leapraid_adapter *adapter) +{ + mutex_init(&adapter->reset_desc.adapter_reset_mutex); + mutex_init(&adapter->reset_desc.host_diag_mutex); + mutex_init(&adapter->access_ctrl.pci_access_lock); + + spin_lock_init(&adapter->reset_desc.adapter_reset_lock); + spin_lock_init(&adapter->dynamic_task_desc.task_lock); + spin_lock_init(&adapter->dev_topo.sas_dev_lock); + spin_lock_init(&adapter->dev_topo.topo_node_lock); + spin_lock_init(&adapter->fw_evt_s.fw_evt_lock); + spin_lock_init(&adapter->dev_topo.raid_volume_lock); + spin_lock_init(&adapter->dev_topo.enc_lock); + spin_lock_init(&adapter->boot_devs.lock); +} + +static void leapraid_list_init(struct leapraid_adapter *adapter) +{ + INIT_LIST_HEAD(&adapter->dev_topo.sas_dev_list); + INIT_LIST_HEAD(&adapter->dev_topo.card_port_list); + INIT_LIST_HEAD(&adapter->dev_topo.sas_dev_init_list); + INIT_LIST_HEAD(&adapter->dev_topo.exp_list); + INIT_LIST_HEAD(&adapter->dev_topo.enc_list); + INIT_LIST_HEAD(&adapter->fw_evt_s.fw_evt_list); + INIT_LIST_HEAD(&adapter->dev_topo.raid_volume_list); + INIT_LIST_HEAD(&adapter->dev_topo.card.sas_port_list); +} + +static int leapraid_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct leapraid_adapter *adapter; + struct Scsi_Host *shost; + int iopoll_q_count; + int rc; + + shost = scsi_host_alloc(&leapraid_driver_template, + sizeof(struct leapraid_adapter)); + if (!shost) { + dev_err(&pdev->dev, + "%s: SCSI host alloc failed\n", __func__); + return -ENODEV; + } + + adapter = shost_priv(shost); + memset(adapter, 0, sizeof(struct leapraid_adapter)); + + init_waitqueue_head(&adapter->access_ctrl.recovery_waitq); + adapter->adapter_attr.id = atomic_inc_return(&leapraid_ids) - 1; + + adapter->adapter_attr.enable_mp = enable_mp; + + adapter = shost_priv(shost); + INIT_LIST_HEAD(&adapter->list); + + adapter->shost = shost; + adapter->pdev = pdev; + adapter->fw_log_desc.open_pcie_trace = open_pcie_trace; + atomic_set(&adapter->fw_log_desc.mmap_refcnt, 0); + init_waitqueue_head(&adapter->fw_log_desc.mmap_waitq); + leapraid_lock_init(adapter); + leapraid_list_init(adapter); + snprintf(adapter->adapter_attr.name, LEAPRAID_NAME_LENGTH, "%s%d", + LEAPRAID_DRIVER_NAME, adapter->adapter_attr.id); + + shost->max_cmd_len = LEAPRAID_MAX_CDB_LEN; + shost->max_lun = LEAPRAID_MAX_LUNS; + shost->transportt = leapraid_transport_template; + shost->unique_id = adapter->adapter_attr.id; + + snprintf(adapter->fw_evt_s.fw_evt_name, + sizeof(adapter->fw_evt_s.fw_evt_name), + "fw_event_%s%d", LEAPRAID_DRIVER_NAME, + adapter->adapter_attr.id); + adapter->fw_evt_s.fw_evt_thread = + alloc_ordered_workqueue(adapter->fw_evt_s.fw_evt_name, 0); + if (!adapter->fw_evt_s.fw_evt_thread) { + dev_err(&adapter->pdev->dev, + "%s: Failed to create fw event workqueue\n", __func__); + rc = -ENODEV; + goto evt_wq_fail; + } + + shost->host_tagset = 1; + init_waitqueue_head(&adapter->scan_dev_desc.wait_driver_loading); + adapter->scan_dev_desc.driver_loading = 1; + if (leapraid_ctrl_init(adapter)) { + dev_err(&adapter->pdev->dev, + "%s: Adapter init failed\n", __func__); + rc = -ENODEV; + goto ctrl_init_fail; + } + + shost->nr_hw_queues = 1; + if (shost->host_tagset) { + shost->nr_hw_queues = adapter->adapter_attr.rq_cnt; + iopoll_q_count = adapter->adapter_attr.rq_cnt - + adapter->notification_desc.iopoll_qdex; + shost->nr_maps = iopoll_q_count ? 3 : 1; + dev_info(&adapter->pdev->dev, + "Max scsi I/O cmds %d shared with nr_hw_queues=%d\n", + shost->can_queue, shost->nr_hw_queues); + } + + rc = scsi_add_host(shost, &pdev->dev); + if (rc) { + dev_err(&pdev->dev, + "%s: SCSI host add failed\n", __func__); + goto scsi_add_shost_fail; + } + + spin_lock(&leapraid_adapter_lock); + list_add_tail(&adapter->list, &leapraid_adapter_list); + spin_unlock(&leapraid_adapter_lock); + + scsi_scan_host(shost); + return 0; + +scsi_add_shost_fail: + leapraid_remove_ctrl(adapter); +ctrl_init_fail: + leapraid_overheat_cleanup(adapter); + destroy_workqueue(adapter->fw_evt_s.fw_evt_thread); +evt_wq_fail: + scsi_host_put(shost); + return rc; +} + +void leapraid_cleanup_lists(struct leapraid_adapter *adapter) +{ + struct leapraid_raid_volume *raid_volume, *next_raid_volume; + struct leapraid_starget_priv *starget_priv_data; + struct leapraid_sas_port *leapraid_port, *next_port; + struct leapraid_card_port *port, *port_next; + struct leapraid_vphy *vphy, *vphy_next; + + list_for_each_entry_safe(raid_volume, next_raid_volume, + &adapter->dev_topo.raid_volume_list, list) { + if (raid_volume->starget) { + starget_priv_data = raid_volume->starget->hostdata; + if (starget_priv_data) + starget_priv_data->deleted = 1; + scsi_remove_target(&raid_volume->starget->dev); + } + dev_info(&adapter->pdev->dev, + "removing hdl=0x%04x, wwid=0x%016llx\n", + raid_volume->hdl, + (unsigned long long)raid_volume->wwid); + leapraid_raid_volume_remove(adapter, raid_volume); + } + + list_for_each_entry_safe(leapraid_port, next_port, + &adapter->dev_topo.card.sas_port_list, + port_list) { + if (leapraid_port->remote_identify.device_type == + SAS_END_DEVICE) + leapraid_sas_dev_remove_by_sas_address( + adapter, + leapraid_port->remote_identify.sas_address, + leapraid_port->card_port); + else if (leapraid_port->remote_identify.device_type == + SAS_EDGE_EXPANDER_DEVICE || + leapraid_port->remote_identify.device_type == + SAS_FANOUT_EXPANDER_DEVICE) + leapraid_exp_rm( + adapter, + leapraid_port->remote_identify.sas_address, + leapraid_port->card_port); + } + + list_for_each_entry_safe(port, port_next, + &adapter->dev_topo.card_port_list, list) { + if (port->vphys_mask) + list_for_each_entry_safe(vphy, vphy_next, + &port->vphys_list, list) { + list_del(&vphy->list); + kfree(vphy); + } + list_del(&port->list); + kfree(port); + } + + if (adapter->dev_topo.card.phys_num) { + kfree(adapter->dev_topo.card.card_phy); + adapter->dev_topo.card.card_phy = NULL; + adapter->dev_topo.card.phys_num = 0; + } +} + +static void leapraid_remove(struct pci_dev *pdev) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct workqueue_struct *wq; + unsigned long flags; + + if (!shost || !adapter) { + dev_err(&pdev->dev, "Unable to remove!\n"); + return; + } + + wait_event(adapter->scan_dev_desc.wait_driver_loading, + !adapter->scan_dev_desc.driver_loading); + wait_event(adapter->scan_dev_desc.wait_driver_loading, + !atomic_read(&adapter->overheat_desc.thermal_alert)); + + WRITE_ONCE(adapter->access_ctrl.host_removing, 1); + spin_lock(&leapraid_adapter_lock); + list_del(&adapter->list); + spin_unlock(&leapraid_adapter_lock); + + leapraid_wait_cmds_done(adapter); + + if (leapraid_pci_removed(adapter)) { + leapraid_mq_polling_pause(adapter); + leapraid_clean_active_scsi_cmds(adapter); + } + leapraid_clean_active_fw_evt(adapter); + + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + wq = adapter->fw_evt_s.fw_evt_thread; + adapter->fw_evt_s.fw_evt_thread = NULL; + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + if (wq) + destroy_workqueue(wq); + + sas_remove_host(shost); + leapraid_cleanup_lists(adapter); + leapraid_remove_ctrl(adapter); + scsi_host_put(shost); +} + +static void leapraid_shutdown(struct pci_dev *pdev) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct workqueue_struct *wq; + unsigned long flags; + + if (!shost || !adapter) { + dev_err(&pdev->dev, "Unable to shutdown!\n"); + return; + } + + adapter->access_ctrl.host_removing = 1; + leapraid_wait_cmds_done(adapter); + leapraid_clean_active_fw_evt(adapter); + leapraid_overheat_cleanup(adapter); + leapraid_fw_log_stop(adapter); + spin_lock_irqsave(&adapter->fw_evt_s.fw_evt_lock, flags); + wq = adapter->fw_evt_s.fw_evt_thread; + adapter->fw_evt_s.fw_evt_thread = NULL; + spin_unlock_irqrestore(&adapter->fw_evt_s.fw_evt_lock, flags); + if (wq) + destroy_workqueue(wq); + + leapraid_disable_controller(adapter); +} + +static pci_ers_result_t leapraid_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + + if (!shost || !adapter) { + dev_err(&pdev->dev, "Failed to error detected for device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + dev_err(&pdev->dev, "%s: PCI error detected, state=%d\n", + adapter->adapter_attr.name, state); + + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + adapter->access_ctrl.pcie_recovering = 1; + scsi_block_requests(adapter->shost); + leapraid_overheat_cleanup(adapter); + leapraid_check_scheduled_fault_stop(adapter); + leapraid_fw_log_stop(adapter); + leapraid_disable_controller(adapter); + return PCI_ERS_RESULT_NEED_RESET; + case pci_channel_io_perm_failure: + adapter->access_ctrl.pcie_recovering = 1; + leapraid_overheat_cleanup(adapter); + leapraid_check_scheduled_fault_stop(adapter); + leapraid_fw_log_stop(adapter); + leapraid_mq_polling_pause(adapter); + leapraid_clean_active_scsi_cmds(adapter); + return PCI_ERS_RESULT_DISCONNECT; + } + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t leapraid_pci_mmio_enabled(struct pci_dev *pdev) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + + if (!shost || !adapter) { + dev_err(&pdev->dev, + "Failed to enable mmio for device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + dev_info(&pdev->dev, "%s: PCI error mmio enabled\n", + adapter->adapter_attr.name); + + return PCI_ERS_RESULT_RECOVERED; +} + +static pci_ers_result_t leapraid_pci_slot_reset(struct pci_dev *pdev) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + int rc; + + if (!shost || !adapter) { + dev_err(&pdev->dev, + "Failed to slot reset for device\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + dev_err(&pdev->dev, "%s PCI error slot reset\n", + adapter->adapter_attr.name); + + adapter->pdev = pdev; + pci_restore_state(pdev); + if (leapraid_set_pcie_and_notification(adapter)) { + dev_err(&pdev->dev, + "%s: Failed to set PCIe state and notification\n", + __func__); + return PCI_ERS_RESULT_DISCONNECT; + } + + adapter->access_ctrl.pcie_recovering = 0; + dev_info(&pdev->dev, "%s: Hard reset triggered by PCI slot reset\n", + adapter->adapter_attr.name); + dev_info(&adapter->pdev->dev, "%s: %d: call hard_reset\n", + __func__, __LINE__); + rc = leapraid_hard_reset_handler(adapter, FULL_RESET); + if (rc) + dev_err(&pdev->dev, "%s hard reset: failed\n", + adapter->adapter_attr.name); + + return rc == 0 ? PCI_ERS_RESULT_RECOVERED : + PCI_ERS_RESULT_DISCONNECT; +} + +static void leapraid_pci_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *shost = pci_get_drvdata(pdev); + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + + if (!shost || !adapter) { + dev_err(&pdev->dev, "Failed to resume\n"); + return; + } + + dev_err(&pdev->dev, "PCI error resume!\n"); + + pci_aer_clear_nonfatal_status(pdev); + leapraid_check_scheduled_fault_start(adapter); + leapraid_fw_log_start(adapter); + scsi_unblock_requests(adapter->shost); +} + +MODULE_DEVICE_TABLE(pci, leapraid_pci_table); +static struct pci_error_handlers leapraid_err_handler = { + .error_detected = leapraid_pci_error_detected, + .mmio_enabled = leapraid_pci_mmio_enabled, + .slot_reset = leapraid_pci_slot_reset, + .resume = leapraid_pci_resume, +}; + +#ifdef CONFIG_PM +static int leapraid_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + pci_power_t device_state; + + if (!shost || !adapter) { + dev_err(&pdev->dev, + "Suspend failed, invalid host or adapter\n"); + return -ENXIO; + } + + leapraid_overheat_cleanup(adapter); + leapraid_check_scheduled_fault_stop(adapter); + leapraid_fw_log_stop(adapter); + scsi_block_requests(shost); + device_state = pci_choose_state(pdev, state); + + dev_info(&pdev->dev, "Entering PCI power state D%d, (slot=%s)\n", + device_state, pci_name(pdev)); + + pci_save_state(pdev); + leapraid_disable_controller(adapter); + pci_set_power_state(pdev, device_state); + return 0; +} + +static int leapraid_resume(struct pci_dev *pdev) +{ + struct leapraid_adapter *adapter = pdev_to_adapter(pdev); + struct Scsi_Host *shost = pci_get_drvdata(pdev); + pci_power_t device_state = pdev->current_state; + int rc; + + if (!shost || !adapter) { + dev_err(&pdev->dev, + "Resume failed, invalid host or adapter\n"); + return -ENXIO; + } + + dev_info(&pdev->dev, + "Resuming device %s, previous state D%d\n", + pci_name(pdev), device_state); + + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + adapter->pdev = pdev; + rc = leapraid_set_pcie_and_notification(adapter); + if (rc) { + dev_err(&pdev->dev, + "%s: Failed to set PCIe state and notification\n", + __func__); + return rc; + } + + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", + __func__, __LINE__); + rc = leapraid_hard_reset_handler(adapter, PART_RESET); + if (rc) { + dev_err(&adapter->pdev->dev, + "%s: Hard reset failed during resume, rc=%d\n", + __func__, rc); + return rc; + } + scsi_unblock_requests(shost); + leapraid_check_scheduled_fault_start(adapter); + leapraid_fw_log_start(adapter); + return 0; +} +#endif /* CONFIG_PM */ + +static struct pci_driver leapraid_driver = { + .name = LEAPRAID_DRIVER_NAME, + .id_table = leapraid_pci_table, + .probe = leapraid_probe, + .remove = leapraid_remove, + .shutdown = leapraid_shutdown, + .err_handler = &leapraid_err_handler, +#ifdef CONFIG_PM + .suspend = leapraid_suspend, + .resume = leapraid_resume, +#endif /* CONFIG_PM */ +}; + +static int __init leapraid_init(void) +{ + int error; + + pr_info("%s initializing\n", LEAPRAID_DRIVER_NAME); + + leapraid_transport_template = + sas_attach_transport(&leapraid_transport_functions); + if (!leapraid_transport_template) { + pr_err("%s: Failed to attach SAS transport\n", + LEAPRAID_DRIVER_NAME); + return -ENODEV; + } + + error = pci_register_driver(&leapraid_driver); + if (error) { + pr_err("%s: PCI driver registration failed: %d\n", + LEAPRAID_DRIVER_NAME, error); + sas_release_transport(leapraid_transport_template); + return error; + } + + error = leapraid_ctl_init(); + if (error) { + pci_unregister_driver(&leapraid_driver); + sas_release_transport(leapraid_transport_template); + return error; + } + + return 0; +} + +static void __exit leapraid_exit(void) +{ + pr_info("%s exiting\n", LEAPRAID_DRIVER_NAME); + + leapraid_ctl_exit(); + pci_unregister_driver(&leapraid_driver); + sas_release_transport(leapraid_transport_template); +} + +module_init(leapraid_init); +module_exit(leapraid_exit); diff --git a/drivers/scsi/leapraid/leapraid_transport.c b/drivers/scsi/leapraid/leapraid_transport.c new file mode 100644 index 000000000000..77ffbd6ddce4 --- /dev/null +++ b/drivers/scsi/leapraid/leapraid_transport.c @@ -0,0 +1,1403 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 LeapIO Tech Inc. + * + * LeapRAID storage and RAID controller driver. + */ +#include + +#include "leapraid_func.h" + +static struct leapraid_topo_node *leapraid_transport_topo_node_by_sas_addr( + struct leapraid_adapter *adapter, + u64 sas_addr, + struct leapraid_card_port *card_port) +{ + if (adapter->dev_topo.card.sas_address == sas_addr) + return &adapter->dev_topo.card; + + return leapraid_exp_find_by_sas_address(adapter, sas_addr, card_port); +} + +static u8 leapraid_get_port_id_by_expander(struct leapraid_adapter *adapter, + struct sas_rphy *rphy) +{ + struct leapraid_topo_node *topo_node_exp; + unsigned long flags; + u8 port_id = 0xFF; + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + list_for_each_entry(topo_node_exp, &adapter->dev_topo.exp_list, list) { + if (topo_node_exp->rphy == rphy) { + port_id = topo_node_exp->card_port->port_id; + break; + } + } + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + return port_id; +} + +static u8 leapraid_get_port_id_by_end_dev(struct leapraid_adapter *adapter, + struct sas_rphy *rphy) +{ + struct leapraid_sas_dev *sas_dev; + unsigned long flags; + u8 port_id = 0xFF; + + spin_lock_irqsave(&adapter->dev_topo.sas_dev_lock, flags); + sas_dev = leapraid_hold_lock_get_sas_dev_by_addr_and_rphy( + adapter, + rphy->identify.sas_address, + rphy); + if (sas_dev) { + port_id = sas_dev->card_port->port_id; + leapraid_sdev_put(sas_dev); + } + spin_unlock_irqrestore(&adapter->dev_topo.sas_dev_lock, flags); + + return port_id; +} + +static u8 leapraid_transport_get_port_id_by_rphy( + struct leapraid_adapter *adapter, + struct sas_rphy *rphy) +{ + if (!rphy) + return 0xFF; + + switch (rphy->identify.device_type) { + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: + return leapraid_get_port_id_by_expander(adapter, rphy); + case SAS_END_DEVICE: + return leapraid_get_port_id_by_end_dev(adapter, rphy); + default: + return 0xFF; + } +} + +static enum sas_linkrate leapraid_transport_convert_phy_link_rate(u8 link_rate) +{ + unsigned int i; + + #define SAS_RATE_12G SAS_LINK_RATE_12_0_GBPS + + const struct linkrate_map { + u8 in; + enum sas_linkrate out; + } linkrate_table[] = { + { + LEAPRAID_SAS_NEG_LINK_RATE_1_5, + SAS_LINK_RATE_1_5_GBPS + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_3_0, + SAS_LINK_RATE_3_0_GBPS + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_6_0, + SAS_LINK_RATE_6_0_GBPS + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_12_0, + SAS_RATE_12G + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_PHY_DISABLED, + SAS_PHY_DISABLED + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_NEGOTIATION_FAILED, + SAS_LINK_RATE_FAILED + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_PORT_SELECTOR, + SAS_SATA_PORT_SELECTOR + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_SMP_RESETTING, + SAS_LINK_RATE_UNKNOWN + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_SATA_OOB_COMPLETE, + SAS_LINK_RATE_UNKNOWN + }, + { + LEAPRAID_SAS_NEG_LINK_RATE_UNKNOWN_LINK_RATE, + SAS_LINK_RATE_UNKNOWN + }, + }; + + for (i = 0; i < ARRAY_SIZE(linkrate_table); i++) + if (linkrate_table[i].in == link_rate) + return linkrate_table[i].out; + + return SAS_LINK_RATE_UNKNOWN; +} + +static void leapraid_set_identify_protocol_flags(u32 dev_info, + struct sas_identify *identify) +{ + unsigned int i; + + const struct protocol_mapping { + u32 mask; + u32 *target; + u32 protocol; + } mappings[] = { + { + LEAPRAID_DEVTYP_SSP_INIT, + &identify->initiator_port_protocols, + SAS_PROTOCOL_SSP + }, + { + LEAPRAID_DEVTYP_STP_INIT, + &identify->initiator_port_protocols, + SAS_PROTOCOL_STP + }, + { + LEAPRAID_DEVTYP_SMP_INIT, + &identify->initiator_port_protocols, + SAS_PROTOCOL_SMP + }, + { + LEAPRAID_DEVTYP_SATA_HOST, + &identify->initiator_port_protocols, + SAS_PROTOCOL_SATA + }, + { + LEAPRAID_DEVTYP_SSP_TGT, + &identify->target_port_protocols, + SAS_PROTOCOL_SSP + }, + { + LEAPRAID_DEVTYP_STP_TGT, + &identify->target_port_protocols, + SAS_PROTOCOL_STP + }, + { + LEAPRAID_DEVTYP_SMP_TGT, + &identify->target_port_protocols, + SAS_PROTOCOL_SMP + }, + { + LEAPRAID_DEVTYP_SATA_DEV, + &identify->target_port_protocols, + SAS_PROTOCOL_SATA + }, + }; + + for (i = 0; i < ARRAY_SIZE(mappings); i++) + if (dev_info & mappings[i].mask && mappings[i].target) + *mappings[i].target |= mappings[i].protocol; +} + +static int leapraid_transport_set_identify(struct leapraid_adapter *adapter, + u16 hdl, + struct sas_identify *identify) +{ + union cfg_param_1 cfgp1 = {0}; + union cfg_param_2 cfgp2 = {0}; + struct leapraid_sas_dev_p0 sas_dev_pg0; + u32 dev_info; + + if ((adapter->access_ctrl.shost_recovering && + !adapter->scan_dev_desc.driver_loading) || + adapter->access_ctrl.pcie_recovering) { + dev_warn(&adapter->pdev->dev, + "%s: Failed, shost_recovering=%d pcie_recovering=%d\n", + __func__, + adapter->access_ctrl.shost_recovering, + adapter->access_ctrl.pcie_recovering); + return -EFAULT; + } + + cfgp1.form = LEAPRAID_SAS_DEV_CFG_PGAD_HDL; + cfgp2.handle = hdl; + if (leapraid_op_config_page(adapter, &sas_dev_pg0, cfgp1, + cfgp2, GET_SAS_DEVICE_PG0)) + return -ENXIO; + + memset(identify, 0, sizeof(struct sas_identify)); + dev_info = le32_to_cpu(sas_dev_pg0.dev_info); + identify->sas_address = le64_to_cpu(sas_dev_pg0.sas_address); + identify->phy_identifier = sas_dev_pg0.phy_num; + + switch (dev_info & LEAPRAID_DEVTYP_MASK_DEV_TYPE) { + case LEAPRAID_DEVTYP_NO_DEV: + identify->device_type = SAS_PHY_UNUSED; + break; + case LEAPRAID_DEVTYP_END_DEV: + identify->device_type = SAS_END_DEVICE; + break; + case LEAPRAID_DEVTYP_EDGE_EXPANDER: + identify->device_type = SAS_EDGE_EXPANDER_DEVICE; + break; + case LEAPRAID_DEVTYP_FANOUT_EXPANDER: + identify->device_type = SAS_FANOUT_EXPANDER_DEVICE; + break; + } + + leapraid_set_identify_protocol_flags(dev_info, identify); + + return 0; +} + +static void leapraid_transport_exp_set_edev(struct leapraid_adapter *adapter, + void *data_out, + struct sas_expander_device *edev) +{ + struct leapraid_smp_passthrough_rep *smp_passthrough_rep; + struct leapraid_rep_manu_reply *rep_manu_reply; + u8 *component_id; + + smp_passthrough_rep = + (void *)&adapter->driver_cmds.transport_cmd.reply; + if (le16_to_cpu(smp_passthrough_rep->resp_data_len) != + sizeof(struct leapraid_rep_manu_reply)) + return; + + rep_manu_reply = data_out + sizeof(struct leapraid_rep_manu_request); + + memcpy(edev->vendor_id, rep_manu_reply->vendor_identification, + SAS_EXPANDER_VENDOR_ID_LEN); + edev->vendor_id[SAS_EXPANDER_VENDOR_ID_LEN] = '\0'; + + memcpy(edev->product_id, rep_manu_reply->product_identification, + SAS_EXPANDER_PRODUCT_ID_LEN); + edev->product_id[SAS_EXPANDER_PRODUCT_ID_LEN] = '\0'; + + memcpy(edev->product_rev, rep_manu_reply->product_revision_level, + SAS_EXPANDER_PRODUCT_REV_LEN); + edev->product_rev[SAS_EXPANDER_PRODUCT_REV_LEN] = '\0'; + + edev->level = rep_manu_reply->sas_format & 1; + if (edev->level) { + memcpy(edev->component_vendor_id, + rep_manu_reply->comp_vendor_identification, + SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN); + edev->component_vendor_id[ + SAS_EXPANDER_COMPONENT_VENDOR_ID_LEN] = '\0'; + + component_id = (u8 *)&rep_manu_reply->component_id; + edev->component_id = component_id[0] << 8 | component_id[1]; + edev->component_revision_id = + rep_manu_reply->component_revision_level; + } +} + +static int leapraid_transport_exp_report_manu(struct leapraid_adapter *adapter, + u64 sas_address, + struct sas_expander_device *edev, + u8 port_id) +{ + struct leapraid_smp_passthrough_req *smp_passthrough_req; + struct leapraid_rep_manu_request *rep_manu_request; + dma_addr_t h2c_dma_addr; + dma_addr_t c2h_dma_addr; + bool issue_reset = false; + void *data_out = NULL; + u16 inter_taskid; + size_t c2h_size; + size_t h2c_size; + void *psge; + int rc; + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.pcie_recovering) { + dev_warn(&adapter->pdev->dev, + "%s: Failed, shost_recovering=%d pcie_recovering=%d\n", + __func__, + adapter->access_ctrl.shost_recovering, + adapter->access_ctrl.pcie_recovering); + return -EFAULT; + } + + mutex_lock(&adapter->driver_cmds.transport_cmd.mutex); + adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_PENDING; + rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, + __func__); + if (rc) + goto out_cleanup; + + h2c_size = sizeof(struct leapraid_rep_manu_request); + c2h_size = sizeof(struct leapraid_rep_manu_reply); + data_out = dma_alloc_coherent(&adapter->pdev->dev, + h2c_size + c2h_size, + &h2c_dma_addr, + GFP_ATOMIC); + if (!data_out) { + rc = -ENOMEM; + goto out_cleanup; + } + + rep_manu_request = data_out; + rep_manu_request->smp_frame_type = + SMP_REPORT_MANUFACTURER_INFORMATION_FRAME_TYPE; + rep_manu_request->function = SMP_REPORT_MANUFACTURER_INFORMATION_FUNC; + rep_manu_request->allocated_response_length = 0; + rep_manu_request->request_length = 0; + + inter_taskid = adapter->driver_cmds.transport_cmd.inter_taskid; + smp_passthrough_req = leapraid_get_task_desc(adapter, inter_taskid); + memset(smp_passthrough_req, 0, + sizeof(struct leapraid_smp_passthrough_req)); + smp_passthrough_req->func = LEAPRAID_FUNC_SMP_PASSTHROUGH; + smp_passthrough_req->physical_port = port_id; + smp_passthrough_req->sas_address = cpu_to_le64(sas_address); + smp_passthrough_req->req_data_len = cpu_to_le16(h2c_size); + psge = &smp_passthrough_req->sgl; + c2h_dma_addr = h2c_dma_addr + sizeof(struct leapraid_rep_manu_request); + leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size, + c2h_dma_addr, c2h_size); + + init_completion(&adapter->driver_cmds.transport_cmd.done); + leapraid_fire_task(adapter, inter_taskid); + wait_for_completion_timeout(&adapter->driver_cmds.transport_cmd.done, + LEAPRAID_TRANSPORT_CMD_TIMEOUT * HZ); + if (!(adapter->driver_cmds.transport_cmd.status & LEAPRAID_CMD_DONE)) { + rc = -ETIMEDOUT; + dev_err(&adapter->pdev->dev, + "%s: SMP passthrough timeout, st=0x%x\n", + __func__, adapter->driver_cmds.transport_cmd.status); + leapraid_log_req_context(adapter, inter_taskid, + smp_passthrough_req); + if (!(adapter->driver_cmds.transport_cmd.status & + LEAPRAID_CMD_RESET)) + issue_reset = true; + + goto hard_reset; + } + + if (adapter->driver_cmds.transport_cmd.status & + LEAPRAID_CMD_REPLY_VALID) + leapraid_transport_exp_set_edev(adapter, data_out, edev); + +hard_reset: + if (issue_reset) { + dev_info(&adapter->pdev->dev, "%s:%d: call hard_reset\n", + __func__, __LINE__); + rc = leapraid_hard_reset_handler(adapter, FULL_RESET); + } +out_cleanup: + adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED; + if (data_out) + dma_free_coherent(&adapter->pdev->dev, h2c_size + c2h_size, + data_out, h2c_dma_addr); + + mutex_unlock(&adapter->driver_cmds.transport_cmd.mutex); + return rc; +} + +static void leapraid_transport_del_port(struct leapraid_adapter *adapter, + struct leapraid_sas_port *sas_port) +{ + dev_info(&sas_port->port->dev, + "Remove port: SAS addr=0x%016llx\n", + (unsigned long long)sas_port->remote_identify.sas_address); + switch (sas_port->remote_identify.device_type) { + case SAS_END_DEVICE: + leapraid_sas_dev_remove_by_sas_address( + adapter, + sas_port->remote_identify.sas_address, + sas_port->card_port); + break; + case SAS_EDGE_EXPANDER_DEVICE: + case SAS_FANOUT_EXPANDER_DEVICE: + leapraid_exp_rm(adapter, sas_port->remote_identify.sas_address, + sas_port->card_port); + break; + default: + break; + } +} + +static void leapraid_transport_del_phy(struct leapraid_adapter *adapter, + struct leapraid_sas_port *sas_port, + struct leapraid_card_phy *card_phy) +{ + dev_info(&card_phy->phy->dev, + "Remove PHY: SAS addr=0x%016llx, phy=%d\n", + (unsigned long long)sas_port->remote_identify.sas_address, + card_phy->phy_id); + list_del(&card_phy->port_siblings); + sas_port->phys_num--; + sas_port_delete_phy(sas_port->port, card_phy->phy); + card_phy->phy_is_assigned = 0; +} + +static void leapraid_transport_add_phy(struct leapraid_adapter *adapter, + struct leapraid_sas_port *sas_port, + struct leapraid_card_phy *card_phy) +{ + dev_info(&card_phy->phy->dev, + "Add PHY: SAS addr=0x%016llx, phy=%d\n", + (unsigned long long)sas_port->remote_identify.sas_address, + card_phy->phy_id); + list_add_tail(&card_phy->port_siblings, &sas_port->phy_list); + sas_port->phys_num++; + sas_port_add_phy(sas_port->port, card_phy->phy); + card_phy->phy_is_assigned = 1; +} + +void leapraid_transport_attach_phy_to_port( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node, + struct leapraid_card_phy *card_phy, + u64 sas_address, + struct leapraid_card_port *card_port) +{ + struct leapraid_sas_port *sas_port; + struct leapraid_card_phy *card_phy_srch; + + if (card_phy->phy_is_assigned) + return; + + if (!card_port) + return; + + list_for_each_entry(sas_port, &topo_node->sas_port_list, port_list) { + if (sas_port->remote_identify.sas_address != sas_address) + continue; + + if (sas_port->card_port != card_port) + continue; + + list_for_each_entry(card_phy_srch, &sas_port->phy_list, + port_siblings) { + if (card_phy_srch == card_phy) + return; + } + leapraid_transport_add_phy(adapter, sas_port, card_phy); + return; + } +} + +void leapraid_transport_detach_phy_to_port( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node, + struct leapraid_card_phy *target_card_phy) +{ + struct leapraid_sas_port *sas_port, *sas_port_next; + struct leapraid_card_phy *cur_card_phy; + + if (!target_card_phy->phy_is_assigned) + return; + + list_for_each_entry_safe(sas_port, sas_port_next, + &topo_node->sas_port_list, port_list) { + list_for_each_entry(cur_card_phy, &sas_port->phy_list, + port_siblings) { + if (cur_card_phy != target_card_phy) + continue; + + if (sas_port->phys_num == 1 && + !adapter->access_ctrl.shost_recovering) + leapraid_transport_del_port(adapter, sas_port); + else + leapraid_transport_del_phy(adapter, sas_port, + target_card_phy); + return; + } + } +} + +static void leapraid_detach_phy_from_old_port( + struct leapraid_adapter *adapter, + struct leapraid_topo_node *topo_node, + u64 sas_address, + struct leapraid_card_port *card_port) +{ + int i; + + for (i = 0; i < topo_node->phys_num; i++) { + if (topo_node->card_phy[i].remote_identify.sas_address != + sas_address || + topo_node->card_phy[i].card_port != card_port) + continue; + if (topo_node->card_phy[i].phy_is_assigned) + leapraid_transport_detach_phy_to_port( + adapter, + topo_node, + &topo_node->card_phy[i]); + } +} + +static struct leapraid_sas_port *leapraid_prepare_sas_port( + struct leapraid_adapter *adapter, + u16 handle, u64 sas_address, + struct leapraid_card_port *card_port, + struct leapraid_topo_node **out_topo_node) +{ + struct leapraid_topo_node *topo_node; + struct leapraid_sas_port *sas_port; + unsigned long flags; + + sas_port = kzalloc_obj(*sas_port); + if (!sas_port) { + dev_warn(&adapter->pdev->dev, + "%s: Failed alloc sas_port\n", __func__); + return NULL; + } + + INIT_LIST_HEAD(&sas_port->port_list); + INIT_LIST_HEAD(&sas_port->phy_list); + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + topo_node = leapraid_transport_topo_node_by_sas_addr(adapter, + sas_address, + card_port); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + if (!topo_node) { + dev_err(&adapter->pdev->dev, + "%s: Failed to locate parent for SAS 0x%016llx!\n", + __func__, sas_address); + goto out_cleanup; + } + + if (leapraid_transport_set_identify(adapter, handle, + &sas_port->remote_identify)) + goto out_cleanup; + + if (sas_port->remote_identify.device_type == SAS_PHY_UNUSED) { + dev_warn(&adapter->pdev->dev, + "%s: Failed, device type is SAS_PHY_UNUSED\n", + __func__); + goto out_cleanup; + } + + sas_port->card_port = card_port; + *out_topo_node = topo_node; + + return sas_port; + +out_cleanup: + kfree(sas_port); + return NULL; +} + +static int leapraid_bind_phys_and_vphy(struct leapraid_adapter *adapter, + struct leapraid_sas_port *sas_port, + struct leapraid_topo_node *topo_node, + struct leapraid_card_port *card_port, + struct leapraid_vphy **out_vphy) +{ + struct leapraid_vphy *vphy = NULL; + int i; + + for (i = 0; i < topo_node->phys_num; i++) { + if (topo_node->card_phy[i].remote_identify.sas_address != + sas_port->remote_identify.sas_address || + topo_node->card_phy[i].card_port != card_port) + continue; + + list_add_tail(&topo_node->card_phy[i].port_siblings, + &sas_port->phy_list); + sas_port->phys_num++; + + if (topo_node->hdl <= adapter->dev_topo.card.phys_num) { + if (!topo_node->card_phy[i].vphy) { + card_port->phy_mask |= BIT(i); + continue; + } + + vphy = leapraid_get_vphy_by_phy(card_port, i); + if (!vphy) + return LEAPRAID_OPERATION_FAILED; + } + } + + *out_vphy = vphy; + return sas_port->phys_num ? 0 : LEAPRAID_OPERATION_FAILED; +} + +static struct sas_rphy *leapraid_create_and_register_rphy( + struct leapraid_adapter *adapter, + struct leapraid_sas_port *sas_port, + struct leapraid_topo_node *topo_node, + struct leapraid_card_port *card_port, + struct leapraid_vphy *vphy) +{ + struct leapraid_sas_dev *sas_dev = NULL; + struct leapraid_card_phy *card_phy; + struct sas_port *port; + struct sas_rphy *rphy; + + if (sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_dev = leapraid_get_sas_dev_by_addr( + adapter, + sas_port->remote_identify.sas_address, + card_port); + if (!sas_dev) + return NULL; + sas_dev->pend_sas_rphy_add = 1; + } + + if (!topo_node->parent_dev) { + dev_warn(&adapter->pdev->dev, + "%s: topo_node parent device is NULL\n", __func__); + goto cleanup_sas_dev; + } + + port = sas_port_alloc_num(topo_node->parent_dev); + if (!port) { + dev_err(&adapter->pdev->dev, + "%s: Failed to allocate SAS port\n", __func__); + goto cleanup_sas_dev; + } + + if (sas_port_add(port)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to add SAS port\n", __func__); + goto out_delete_port; + } + + list_for_each_entry(card_phy, &sas_port->phy_list, port_siblings) { + sas_port_add_phy(port, card_phy->phy); + card_phy->phy_is_assigned = 1; + card_phy->card_port = card_port; + } + + if (sas_dev) { + rphy = sas_end_device_alloc(port); + sas_dev->rphy = rphy; + + if (topo_node->hdl <= adapter->dev_topo.card.phys_num) { + if (!vphy) + card_port->sas_address = sas_dev->sas_addr; + else + vphy->sas_address = sas_dev->sas_addr; + } + } else { + rphy = sas_expander_alloc( + port, + sas_port->remote_identify.device_type); + if (topo_node->hdl <= adapter->dev_topo.card.phys_num) + card_port->sas_address = + sas_port->remote_identify.sas_address; + } + + if (!rphy) { + dev_err(&adapter->pdev->dev, + "%s: Failed to allocate RPHY\n", __func__); + goto cleanup_card_phy; + } + + rphy->identify = sas_port->remote_identify; + + if (sas_rphy_add(rphy)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to add RPHY\n", __func__); + if (sas_dev) + sas_dev->rphy = NULL; + + sas_rphy_free(rphy); + goto cleanup_card_phy; + } + sas_port->port = port; + + if (sas_dev) { + sas_dev->pend_sas_rphy_add = 0; + leapraid_sdev_put(sas_dev); + } + + return rphy; + +cleanup_card_phy: + if (topo_node->hdl <= adapter->dev_topo.card.phys_num) { + if (!vphy) + card_port->sas_address = 0; + else + vphy->sas_address = 0; + } + + list_for_each_entry(card_phy, &sas_port->phy_list, port_siblings) { + card_phy->phy_is_assigned = 0; + card_phy->card_port = NULL; + } + +out_delete_port: + sas_port_delete(port); + +cleanup_sas_dev: + if (sas_dev) { + sas_dev->pend_sas_rphy_add = 0; + leapraid_sdev_put(sas_dev); + } + return NULL; +} + +struct leapraid_sas_port *leapraid_transport_port_add( + struct leapraid_adapter *adapter, + u16 hdl, u64 sas_address, + struct leapraid_card_port *card_port) +{ + struct leapraid_card_phy *card_phy, *card_phy_next; + struct leapraid_topo_node *topo_node = NULL; + struct leapraid_sas_port *sas_port; + struct leapraid_vphy *vphy = NULL; + struct sas_rphy *rphy; + unsigned long flags; + + if (!card_port) { + dev_warn(&adapter->pdev->dev, + "%s: Invalid card_port\n", __func__); + return NULL; + } + + sas_port = leapraid_prepare_sas_port(adapter, hdl, sas_address, + card_port, &topo_node); + if (!sas_port) + return NULL; + + leapraid_detach_phy_from_old_port( + adapter, + topo_node, + sas_port->remote_identify.sas_address, + card_port); + + if (leapraid_bind_phys_and_vphy(adapter, sas_port, topo_node, + card_port, &vphy)) { + dev_err(&adapter->pdev->dev, + "%s: Failed to bind phy to vphy\n", __func__); + goto out_fail; + } + + rphy = leapraid_create_and_register_rphy(adapter, sas_port, topo_node, + card_port, vphy); + if (!rphy) { + dev_err(&adapter->pdev->dev, + "%s: Failed to create rphy\n", __func__); + goto out_fail; + } + + dev_info(&rphy->dev, + "%s: Added dev: hdl=0x%04x, SAS addr=0x%016llx\n", + __func__, hdl, + (unsigned long long)sas_port->remote_identify.sas_address); + + sas_port->rphy = rphy; + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + list_add_tail(&sas_port->port_list, &topo_node->sas_port_list); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + if (sas_port->remote_identify.device_type == + SAS_EDGE_EXPANDER_DEVICE || + sas_port->remote_identify.device_type == + SAS_FANOUT_EXPANDER_DEVICE) + leapraid_transport_exp_report_manu( + adapter, + sas_port->remote_identify.sas_address, + rphy_to_expander_device(rphy), + card_port->port_id); + + return sas_port; + +out_fail: + list_for_each_entry_safe(card_phy, card_phy_next, + &sas_port->phy_list, port_siblings) { + if (topo_node->hdl <= adapter->dev_topo.card.phys_num && + !card_phy->vphy) + card_port->phy_mask &= ~BIT(card_phy->phy_id); + + list_del_init(&card_phy->port_siblings); + } + + kfree(sas_port); + return NULL; +} + +static struct leapraid_sas_port *leapraid_find_and_remove_sas_port( + struct leapraid_topo_node *topo_node, + u64 sas_address, + struct leapraid_card_port *remove_card_port, + bool *found) +{ + struct leapraid_sas_port *sas_port, *sas_port_next; + + list_for_each_entry_safe(sas_port, sas_port_next, + &topo_node->sas_port_list, port_list) { + if (sas_port->remote_identify.sas_address != sas_address) + continue; + + if (sas_port->card_port != remove_card_port) + continue; + + *found = true; + list_del(&sas_port->port_list); + return sas_port; + } + return NULL; +} + +static void leapraid_cleanup_card_port_and_vphys( + struct leapraid_adapter *adapter, + u64 sas_address, + struct leapraid_card_port *remove_card_port) +{ + struct leapraid_card_port *card_port, *card_port_next; + struct leapraid_vphy *vphy, *vphy_next; + + if (remove_card_port->vphys_mask) { + list_for_each_entry_safe(vphy, vphy_next, + &remove_card_port->vphys_list, list) { + if (vphy->sas_address != sas_address) + continue; + + dev_dbg(&adapter->pdev->dev, + "%s: Remove vphy=%p from port=%p port_id=%d\n", + __func__, vphy, remove_card_port, + remove_card_port->port_id); + + remove_card_port->vphys_mask &= ~vphy->phy_mask; + list_del(&vphy->list); + kfree(vphy); + } + + if (!remove_card_port->vphys_mask && + !remove_card_port->sas_address) { + dev_dbg(&adapter->pdev->dev, + "%s: Remove empty hba_port: %p, port_id=%d\n", + __func__, + remove_card_port, + remove_card_port->port_id); + list_del(&remove_card_port->list); + kfree(remove_card_port); + remove_card_port = NULL; + } + } + + list_for_each_entry_safe(card_port, card_port_next, + &adapter->dev_topo.card_port_list, list) { + if (card_port != remove_card_port) + continue; + + if (card_port->sas_address != sas_address) + continue; + + if (!remove_card_port->vphys_mask) { + dev_dbg(&adapter->pdev->dev, + "%s: Remove hba_port: %p, port_id=%d\n", + __func__, card_port, card_port->port_id); + list_del(&card_port->list); + kfree(card_port); + } else { + dev_dbg(&adapter->pdev->dev, + "Clear sas_address of port=%p, port_id=%d\n", + card_port, card_port->port_id); + remove_card_port->sas_address = 0; + } + break; + } +} + +static void leapraid_clear_topo_node_phys(struct leapraid_topo_node *topo_node, + u64 sas_address) +{ + int i; + + for (i = 0; i < topo_node->phys_num; i++) + if (topo_node->card_phy[i].remote_identify.sas_address == + sas_address) { + memset(&topo_node->card_phy[i].remote_identify, 0, + sizeof(struct sas_identify)); + topo_node->card_phy[i].vphy = 0; + } +} + +void leapraid_transport_port_remove( + struct leapraid_adapter *adapter, + u64 sas_address, + u64 sas_address_parent, + struct leapraid_card_port *remove_card_port) +{ + struct leapraid_card_phy *card_phy, *card_phy_next; + struct leapraid_sas_port *sas_port; + struct leapraid_topo_node *topo_node; + unsigned long flags; + bool found = false; + + if (!remove_card_port) + return; + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + + topo_node = leapraid_transport_topo_node_by_sas_addr(adapter, + sas_address_parent, + remove_card_port); + if (!topo_node) { + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, + flags); + return; + } + + sas_port = leapraid_find_and_remove_sas_port(topo_node, sas_address, + remove_card_port, &found); + + if (!found) { + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, + flags); + return; + } + + if (topo_node->hdl <= adapter->dev_topo.card.phys_num && + adapter->adapter_attr.enable_mp) + leapraid_cleanup_card_port_and_vphys(adapter, sas_address, + remove_card_port); + + leapraid_clear_topo_node_phys(topo_node, sas_address); + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + list_for_each_entry_safe(card_phy, card_phy_next, + &sas_port->phy_list, port_siblings) { + card_phy->phy_is_assigned = 0; + if (!adapter->access_ctrl.host_removing) + sas_port_delete_phy(sas_port->port, card_phy->phy); + + list_del(&card_phy->port_siblings); + } + + if (!adapter->access_ctrl.host_removing) + sas_port_delete(sas_port->port); + + dev_dbg(&adapter->pdev->dev, + "%s: Removed sas_port for SAS addr=0x%016llx\n", + __func__, (unsigned long long)sas_address); + + kfree(sas_port); +} + +static void leapraid_init_sas_or_exp_phy(struct leapraid_adapter *adapter, + struct leapraid_card_phy *card_phy, + struct sas_phy *phy, + struct leapraid_sas_phy_p0 *phy_pg0, + struct leapraid_exp_p1 *exp_pg1) +{ + if (exp_pg1 && phy_pg0) + return; + + if (!exp_pg1 && !phy_pg0) + return; + + phy->identify = card_phy->identify; + phy->identify.phy_identifier = card_phy->phy_id; + phy->negotiated_linkrate = phy_pg0 ? + leapraid_transport_convert_phy_link_rate( + phy_pg0->neg_link_rate & + LEAPRAID_SAS_NEG_LINK_RATE_MASK_PHYSICAL) : + leapraid_transport_convert_phy_link_rate( + exp_pg1->neg_link_rate & + LEAPRAID_SAS_NEG_LINK_RATE_MASK_PHYSICAL); + phy->minimum_linkrate_hw = phy_pg0 ? + leapraid_transport_convert_phy_link_rate( + phy_pg0->hw_link_rate & + LEAPRAID_SAS_HWRATE_MIN_RATE_MASK) : + leapraid_transport_convert_phy_link_rate( + exp_pg1->hw_link_rate & + LEAPRAID_SAS_HWRATE_MIN_RATE_MASK); + phy->maximum_linkrate_hw = phy_pg0 ? + leapraid_transport_convert_phy_link_rate( + phy_pg0->hw_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) : + leapraid_transport_convert_phy_link_rate( + exp_pg1->hw_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT); + phy->minimum_linkrate = phy_pg0 ? + leapraid_transport_convert_phy_link_rate( + phy_pg0->p_link_rate & + LEAPRAID_SAS_PRATE_MIN_RATE_MASK) : + leapraid_transport_convert_phy_link_rate( + exp_pg1->p_link_rate & + LEAPRAID_SAS_PRATE_MIN_RATE_MASK); + phy->maximum_linkrate = phy_pg0 ? + leapraid_transport_convert_phy_link_rate( + phy_pg0->p_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT) : + leapraid_transport_convert_phy_link_rate( + exp_pg1->p_link_rate >> + LEAPRAID_SAS_NEG_LINK_RATE_SHIFT); + phy->hostdata = card_phy->card_port; +} + +void leapraid_transport_add_card_phy(struct leapraid_adapter *adapter, + struct leapraid_card_phy *card_phy, + struct leapraid_sas_phy_p0 *phy_pg0, + struct device *parent_dev) +{ + struct sas_phy *phy; + int ret; + + INIT_LIST_HEAD(&card_phy->port_siblings); + phy = sas_phy_alloc(parent_dev, card_phy->phy_id); + if (!phy) { + dev_err(&adapter->pdev->dev, + "%s: sas_phy_alloc failed!\n", __func__); + return; + } + + if (leapraid_transport_set_identify(adapter, card_phy->hdl, + &card_phy->identify)) { + dev_err(&adapter->pdev->dev, + "%s: Set PHY handle identify failed!\n", __func__); + sas_phy_free(phy); + return; + } + + card_phy->attached_hdl = le16_to_cpu(phy_pg0->attached_dev_hdl); + if (card_phy->attached_hdl) { + ret = leapraid_transport_set_identify( + adapter, + card_phy->attached_hdl, + &card_phy->remote_identify); + if (ret) { + dev_err(&adapter->pdev->dev, + "%s: Set PHY attached hdl identify failed!\n", + __func__); + sas_phy_free(phy); + return; + } + } + + leapraid_init_sas_or_exp_phy(adapter, card_phy, phy, phy_pg0, NULL); + + if (sas_phy_add(phy)) { + dev_err(&adapter->pdev->dev, + "%s: SAS PHY add failed!\n", __func__); + sas_phy_free(phy); + return; + } + + card_phy->phy = phy; +} + +int leapraid_transport_add_exp_phy(struct leapraid_adapter *adapter, + struct leapraid_card_phy *card_phy, + struct leapraid_exp_p1 *exp_pg1, + struct device *parent_dev) +{ + struct sas_phy *phy; + int ret; + + INIT_LIST_HEAD(&card_phy->port_siblings); + phy = sas_phy_alloc(parent_dev, card_phy->phy_id); + if (!phy) { + dev_err(&adapter->pdev->dev, + "%s: sas_phy_alloc failed!\n", __func__); + return -EFAULT; + } + + if (leapraid_transport_set_identify(adapter, card_phy->hdl, + &card_phy->identify)) { + dev_err(&adapter->pdev->dev, + "%s: Set PHY hdl identify failed!\n", __func__); + sas_phy_free(phy); + return -EFAULT; + } + + card_phy->attached_hdl = le16_to_cpu(exp_pg1->attached_dev_hdl); + if (card_phy->attached_hdl) { + ret = leapraid_transport_set_identify( + adapter, + card_phy->attached_hdl, + &card_phy->remote_identify); + if (ret) + dev_warn(&adapter->pdev->dev, + "%s: Set PHY attached hdl identify failed!\n", + __func__); + } + + leapraid_init_sas_or_exp_phy(adapter, card_phy, phy, NULL, exp_pg1); + + if (sas_phy_add(phy)) { + dev_err(&adapter->pdev->dev, + "%s: SAS PHY add failed!\n", __func__); + sas_phy_free(phy); + return -EFAULT; + } + + card_phy->phy = phy; + return 0; +} + +void leapraid_transport_update_links( + struct leapraid_adapter *adapter, + u64 sas_address, u16 hdl, u8 phy_index, + u8 link_rate, struct leapraid_card_port *target_card_port) +{ + struct leapraid_topo_node *topo_node; + struct leapraid_card_phy *card_phy; + struct leapraid_card_port *card_port; + unsigned long flags; + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.pcie_recovering) + return; + + spin_lock_irqsave(&adapter->dev_topo.topo_node_lock, flags); + topo_node = leapraid_transport_topo_node_by_sas_addr(adapter, + sas_address, + target_card_port); + if (!topo_node) { + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, + flags); + return; + } + + card_phy = &topo_node->card_phy[phy_index]; + card_phy->attached_hdl = hdl; + spin_unlock_irqrestore(&adapter->dev_topo.topo_node_lock, flags); + + if (hdl && link_rate >= LEAPRAID_SAS_NEG_LINK_RATE_1_5) { + leapraid_transport_set_identify(adapter, hdl, + &card_phy->remote_identify); + if (topo_node->hdl <= adapter->dev_topo.card.phys_num && + adapter->adapter_attr.enable_mp) { + list_for_each_entry(card_port, + &adapter->dev_topo.card_port_list, + list) { + if (card_port->sas_address == sas_address && + card_port == target_card_port) + card_port->phy_mask |= + BIT(card_phy->phy_id); + } + } + leapraid_transport_attach_phy_to_port( + adapter, + topo_node, + card_phy, + card_phy->remote_identify.sas_address, + target_card_port); + } else { + memset(&card_phy->remote_identify, 0, + sizeof(struct sas_identify)); + } + + if (card_phy->phy) + card_phy->phy->negotiated_linkrate = + leapraid_transport_convert_phy_link_rate(link_rate); +} + +static int leapraid_dma_map_buffer(struct device *dev, struct bsg_buffer *buf, + dma_addr_t *dma_addr, + size_t *dma_len, void **p) +{ + if (buf->sg_cnt > 1) { + *p = dma_alloc_coherent(dev, buf->payload_len, dma_addr, + GFP_KERNEL); + if (!*p) + return -ENOMEM; + + *dma_len = buf->payload_len; + } else { + if (!dma_map_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL)) { + dev_err(dev, + "%s: Failed to map sg buffer for dma\n", + __func__); + return -ENOMEM; + } + + *dma_addr = sg_dma_address(buf->sg_list); + *dma_len = sg_dma_len(buf->sg_list); + *p = NULL; + } + return 0; +} + +static void leapraid_dma_unmap_buffer(struct device *dev, + struct bsg_buffer *buf, + dma_addr_t dma_addr, + void *p) +{ + if (p) + dma_free_coherent(dev, buf->payload_len, p, dma_addr); + else + dma_unmap_sg(dev, buf->sg_list, 1, DMA_BIDIRECTIONAL); +} + +static void leapraid_build_smp_task(struct leapraid_adapter *adapter, + struct sas_rphy *rphy, + dma_addr_t h2c_dma_addr, size_t h2c_size, + dma_addr_t c2h_dma_addr, size_t c2h_size) +{ + struct leapraid_smp_passthrough_req *smp_passthrough_req; + u16 inter_taskid; + void *psge; + + inter_taskid = adapter->driver_cmds.transport_cmd.inter_taskid; + smp_passthrough_req = leapraid_get_task_desc(adapter, inter_taskid); + memset(smp_passthrough_req, 0, sizeof(*smp_passthrough_req)); + + smp_passthrough_req->func = LEAPRAID_FUNC_SMP_PASSTHROUGH; + smp_passthrough_req->physical_port = + leapraid_transport_get_port_id_by_rphy(adapter, rphy); + smp_passthrough_req->sas_address = (rphy) ? + cpu_to_le64(rphy->identify.sas_address) : + cpu_to_le64(adapter->dev_topo.card.sas_address); + smp_passthrough_req->req_data_len = + cpu_to_le16(h2c_size - LEAPRAID_SMP_FRAME_HEADER_SIZE); + psge = &smp_passthrough_req->sgl; + leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, + h2c_size - LEAPRAID_SMP_FRAME_HEADER_SIZE, + c2h_dma_addr, + c2h_size - LEAPRAID_SMP_FRAME_HEADER_SIZE); +} + +static int leapraid_send_smp_req(struct leapraid_adapter *adapter) +{ + const struct leapraid_smp_passthrough_req *smp_passthrough_req; + u16 inter_taskid; + + dev_dbg(&adapter->pdev->dev, + "%s: Sending smp request\n", __func__); + inter_taskid = adapter->driver_cmds.transport_cmd.inter_taskid; + smp_passthrough_req = leapraid_get_task_desc(adapter, inter_taskid); + init_completion(&adapter->driver_cmds.transport_cmd.done); + leapraid_fire_task(adapter, inter_taskid); + wait_for_completion_timeout(&adapter->driver_cmds.transport_cmd.done, + LEAPRAID_TRANSPORT_CMD_TIMEOUT * HZ); + if (!(adapter->driver_cmds.transport_cmd.status & LEAPRAID_CMD_DONE)) { + dev_err(&adapter->pdev->dev, "%s: timeout, st=0x%x\n", + __func__, adapter->driver_cmds.transport_cmd.status); + leapraid_log_req_context(adapter, inter_taskid, + smp_passthrough_req); + if (!(adapter->driver_cmds.transport_cmd.status & + LEAPRAID_CMD_RESET)) { + dev_dbg(&adapter->pdev->dev, + "%s:%d: call hard_reset\n", + __func__, __LINE__); + leapraid_hard_reset_handler(adapter, FULL_RESET); + return -ETIMEDOUT; + } + } + + dev_dbg(&adapter->pdev->dev, "%s: SMP request complete\n", __func__); + if (!(adapter->driver_cmds.transport_cmd.status & + LEAPRAID_CMD_REPLY_VALID)) { + dev_err(&adapter->pdev->dev, + "%s: SMP request no reply\n", __func__); + return -ENXIO; + } + + return 0; +} + +static void leapraid_handle_smp_rep(struct leapraid_adapter *adapter, + struct bsg_job *job, void *addr_in, + unsigned int *reslen) +{ + struct leapraid_smp_passthrough_rep *smp_passthrough_rep; + + smp_passthrough_rep = + (void *)&adapter->driver_cmds.transport_cmd.reply; + + dev_dbg(&adapter->pdev->dev, "%s: Response data len=%d\n", + __func__, le16_to_cpu(smp_passthrough_rep->resp_data_len)); + + memcpy(job->reply, smp_passthrough_rep, sizeof(*smp_passthrough_rep)); + job->reply_len = sizeof(*smp_passthrough_rep); + *reslen = le16_to_cpu(smp_passthrough_rep->resp_data_len); + + if (addr_in) + sg_copy_from_buffer(job->reply_payload.sg_list, + job->reply_payload.sg_cnt, addr_in, + job->reply_payload.payload_len); +} + +static void leapraid_transport_smp_handler(struct bsg_job *job, + struct Scsi_Host *shost, + struct sas_rphy *rphy) +{ + struct leapraid_adapter *adapter = shost_priv(shost); + dma_addr_t c2h_dma_addr; + dma_addr_t h2c_dma_addr; + void *addr_in = NULL; + void *addr_out = NULL; + size_t c2h_size; + size_t h2c_size; + int rc; + unsigned int reslen = 0; + + if (adapter->access_ctrl.shost_recovering || + adapter->access_ctrl.pcie_recovering) { + dev_err(&adapter->pdev->dev, + "%s: Failed, shost_recovering=%d pcie_recovering=%d\n", + __func__, + adapter->access_ctrl.shost_recovering, + adapter->access_ctrl.pcie_recovering); + rc = -EFAULT; + goto exit_bsg_job; + } + + rc = mutex_lock_interruptible(&adapter->driver_cmds + .transport_cmd.mutex); + if (rc) + goto exit_bsg_job; + + adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_PENDING; + rc = leapraid_dma_map_buffer(&adapter->pdev->dev, + &job->request_payload, + &h2c_dma_addr, &h2c_size, &addr_out); + if (rc) + goto release_lock; + + if (h2c_size < LEAPRAID_SMP_FRAME_HEADER_SIZE) { + dev_err(&adapter->pdev->dev, + "%s: Invalid SMP request payload size=%zu\n", + __func__, h2c_size); + rc = -EINVAL; + goto free_req_buf; + } + + if (addr_out) + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, addr_out, + job->request_payload.payload_len); + + rc = leapraid_dma_map_buffer(&adapter->pdev->dev, &job->reply_payload, + &c2h_dma_addr, &c2h_size, &addr_in); + if (rc) + goto free_req_buf; + + if (c2h_size < LEAPRAID_SMP_FRAME_HEADER_SIZE) { + dev_err(&adapter->pdev->dev, + "%s: Invalid SMP reply payload size=%zu\n", + __func__, c2h_size); + rc = -EINVAL; + goto free_rep_buf; + } + + rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT, + __func__); + if (rc) + goto free_rep_buf; + + leapraid_build_smp_task(adapter, rphy, h2c_dma_addr, + h2c_size, c2h_dma_addr, c2h_size); + + rc = leapraid_send_smp_req(adapter); + if (rc) + goto free_rep_buf; + + leapraid_handle_smp_rep(adapter, job, addr_in, &reslen); + +free_rep_buf: + leapraid_dma_unmap_buffer(&adapter->pdev->dev, &job->reply_payload, + c2h_dma_addr, addr_in); +free_req_buf: + leapraid_dma_unmap_buffer(&adapter->pdev->dev, &job->request_payload, + h2c_dma_addr, addr_out); +release_lock: + adapter->driver_cmds.transport_cmd.status = LEAPRAID_CMD_NOT_USED; + mutex_unlock(&adapter->driver_cmds.transport_cmd.mutex); +exit_bsg_job: + bsg_job_done(job, rc, reslen); +} + +struct sas_function_template leapraid_transport_functions = { + .smp_handler = leapraid_transport_smp_handler, +}; + +struct scsi_transport_template *leapraid_transport_template; diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 160f02f2f51d..5cbc51899de0 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } senselen = get_unaligned_be16(data); - if (datalen < senselen) + if (datalen < senselen + 2) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e90805ba868f..7223bb18b048 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); spin_unlock(&conn->session->back_lock); break; - case ISCSI_OP_SCSI_CMD_RSP: - if (tcp_conn->in.datalen) { - iscsi_tcp_data_recv_prep(tcp_conn); - return 0; - } - rc = iscsi_complete_pdu(conn, hdr, NULL, 0); - break; case ISCSI_OP_R2T: if (ahslen) { rc = ISCSI_ERR_AHSLEN; @@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) } rc = iscsi_tcp_r2t_rsp(conn, hdr); break; + case ISCSI_OP_SCSI_CMD_RSP: case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_TEXT_RSP: case ISCSI_OP_REJECT: diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 0bec236f0fb5..c3f3d05b46de 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) } } -static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) +void sas_resume_ha(struct sas_ha_struct *ha) { const unsigned long tmo = msecs_to_jiffies(25000); int i; @@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", i, i > 1 ? "s" : ""); wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); + + /* + * All phys are back up or timed out. Turn on I/O and drain + * pending work. + */ + scsi_unblock_requests(ha->shost); + sas_drain_work(ha); + + /* + * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler + * calls sas_deform_port() -> sas_destruct_devices(), which removes + * SCSI devices and, for LLDDs using device_link() PM sync, waits + * for the host to be runtime-active. Sending it before the drain + * would deadlock: the drain waits for the handler, the handler + * waits for host resume, and host resume waits for the drain to + * finish. + */ for (i = 0; i < ha->num_phys; i++) { struct asd_sas_phy *phy = ha->sas_phy[i]; @@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) } } - /* all phys are back up or timed out, turn on i/o so we can - * flush out disks that did not return - */ - scsi_unblock_requests(ha->shost); - if (drain) - sas_drain_work(ha); clear_bit(SAS_HA_RESUMING, &ha->state); sas_queue_deferred_work(ha); @@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) */ sas_resume_insert_broadcast_ha(ha); } - -void sas_resume_ha(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, true); -} EXPORT_SYMBOL(sas_resume_ha); -/* A no-sync variant, which does not call sas_drain_ha(). */ -void sas_resume_ha_no_sync(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, false); -} -EXPORT_SYMBOL(sas_resume_ha_no_sync); - void sas_suspend_ha(struct sas_ha_struct *ha) { int i; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 82af59c913e9..23355f12fbff 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -8189,6 +8189,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) mempool_free(mboxq, phba->mbox_mem_pool); goto out_free_bsmbx; } + mempool_free(mboxq, phba->mbox_mem_pool); /* * 1 for cmd, 1 for rsp, NVME adds an extra one @@ -8311,8 +8312,6 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) goto out_free_sg_dma_buf; } - mempool_free(mboxq, phba->mbox_mem_pool); - /* Verify OAS is supported */ lpfc_sli4_oas_verify(phba); diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index 31b19ed1528e..681868716ebd 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -9,6 +9,7 @@ #include "mpi3mr.h" #include +#include static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason); @@ -1287,11 +1288,14 @@ static void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_ioc *mrioc) static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) { struct kobj_uevent_env *env; + unsigned int noio_flag; int ret; + noio_flag = memalloc_noio_save(); + env = kzalloc_obj(*env); if (!env) - return; + goto out_restore; ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name); if (ret) @@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) out_free: kfree(env); - +out_restore: + memalloc_noio_restore(noio_flag); } /** diff --git a/drivers/scsi/mpt3sas/Makefile b/drivers/scsi/mpt3sas/Makefile index e76d994dbed3..18e2d87eb4a2 100644 --- a/drivers/scsi/mpt3sas/Makefile +++ b/drivers/scsi/mpt3sas/Makefile @@ -9,3 +9,5 @@ mpt3sas-y += mpt3sas_base.o \ mpt3sas_trigger_diag.o \ mpt3sas_warpdrive.o \ mpt3sas_debugfs.o \ + +mpt3sas-$(CONFIG_HWMON) += mpt3sas_hwmon.o diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 79052f2accbd..791a3c5fbf44 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -8369,11 +8369,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc) return r; /* scan_start and scan_finished support */ } - r = _base_send_port_enable(ioc); - if (r) - return r; - - return r; + return _base_send_port_enable(ioc); } /** diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.h b/drivers/scsi/mpt3sas/mpt3sas_base.h index d4597d058705..47255bf9cdda 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.h +++ b/drivers/scsi/mpt3sas/mpt3sas_base.h @@ -1629,6 +1629,9 @@ struct MPT3SAS_ADAPTER { u8 is_aero_ioc; struct dentry *debugfs_root; struct dentry *ioc_dump; +#if IS_REACHABLE(CONFIG_HWMON) + struct mpt3sas_hwmon *hwmon; +#endif PUT_SMID_IO_FP_HIP put_smid_scsi_io; PUT_SMID_IO_FP_HIP put_smid_fast_path; PUT_SMID_IO_FP_HIP put_smid_hi_priority; @@ -1904,6 +1907,9 @@ int mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage3_t *config_page, u16 sz); int mpt3sas_config_set_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page); +int mpt3sas_config_get_iounit_pg7(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, + Mpi2IOUnitPage7_t *config_page); int mpt3sas_config_get_iounit_pg8(struct MPT3SAS_ADAPTER *ioc, Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage8_t *config_page); int mpt3sas_config_get_sas_iounit_pg1(struct MPT3SAS_ADAPTER *ioc, @@ -2047,6 +2053,20 @@ void mpt3sas_destroy_debugfs(struct MPT3SAS_ADAPTER *ioc); void mpt3sas_init_debugfs(void); void mpt3sas_exit_debugfs(void); +#if IS_REACHABLE(CONFIG_HWMON) +int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc); +void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc); +#else +static inline int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc) +{ + return 0; +} + +static inline void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc) +{ +} +#endif + /** * _scsih_is_pcie_scsi_device - determines if device is an pcie scsi device * @device_info: bitfield providing information about the device. diff --git a/drivers/scsi/mpt3sas/mpt3sas_config.c b/drivers/scsi/mpt3sas/mpt3sas_config.c index 45ac853e1289..b0d5ef893600 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_config.c +++ b/drivers/scsi/mpt3sas/mpt3sas_config.c @@ -991,6 +991,42 @@ mpt3sas_config_get_iounit_pg3(struct MPT3SAS_ADAPTER *ioc, return r; } +/** + * mpt3sas_config_get_iounit_pg7 - obtain iounit page 7 + * @ioc: per adapter object + * @mpi_reply: reply mf payload returned from firmware + * @config_page: contents of the config page + * Context: sleep. + * + * Return: 0 for success, non-zero for failure. + */ +int mpt3sas_config_get_iounit_pg7(struct MPT3SAS_ADAPTER *ioc, + Mpi2ConfigReply_t *mpi_reply, + Mpi2IOUnitPage7_t *config_page) +{ + Mpi2ConfigRequest_t mpi_request; + int r; + + memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); + mpi_request.Function = MPI2_FUNCTION_CONFIG; + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; + mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT; + mpi_request.Header.PageNumber = 7; + mpi_request.Header.PageVersion = MPI2_IOUNITPAGE7_PAGEVERSION; + ioc->build_zero_len_sge_mpi(ioc, &mpi_request.PageBufferSGE); + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0); + if (r) + goto out; + + mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT; + r = _config_request(ioc, &mpi_request, mpi_reply, + MPT3_CONFIG_PAGE_DEFAULT_TIMEOUT, + config_page, sizeof(*config_page)); + out: + return r; +} + /** * mpt3sas_config_get_iounit_pg8 - obtain iounit page 8 * @ioc: per adapter object diff --git a/drivers/scsi/mpt3sas/mpt3sas_hwmon.c b/drivers/scsi/mpt3sas/mpt3sas_hwmon.c new file mode 100644 index 000000000000..297a8efecadb --- /dev/null +++ b/drivers/scsi/mpt3sas/mpt3sas_hwmon.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hardware monitoring (hwmon) support for the LSI / Broadcom mpt3sas + * SAS HBA driver. Exposes the IOC and board temperature sensors by + * reading MPI IO Unit Page 7. + */ + +#include +#include +#include + +#include "mpt3sas_base.h" + +struct mpt3sas_hwmon { + struct MPT3SAS_ADAPTER *ioc; + struct device *hwmon_dev; + bool ioc_present; + bool board_present; +}; + +/* + * Convert a (raw, units) reading to millidegrees Celsius. + * Returns -ENODATA when the sensor reports "not present" or + * unknown units. Temperature values are interpreted as signed + * two's-complement integers. + * + * The MPI2_IOUNITPAGE7_IOC_TEMP_* and MPI2_IOUNITPAGE7_BOARD_TEMP_* + * defines in mpi2_cnfg.h share the same values; the IOC ones are + * used for both channels. + */ +static int _hwmon_to_mdegc(s16 raw, u8 units, long *out) +{ + switch (units) { + case MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS: + *out = (long)raw * 1000; + return 0; + case MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT: + /* (F - 32) * 5 / 9, expressed in milli-units */ + *out = ((long)raw - 32) * 5000 / 9; + return 0; + default: + return -ENODATA; + } +} + +static umode_t _hwmon_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct mpt3sas_hwmon *h = drvdata; + + if (type != hwmon_temp) + return 0; + if (attr != hwmon_temp_input && attr != hwmon_temp_label) + return 0; + if (channel == 0 && h->ioc_present) + return 0444; + if (channel == 1 && h->board_present) + return 0444; + return 0; +} + +static int _hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct mpt3sas_hwmon *h = dev_get_drvdata(dev); + Mpi2ConfigReply_t mpi_reply; + Mpi2IOUnitPage7_t page; + int r; + + if (type != hwmon_temp || attr != hwmon_temp_input) + return -EOPNOTSUPP; + + r = mpt3sas_config_get_iounit_pg7(h->ioc, &mpi_reply, &page); + if (r) + return r; + + if (channel == 0) + return _hwmon_to_mdegc((s16)le16_to_cpu(page.IOCTemperature), + page.IOCTemperatureUnits, val); + if (channel == 1) + return _hwmon_to_mdegc((s16)le16_to_cpu(page.BoardTemperature), + page.BoardTemperatureUnits, val); + return -EOPNOTSUPP; +} + +static const char * const mpt3sas_hwmon_temp_labels[] = { + "IOC", + "Board", +}; + +static int _hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + if (type != hwmon_temp || attr != hwmon_temp_label) + return -EOPNOTSUPP; + *str = mpt3sas_hwmon_temp_labels[channel]; + return 0; +} + +static const struct hwmon_channel_info * const mpt3sas_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + NULL, +}; + +static const struct hwmon_ops mpt3sas_hwmon_ops = { + .is_visible = _hwmon_is_visible, + .read = _hwmon_read, + .read_string = _hwmon_read_string, +}; + +static const struct hwmon_chip_info mpt3sas_hwmon_chip_info = { + .ops = &mpt3sas_hwmon_ops, + .info = mpt3sas_hwmon_info, +}; + +/** + * mpt3sas_hwmon_register - register an hwmon device for the IOC + * @ioc: per adapter object + * Context: sleep. + * + * Succeeds without registering when no temperature sensors are present, + * so cards without thermal monitoring do not expose an empty hwmon node. + * Paired with mpt3sas_hwmon_unregister() from the driver's remove path. + * + * Return: 0 for success, non-zero for failure. + */ +int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc) +{ + struct device *parent = &ioc->pdev->dev; + struct mpt3sas_hwmon *h; + struct device *hwdev; + Mpi2ConfigReply_t mpi_reply; + Mpi2IOUnitPage7_t page; + int r; + + r = mpt3sas_config_get_iounit_pg7(ioc, &mpi_reply, &page); + if (r) + return r; + + /* + * A page where both *TemperatureUnits are NOT_PRESENT covers + * two cases: cards that genuinely lack sensors, and firmware + * errors that left the page zero-filled (the accessor mirrors + * _config_request() behaviour). Either way: skip registration. + */ + if (page.IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT && + page.BoardTemperatureUnits == MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT) + return 0; + + h = kzalloc_obj(*h); + if (!h) + return -ENOMEM; + + h->ioc = ioc; + h->ioc_present = page.IOCTemperatureUnits != MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT; + h->board_present = page.BoardTemperatureUnits != MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT; + + hwdev = hwmon_device_register_with_info(parent, "mpt3sas", h, + &mpt3sas_hwmon_chip_info, + NULL); + if (IS_ERR(hwdev)) { + kfree(h); + return PTR_ERR(hwdev); + } + + h->hwmon_dev = hwdev; + ioc->hwmon = h; + return 0; +} + +/** + * mpt3sas_hwmon_unregister - tear down the hwmon device, if any + * @ioc: per adapter object + * + * Safe to call when registration was skipped (no sensors) or + * failed; in those cases ioc->hwmon is NULL and this is a no-op. + */ +void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc) +{ + struct mpt3sas_hwmon *h = ioc->hwmon; + + if (!h) + return; + hwmon_device_unregister(h->hwmon_dev); + kfree(h); + ioc->hwmon = NULL; +} diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 12caffeed3a0..dea78688cc9b 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -12562,6 +12562,7 @@ static void scsih_remove(struct pci_dev *pdev) /* release all the volumes */ _scsih_ir_shutdown(ioc); mpt3sas_destroy_debugfs(ioc); + mpt3sas_hwmon_unregister(ioc); sas_remove_host(shost); list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, list) { @@ -13651,6 +13652,11 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id) } scsi_scan_host(shost); + + if (mpt3sas_hwmon_register(ioc)) + ioc_warn(ioc, + "hwmon registration failed; temperatures not exposed\n"); + mpt3sas_setup_debugfs(ioc); return 0; out_add_shost_fail: diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 6a05ce195aa0..a4ca22024ede 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -580,7 +580,6 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj, ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size); mutex_unlock(&ha->optrom_mutex); - ha->isp_ops->read_optrom(vha, ha->vpd, faddr, ha->vpd_size); skip: return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size); } @@ -816,7 +815,9 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, "Unable to allocate memory for VPD information update.\n"); return -ENOMEM; } + mutex_lock(&ha->optrom_mutex); ha->isp_ops->get_flash_version(vha, tmp_data); + mutex_unlock(&ha->optrom_mutex); vfree(tmp_data); break; } @@ -1472,6 +1473,9 @@ qla2x00_optrom_gold_fw_version_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; + if (IS_QLA29XX(ha)) + return -EPERM; + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); @@ -1500,6 +1504,9 @@ qla24xx_84xx_fw_version_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; + if (IS_QLA29XX(ha)) + return -EPERM; + if (!IS_QLA84XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); @@ -1521,7 +1528,7 @@ qla2x00_serdes_version_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n", @@ -1537,7 +1544,7 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr, struct qla_hw_data *ha = vha->hw; if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n", @@ -1566,6 +1573,9 @@ qla2x00_flash_block_size_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; + if (IS_QLA29XX(ha)) + return -EPERM; + return scnprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size); } @@ -1574,6 +1584,10 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA29XX(ha)) + return -EPERM; if (!IS_CNA_CAPABLE(vha->hw)) return scnprintf(buf, PAGE_SIZE, "\n"); @@ -1586,6 +1600,10 @@ qla2x00_vn_port_mac_address_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA29XX(ha)) + return -EPERM; if (!IS_CNA_CAPABLE(vha->hw)) return scnprintf(buf, PAGE_SIZE, "\n"); @@ -1660,10 +1678,8 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, rval = qla2x00_get_firmware_state(vha, state); mutex_unlock(&vha->hw->optrom_mutex); out: - if (rval != QLA_SUCCESS) { + if (rval != QLA_SUCCESS) memset(state, -1, sizeof(state)); - rval = qla2x00_get_firmware_state(vha, state); - } return scnprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0], state[1], state[2], state[3], state[4], state[5]); @@ -1717,6 +1733,10 @@ qla2x00_allow_cna_fw_dump_show(struct device *dev, struct device_attribute *attr, char *buf) { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA29XX(ha)) + return -EPERM; if (!IS_P3P_TYPE(vha->hw)) return scnprintf(buf, PAGE_SIZE, "\n"); @@ -1750,7 +1770,7 @@ qla2x00_pep_version_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n", @@ -1764,10 +1784,11 @@ qla2x00_min_supported_speed_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%s\n", + ha->min_supported_speed == 7 ? "128Gps" : ha->min_supported_speed == 6 ? "64Gps" : ha->min_supported_speed == 5 ? "32Gps" : ha->min_supported_speed == 4 ? "16Gps" : @@ -1783,10 +1804,11 @@ qla2x00_max_supported_speed_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%s\n", + ha->max_supported_speed == 3 ? "128Gps" : ha->max_supported_speed == 2 ? "64Gps" : ha->max_supported_speed == 1 ? "32Gps" : ha->max_supported_speed == 0 ? "16Gps" : "unknown"); @@ -1802,7 +1824,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr, int mode = QLA_SET_DATA_RATE_LR; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) { + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) { ql_log(ql_log_warn, vha, 0x70d8, "Speed setting not supported \n"); return -EINVAL; @@ -1813,7 +1835,7 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr, return rval; speed = type; if (type == 40 || type == 80 || type == 160 || - type == 320) { + type == 320 || type == 640 || type == 1280) { ql_dbg(ql_dbg_user, vha, 0x70d9, "Setting will be affected after a loss of sync\n"); type = type/10; @@ -1838,6 +1860,12 @@ qla2x00_port_speed_store(struct device *dev, struct device_attribute *attr, case 32: ha->set_data_rate = PORT_SPEED_32GB; break; + case 64: + ha->set_data_rate = PORT_SPEED_64GB; + break; + case 128: + ha->set_data_rate = PORT_SPEED_128GB; + break; default: ql_log(ql_log_warn, vha, 0x1199, "Unrecognized speed setting:%lx. Setting Autoneg\n", @@ -1867,6 +1895,7 @@ static const struct { { PORT_SPEED_16GB, "16" }, { PORT_SPEED_32GB, "32" }, { PORT_SPEED_64GB, "64" }, + { PORT_SPEED_128GB, "128" }, { PORT_SPEED_10GB, "10" }, }; @@ -2366,7 +2395,7 @@ qla2x00_fw_attr_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return scnprintf(buf, PAGE_SIZE, "\n"); return scnprintf(buf, PAGE_SIZE, "%llx\n", @@ -2413,7 +2442,7 @@ qla2x00_mpi_fw_state_show(struct device *dev, struct device_attribute *attr, u16 mpi_state; struct qla_hw_data *ha = vha->hw; - if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha))) + if (!(IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))) return scnprintf(buf, PAGE_SIZE, "MPI state reporting is not supported for this HBA.\n"); @@ -2648,6 +2677,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) case PORT_SPEED_64GB: speed = FC_PORTSPEED_64GBIT; break; + case PORT_SPEED_128GB: + speed = FC_PORTSPEED_128GBIT; + break; default: speed = FC_PORTSPEED_UNKNOWN; break; @@ -2964,7 +2996,8 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) p->error_frames = le32_to_cpu(stats->dropped_frames) + le32_to_cpu(stats->discarded_frames); - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { p->rx_words = le64_to_cpu(stats->fpm_recv_word_cnt); p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt); } else { @@ -3409,6 +3442,8 @@ qla2x00_get_host_supported_speeds(scsi_qla_host_t *vha, uint speeds) { uint supported_speeds = FC_PORTSPEED_UNKNOWN; + if (speeds & FDMI_PORT_SPEED_128GB) + supported_speeds |= FC_PORTSPEED_128GBIT; if (speeds & FDMI_PORT_SPEED_64GB) supported_speeds |= FC_PORTSPEED_64GBIT; if (speeds & FDMI_PORT_SPEED_32GB) diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index 5e910b5ca670..ab559048dbb8 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -160,6 +160,12 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job) goto exit_fcp_prio_cfg; } + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) { + ret = -EINVAL; + goto exit_fcp_prio_cfg; + } + /* Get the sub command */ oper = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; @@ -758,6 +764,10 @@ qla2x00_process_loopback(struct bsg_job *bsg_job) return -EIO; } + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 3 * sizeof(uint32_t)) + return -EINVAL; + memset(&elreq, 0, sizeof(elreq)); elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev, @@ -990,6 +1000,10 @@ qla84xx_reset(struct bsg_job *bsg_job) return -EINVAL; } + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) + return -EINVAL; + flag = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW); @@ -1034,6 +1048,10 @@ qla84xx_updatefw(struct bsg_job *bsg_job) return -EINVAL; } + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) + return -EINVAL; + sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); if (!sg_cnt) { @@ -1407,23 +1425,14 @@ qla24xx_iidma(struct bsg_job *bsg_job) static int qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha, - uint8_t is_update) + uint32_t start, uint8_t is_update) { - struct fc_bsg_request *bsg_request = bsg_job->request; - uint32_t start = 0; int valid = 0; struct qla_hw_data *ha = vha->hw; if (unlikely(pci_channel_offline(ha->pdev))) return -EINVAL; - start = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; - if (start > ha->optrom_size) { - ql_log(ql_log_warn, vha, 0x7055, - "start %d > optrom_size %d.\n", start, ha->optrom_size); - return -EINVAL; - } - if (ha->optrom_state != QLA_SWAITING) { ql_log(ql_log_info, vha, 0x7056, "optrom_state %d.\n", ha->optrom_state); @@ -1431,42 +1440,79 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha, } ha->optrom_region_start = start; - ql_dbg(ql_dbg_user, vha, 0x7057, "is_update=%d.\n", is_update); - if (is_update) { - if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0) - valid = 1; - else if (start == (ha->flt_region_boot * 4) || - start == (ha->flt_region_fw * 4)) - valid = 1; - else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || - IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) - valid = 1; - if (!valid) { - ql_log(ql_log_warn, vha, 0x7058, - "Invalid start region 0x%x/0x%x.\n", start, - bsg_job->request_payload.payload_len); + + if (IS_QLA29XX(ha)) { + if (start > ha->optrom_size) { + ql_log(ql_log_warn, vha, 0x7055, + "start %d > optrom_size %d.\n", start, + ha->optrom_size); return -EINVAL; } - ha->optrom_region_size = start + - bsg_job->request_payload.payload_len > ha->optrom_size ? - ha->optrom_size - start : - bsg_job->request_payload.payload_len; - ha->optrom_state = QLA_SWRITING; + if (is_update) { + ha->optrom_region_size = start + + bsg_job->request_payload.payload_len > + ha->optrom_size ? + ha->optrom_size - start : + bsg_job->request_payload.payload_len; + ha->optrom_state = QLA_SWRITING; + } else { + ha->optrom_region_size = start + + bsg_job->reply_payload.payload_len > + ha->optrom_size ? + ha->optrom_size - start : + bsg_job->reply_payload.payload_len; + ha->optrom_state = QLA_SREADING; + } } else { - ha->optrom_region_size = start + - bsg_job->reply_payload.payload_len > ha->optrom_size ? - ha->optrom_size - start : - bsg_job->reply_payload.payload_len; - ha->optrom_state = QLA_SREADING; + if (start > ha->optrom_size) { + ql_log(ql_log_warn, vha, 0x7055, + "start %d > optrom_size %d.\n", start, + ha->optrom_size); + return -EINVAL; + } + + ql_dbg(ql_dbg_user, vha, 0x7057, + "is_update=%d.\n", is_update); + if (is_update) { + if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0) + valid = 1; + else if (start == (ha->flt_region_boot * 4) || + start == (ha->flt_region_fw * 4)) + valid = 1; + else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || + IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || + IS_QLA27XX(ha) || IS_QLA28XX(ha)) + valid = 1; + if (!valid) { + ql_log(ql_log_warn, vha, 0x7058, + "Invalid start region 0x%x/0x%x.\n", + start, + bsg_job->request_payload.payload_len); + return -EINVAL; + } + + ha->optrom_region_size = start + + bsg_job->request_payload.payload_len > + ha->optrom_size ? + ha->optrom_size - start : + bsg_job->request_payload.payload_len; + ha->optrom_state = QLA_SWRITING; + } else { + ha->optrom_region_size = start + + bsg_job->reply_payload.payload_len > + ha->optrom_size ? + ha->optrom_size - start : + bsg_job->reply_payload.payload_len; + ha->optrom_state = QLA_SREADING; + } } ha->optrom_buffer = vzalloc(ha->optrom_region_size); if (!ha->optrom_buffer) { ql_log(ql_log_warn, vha, 0x7059, - "Read: Unable to allocate memory for optrom retrieval " - "(%x)\n", ha->optrom_region_size); + "%s: Unable to allocate memory for optrom retrieval (%x)\n", + __func__, ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; return -ENOMEM; @@ -1478,17 +1524,31 @@ qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha, static int qla2x00_read_optrom(struct bsg_job *bsg_job) { + struct fc_bsg_request *bsg_request = bsg_job->request; struct fc_bsg_reply *bsg_reply = bsg_job->reply; struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; + uint32_t start; int rval = 0; + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) + return -EINVAL; + + start = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; + if (ha->flags.nic_core_reset_hdlr_active) return -EBUSY; + if (IS_QLA29XX(ha)) { + ql_log(ql_log_warn, vha, 0x7070, + "Legacy optrom read not supported on 29xx.\n"); + return -EINVAL; + } + mutex_lock(&ha->optrom_mutex); - rval = qla2x00_optrom_setup(bsg_job, vha, 0); + rval = qla2x00_optrom_setup(bsg_job, vha, start, 0); if (rval) { mutex_unlock(&ha->optrom_mutex); return rval; @@ -1515,14 +1575,22 @@ qla2x00_read_optrom(struct bsg_job *bsg_job) static int qla2x00_update_optrom(struct bsg_job *bsg_job) { + struct fc_bsg_request *bsg_request = bsg_job->request; struct fc_bsg_reply *bsg_reply = bsg_job->reply; struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; + uint32_t start; int rval = 0; + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) + return -EINVAL; + + start = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; + mutex_lock(&ha->optrom_mutex); - rval = qla2x00_optrom_setup(bsg_job, vha, 1); + rval = qla2x00_optrom_setup(bsg_job, vha, start, 1); if (rval) { mutex_unlock(&ha->optrom_mutex); return rval; @@ -1554,6 +1622,379 @@ qla2x00_update_optrom(struct bsg_job *bsg_job) return rval; } +/** + * qla29xx_bsg_flash_block_read - Read flash block for QLA29XX. + * @bsg_job: BSG job structure. + * + * Returns 0 on success, error code on failure. + */ +static int qla29xx_bsg_flash_block_read(struct bsg_job *bsg_job) +{ + struct fc_bsg_request *bsg_req = bsg_job->request; + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct qla_block_rw *brcmd; + void *buf; + uint16_t opts = 0; + int rval = 0; + + if (bsg_job->request_len < sizeof(struct fc_bsg_request) + + 2 * sizeof(uint32_t) + sizeof(struct qla_block_rw)) + return -EINVAL; + + brcmd = + (struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2]; + + ql_log(ql_log_info, vha, 0x7062, + "%s: region 0x%x options 0x%x rw_length 0x%x offset 0x%x chunk_length 0x%x\n", + __func__, brcmd->region, brcmd->options, brcmd->rw_length, + brcmd->region_offset, brcmd->chunk_length); + + if (brcmd->region > U16_MAX) { + ql_log(ql_log_warn, vha, 0x7066, + "%s: invalid region 0x%x\n", __func__, brcmd->region); + return -EINVAL; + } + + mutex_lock(&ha->optrom_mutex); + rval = qla2x00_optrom_setup(bsg_job, vha, brcmd->region_offset, 0); + if (rval) { + mutex_unlock(&ha->optrom_mutex); + return rval; + } + + check_and_set_mbc_bits(brcmd->options, opts, QLA_IS_TIM, BIT_15); + check_and_set_mbc_bits(brcmd->options, opts, QLA_IS_SECURE, BIT_7); + check_and_set_mbc_bits(brcmd->options, opts, QLA_UPDATE_MBR, BIT_8); + + if (!ha->isp_ops->read_optrom_region) { + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); + return -EINVAL; + } + + buf = ha->isp_ops->read_optrom_region(vha, brcmd->region, opts, + ha->optrom_buffer, ha->optrom_region_start, + ha->optrom_region_size); + if (!buf) { + ql_log(ql_log_warn, vha, 0x7063, + "%s failed to read flash region 0x%x\n", + __func__, brcmd->region); + bsg_reply->result = -EINVAL; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + bsg_reply->reply_payload_rcv_len = 0; + } else { + bsg_reply->result = DID_OK; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_OK; + + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, vha, 0x72a6, + ha->optrom_buffer, ha->optrom_region_size); + + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + ha->optrom_buffer, + ha->optrom_region_size); + + bsg_reply->reply_payload_rcv_len = ha->optrom_region_size; + } + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + + return 0; +} + +/** + * qla29xx_bsg_flash_block_write - Write flash block for QLA29XX. + * @bsg_job: BSG job structure. + * + * Returns 0 on success, error code on failure. + */ +static int qla29xx_bsg_flash_block_write(struct bsg_job *bsg_job) +{ + struct fc_bsg_request *bsg_req = bsg_job->request; + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct qla_block_rw *bwcmd; + uint16_t opts = 0; + int rval = 0; + + if (bsg_job->request_len < sizeof(struct fc_bsg_request) + + 2 * sizeof(uint32_t) + sizeof(struct qla_block_rw)) + return -EINVAL; + + bwcmd = + (struct qla_block_rw *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2]; + + ql_log(ql_log_info, vha, 0x7064, + "%s: region 0x%x options 0x%x rw_length 0x%x offset 0x%x chunk_length 0x%x\n", + __func__, bwcmd->region, bwcmd->options, bwcmd->rw_length, + bwcmd->region_offset, bwcmd->chunk_length); + + if (bwcmd->region > U16_MAX) { + ql_log(ql_log_warn, vha, 0x7067, + "%s: invalid region 0x%x\n", __func__, bwcmd->region); + return -EINVAL; + } + + mutex_lock(&ha->optrom_mutex); + rval = qla2x00_optrom_setup(bsg_job, vha, bwcmd->region_offset, 1); + if (rval) { + mutex_unlock(&ha->optrom_mutex); + return rval; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, ha->optrom_buffer, + ha->optrom_region_size); + + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, vha, 0x73a6, + ha->optrom_buffer, ha->optrom_region_size); + + check_and_set_mbc_bits(bwcmd->options, opts, QLA_IS_TIM, BIT_15); + check_and_set_mbc_bits(bwcmd->options, opts, QLA_IS_SECURE, BIT_7); + check_and_set_mbc_bits(bwcmd->options, opts, QLA_UPDATE_MBR, BIT_8); + + if (!ha->isp_ops->write_optrom_region) { + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); + return -EINVAL; + } + + rval = ha->isp_ops->write_optrom_region(vha, bwcmd->region, opts, + ha->optrom_buffer, ha->optrom_region_start, + ha->optrom_region_size); + if (rval) { + ql_log(ql_log_warn, vha, 0x7065, + "%s failed to write flash %x\n", __func__, rval); + bsg_reply->result = -EINVAL; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + } else { + bsg_reply->result = DID_OK; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_OK; + } + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + return 0; +} + +static int +qla29xx_mpi_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha, + uint32_t start, uint8_t is_update) +{ + struct qla_hw_data *ha = vha->hw; + + if (unlikely(pci_channel_offline(ha->pdev))) + return -EINVAL; + + if (ha->optrom_state != QLA_SWAITING) { + ql_log(ql_log_info, vha, 0x7068, + "optrom_state %d.\n", ha->optrom_state); + return -EBUSY; + } + + ha->optrom_region_start = start; + if (is_update) { + ha->optrom_region_size = bsg_job->request_payload.payload_len; + ha->optrom_state = QLA_SWRITING; + } else { + ha->optrom_region_size = bsg_job->reply_payload.payload_len; + ha->optrom_state = QLA_SREADING; + } + + ha->optrom_buffer = vzalloc(ha->optrom_region_size); + if (!ha->optrom_buffer) { + ql_log(ql_log_warn, vha, 0x7069, + "%s: Unable to allocate memory for optrom retrieval (%x)\n", + __func__, ha->optrom_region_size); + ha->optrom_state = QLA_SWAITING; + return -ENOMEM; + } + + return 0; +} + +static int qla2900_bsg_dump_mpi(struct bsg_job *bsg_job) +{ + struct fc_bsg_request *bsg_req = bsg_job->request; + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct qla_load_dump_mpi *dmcmd; + uint16_t opts = 0; + int rval = 0; + + if (bsg_job->request_len < sizeof(struct fc_bsg_request) + + 2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi)) + return -EINVAL; + + if (!IS_QLA29XX(ha)) { + ql_log(ql_log_warn, vha, 0x706c, + "%s: MPI dump not supported on this adapter.\n", + __func__); + return -EINVAL; + } + + dmcmd = + (struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2]; + + ql_log(ql_log_info, vha, 0xffff, + "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n", + __func__, dmcmd->mpi_address, dmcmd->options, dmcmd->length); + + mutex_lock(&ha->optrom_mutex); + rval = qla29xx_mpi_optrom_setup(bsg_job, vha, dmcmd->mpi_address, 1); + if (rval) { + mutex_unlock(&ha->optrom_mutex); + return rval; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, ha->optrom_buffer, + ha->optrom_region_size); + + ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer, + ha->optrom_region_size); + + check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_SECURE_ENABLE, + BIT_3); + check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_OTP_PROV, BIT_4); + check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5); + check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_AUTH_CMD_BIN, + BIT_6); + check_and_set_mbc_bits(dmcmd->options, opts, QLA_LDM_MLDSA_ALGO, + BIT_9); + + rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer, + ha->optrom_region_start, + ha->optrom_region_size, + QLA29XX_MPI_OP_DUMP); + if (rval) { + ql_log(ql_log_warn, vha, 0xffff, + "%s failed mpi dump %x\n", __func__, rval); + bsg_reply->result = -EINVAL; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + } else { + bsg_reply->result = DID_OK; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_OK; + } + + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + return 0; +} + +static int qla2900_bsg_load_mpi(struct bsg_job *bsg_job) +{ + struct fc_bsg_request *bsg_req = bsg_job->request; + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + struct qla_load_dump_mpi *lmcmd; + uint16_t opts = 0; + int rval = 0; + + if (bsg_job->request_len < sizeof(struct fc_bsg_request) + + 2 * sizeof(uint32_t) + sizeof(struct qla_load_dump_mpi)) + return -EINVAL; + + if (!IS_QLA29XX(ha)) { + ql_log(ql_log_warn, vha, 0x706d, + "%s: MPI load not supported on this adapter.\n", + __func__); + return -EINVAL; + } + + lmcmd = + (struct qla_load_dump_mpi *)&bsg_req->rqst_data.h_vendor.vendor_cmd[2]; + + ql_log(ql_log_info, vha, 0xffff, + "%s: mpi_address 0x%x mpi options 0x%x length 0x%x\n", + __func__, lmcmd->mpi_address, lmcmd->options, lmcmd->length); + + mutex_lock(&ha->optrom_mutex); + rval = qla29xx_mpi_optrom_setup(bsg_job, vha, lmcmd->mpi_address, 0); + if (rval) { + mutex_unlock(&ha->optrom_mutex); + return rval; + } + + check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SECURE_ENABLE, + BIT_3); + check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_DEV_CSR, BIT_5); + check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_SHADOW_REGS, + BIT_7); + check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_CA_CSR, BIT_8); + check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_ALGO, + BIT_9); + check_and_set_mbc_bits(lmcmd->options, opts, QLA_LDM_MLDSA_SIGNATURE, + BIT_10); + + rval = qla29xx_mpi_optrom_data(vha, opts, ha->optrom_buffer, + ha->optrom_region_start, + ha->optrom_region_size, + QLA29XX_MPI_OP_LOAD); + if (rval) { + ql_log(ql_log_warn, vha, 0xffff, + "%s failed mpi load %x\n", __func__, rval); + bsg_reply->result = -EINVAL; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + } else { + bsg_reply->result = DID_OK; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_OK; + } + + ql_dump_buffer(ql_dbg_init, vha, 0x00d7, ha->optrom_buffer, + ha->optrom_region_size); + + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + ha->optrom_buffer, + ha->optrom_region_size); + + bsg_reply->reply_payload_rcv_len = ha->optrom_region_size; + vfree(ha->optrom_buffer); + ha->optrom_buffer = NULL; + ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + + return 0; +} + static int qla2x00_update_fru_versions(struct bsg_job *bsg_job) { @@ -1562,12 +2003,12 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_image_version_list *list = (void *)bsg; struct qla_image_version *image; uint32_t count; dma_addr_t sfp_dma; - void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + void *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1580,6 +2021,13 @@ qla2x00_update_fru_versions(struct bsg_job *bsg_job) image = list->version; count = list->count; + + if (struct_size(list, version, count) > sizeof(bsg)) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_INVALID_PARAM; + goto dealloc; + } + while (count--) { memcpy(sfp, &image->field_info, sizeof(image->field_info)); rval = qla2x00_write_sfp(vha, sfp_dma, sfp, @@ -1615,10 +2063,10 @@ qla2x00_read_fru_status(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_status_reg *sr = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1666,10 +2114,10 @@ qla2x00_write_fru_status(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_status_reg *sr = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1713,10 +2161,10 @@ qla2x00_write_i2c(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_i2c_access *i2c = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1727,6 +2175,12 @@ qla2x00_write_i2c(struct bsg_job *bsg_job) sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c)); + if (i2c->length > sizeof(i2c->buffer)) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_INVALID_PARAM; + goto dealloc; + } + memcpy(sfp, i2c->buffer, i2c->length); rval = qla2x00_write_sfp(vha, sfp_dma, sfp, i2c->device, i2c->offset, i2c->length, i2c->option); @@ -1759,10 +2213,10 @@ qla2x00_read_i2c(struct bsg_job *bsg_job) scsi_qla_host_t *vha = shost_priv(host); struct qla_hw_data *ha = vha->hw; int rval = 0; - uint8_t bsg[DMA_POOL_SIZE]; + uint8_t bsg[DMA_POOL_SIZE] = {}; struct qla_i2c_access *i2c = (void *)bsg; dma_addr_t sfp_dma; - uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + uint8_t *sfp = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); if (!sfp) { bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = @@ -1773,6 +2227,12 @@ qla2x00_read_i2c(struct bsg_job *bsg_job) sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c)); + if (i2c->length > sizeof(i2c->buffer)) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_INVALID_PARAM; + goto dealloc; + } + rval = qla2x00_read_sfp(vha, sfp_dma, sfp, i2c->device, i2c->offset, i2c->length, i2c->option); @@ -1993,6 +2453,11 @@ qlafx00_mgmt_cmd(struct bsg_job *bsg_job) struct fc_port *fcport; char *type = "FC_BSG_HST_FX_MGMT"; + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + sizeof(uint32_t) + + sizeof(struct qla_mt_iocb_rqst_fx00)) + return -EINVAL; + /* Copy the IOCB specific information */ piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *) &bsg_request->rqst_data.h_vendor.vendor_cmd[1]; @@ -2195,7 +2660,7 @@ qla27xx_get_flash_upd_cap(struct bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; struct qla_flash_update_caps cap; - if (!(IS_QLA27XX(ha)) && !IS_QLA28XX(ha)) + if (!(IS_QLA27XX(ha)) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return -EPERM; memset(&cap, 0, sizeof(cap)); @@ -2228,7 +2693,7 @@ qla27xx_set_flash_upd_cap(struct bsg_job *bsg_job) uint64_t online_fw_attr = 0; struct qla_flash_update_caps cap; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return -EPERM; memset(&cap, 0, sizeof(cap)); @@ -2276,7 +2741,7 @@ qla27xx_get_bbcr_data(struct bsg_job *bsg_job) uint8_t domain, area, al_pa, state; int rval; - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return -EPERM; memset(&bbcr, 0, sizeof(bbcr)); @@ -2392,10 +2857,10 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job) struct qla_dport_diag *dd; if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) && - !IS_QLA28XX(vha->hw)) + !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw)) return -EPERM; - dd = kmalloc_obj(*dd); + dd = kzalloc_obj(*dd); if (!dd) { ql_log(ql_log_warn, vha, 0x70db, "Failed to allocate memory for dport.\n"); @@ -2518,8 +2983,13 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job) struct qla_active_regions regions = { }; struct active_regions active_regions = { }; - qla27xx_get_active_image(vha, &active_regions); - regions.global_image = active_regions.global; + if (IS_QLA29XX(ha)) + return -EPERM; + + if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + qla27xx_get_active_image(vha, &active_regions); + regions.global_image = active_regions.global; + } if (IS_QLA27XX(ha)) regions.nvme_params = QLA27XX_PRIMARY_IMAGE; @@ -2555,11 +3025,14 @@ qla2x00_get_flash_image_status(struct bsg_job *bsg_job) static int qla2x00_get_drv_attr(struct bsg_job *bsg_job) { + scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job)); struct qla_drv_attr drv_attr; struct fc_bsg_reply *bsg_reply = bsg_job->reply; + struct qla_hw_data *ha = vha->hw; memset(&drv_attr, 0, sizeof(struct qla_drv_attr)); - drv_attr.ext_attributes |= QLA_IMG_SET_VALID_SUPPORT; + if (!IS_QLA29XX(ha)) + drv_attr.ext_attributes |= QLA_IMG_SET_VALID_SUPPORT; sg_copy_from_buffer(bsg_job->reply_payload.sg_list, @@ -2906,6 +3379,13 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j { struct fc_bsg_request *bsg_request = bsg_job->request; + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + sizeof(uint32_t)) { + ql_log(ql_log_warn, vha, 0x7000, + "BSG request too small for vendor cmd.\n"); + return -EINVAL; + } + ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n", __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]); @@ -3007,6 +3487,18 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j case QL_VND_MBX_PASSTHRU: return qla2x00_mailbox_passthru(bsg_job); + case QL_VND_READ_FLASH_BLOCK: + return qla29xx_bsg_flash_block_read(bsg_job); + + case QL_VND_WRITE_FLASH_BLOCK: + return qla29xx_bsg_flash_block_write(bsg_job); + + case QL_VND_LOAD_MPI: + return qla2900_bsg_load_mpi(bsg_job); + + case QL_VND_DUMP_MPI: + return qla2900_bsg_dump_mpi(bsg_job); + default: return -ENOSYS; } @@ -3037,8 +3529,11 @@ qla24xx_bsg_request(struct bsg_job *bsg_job) } /* Disable port will bring down the chip, allow enable command */ - if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT || - bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS) + if (bsg_request->msgcode == FC_BSG_HST_VENDOR && + bsg_job->request_len >= + sizeof(struct fc_bsg_request) + sizeof(uint32_t) && + (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT || + bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)) goto skip_chip_chk; if (vha->hw->flags.port_isolated) { @@ -3347,6 +3842,10 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job) if (!IS_QLA28XX(ha) || vha->vp_idx != 0) return -EPERM; + if (bsg_job->request_len < + sizeof(struct fc_bsg_request) + 2 * sizeof(uint32_t)) + return -EINVAL; + mutex_lock(&ha->optrom_mutex); rval = qla28xx_do_validate_flash_image(bsg_job, &state); if (rval) @@ -3365,9 +3864,8 @@ static int qla28xx_validate_flash_image(struct bsg_job *bsg_job) bsg_reply->result = DID_OK << 16; bsg_reply->reply_payload_rcv_len = 0; bsg_job->reply_len = sizeof(struct fc_bsg_reply); - if (!rval) - bsg_job_done(bsg_job, bsg_reply->result, - bsg_reply->reply_payload_rcv_len); + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); return QLA_SUCCESS; } diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h index a920c8e482bc..8a784b8226cc 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.h +++ b/drivers/scsi/qla2xxx/qla_bsg.h @@ -40,6 +40,10 @@ #define QL_VND_MBX_PASSTHRU 0x2B #define QL_VND_DPORT_DIAGNOSTICS_V2 0x2C #define QL_VND_IMG_SET_VALID 0x30 +#define QL_VND_READ_FLASH_BLOCK 0x33 +#define QL_VND_WRITE_FLASH_BLOCK 0x34 +#define QL_VND_LOAD_MPI 0x35 +#define QL_VND_DUMP_MPI 0x36 /* BSG Vendor specific subcode returns */ #define EXT_STATUS_OK 0 @@ -83,6 +87,36 @@ #define ELS_OPCODE_BYTE 0x10 /* BSG Vendor specific definations */ + +#define QLA_IS_TIM 0x1 +#define QLA_IS_SECURE 0x2 +#define QLA_UPDATE_MBR 0x4 + +struct qla_block_rw { + uint32_t region; + uint32_t rw_length; + uint32_t options; + uint32_t region_offset; + uint32_t chunk_length; + uint8_t reserved[44]; +} __packed; + +struct qla_load_dump_mpi { + uint32_t mpi_address; + uint32_t length; + uint32_t options; +#define QLA_LDM_SECURE_ENABLE 0x1 +#define QLA_LDM_OTP_PROV 0x2 +#define QLA_LDM_DEV_CSR 0x4 +#define QLA_LDM_AUTH_CMD_BIN 0x8 +#define QLA_LDM_SHADOW_REGS 0x10 +#define QLA_LDM_CA_CSR 0x20 +#define QLA_LDM_MLDSA_ALGO 0x40 +#define QLA_LDM_MLDSA_SIGNATURE 0x80 + + uint8_t reserved[52]; +} __packed; + #define A84_ISSUE_WRITE_TYPE_CMD 0 #define A84_ISSUE_READ_TYPE_CMD 1 #define A84_CLEANUP_CMD 2 diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index a7e3ec9bba47..196cfa8f8623 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -16,7 +16,7 @@ * | | | 0x2127-0x2128 | * | Queue Command and IO tracing | 0x3074 | 0x300b | * | | | 0x3027-0x3028 | - * | | | 0x303d-0x3041 | + * | | | 0x303e-0x3041 | * | | | 0x302e,0x3033 | * | | | 0x3036,0x3038 | * | | | 0x303a | @@ -89,16 +89,17 @@ qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr) { struct req_que *req = ha->req_q_map[0]; struct rsp_que *rsp = ha->rsp_q_map[0]; + size_t req_entry_size = qla_req_entry_size(ha); + size_t rsp_entry_size = qla_rsp_entry_size(ha); + /* Request queue. */ - memcpy(ptr, req->ring, req->length * - sizeof(request_t)); + memcpy(ptr, req->ring, req->length * req_entry_size); /* Response queue. */ - ptr += req->length * sizeof(request_t); - memcpy(ptr, rsp->ring, rsp->length * - sizeof(response_t)); + ptr += req->length * req_entry_size; + memcpy(ptr, rsp->ring, rsp->length * rsp_entry_size); - return ptr + (rsp->length * sizeof(response_t)); + return ptr + (rsp->length * rsp_entry_size); } int @@ -171,7 +172,7 @@ qla27xx_dump_mpi_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { /* no interrupt, timed out*/ - return rval; + return QLA_FUNCTION_TIMEOUT; } if (rval) { /* error completion status */ @@ -254,7 +255,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, __be32 *ram, if (!test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { /* no interrupt, timed out*/ - return rval; + return QLA_FUNCTION_TIMEOUT; } if (rval) { /* error completion status */ @@ -606,6 +607,8 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain) struct req_que *req; struct rsp_que *rsp; int que; + size_t req_entry_size = qla_req_entry_size(ha); + size_t rsp_entry_size = qla_rsp_entry_size(ha); if (!ha->mqenable) return ptr; @@ -623,19 +626,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain) q->chain_size = htonl( sizeof(struct qla2xxx_mqueue_chain) + sizeof(struct qla2xxx_mqueue_header) + - (req->length * sizeof(request_t))); + (req->length * req_entry_size)); ptr += sizeof(struct qla2xxx_mqueue_chain); /* Add header. */ qh = ptr; qh->queue = htonl(TYPE_REQUEST_QUEUE); qh->number = htonl(que); - qh->size = htonl(req->length * sizeof(request_t)); + qh->size = htonl(req->length * req_entry_size); ptr += sizeof(struct qla2xxx_mqueue_header); /* Add data. */ - memcpy(ptr, req->ring, req->length * sizeof(request_t)); - ptr += req->length * sizeof(request_t); + memcpy(ptr, req->ring, req->length * req_entry_size); + ptr += req->length * req_entry_size; } /* Response queues */ @@ -651,19 +654,19 @@ qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, __be32 **last_chain) q->chain_size = htonl( sizeof(struct qla2xxx_mqueue_chain) + sizeof(struct qla2xxx_mqueue_header) + - (rsp->length * sizeof(response_t))); + (rsp->length * rsp_entry_size)); ptr += sizeof(struct qla2xxx_mqueue_chain); /* Add header. */ qh = ptr; qh->queue = htonl(TYPE_RESPONSE_QUEUE); qh->number = htonl(que); - qh->size = htonl(rsp->length * sizeof(response_t)); + qh->size = htonl(rsp->length * rsp_entry_size); ptr += sizeof(struct qla2xxx_mqueue_header); /* Add data. */ - memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t)); - ptr += rsp->length * sizeof(response_t); + memcpy(ptr, rsp->ring, rsp->length * rsp_entry_size); + ptr += rsp->length * rsp_entry_size; } return ptr; diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 5593ad7fad27..97e2a1a9ce3f 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -337,6 +337,7 @@ static inline void wrt_reg_dword(volatile __le32 __iomem *addr, u32 data) #define MAX_CMDSZ 16 /* SCSI maximum CDB size. */ #include "qla_fw.h" +#include "qla_fw29.h" struct name_list_extended { struct get_name_list_extended *l; @@ -576,7 +577,7 @@ struct srb_iocb { __le16 comp_status; uint8_t modifier; - uint8_t vp_index; + uint16_t vp_index; uint16_t loop_id; } tmf; struct { @@ -1278,6 +1279,7 @@ static inline bool qla2xxx_is_valid_mbs(unsigned int mbs) #define MBC_LOAD_RISC_RAM 9 /* Load RAM command. */ #define MBC_DUMP_RISC_RAM 0xa /* Dump RAM command. */ #define MBC_SECURE_FLASH_UPDATE 0xa /* Secure Flash Update(28xx) */ +#define MBC_RD_WR_FLASH 0xa /* Read/write Dword/block Flash(29xx) */ #define MBC_LOAD_RISC_RAM_EXTENDED 0xb /* Load RAM extended. */ #define MBC_DUMP_RISC_RAM_EXTENDED 0xc /* Dump RAM extended. */ #define MBC_WRITE_RAM_WORD_EXTENDED 0xd /* Write RAM word extended */ @@ -1547,6 +1549,11 @@ typedef struct { #define PD_STATE_PORT_LOGOUT 10 #define PD_STATE_WAIT_PORT_LOGOUT_ACK 11 +enum qla29xx_mpi_optrom_op { + QLA29XX_MPI_OP_DUMP, + QLA29XX_MPI_OP_LOAD, +}; + #define QLA_ZIO_MODE_6 (BIT_2 | BIT_1) #define QLA_ZIO_DISABLED 0 @@ -1988,6 +1995,18 @@ typedef struct { #define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */ } response_t; +struct response_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System defined handle */ + uint8_t data[52]; + uint32_t signature; + uint8_t reserved[64]; +#define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */ +}; + /* * ISP queue - ATIO queue entry definition. */ @@ -2066,6 +2085,24 @@ typedef struct { struct dsd64 dsd[2]; } cmd_a64_entry_t, request_t; +struct request_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System handle. */ + target_id_t target; /* SCSI ID */ + __le16 lun; /* SCSI LUN */ + __le16 control_flags; /* Control flags. */ + uint16_t reserved_1; + __le16 timeout; /* Command timeout. */ + __le16 dseg_count; /* Data segment count. */ + uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */ + uint32_t byte_count; /* Total byte count. */ + struct dsd64 dsd[2]; + uint8_t reserved[64]; +}; + /* * ISP queue - continuation entry structure definition. */ @@ -2324,6 +2361,15 @@ typedef struct { uint8_t reserved_2[48]; } mrk_entry_t; +/* 29xx definitions */ +struct sts_cont_entry_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint8_t data[124]; /* data */ +}; + /* * ISP queue - Management Server entry structure definition. */ @@ -2482,6 +2528,14 @@ struct imm_ntfy_from_isp { #define REQUEST_ENTRY_SIZE (sizeof(request_t)) +/* + * 29xx (qla29xx) uses 128-byte ring entries for both request and response + * queues. These macros give the size of an extended IOCB slot and are + * used when allocating from / zeroing the 29xx request ring via ring_ext_ptr. + */ +#define RESPONSE_ENTRY_SIZE_EXT (sizeof(struct response_ext)) +#define REQUEST_ENTRY_SIZE_EXT (sizeof(struct request_ext)) + /* * Switch info gathering structure. @@ -2591,6 +2645,8 @@ typedef struct fc_port { struct list_head list; struct scsi_qla_host *vha; struct list_head unsol_ctx_head; + /* Serializes unsol_ctx_head against ISR, DPC and NVMe transport. */ + spinlock_t unsol_ctx_lock; unsigned int conf_compl_supported:1; unsigned int deleted:2; @@ -3503,6 +3559,13 @@ struct isp_operations { int (*write_optrom)(struct scsi_qla_host *, void *, uint32_t, uint32_t); + void *(*read_optrom_region)(struct scsi_qla_host *vha, + uint16_t reg_code, uint16_t opts, void *buf, + uint32_t offset, uint32_t length); + int (*write_optrom_region)(struct scsi_qla_host *vha, + uint16_t reg_code, uint16_t opts, void *buf, + uint32_t offset, uint32_t length); + int (*get_flash_version) (struct scsi_qla_host *, void *); int (*start_scsi) (srb_t *); int (*start_scsi_mq) (srb_t *); @@ -3528,7 +3591,6 @@ struct isp_operations { #define QLA_MIDX_DEFAULT 0 #define QLA_MIDX_RSP_Q 1 #define QLA_PCI_MSIX_CONTROL 0xa2 -#define QLA_83XX_PCI_MSIX_CONTROL 0x92 struct scsi_qla_host; @@ -3737,6 +3799,14 @@ struct rsp_que { dma_addr_t dma; response_t *ring; response_t *ring_ptr; + /* + * 29xx extended IOCB ring (128-byte entries) aliases of ring/ring_ptr. + * Allocated when IS_QLA29XX(ha); set up at queue-init time to point at + * the same DMA memory as 'ring' but typed for the 29xx stride. 24xx + * code paths walk via ring_ptr; 29xx paths walk via ring_ext_ptr. + */ + struct response_ext *ring_ext; + struct response_ext *ring_ext_ptr; __le32 __iomem *rsp_q_in; /* FWI2-capable only. */ __le32 __iomem *rsp_q_out; uint16_t ring_index; @@ -3764,6 +3834,14 @@ struct req_que { dma_addr_t dma; request_t *ring; request_t *ring_ptr; + /* + * 29xx extended IOCB ring (128-byte entries) aliases of ring/ring_ptr. + * Allocated when IS_QLA29XX(ha); set up at queue-init time to point at + * the same DMA memory as 'ring' but typed for the 29xx stride. 24xx + * code paths must not run on 29xx HW, and vice-versa. + */ + struct request_ext *ring_ext; + struct request_ext *ring_ext_ptr; __le32 __iomem *req_q_in; /* FWI2-capable only. */ __le32 __iomem *req_q_out; uint16_t ring_index; @@ -4100,6 +4178,7 @@ struct qla_hw_data { #define SRB_MIN_REQ 128 mempool_t *srb_mempool; u8 port_name[WWN_SIZE]; + u16 mbregs[32]; volatile struct { uint32_t mbox_int :1; @@ -4170,7 +4249,10 @@ struct qla_hw_data { uint32_t eeh_flush:2; #define EEH_FLUSH_RDY 1 #define EEH_FLUSH_DONE 2 + uint32_t t262_fail:1; + uint32_t t272_fail:1; uint32_t secure_mcu:1; + uint32_t valid_flt:1; } flags; uint16_t max_exchg; @@ -4256,6 +4338,7 @@ struct qla_hw_data { #define PORT_SPEED_16GB 0x05 #define PORT_SPEED_32GB 0x06 #define PORT_SPEED_64GB 0x07 +#define PORT_SPEED_128GB 0x08 #define PORT_SPEED_10GB 0x13 uint16_t link_data_rate; /* F/W operating speed */ uint16_t set_data_rate; /* Set by user */ @@ -4287,6 +4370,10 @@ struct qla_hw_data { #define PCI_DEVICE_ID_QLOGIC_ISP2089 0x2089 #define PCI_DEVICE_ID_QLOGIC_ISP2281 0x2281 #define PCI_DEVICE_ID_QLOGIC_ISP2289 0x2289 +#define PCI_DEVICE_ID_QLOGIC_ISP2099 0x2099 +#define PCI_DEVICE_ID_QLOGIC_ISP2299 0x2299 +#define PCI_DEVICE_ID_QLOGIC_ISP2091 0x2091 +#define PCI_DEVICE_ID_QLOGIC_ISP2291 0x2291 uint32_t isp_type; #define DT_ISP2100 BIT_0 @@ -4316,7 +4403,11 @@ struct qla_hw_data { #define DT_ISP2089 BIT_24 #define DT_ISP2281 BIT_25 #define DT_ISP2289 BIT_26 -#define DT_ISP_LAST (DT_ISP2289 << 1) +#define DT_ISP2299 BIT_27 +#define DT_ISP2099 BIT_28 +#define DT_ISP2091 BIT_29 +#define DT_ISP2291 BIT_30 +#define DT_ISP_LAST ((uint32_t)DT_ISP2291 << 1) uint32_t device_type; #define DT_T10_PI BIT_25 @@ -4353,6 +4444,10 @@ struct qla_hw_data { #define IS_QLA2261(ha) (DT_MASK(ha) & DT_ISP2261) #define IS_QLA2081(ha) (DT_MASK(ha) & DT_ISP2081) #define IS_QLA2281(ha) (DT_MASK(ha) & DT_ISP2281) +#define IS_QLA2299(ha) (DT_MASK(ha) & DT_ISP2299) +#define IS_QLA2099(ha) (DT_MASK(ha) & DT_ISP2099) +#define IS_QLA2091(ha) (DT_MASK(ha) & DT_ISP2091) +#define IS_QLA2291(ha) (DT_MASK(ha) & DT_ISP2291) #define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \ IS_QLA6312(ha) || IS_QLA6322(ha)) @@ -4363,6 +4458,9 @@ struct qla_hw_data { #define IS_QLA84XX(ha) (IS_QLA8432(ha)) #define IS_QLA27XX(ha) (IS_QLA2071(ha) || IS_QLA2271(ha) || IS_QLA2261(ha)) #define IS_QLA28XX(ha) (IS_QLA2081(ha) || IS_QLA2281(ha)) +#define IS_QLA29XX(ha) (IS_QLA2099(ha) || IS_QLA2299(ha) || \ + IS_QLA2091(ha) || IS_QLA2291(ha)) + #define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ IS_QLA84XX(ha)) #define IS_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \ @@ -4372,9 +4470,10 @@ struct qla_hw_data { IS_QLA25XX(ha) || IS_QLA81XX(ha) || \ IS_QLA82XX(ha) || IS_QLA83XX(ha) || \ IS_QLA8044(ha) || IS_QLA27XX(ha) || \ - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \ - IS_QLA27XX(ha) || IS_QLA28XX(ha)) + IS_QLA27XX(ha) || IS_QLA28XX(ha) || \ + IS_QLA29XX(ha)) #define IS_NOPOLLING_TYPE(ha) (IS_QLA81XX(ha) && (ha)->flags.msix_enabled) #define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \ IS_QLA27XX(ha) || IS_QLA28XX(ha)) @@ -4390,9 +4489,9 @@ struct qla_hw_data { #define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS) #define IS_CT6_SUPPORTED(ha) ((ha)->device_type & DT_CT6_SUPPORTED) #define IS_MQUE_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_BIDI_CAPABLE(ha) \ - (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) /* Bit 21 of fw_attributes decides the MCTP capabilities */ #define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \ ((ha)->fw_attributes_ext[0] & BIT_0)) @@ -4408,18 +4507,19 @@ struct qla_hw_data { (QLA_NVME_IOS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw)) #define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_PI_DIFB_DIX0_CAPABLE(ha) (0) #define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_PI_SPLIT_DET_CAPABLE(ha) (IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \ (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22)) #define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ IS_QLA28XX(ha)) #define IS_TGT_MODE_CAPABLE(ha) (ha->tgt.atio_q_length) -#define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha) || IS_QLA28XX(ha)) +#define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha) || IS_QLA28XX(ha) || \ + IS_QLA29XX(ha)) #define IS_DPORT_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ IS_QLA28XX(ha)) #define IS_FAWWN_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ @@ -4452,6 +4552,7 @@ struct qla_hw_data { uint16_t vpd_size; uint16_t vpd_base; void *vpd; + struct qla_flash_memo_block *fiv; uint16_t loop_reset_delay; uint8_t retry_count; @@ -4483,6 +4584,8 @@ struct qla_hw_data { struct qla_flt_header *flt; dma_addr_t flt_dma; + struct qla_flash_layout *flt_data; + uint32_t fw_dump_tmplt_len; #define XGMAC_DATA_SIZE 4096 void *xgmac_data; @@ -4707,6 +4810,9 @@ struct qla_hw_data { uint32_t fdt_protect_sec_cmd; uint32_t fdt_wrt_sts_reg_cmd; +#define QLA_SEGMENT_LENGTH 0x25000 + uint32_t flt_segment_length; + struct { uint32_t flt_region_flt; uint32_t flt_region_fdt; @@ -4900,6 +5006,7 @@ struct active_regions { #define QLA_SET_DATA_RATE_LR 2 /* Set speed and initiate LR */ #define QLA_DEFAULT_PAYLOAD_SIZE 64 +#define QLA_MAX_IOCB_SIZE 128 /* * This item might be allocated with a size > sizeof(struct purex_item). * The "size" variable gives the size of the payload (which @@ -4914,7 +5021,7 @@ struct purex_item { atomic_t in_use; uint16_t size; struct { - uint8_t iocb[64]; + u8 iocb[QLA_MAX_IOCB_SIZE]; } iocb; }; @@ -5333,6 +5440,14 @@ static inline bool qla_vha_mark_busy(scsi_qla_host_t *vha) /* * Flash support definitions */ +#define check_and_set_mbc_bits(bopt, dopt, bit_to_check, bit_to_set) { \ + if (bopt & bit_to_check) \ + dopt |= bit_to_set; \ +} + +#define SET_FW_BIT(__opts, bit) ((__opts) |= (bit)) +#define CLEAR_FW_BIT(__opts, bit) ((__opts) &= ~(bit)) + #define OPTROM_SIZE_2300 0x20000 #define OPTROM_SIZE_2322 0x100000 #define OPTROM_SIZE_24XX 0x100000 @@ -5474,9 +5589,9 @@ struct sff_8247_a0 { /* BPM -- Buffer Plus Management support. */ #define IS_BPM_CAPABLE(ha) \ (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) || \ - IS_QLA27XX(ha) || IS_QLA28XX(ha)) + IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_BPM_RANGE_CAPABLE(ha) \ - (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) #define IS_BPM_ENABLED(vha) \ (ql2xautodetectsfp && !vha->vp_idx && IS_BPM_CAPABLE(vha->hw)) @@ -5597,4 +5712,9 @@ struct ql_vnd_tgt_stats_resp { (!_fcport || IS_SESSION_DELETED(_fcport) || atomic_read(&_fcport->state) != FCS_ONLINE || \ !_fcport->vha->hw->flags.fw_started) +#define is_flash_read(_opt) \ + (!(_opt & BIT_9) && !(_opt & BIT_6)) + +#define is_flash_write(_opt) \ + (!(_opt & BIT_9) && (_opt & BIT_6)) #endif diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 43970caca7b3..5d08bdbcf70a 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -497,7 +497,7 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer, unsigned long enable; if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) { + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) { ql_dbg(ql_dbg_user, vha, 0xd034, "this adapter does not support FCE."); return -EINVAL; @@ -510,7 +510,9 @@ qla2x00_dfs_fce_write(struct file *file, const char __user *buffer, return PTR_ERR(buf); } - enable = kstrtoul(buf, 0, 0); + rc = kstrtoul(buf, 0, &enable); + if (rc) + goto out_free; rc = count; mutex_lock(&ha->fce_mutex); @@ -698,7 +700,7 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) goto out; if (qla2x00_dfs_root) diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index eccedb38a515..bfa520f936a2 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -2534,7 +2534,7 @@ qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb) void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) { - struct purex_entry_24xx *p = *pkt; + struct qla_hw_data *ha = vha->hw; struct enode *ptr; int sid; u16 totlen; @@ -2544,26 +2544,55 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) struct fc_port *fcport; struct qla_els_pt_arg a; be_id_t beid; + __le16 nport_handle; + __le32 rx_xchg_addr; + __le16 ox_id; + __le16 frame_size, status_flags, trunc_frame_size; + uint8_t s_id[3], d_id[3]; + uint8_t vp_idx; + struct purex_entry_24xx *p = *pkt; memset(&a, 0, sizeof(a)); + /* + * purex_entry_24xx_ext (29xx) overlays purex_entry_24xx for every + * field touched here -- nport_handle, rx_xchg_addr, ox_id, frame_size, + * status_flags, trunc_frame_size, s_id[3], d_id[3] -- with only + * vp_idx differing in width (u8 at offset 6 vs __le16 at offsets 6-7, + * with reserved2 at offset 7 in the 24xx layout). So all reads but + * vp_idx go through a single struct purex_entry_24xx * view. + */ + nport_handle = p->nport_handle; + rx_xchg_addr = p->rx_xchg_addr; + ox_id = p->ox_id; + frame_size = p->frame_size; + status_flags = p->status_flags; + trunc_frame_size = p->trunc_frame_size; + memcpy(s_id, p->s_id, sizeof(s_id)); + memcpy(d_id, p->d_id, sizeof(d_id)); + if (IS_QLA29XX(ha)) + vp_idx = le16_to_cpu(((struct purex_entry_24xx_ext *) + *pkt)->vp_idx); + else + vp_idx = p->vp_idx; + a.els_opcode = ELS_AUTH_ELS; - a.nport_handle = p->nport_handle; - a.rx_xchg_address = p->rx_xchg_addr; - a.did.b.domain = p->s_id[2]; - a.did.b.area = p->s_id[1]; - a.did.b.al_pa = p->s_id[0]; + a.nport_handle = nport_handle; + a.rx_xchg_address = rx_xchg_addr; + a.did.b.domain = s_id[2]; + a.did.b.area = s_id[1]; + a.did.b.al_pa = s_id[0]; a.tx_byte_count = a.tx_len = sizeof(struct fc_els_ls_rjt); - a.tx_addr = vha->hw->elsrej.cdma; + a.tx_addr = ha->elsrej.cdma; a.vp_idx = vha->vp_idx; a.control_flags = EPD_ELS_RJT; - a.ox_id = le16_to_cpu(p->ox_id); + a.ox_id = le16_to_cpu(ox_id); - sid = p->s_id[0] | (p->s_id[1] << 8) | (p->s_id[2] << 16); + sid = s_id[0] | (s_id[1] << 8) | (s_id[2] << 16); - totlen = (le16_to_cpu(p->frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE; - if (le16_to_cpu(p->status_flags) & 0x8000) { - totlen = le16_to_cpu(p->trunc_frame_size); + totlen = (le16_to_cpu(frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE; + if (le16_to_cpu(status_flags) & 0x8000) { + totlen = le16_to_cpu(trunc_frame_size); qla_els_reject_iocb(vha, (*rsp)->qpair, &a); __qla_consume_iocb(vha, pkt, rsp); return; @@ -2600,12 +2629,12 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) purex = &ptr->u.purexinfo; purex->pur_info.pur_sid = a.did; purex->pur_info.pur_bytes_rcvd = totlen; - purex->pur_info.pur_rx_xchg_address = le32_to_cpu(p->rx_xchg_addr); - purex->pur_info.pur_nphdl = le16_to_cpu(p->nport_handle); - purex->pur_info.pur_did.b.domain = p->d_id[2]; - purex->pur_info.pur_did.b.area = p->d_id[1]; - purex->pur_info.pur_did.b.al_pa = p->d_id[0]; - purex->pur_info.vp_idx = p->vp_idx; + purex->pur_info.pur_rx_xchg_address = le32_to_cpu(rx_xchg_addr); + purex->pur_info.pur_nphdl = le16_to_cpu(nport_handle); + purex->pur_info.pur_did.b.domain = d_id[2]; + purex->pur_info.pur_did.b.area = d_id[1]; + purex->pur_info.pur_did.b.al_pa = d_id[0]; + purex->pur_info.vp_idx = vp_idx; a.sid = purex->pur_info.pur_did; @@ -2989,6 +3018,21 @@ qla28xx_start_scsi_edif(srb_t *sp) struct req_que *req = sp->qpair->req; spinlock_t *lock = sp->qpair->qp_lock_ptr; + /* + * EDIF on 29xx is not supported by this driver yet. The EDIF fast + * path builds 64-byte cmd_type_6 IOCBs and advances req->ring_ptr + * with a 64-byte stride, which would corrupt the 128-byte extended + * request ring that 29xx hardware uses. A proper 29xx EDIF port + * requires a cmd_type_6_ext-shaped submission path; until that is + * implemented, refuse the command rather than risk ring corruption. + */ + if (IS_QLA29XX(ha)) { + ql_log(ql_log_warn, vha, 0x13ae, + "EDIF is not supported on 29xx hardware; failing cmd sp=%p.\n", + sp); + return QLA_FUNCTION_FAILED; + } + /* Setup device pointers. */ cmd = GET_CMD_SP(sp); @@ -3129,7 +3173,8 @@ qla28xx_start_scsi_edif(srb_t *sp) * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + ha, req); cur_dsd = cont_pkt->dsd; avail_dsds = 5; } @@ -3485,12 +3530,18 @@ static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, } void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, - srb_t *sp, struct sts_entry_24xx *sts24) + srb_t *sp, void *pkt) { + struct sts_entry_24xx *sts24 = pkt; + struct sts_entry_24xx_ext *stsext = pkt; + struct qla_hw_data *ha = vha->hw; fc_port_t *fcport = sp->fcport; - /* sa_index used by this iocb */ struct scsi_cmnd *cmd = GET_CMD_SP(sp); uint32_t handle; + uint16_t sa_index; + + if (!cmd) + return; handle = (uint32_t)LSW(sts24->handle); @@ -3498,8 +3549,12 @@ void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, if (cmd->sc_data_direction != DMA_FROM_DEVICE) return; - return __chk_edif_rx_sa_delete_pending(vha, fcport, handle, - le16_to_cpu(sts24->edif_sa_index)); + if (IS_QLA29XX(ha)) + sa_index = le16_to_cpu(stsext->read_sa_index); + else + sa_index = le16_to_cpu(sts24->edif_sa_index); + + return __chk_edif_rx_sa_delete_pending(vha, fcport, handle, sa_index); } void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport, diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index f307beed9d29..98bc4a57b59b 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1442,6 +1442,10 @@ struct vp_ctrl_entry_24xx { uint8_t reserved_5[24]; }; +/* vp_idx_map is a 128-bit (16-byte) bitmap selecting target VPs. */ +#define VP_CTRL_IDX_MAP_BITS \ + (sizeof_field(struct vp_ctrl_entry_24xx, vp_idx_map) * 8) + /* * Modify Virtual Port Configuration IOCB */ @@ -1538,7 +1542,7 @@ struct vp_rpt_id_entry_24xx { #define TOPO_N2N 0x4 #define TOPO_F 0x6 - uint16_t fip_flags; + __le16 fip_flags; uint8_t rsv2[12]; uint8_t ls_rjt_vendor; @@ -1548,13 +1552,13 @@ struct vp_rpt_id_entry_24xx { uint8_t port_name[8]; uint8_t node_name[8]; - uint16_t bbcr; + __le16 bbcr; uint8_t reserved_5[6]; } f1; struct _f2 { /* format 2: N2N direct connect */ uint8_t vpstat1_subcode; uint8_t flags; - uint16_t fip_flags; + __le16 fip_flags; uint8_t rsv2[12]; uint8_t ls_rjt_vendor; @@ -1564,7 +1568,7 @@ struct vp_rpt_id_entry_24xx { uint8_t port_name[8]; uint8_t node_name[8]; - uint16_t bbcr; + __le16 bbcr; uint8_t reserved_5[2]; uint8_t remote_nport_id[4]; } f2; @@ -1676,6 +1680,8 @@ struct qla_flt_location { #define FLT_REG_VPD_SEC_27XX_2 0xD8 #define FLT_REG_VPD_SEC_27XX_3 0xDA #define FLT_REG_NVME_PARAMS_27XX 0x21 +#define FLT_REG_FMB_PRI 0xDF +#define FLT_REG_FMB_SEC 0x124 /* 28xx */ #define FLT_REG_AUX_IMG_PRI_28XX 0x125 @@ -1695,6 +1701,10 @@ struct qla_flt_location { #define FLT_REG_NVME_PARAMS_PRI_28XX 0x14E #define FLT_REG_NVME_PARAMS_SEC_28XX 0x179 +/* 29xx */ +#define FLT_REG_MINI_FLT 0x201 +#define FLT_REG_FW_DUMP_TMPLT 0x1A0 + struct qla_flt_region { __le16 code; uint8_t attribute; @@ -1716,6 +1726,56 @@ struct qla_flt_header { #define FLT_MAX_REGIONS 0xFF #define FLT_REGIONS_SIZE (FLT_REGION_SIZE * FLT_MAX_REGIONS) +/* 29xx */ +#define FLT_HDR_VERSION 0x2 + +struct qla_flt_region_header { + __le32 signature; + __le32 version; + __le32 length; + __le32 checksum; + __le16 region_count; + __le16 region_size; + __le32 segment_size; + __le32 res3; + __le32 res4; + __le32 res5; + __le32 res6; + __le32 res7; + __le32 res8; + __le32 res9; + __le32 res10; + __le32 res11; + __le32 res12; +}; + +struct qla_flt_region_data { + __le16 region_code; + __le16 reserved; + __le32 attribute; + __le32 image_length; + __le32 mbi_offset; + __le32 version; + __le32 card_type; + __le32 chip_revision; + __le32 res4; + __le32 res5; + __le32 res6; + __le32 res7; + __le32 res8; + __le32 res9; + __le32 res10; + __le32 res11; + __le32 res12; +}; + +struct qla_flash_layout { + struct qla_flt_region_header flt_header; + struct qla_flt_region_data region[]; +}; + +#define FLT_DATA_MAX_REGIONS 0xFF + /* Flash NPIV Configuration Table ********************************************/ struct qla_npiv_header { @@ -2056,7 +2116,7 @@ struct nvram_81xx { * BIT 7 = SCM Disabled if BIT is set (1) * BIT 8-15 = Unused */ - uint16_t enhanced_features; + __le16 enhanced_features; uint16_t reserved_24[4]; @@ -2284,4 +2344,78 @@ struct qla_fcp_prio_cfg { #define NVRAM_DUAL_FCP_NVME_FLAG_OFFSET 0x196 +struct qla_fmb_version { + uint8_t major; + uint8_t minor; + uint8_t sub; + uint8_t build; +}; + +struct qla_fmb_upd_time { + __le16 year; + uint8_t month; + uint8_t day; + + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t reserved; +}; + +struct qla_flash_memo_block { + __le32 signature; /* "FMBS" */ +#define QLFC_FMB_SIG cpu_to_le32(0x53424D46) + __le32 length; + __le32 version; +#define QLFC_FMB_VERSION 3 + __le32 checksum; + struct qla_fmb_version ffv_ver; + struct qla_fmb_version mbi_ver; + struct { + __le16 year; + uint8_t month; + uint8_t day; + uint8_t reserve[4]; + } bld_time; + uint8_t tool_id[4]; + struct qla_fmb_upd_time upd_time; + struct qla_fmb_version tool_version; +}; + +#define TIM_DEST_ADDR 0xffffffff +#define CHUNK_SIZE 0x10000 + +#define TIM 0 +#define ARR1 1 +#define ARR2 2 +#define ARR3 3 +#define ARR4 4 + +#define LD_FL_HEADER_SIGNATURE 0x46434F50 +#define LD_FL_HEADER_VERSION 0x01 +#define LD_FL_HEADER_SIZE (0x14 * 4) + +struct fcop_header { + uint32_t signature; + uint32_t header_length; + uint32_t header_version; + uint32_t segment_size; + uint32_t tim_length; + uint32_t fc_major_version; + uint32_t fc_minor_version; + uint32_t fc_subminor_version; + uint32_t array1_length; + uint32_t array1_destination_addr; + uint32_t array2_length; + uint32_t array2_destination_addr; + uint32_t array3_length; + uint32_t array4_length; + uint32_t attribute; + uint32_t extended_attribute; + uint32_t reserved0; + uint32_t reserved1; + uint32_t reserved2; + uint32_t image_checksum; +}; + #endif diff --git a/drivers/scsi/qla2xxx/qla_fw29.h b/drivers/scsi/qla2xxx/qla_fw29.h new file mode 100644 index 000000000000..6382a054310a --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_fw29.h @@ -0,0 +1,830 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2026- Marvell. + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#ifndef __QLA_FW29_H +#define __QLA_FW29_H + +#include "qla_fw.h" + +/* Control Flags 2 common for cmd6 and 7 */ +#define CF2_VMID_ENABLE BIT_0 +#define CF2_CSCTL_PRIORITY_TAG BIT_1 +#define CF2_NO_TRNF_READY_ENABLE BIT_2 +#define CF2_RX_ID_ENABLE BIT_3 + +/* + * vp_index layout for 29xx extended command IOCBs + * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, ...): + * bits [8:0] - VP index (9 bits) + * bits [15:9] - reserved, must be zero + * Access on a host-endian value via le16_to_cpu(vp_index) & CMD_EXT_VP_INDEX_MASK. + */ +#define CMD_EXT_VP_INDEX_MASK 0x01ff + +/* + * Combined vp_index/sof_type field layout (used by ELS and ABTS ext IOCBs): + * bits [8:0] - VP index (9 bits) + * bits [11:9] - reserved + * bits [15:12] - SOF type (4 bits) + */ +#define EXT_VP_SOF_VP_INDEX_MASK 0x01ff +#define EXT_VP_SOF_SOF_TYPE_SHIFT 12 +#define EXT_VP_SOF_SOF_TYPE_MASK 0xf000 + +static inline u16 qla_ext_get_vp_index(__le16 vp_sof) +{ + return le16_to_cpu(vp_sof) & EXT_VP_SOF_VP_INDEX_MASK; +} + +static inline u16 qla_ext_get_sof_type(__le16 vp_sof) +{ + return (le16_to_cpu(vp_sof) >> EXT_VP_SOF_SOF_TYPE_SHIFT) & 0xf; +} + +static inline __le16 qla_ext_build_vp_sof(u16 vp_idx, u16 sof_type) +{ + return cpu_to_le16((vp_idx & EXT_VP_SOF_VP_INDEX_MASK) | + ((sof_type & 0xf) << EXT_VP_SOF_SOF_TYPE_SHIFT)); +} + +/* + * Combined vp_idx/vp_status field layout (vp_rpt_id_entry_24xx_ext): + * bits [8:0] - VP index (9 bits) + * bits [15:9] - VP status (7 bits) + */ +#define EXT_VP_STATUS_VP_INDEX_MASK 0x01ff +#define EXT_VP_STATUS_VP_STATUS_SHIFT 9 +#define EXT_VP_STATUS_VP_STATUS_MASK 0xfe00 + +/* + * ISP queue - command entry structure definition. + */ +#define NUM_CMD67_DSDS 4 +struct cmd_type_6_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 nport_handle; /* N_PORT handle. */ + __le16 timeout; /* Command timeout. */ + + __le16 dseg_count; /* Data segment count. */ + + __le16 fcp_rsp_dsd_len; /* FCP_RSP DSD length. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + __le16 control_flags; /* Control flags. */ + + __le16 fcp_cmnd_dseg_len; /* Data segment length. */ + /* Data segment address. */ + __le64 fcp_cmnd_dseg_address __packed; + /* Data segment address. */ + __le64 fcp_rsp_dseg_address __packed; + + __le32 byte_count; /* Total byte count. */ + __le16 control_flags_2; /* Control flags 2. */ + + __le16 vp_index; /* VP Index 9bits*/ + __le32 fburstlen_rxid; /* First Burst length/RX ID */ + __le16 io_tag; /* I/O Tag */ + uint8_t vl_n_fctl; /* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */ + uint8_t prtag_csctl; /* Priority Tag or CS_CTL */ + __le32 src_vm_id; /* Source VM ID */ + uint8_t reserved_2[16]; /* Reserved */ + struct dsd64 dsd[NUM_CMD67_DSDS]; /* Data Segment Descriptors */ +}; + +struct cmd_type_7_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t handle; /* System handle. */ + __le16 nport_handle; /* N_PORT handle. */ + __le16 timeout; /* Command timeout. */ + + __le16 dseg_count; /* Data segment count. */ + uint16_t reserved_1; + + struct scsi_lun lun; /* FCP LUN (BE). */ + + __le16 task_mgmt_flags; /* Task management flags. */ + + uint8_t task; + uint8_t crn; + uint8_t fcp_cdb[MAX_CMDSZ]; /* SCSI command words. */ + __le32 byte_count; /* Total byte count. */ + __le16 ctrl_flags_2; /* Control flags 2 */ + __le16 vp_index; /* VP Index 9bits*/ + __le32 rx_id; /* Receive Exchange ID */ + __le16 io_tag; /* I/O Tag */ + uint8_t vl_n_fctl; /* VL (7-4) | RSVD (3-2) | F_CTL [17] (1) | RSVD (0) */ + uint8_t reserved_3[21]; /* Reserved */ + struct dsd64 dsd[NUM_CMD67_DSDS]; /* Data Segment Descriptors */ +}; + +/* + * Inline data-DSD capacity of the 29xx cmd_type_crc_2_ext IOCB. Unlike + * cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS inline DSDs), + * CRC_2 places the bulk of its DSDs in the separate CRC-context DMA; only + * a single data_dsd is carried inline in both the u.nobundling and + * u.bundling variants. Use this constant wherever the IOCB-reservation + * calculator needs the CRC_2 ext inline capacity so it stays in sync with + * the firmware-facing layout below. + */ +#define NUM_CRC2_EXT_INLINE_DSDS 1 + +struct cmd_type_crc_2_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 nport_handle; /* N_PORT handle. */ + __le16 timeout; /* Command timeout. */ + + __le16 dseg_count; /* Data segment count. */ + __le16 fcp_rsp_dseg_len; /* FCP_RSP DSD length. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + __le16 control_flags_1; /* Control flags. */ + __le16 fcp_cmnd_dseg_len; /* Data segment length. */ + + __le64 fcp_cmnd_dseg_address __packed; + /* Data segment address. */ + __le64 fcp_rsp_dseg_address __packed; + + __le32 byte_count; /* Total byte count. */ + + __le16 control_flags_2; /* Control flags - 2 */ + __le16 vp_index; /* VP Index (bits [8:0]); bits [15:9] reserved. + * See CMD_EXT_VP_INDEX_MASK. + */ + + uint32_t reserved_1; + + __le16 iocb_tag; /* Unused */ + __le16 vl_prio; /* Bit 1 - F_CTL, Bits 4-7 VL, rest are rsvd */ + + uint32_t reserved_2; /* 3C-3F offset */ + + __le32 ref_tag; + uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/ + + __le16 app_tag; + uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/ + + __le16 blk_size; /* Data size in bytes */ + __le16 prot_opts; /* Requested Data Protection Mode */ + + __le32 tot_byte_count; /* Total byte count/ total data + * transfer count + */ + union { + struct { + uint32_t reserved_1; /* offset 54 */ + uint16_t reserved_2; + __le16 guard_seed; /* offset 5A */ + struct dsd64 data_dsd[1]; + uint32_t reserved_5[2]; + uint32_t reserved_6; + } nobundling; + struct { + __le32 dif_byte_count; /* Total DIF byte + * count + */ + __le16 dseg_count; /* Data segment count */ + __le16 guard_seed; /* Initial Guard Seed */ + struct dsd64 data_dsd[1]; + struct dsd64 dif_dsd; + } bundling; + } u; + uint8_t reserved_3[12]; /* MUST be set to 0. */ +}; + +/* + * ISP queue - status entry structure definition. + */ +struct sts_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 comp_status; /* Completion status. */ + __le16 ox_id; /* OX_ID used by the firmware. */ + + __le32 residual_len; /* FW calc residual transfer length. */ + + union { + __le16 reserved_1; + __le16 nvme_rsp_pyld_len; + } u1; + + __le16 state_flags; /* State flags. */ + + __le16 read_sa_index; + __le16 wr_sa_index; + uint8_t reserved_2[8]; + uint8_t act_dif[8]; + uint8_t exp_dif[8]; + union { + struct { + __le32 rsp_data_len_dma; /* FCP response data length */ + uint8_t reserved_3[76]; + }; + struct { + uint8_t nvme_ersp_data[32]; + uint8_t reserved_4[48]; + }; + struct { + __le32 bid_rd_rsp_residual_count; /* BID read rsp residual cnt */ + __le16 retry_delay_timer; /* Retry delay timer. */ + __le16 scsi_status; /* SCSI status. */ + __le32 rsp_residual_count; /* FCP RSP residual count. */ + __le32 sense_len; /* FCP SENSE length. */ + __le32 rsp_data_len_ndma; /* FCP response data length */ + uint8_t data[60]; /* FCP rsp/sense information */ + }; + } u2; + + /* + * If DIF Error is set in comp_status, these additional fields are + * defined: + * + * !!! NOTE: Firmware sends expected/actual DIF data in big endian + * format; but all of the "data" field gets swab32-d in the beginning + * of qla2900_status_entry(). + * + * &data[10] : uint8_t report_runt_bg[2]; - computed guard + * &data[12] : uint8_t actual_dif[8]; - DIF Data received + * &data[20] : uint8_t expected_dif[8]; - DIF Data computed + */ +}; + +/* + * ISP queue - marker entry structure definition. + */ +struct mrk_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t handle_count; /* Handle count. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 nport_handle; /* N_PORT handle. */ + + uint8_t modifier; /* Modifier (7-0). */ + uint8_t reserved_1; + + __le16 vp_index; /* VP Index. 9bits*/ + uint16_t reserved_3; + + uint8_t lun[8]; /* FCP LUN (BE). */ + uint8_t reserved_4[104]; +}; + +/* + * ISP queue - CT Pass-Through entry structure definition. + */ +#define NUM_CT_DSDS 5 +struct ct_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System Defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 comp_status; /* Completion status. */ + + __le16 nport_handle; /* N_PORT handle. */ + + __le16 cmd_dsd_count; + + __le16 vp_index; /* vp index 9 bits*/ + + __le16 timeout; /* Command timeout. */ + uint16_t reserved_2; + + __le16 rsp_dsd_count; + + uint8_t reserved_3[10]; + uint8_t reserved_4[28]; /* Reserved. */ + + __le32 rsp_byte_count; + __le32 cmd_byte_count; + struct dsd64 dsd[NUM_CT_DSDS]; /* Data Segment Descriptors */ +}; + +/* + * 29xx extended Link Service pass-through request IOCB (128 bytes). + * + * Same wire purpose as the 64-byte struct pt_ls4_request used on 24xx-class + * adapters, but laid out for the 128-byte 29xx request ring: + * - vp_index widened to __le16 (bits [8:0] meaningful, see + * CMD_EXT_VP_INDEX_MASK). + * - reserved area expanded to 32 bytes between exchange_address and + * rx_byte_count. + * - inline DSD capacity grown from 2 to 5. + * Header through 'tx_dseg_count' (offset 14) and the control_flags / + * exchange_address fields keep the same offsets as struct pt_ls4_request, + * so common code can populate them via either type once IS_QLA29XX(ha) is + * branched for the layout-divergent fields. + */ +#define NUM_PT_LS4_EXT_DSDS 5 +struct pt_ls4_request_ext { + uint8_t entry_type; + uint8_t entry_count; + uint8_t sys_define; + uint8_t entry_status; + uint32_t handle; + __le16 status; + __le16 nport_handle; + __le16 tx_dseg_count; + __le16 vp_index; /* VP Index 9 bits; see CMD_EXT_VP_INDEX_MASK */ + __le16 timeout; + __le16 control_flags; /* CF_LS4_* (see struct pt_ls4_request) */ + __le16 rx_dseg_count; + __le16 rsvd2; + __le32 exchange_address; + uint8_t rsvd3[32]; + __le32 rx_byte_count; + __le32 tx_byte_count; + struct dsd64 dsd[NUM_PT_LS4_EXT_DSDS]; +}; + +/* + * ISP queue - PUREX IOCB entry structure definition + */ +struct purex_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + __le16 reserved1; + __le16 vp_idx; /* VP index 9 bits*/ + + __le16 status_flags; + __le16 nport_handle; + + __le16 frame_size; + __le16 trunc_frame_size; + + __le32 rx_xchg_addr; + + uint8_t d_id[3]; + uint8_t r_ctl; + + uint8_t s_id[3]; + uint8_t cs_ctl; + + uint8_t f_ctl[3]; + uint8_t type; + + __le16 seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + + __le16 rx_id; + __le16 ox_id; + __le32 param; + + uint8_t els_frame_payload[84]; +}; + +/* + * ISP queue - ELS Pass-Through entry structure definition. + * ELS_EXT_EST_SOFI*: 4-bit sof_type for extended IOCBs (qla_fw.h EST_SOFI* + * is for els_entry_24xx byte layout). + */ +#define ELS_EXT_EST_SOFI3 (1 << 1) +#define ELS_EXT_EST_SOFI2 (3 << 3) + +struct els_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System Defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 comp_status; /* response only */ + __le16 nport_handle; + + __le16 tx_dsd_count; + + __le16 vp_index_sof; /* bits [8:0]=VP index, [15:12]=SOF type */ + + __le32 rx_xchg_address; /* Receive exchange address. */ + __le16 rx_dsd_count; + + uint8_t opcode; + uint8_t reserved_2; + + uint8_t d_id[3]; + uint8_t s_id[3]; + + __le16 control_flags; /* Control flags. */ + + union { + struct { + __le32 rx_byte_count; + __le32 tx_byte_count; + + __le64 tx_address __packed; /* DSD 0 address. */ + __le32 tx_len; /* DSD 0 length. */ + + __le64 rx_address __packed; /* DSD 1 address. */ + __le32 rx_len; /* DSD 1 length. */ + }; + struct { + __le32 total_byte_count; + __le32 error_subcode_1; + __le32 error_subcode_2; + __le32 error_subcode_3; + uint8_t reserved_3[16]; + }; + }; + uint8_t reserved_4[64]; +}; + +struct els_sts_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System Defined. */ + uint8_t entry_status; /* Entry Status. */ + + __le32 handle; /* System handle. */ + + __le16 comp_status; + + __le16 nport_handle; /* N_PORT handle. */ + + __le16 reserved_1; + + __le16 vp_index_sof; /* bits [8:0]=VP index, [15:12]=SOF type */ + + __le32 rx_xchg_address; /* Receive exchange address. */ + __le16 reserved_2; + + uint8_t opcode; + uint8_t reserved_3; + + uint8_t d_id[3]; + uint8_t s_id[3]; + + __le16 control_flags; /* Control flags. */ + __le32 total_byte_count; + __le32 error_subcode_1; + __le32 error_subcode_2; + __le32 error_subcode_3; + + uint8_t reserved_4[80]; +}; + +struct logio_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 comp_status; /* Completion status. */ + + __le16 nport_handle; /* N_PORT handle. */ + + __le16 control_flags; /* Control flags. */ + + __le16 vp_index; /* VP Index 9bits*/ + + uint8_t port_id[3]; /* PortID of destination port. */ + + uint8_t rsp_size; /* Response size in 32bit words. */ + + __le32 io_parameter[11]; /* General I/O parameters. */ + uint8_t reserved_2[64]; /* Reserved*/ +}; + +struct tsk_mgmt_entry_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t handle_count; /* Handle count. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 nport_handle; /* N_PORT handle. */ + + __le16 reserved_1; + + __le16 delay; /* Activity delay in seconds. */ + + __le16 timeout; /* Command timeout. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + __le32 control_flags; /* Control Flags. */ + + __le16 vp_index; /* VP Index 9bits */ + + uint8_t reserved_3[98]; +}; + +struct abort_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t handle_count; /* Handle count. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + union { + __le16 nport_handle; /* N_PORT handle. */ + __le16 comp_status; /* Completion status. */ + }; + + __le16 options; /* Options. */ + + uint32_t handle_to_abort; /* System handle to abort. */ + + __le16 req_que_no; + + __le16 vp_index; /* VP Index 9bits*/ + u8 reserved_2[4]; + union { + struct { + __le16 abts_rty_cnt; + __le16 rsp_timeout; + } drv; + struct { + u8 ba_rjt_vendorUnique; + u8 ba_rjt_reasonCodeExpl; + u8 ba_rjt_reasonCode; + u8 reserved_3; + } fw; + }; + u8 reserved_4[100]; +}; + +struct abts_entry_24xx_ext { + uint8_t entry_type; + uint8_t entry_count; + uint8_t handle_count; + uint8_t entry_status; + + __le32 handle; /* type 0x55 only */ + + __le16 comp_status; /* type 0x55 only */ + __le16 nport_handle; /* type 0x54 only */ + + __le16 control_flags; /* type 0x55 only */ + __le16 vp_idx_sof; /* bits [8:0]=VP index, [15:12]=SOF type */ + + __le32 rx_xch_addr; + + uint8_t d_id[3]; + uint8_t r_ctl; + + uint8_t s_id[3]; + uint8_t cs_ctl; + + uint8_t f_ctl[3]; + uint8_t type; + + __le16 seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + + __le16 rx_id; + __le16 ox_id; + + __le32 param; + + union { + struct { + __le32 subcode3; + __le32 rsvd; + __le32 subcode1; + __le32 subcode2; + } error; + struct { + __le16 rsrvd1; + uint8_t last_seq_id; + uint8_t seq_id_valid; + __le16 aborted_rx_id; + __le16 aborted_ox_id; + __le16 high_seq_cnt; + __le16 low_seq_cnt; + } ba_acc; + struct { + uint8_t vendor_unique; + uint8_t explanation; + uint8_t reason; + } ba_rjt; + } payload; + + __le32 rx_xch_addr_to_abort; + uint8_t reserved_2[64]; +} __packed; +/* + * Virtual Port Control IOCB + */ +struct vp_ctrl_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 vp_idx_failed; + + __le16 comp_status; /* Completion status. */ + + __le16 command; + + __le16 vp_count; + + uint8_t vp_idx_map[16]; + __le16 flags; + __le16 id; + uint16_t reserved_4; + __le16 hopct; + uint8_t reserved_5[88]; +}; + +/* + * Modify Virtual Port Configuration IOCB + */ +struct vp_config_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t handle_count; + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + + __le16 flags; + + __le16 comp_status; /* Completion status. */ + + uint8_t command; + + uint8_t vp_count; + + uint8_t vp_index1; + uint8_t vp_index2; + + uint8_t options_idx1; + uint8_t hard_address_idx1; + uint16_t reserved_vp1; + uint8_t port_name_idx1[WWN_SIZE]; + uint8_t node_name_idx1[WWN_SIZE]; + + uint8_t options_idx2; + uint8_t hard_address_idx2; + uint16_t reserved_vp2; + uint8_t port_name_idx2[WWN_SIZE]; + uint8_t node_name_idx2[WWN_SIZE]; + __le16 id; + uint16_t reserved_4; + __le16 hopct; + uint8_t reserved_5[66]; +}; + +struct vp_rpt_id_entry_24xx_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + __le32 resv1; + uint8_t vp_acquired; + uint8_t vp_setup; + __le16 vp_idx_status; /* bits [8:0]=VP index, [15:9]=VP status */ + + uint8_t port_id[3]; + uint8_t format; + union { + struct vp_rpt_id_ext_f1 { + /* format 1 fabric */ + uint8_t vpstat1_subcode; /* vp_status=1 subcode */ + uint8_t flags; + + __le16 fip_flags; + uint8_t rsv2[12]; + + uint8_t ls_rjt_vendor; + uint8_t ls_rjt_explanation; + uint8_t ls_rjt_reason; + uint8_t rsv3; + __le16 rsv8; + __le16 flogi_acc_payload_size; /* bits [8:0] meaningful */ + uint8_t port_name[8]; + uint8_t node_name[8]; + __le16 bbcr; + uint8_t reserved_5[6]; + } f1; + struct vp_rpt_id_ext_f2 { /* format 2: N2N direct connect */ + uint8_t vpstat1_subcode; + uint8_t flags; + __le16 fip_flags; + uint8_t rsv2[12]; + + uint8_t ls_rjt_vendor; + uint8_t ls_rjt_explanation; + uint8_t ls_rjt_reason; + uint8_t rsv3[5]; + + uint8_t port_name[8]; + uint8_t node_name[8]; + __le16 bbcr; + uint8_t reserved_5[2]; + uint8_t remote_nport_id[4]; + } f2; + } u; + uint8_t reserved_end[64]; +}; + +/* + * ISP queue - 64-Bit addressing, continuation entry structure definition + * for the 29xx extended (128-byte) IOCB ring. Mirrors cont_a64_entry_t + * but carries 10 DSDs per entry instead of 5. + */ +#define NUM_CONT1_DSDS 10 +struct cont_a64_entry_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t reserved; + struct dsd64 dsd[NUM_CONT1_DSDS]; +}; + +/* + * 29xx extended Command Type FC-NVMe IOCB (128 bytes). + * + * The header layout up through 'byte_count' (offset 48) is identical to the + * 64-byte struct cmd_nvme used by 24xx-class adapters, so common code can + * populate those fields via either type. Fields beyond 'byte_count' diverge: + * 29xx adds control_flags_2/vp_index/first_burst_rx_id/io_tag/..., drops + * port_id[3]+vp_index(byte), and carries NUM_NVME_DSDS inline DSDs. + */ +#define NUM_NVME_DSDS 4 +struct cmd_nvme_ext { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + uint32_t handle; /* System handle. */ + __le16 nport_handle; /* N_PORT handle. */ + __le16 timeout; /* Command timeout. */ + + __le16 dseg_count; /* Data segment count. */ + __le16 nvme_rsp_dsd_len; /* NVMe RSP DSD length */ + + uint64_t rsvd; + + __le16 control_flags; /* Control Flags (see struct cmd_nvme) */ + __le16 nvme_cmnd_dseg_len; /* Data segment length. */ + + __le64 nvme_cmnd_dseg_address __packed; /* Data segment address. */ + + __le64 nvme_rsp_dseg_address __packed; /* Data segment address. */ + + __le32 byte_count; /* Total byte count. */ + + __le16 control_flags_2; + /* + * vp_index layout matches the other 29xx extended IOCBs: only bits + * [8:0] are meaningful (see CMD_EXT_VP_INDEX_MASK). + */ + __le16 vp_index; + __le32 first_burst_rx_id; + __le16 io_tag; + uint8_t vl_n_fctl; /* VL(7:4) | RSVD(3:2) | F_CTL[17](1) | RSVD(0) */ + uint8_t prtag_csctl; /* Priority Tag or CS_CTL */ + __le32 src_vm_id; /* Source VM ID */ + uint8_t reserved_2[16]; + + struct dsd64 nvme_dsd[NUM_NVME_DSDS]; +}; + +#endif diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 9e328c235e39..cb48b9e15a44 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -38,6 +38,7 @@ extern void qla24xx_update_fw_options(scsi_qla_host_t *); extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *); extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *); extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern int qla29xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr); extern int qla2x00_perform_loop_resync(scsi_qla_host_t *); extern int qla2x00_loop_resync(scsi_qla_host_t *); @@ -135,7 +136,7 @@ void qla_edif_sadb_release(struct qla_hw_data *ha); int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha); void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha); void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, - srb_t *sp, struct sts_entry_24xx *sts24); + srb_t *sp, void *pkt); void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport, struct ctio7_from_24xx *ctio); void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport); @@ -265,8 +266,7 @@ extern int qla24xx_modify_vp_config(scsi_qla_host_t *); extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t); extern void qla2x00_vp_stop_timer(scsi_qla_host_t *); extern int qla24xx_configure_vhba (scsi_qla_host_t *); -extern void qla24xx_report_id_acquisition(scsi_qla_host_t *, - struct vp_rpt_id_entry_24xx *); +extern void qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt); extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *); extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *); extern scsi_qla_host_t *qla24xx_create_vhost(struct fc_vport *); @@ -294,9 +294,11 @@ void qla_adjust_buf(struct scsi_qla_host *); * Global Function Prototypes in qla_iocb.c source file. */ void qla_els_pt_iocb(struct scsi_qla_host *vha, - struct els_entry_24xx *pkt, struct qla_els_pt_arg *a); + void *pkt, struct qla_els_pt_arg *a); cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, - struct req_que *que); + struct qla_hw_data *ha, struct req_que *que); +struct cont_a64_entry_ext *qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha, + struct req_que *req); extern uint16_t qla2x00_calc_iocbs_32(uint16_t); extern uint16_t qla2x00_calc_iocbs_64(uint16_t); extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t); @@ -336,7 +338,7 @@ void qla2x00_els_dcmd2_iocb_timeout(void *data); * Global Function Prototypes in qla_mbx.c source file. */ extern int -qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); +qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t, int); extern int qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); @@ -580,6 +582,12 @@ extern int qla2xxx_read_remote_register(scsi_qla_host_t *, uint32_t, extern int qla2xxx_write_remote_register(scsi_qla_host_t *, uint32_t, uint32_t); void qla_no_op_mb(struct scsi_qla_host *vha); +extern int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma, + uint32_t flash_addr, uint32_t flash_size, + uint16_t reg_code, uint16_t opt); +extern int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma, + uint32_t flash_addr, uint32_t flash_size, + uint16_t reg_code, uint16_t opt); /* * Global Function Prototypes in qla_isr.c source file. @@ -682,7 +690,19 @@ struct purex_item *qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order); int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in, uint16_t *mbx_out); - +void *qla29xx_read_optrom_data(struct scsi_qla_host *vha, + uint16_t reg_code, uint16_t opts, + void *buf, uint32_t offset, + uint32_t length); +extern int qla29xx_write_optrom_data(struct scsi_qla_host *vha, + uint16_t reg_code, uint16_t opts, + void *buf, uint32_t offset, + uint32_t length); +extern int qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, + void *buf, uint32_t offset, uint32_t length, + enum qla29xx_mpi_optrom_op op); +extern int qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt, + uint32_t mpi_addr, uint32_t dlen, dma_addr_t req_dma); /* * Global Function Prototypes in qla_dbg.c source file. */ @@ -968,10 +988,8 @@ extern void qla24xx_process_purex_list(struct purex_list *); extern void qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp); extern void qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp); extern void qla_wait_nvme_release_cmd_kref(srb_t *sp); -extern void qla_nvme_abort_set_option - (struct abort_entry_24xx *abt, srb_t *sp); -extern void qla_nvme_abort_process_comp_status - (struct abort_entry_24xx *abt, srb_t *sp); +extern void qla_nvme_abort_set_option(void *pkt, srb_t *sp); +extern void qla_nvme_abort_process_comp_status(void *pkt, srb_t *sp); struct scsi_qla_host *qla_find_host_by_vp_idx(struct scsi_qla_host *vha, uint16_t vp_idx); diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 880cd73feaca..20b1aef455c4 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -68,30 +68,62 @@ void * qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, struct ct_arg *arg) { struct qla_hw_data *ha = vha->hw; - struct ct_entry_24xx *ct_pkt; - ct_pkt = (struct ct_entry_24xx *)arg->iocb; - memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); + if (IS_QLA29XX(ha)) { + struct ct_entry_24xx_ext *ct_pkt; - ct_pkt->entry_type = CT_IOCB_TYPE; - ct_pkt->entry_count = 1; - ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle); - ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ct_pkt->cmd_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size); - ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size); + ct_pkt = (struct ct_entry_24xx_ext *)arg->iocb; + memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext)); - put_unaligned_le64(arg->req_dma, &ct_pkt->dsd[0].address); - ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; + ct_pkt->entry_type = CT_IOCB_TYPE; + ct_pkt->entry_count = 1; + ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle); + ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size); + ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size); - put_unaligned_le64(arg->rsp_dma, &ct_pkt->dsd[1].address); - ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count; - ct_pkt->vp_index = vha->vp_idx; + put_unaligned_le64(arg->req_dma, + &ct_pkt->dsd[0].address); + ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; - vha->qla_stats.control_requests++; + put_unaligned_le64(arg->rsp_dma, + &ct_pkt->dsd[1].address); + ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = cpu_to_le16(vha->vp_idx); + + vha->qla_stats.control_requests++; + + return ct_pkt; + } else { + struct ct_entry_24xx *ct_pkt; + + ct_pkt = (struct ct_entry_24xx *)arg->iocb; + memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); + + ct_pkt->entry_type = CT_IOCB_TYPE; + ct_pkt->entry_count = 1; + ct_pkt->nport_handle = cpu_to_le16(arg->nport_handle); + ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_byte_count = cpu_to_le32(arg->rsp_size); + ct_pkt->cmd_byte_count = cpu_to_le32(arg->req_size); + + put_unaligned_le64(arg->req_dma, + &ct_pkt->dsd[0].address); + ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; + + put_unaligned_le64(arg->rsp_dma, + &ct_pkt->dsd[1].address); + ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = vha->vp_idx; - return (ct_pkt); + vha->qla_stats.control_requests++; + + return ct_pkt; + } } /** @@ -132,7 +164,10 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, routine, ms_pkt->entry_status, vha->d_id.b.domain, vha->d_id.b.area, vha->d_id.b.al_pa); } else { - if (IS_FWI2_CAPABLE(ha)) + if (IS_QLA29XX(ha)) + comp_status = le16_to_cpu( + ((struct ct_entry_24xx_ext *)ms_pkt)->comp_status); + else if (IS_FWI2_CAPABLE(ha)) comp_status = le16_to_cpu( ((struct ct_entry_24xx *)ms_pkt)->comp_status); else @@ -157,8 +192,8 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, break; case CS_PORT_LOGGED_OUT: if (IS_FWI2_CAPABLE(ha)) { - if (le16_to_cpu(ms_pkt->loop_id.extended) == - NPH_SNS) + if (le16_to_cpu(((struct ct_entry_24xx *) + ms_pkt)->nport_handle) == NPH_SNS) lid_is_sns = true; } else { if (le16_to_cpu(ms_pkt->loop_id.extended) == @@ -1437,42 +1472,85 @@ void * qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) { - struct ct_entry_24xx *ct_pkt; struct qla_hw_data *ha = vha->hw; - ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; - memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); + if (IS_QLA29XX(ha)) { + struct ct_entry_24xx_ext *ct_pkt; + + ct_pkt = (struct ct_entry_24xx_ext *)ha->ms_iocb; + memset(ct_pkt, 0, sizeof(struct ct_entry_24xx_ext)); + + ct_pkt->entry_type = CT_IOCB_TYPE; + ct_pkt->entry_count = 1; + ct_pkt->nport_handle = + cpu_to_le16(vha->mgmt_svr_loop_id); + ct_pkt->timeout = + cpu_to_le16(ha->r_a_tov / 10 * 2); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); + ct_pkt->cmd_byte_count = cpu_to_le32(req_size); - ct_pkt->entry_type = CT_IOCB_TYPE; - ct_pkt->entry_count = 1; - ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id); - ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); - ct_pkt->cmd_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_dsd_count = cpu_to_le16(1); - ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); - ct_pkt->cmd_byte_count = cpu_to_le32(req_size); + put_unaligned_le64(ha->ct_sns_dma, + &ct_pkt->dsd[0].address); + ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; - put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[0].address); - ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; + put_unaligned_le64(ha->ct_sns_dma, + &ct_pkt->dsd[1].address); + ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = cpu_to_le16(vha->vp_idx); - put_unaligned_le64(ha->ct_sns_dma, &ct_pkt->dsd[1].address); - ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count; - ct_pkt->vp_index = vha->vp_idx; + return ct_pkt; + } else { + struct ct_entry_24xx *ct_pkt; + + ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; + memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); + + ct_pkt->entry_type = CT_IOCB_TYPE; + ct_pkt->entry_count = 1; + ct_pkt->nport_handle = + cpu_to_le16(vha->mgmt_svr_loop_id); + ct_pkt->timeout = + cpu_to_le16(ha->r_a_tov / 10 * 2); + ct_pkt->cmd_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_dsd_count = cpu_to_le16(1); + ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); + ct_pkt->cmd_byte_count = cpu_to_le32(req_size); + + put_unaligned_le64(ha->ct_sns_dma, + &ct_pkt->dsd[0].address); + ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; + + put_unaligned_le64(ha->ct_sns_dma, + &ct_pkt->dsd[1].address); + ct_pkt->dsd[1].length = ct_pkt->rsp_byte_count; + ct_pkt->vp_index = vha->vp_idx; - return ct_pkt; + return ct_pkt; + } } static void qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size) { struct qla_hw_data *ha = vha->hw; - ms_iocb_entry_t *ms_pkt = ha->ms_iocb; - struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; - if (IS_FWI2_CAPABLE(ha)) { + if (IS_QLA29XX(ha)) { + struct ct_entry_24xx_ext *ct_pkt = + (struct ct_entry_24xx_ext *)ha->ms_iocb; + + ct_pkt->cmd_byte_count = cpu_to_le32(req_size); + ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; + } else if (IS_FWI2_CAPABLE(ha)) { + struct ct_entry_24xx *ct_pkt = + (struct ct_entry_24xx *)ha->ms_iocb; + ct_pkt->cmd_byte_count = cpu_to_le32(req_size); ct_pkt->dsd[0].length = ct_pkt->cmd_byte_count; } else { + ms_iocb_entry_t *ms_pkt = ha->ms_iocb; + ms_pkt->req_bytecount = cpu_to_le32(req_size); ms_pkt->req_dsd.length = ms_pkt->req_bytecount; } @@ -1508,12 +1586,18 @@ qla25xx_fdmi_port_speed_capability(struct qla_hw_data *ha) if (IS_CNA_CAPABLE(ha)) return FDMI_PORT_SPEED_10GB; - if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) { - if (ha->max_supported_speed == 2) { + if (IS_QLA28XX(ha) || IS_QLA27XX(ha) || IS_QLA29XX(ha)) { + if (ha->max_supported_speed == 3) { + if (ha->min_supported_speed <= 7) + speeds |= FDMI_PORT_SPEED_128GB; + } + if (ha->max_supported_speed == 3 || + ha->max_supported_speed == 2) { if (ha->min_supported_speed <= 6) speeds |= FDMI_PORT_SPEED_64GB; } - if (ha->max_supported_speed == 2 || + if (ha->max_supported_speed == 3 || + ha->max_supported_speed == 2 || ha->max_supported_speed == 1) { if (ha->min_supported_speed <= 5) speeds |= FDMI_PORT_SPEED_32GB; @@ -1577,6 +1661,8 @@ qla25xx_fdmi_port_speed_currently(struct qla_hw_data *ha) return FDMI_PORT_SPEED_32GB; case PORT_SPEED_64GB: return FDMI_PORT_SPEED_64GB; + case PORT_SPEED_128GB: + return FDMI_PORT_SPEED_128GB; default: return FDMI_PORT_SPEED_UNKNOWN; } @@ -2620,6 +2706,8 @@ qla2x00_port_speed_capability(uint16_t speed) return PORT_SPEED_32GB; case BIT_7: return PORT_SPEED_64GB; + case BIT_6: + return PORT_SPEED_128GB; default: return PORT_SPEED_UNKNOWN; } diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index e746c9274cde..2b9a9c672ec6 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -228,7 +228,7 @@ qla2x00_async_iocb_timeout(void *data) srb_t *sp = data; fc_port_t *fcport = sp->fcport; struct srb_iocb *lio = &sp->u.iocb_cmd; - int rc, h; + int rc, h, found; unsigned long flags; if (fcport) { @@ -251,6 +251,7 @@ qla2x00_async_iocb_timeout(void *data) lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? QLA_LOGIO_LOGIN_RETRIED : 0; + found = 0; spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) { @@ -258,11 +259,19 @@ qla2x00_async_iocb_timeout(void *data) sp) { sp->qpair->req->outstanding_cmds[h] = NULL; + found = 1; break; } } spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); - sp->done(sp, QLA_FUNCTION_TIMEOUT); + /* + * Only complete the command if this path removed it + * from outstanding_cmds. Otherwise the ISR already + * completed it and a second sp->done() would race the + * submitter's freeing of the on-stack completion. + */ + if (found) + sp->done(sp, QLA_FUNCTION_TIMEOUT); } break; case SRB_LOGOUT_CMD: @@ -275,6 +284,7 @@ qla2x00_async_iocb_timeout(void *data) default: rc = qla24xx_async_abort_cmd(sp, false); if (rc) { + found = 0; spin_lock_irqsave(sp->qpair->qp_lock_ptr, flags); for (h = 1; h < sp->qpair->req->num_outstanding_cmds; h++) { @@ -282,11 +292,19 @@ qla2x00_async_iocb_timeout(void *data) sp) { sp->qpair->req->outstanding_cmds[h] = NULL; + found = 1; break; } } spin_unlock_irqrestore(sp->qpair->qp_lock_ptr, flags); - sp->done(sp, QLA_FUNCTION_TIMEOUT); + /* + * Only complete the command if this path removed it + * from outstanding_cmds. Otherwise the ISR already + * completed it and a second sp->done() would race the + * submitter's freeing of the on-stack completion. + */ + if (found) + sp->done(sp, QLA_FUNCTION_TIMEOUT); } break; } @@ -2773,7 +2791,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) ha->isp_ops->reset_chip(vha); /* Check for secure flash support */ - if (IS_QLA28XX(ha)) { + if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) { if (rd_reg_word(®->mailbox12) & BIT_0) ha->flags.secure_adapter = 1; ql_log(ql_log_info, vha, 0xffff, "Secure Adapter: %s\n", @@ -3275,47 +3293,37 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha) return qla81xx_write_mpi_register(vha, mb); } -static int -qla_chk_risc_recovery(scsi_qla_host_t *vha) +/* save MB regs at start of day for fw dump */ +static void +qla_save_mbregs(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; __le16 __iomem *mbptr = ®->mailbox0; int i; - u16 mb[32]; - int rc = QLA_SUCCESS; - - if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha)) - return rc; + u16 *mb = ha->mbregs; - /* this check is only valid after RISC reset */ - mb[0] = rd_reg_word(mbptr); - mbptr++; - if (mb[0] == 0xf) { - rc = QLA_FUNCTION_FAILED; + if ((!IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) || + vha->flags.init_done) + return; - for (i = 1; i < 32; i++) { - mb[i] = rd_reg_word(mbptr); - mbptr++; - } - - ql_log(ql_log_warn, vha, 0x1015, - "RISC reset failed. mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", - mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]); - ql_log(ql_log_warn, vha, 0x1015, - "RISC reset failed. mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", - mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14], - mb[15]); - ql_log(ql_log_warn, vha, 0x1015, - "RISC reset failed. mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", - mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22], - mb[23]); - ql_log(ql_log_warn, vha, 0x1015, - "RISC reset failed. mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", - mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30], - mb[31]); + for (i = 0; i < 32; i++) { + mb[i] = rd_reg_word(mbptr); + mbptr++; } - return rc; + + ql_log(ql_log_info, vha, 0x1015, + "mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", + mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]); + ql_log(ql_log_info, vha, 0x1015, + "mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", + mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14], mb[15]); + ql_log(ql_log_info, vha, 0x1015, + "mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", + mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22], mb[23]); + ql_log(ql_log_info, vha, 0x1015, + "mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n", + mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30], mb[31]); } /** @@ -3334,7 +3342,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) uint16_t wd; static int abts_cnt; /* ISP abort retry counts */ int rval = QLA_SUCCESS; - int print = 1; spin_lock_irqsave(&ha->hardware_lock, flags); @@ -3431,9 +3438,6 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) barrier(); if (cnt) { mdelay(1); - if (print && qla_chk_risc_recovery(vha)) - print = 0; - wd = rd_reg_word(®->mailbox0); } else { rval = QLA_FUNCTION_TIMEOUT; @@ -3453,6 +3457,8 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) spin_unlock_irqrestore(&ha->hardware_lock, flags); + qla_save_mbregs(vha); + ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x015f, "Driver in %s mode\n", IS_NOPOLLING_TYPE(ha) ? "Interrupt" : "Polling"); @@ -3733,7 +3739,7 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha) return -EINVAL; if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return -EINVAL; if (ha->fce) { @@ -3764,11 +3770,27 @@ int qla2x00_alloc_fce_trace(scsi_qla_host_t *vha) void qla2x00_free_fce_trace(struct qla_hw_data *ha) { - if (!ha->fce) + void *fce; + dma_addr_t fce_dma; + unsigned long flags; + + /* + * Unpublish ha->fce under hardware_lock so a firmware dump in + * progress (which reads ha->fce under the same lock) cannot race + * with the buffer being freed. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + if (!ha->fce) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); return; - dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce, ha->fce_dma); + } + fce = ha->fce; + fce_dma = ha->fce_dma; ha->fce = NULL; ha->fce_dma = 0; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + dma_free_coherent(&ha->pdev->dev, FCE_SIZE, fce, fce_dma); } static void @@ -3813,16 +3835,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; struct rsp_que *rsp = ha->rsp_q_map[0]; - struct qla2xxx_fw_dump *fw_dump; - - if (ha->fw_dump) { - ql_dbg(ql_dbg_init, vha, 0x00bd, - "Firmware dump already allocated.\n"); - return; - } + struct qla2xxx_fw_dump *fw_dump, *prev_fw_dump; + void *prev_mpi_fw_dump; + size_t req_entry_size = qla_req_entry_size(ha); + size_t rsp_entry_size = qla_rsp_entry_size(ha); - ha->fw_dumped = 0; - ha->fw_dump_cap_flags = 0; dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0; req_q_size = rsp_q_size = 0; @@ -3852,9 +3869,9 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) * Resizing must be done at end-of-dump processing. */ mq_size += (ha->max_req_queues - 1) * - (req->length * sizeof(request_t)); + (req->length * req_entry_size); mq_size += (ha->max_rsp_queues - 1) * - (rsp->length * sizeof(response_t)); + (rsp->length * rsp_entry_size); } if (ha->tgt.atio_ring) mq_size += ha->tgt.atio_q_length * sizeof(request_t); @@ -3868,7 +3885,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) eft_size = EFT_SIZE; } - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { struct fwdt *fwdt = ha->fwdt; uint j; @@ -3890,8 +3907,8 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) /* Add space for spare MPI fw dump. */ dump_size += ha->fwdt[1].dump_size; } else { - req_q_size = req->length * sizeof(request_t); - rsp_q_size = rsp->length * sizeof(response_t); + req_q_size = req->length * req_entry_size; + rsp_q_size = rsp->length * rsp_entry_size; dump_size = offsetof(struct qla2xxx_fw_dump, isp); dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + eft_size; @@ -3905,13 +3922,11 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) ha->exlogin_size; } - if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) { - - ql_dbg(ql_dbg_init, vha, 0x00c5, - "%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n", - __func__, dump_size, ha->fw_dump_len, - ha->fw_dump_alloc_len); + ql_dbg(ql_dbg_init, vha, 0x00c5, + "%s dump_size %d fw_dump_len %d fw_dump_alloc_len %d\n", + __func__, dump_size, ha->fw_dump_len, ha->fw_dump_alloc_len); + if (!ha->fw_dump_len || dump_size > ha->fw_dump_alloc_len) { fw_dump = vmalloc(dump_size); if (!fw_dump) { ql_log(ql_log_warn, vha, 0x00c4, @@ -3919,9 +3934,26 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) dump_size / 1024); } else { mutex_lock(&ha->optrom_mutex); - if (ha->fw_dumped) { - memcpy(fw_dump, ha->fw_dump, ha->fw_dump_len); - vfree(ha->fw_dump); + + if (ha->fw_dumped || ha->mpi_fw_dumped) { + prev_fw_dump = ha->fw_dump; + + if (ha->fw_dumped) + memcpy(fw_dump, prev_fw_dump, + ha->fw_dump_len); + + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { + prev_mpi_fw_dump = ha->mpi_fw_dump; + ha->mpi_fw_dump = (char *)fw_dump + + ha->fwdt[0].dump_size; + + if (ha->mpi_fw_dumped) + memcpy(ha->mpi_fw_dump, + prev_mpi_fw_dump, + ha->mpi_fw_dump_len); + } + vfree(prev_fw_dump); ha->fw_dump = fw_dump; ha->fw_dump_alloc_len = dump_size; ql_dbg(ql_dbg_init, vha, 0x00c5, @@ -3937,9 +3969,10 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) "Allocated (%d KB) for firmware dump.\n", dump_size / 1024); - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { ha->mpi_fw_dump = (char *)fw_dump + - ha->fwdt[1].dump_size; + ha->fwdt[0].dump_size; mutex_unlock(&ha->optrom_mutex); return; } @@ -4194,11 +4227,11 @@ qla24xx_detect_sfp(scsi_qla_host_t *vha) used_nvram = 0; ha->flags.lr_detected = 0; if (IS_BPM_RANGE_CAPABLE(ha) && - (nv->enhanced_features & NEF_LR_DIST_ENABLE)) { + (le16_to_cpu(nv->enhanced_features) & NEF_LR_DIST_ENABLE)) { used_nvram = 1; ha->flags.lr_detected = 1; ha->lr_distance = - (nv->enhanced_features >> LR_DIST_NV_POS) + (le16_to_cpu(nv->enhanced_features) >> LR_DIST_NV_POS) & LR_DIST_NV_MASK; } @@ -4336,6 +4369,16 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) rval = qla2x00_verify_checksum(vha, srisc_address); if (rval == QLA_SUCCESS) { + /* + * Alloc a guestimate dump buffer to capture any failure + * during early phase of driver load. + */ + if (ql2xallocfwdump && + (IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) && + !vha->flags.init_done) + qla2x00_alloc_fw_dump(vha); + /* Start firmware execution. */ ql_dbg(ql_dbg_init, vha, 0x00ca, "Starting firmware.\n"); @@ -4387,6 +4430,19 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) MIN_MULTI_ID_FABRIC)) ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; + + /* + * The VP_CTRL IOCB selects target VPs + * through the fixed vp_idx_map bitmap, + * so a vp_index beyond it can be enabled + * via VP_CONFIG but never disabled via + * VP_CTRL, leaking the VP. Cap the count + * to the bitmap capacity. + */ + if (ha->max_npiv_vports >= + VP_CTRL_IDX_MAP_BITS) + ha->max_npiv_vports = + VP_CTRL_IDX_MAP_BITS - 1; } qlt_config_nvram_with_fw_version(vha); qla2x00_get_resource_cnts(vha); @@ -4478,15 +4534,33 @@ void qla2x00_init_response_q_entries(struct rsp_que *rsp) { uint16_t cnt; - response_t *pkt; rsp->ring_ptr = rsp->ring; rsp->ring_index = 0; rsp->status_srb = NULL; - pkt = rsp->ring_ptr; - for (cnt = 0; cnt < rsp->length; cnt++) { - pkt->signature = RESPONSE_PROCESSED; - pkt++; + + if (rsp->hw && IS_QLA29XX(rsp->hw)) { + /* + * 29xx uses a 128-byte response-ring stride. The signature + * field offset matches response_t, but the entry pitch is + * sizeof(struct response_ext); walk via ring_ext_ptr / struct response_ext. + */ + struct response_ext *pkt; + + rsp->ring_ext_ptr = rsp->ring_ext; + pkt = rsp->ring_ext_ptr; + for (cnt = 0; cnt < rsp->length; cnt++) { + pkt->signature = RESPONSE_PROCESSED; + pkt++; + } + } else { + response_t *pkt; + + pkt = rsp->ring_ptr; + for (cnt = 0; cnt < rsp->length; cnt++) { + pkt->signature = RESPONSE_PROCESSED; + pkt++; + } } } @@ -4772,7 +4846,14 @@ qla24xx_config_rings(struct scsi_qla_host *vha) ql_dbg(ql_dbg_init, vha, 0x00fd, "Speed set by user : %s Gbps \n", qla2x00_get_link_speed_str(ha, ha->set_data_rate)); - icb->firmware_options_3 = cpu_to_le32(ha->set_data_rate << 13); + /* + * The ICB data-rate field is 3 bits (bits 13-15); rates above + * 64G do not fit and would overflow into bit 16 (75 ohm + * termination select). Such rates are forced via MBC_DATA_RATE. + */ + if (ha->set_data_rate <= PORT_SPEED_64GB) + icb->firmware_options_3 = + cpu_to_le32(ha->set_data_rate << 13); } /* PCI posting */ @@ -4807,7 +4888,12 @@ qla2x00_init_rings(scsi_qla_host_t *vha) req = ha->req_q_map[que]; if (!req || !test_bit(que, ha->req_qid_map)) continue; - req->out_ptr = (uint16_t *)(req->ring + req->length); + if (IS_QLA29XX(ha)) + req->out_ptr = + (uint16_t *)(req->ring_ext + req->length); + else + req->out_ptr = + (uint16_t *)(req->ring + req->length); *req->out_ptr = 0; for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) req->outstanding_cmds[cnt] = NULL; @@ -4818,13 +4904,19 @@ qla2x00_init_rings(scsi_qla_host_t *vha) req->ring_ptr = req->ring; req->ring_index = 0; req->cnt = req->length; + if (IS_QLA29XX(ha)) + req->ring_ext_ptr = req->ring_ext; } for (que = 0; que < ha->max_rsp_queues; que++) { rsp = ha->rsp_q_map[que]; if (!rsp || !test_bit(que, ha->rsp_qid_map)) continue; - rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length); + if (IS_QLA29XX(ha)) + rsp->in_ptr = + (uint16_t *)(rsp->ring_ext + rsp->length); + else + rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length); *rsp->in_ptr = 0; /* Initialize response queue entries */ if (IS_QLAFX00(ha)) @@ -4896,6 +4988,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha) ql_dbg(ql_dbg_init, vha, 0x00d3, "Init Firmware -- success.\n"); vha->u_ql2xexchoffld = vha->u_ql2xiniexchg = 0; + vha->hw->flags.t262_fail = 0; + vha->hw->flags.t272_fail = 0; } return (rval); @@ -5616,6 +5710,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) INIT_LIST_HEAD(&fcport->gnl_entry); INIT_LIST_HEAD(&fcport->list); INIT_LIST_HEAD(&fcport->unsol_ctx_head); + spin_lock_init(&fcport->unsol_ctx_lock); INIT_LIST_HEAD(&fcport->sess_cmd_list); spin_lock_init(&fcport->sess_cmd_lock); @@ -8669,7 +8764,8 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, for (i = 0; i < dlen; i++) dcode[i] = swab32(dcode[i]); - rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen); + rval = qla2x00_load_ram(vha, req->dma, risc_addr, + dlen, 0); if (rval) { ql_log(ql_log_fatal, vha, 0x008f, "-> Failed load firmware fragment %u.\n", @@ -8839,7 +8935,7 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) wcode[i] = swab16((__force u32)fwcode[i]); rval = qla2x00_load_ram(vha, req->dma, risc_addr, - wlen); + wlen, 0); if (rval) { ql_log(ql_log_fatal, vha, 0x008a, "Failed to load segment %d of firmware.\n", @@ -8929,7 +9025,8 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) for (i = 0; i < dlen; i++) dcode[i] = swab32((__force u32)fwcode[i]); - rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen); + rval = qla2x00_load_ram(vha, req->dma, risc_addr, + dlen, 0); if (rval) { ql_log(ql_log_fatal, vha, 0x0098, "-> Failed load firmware fragment %u.\n", @@ -9128,6 +9225,539 @@ qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) return rval; } +static int qla29xx_validate_firmware_image(scsi_qla_host_t *vha, + __be32 *dword) +{ + if (be32_to_cpu(dword[0]) != LD_FL_HEADER_SIGNATURE) { + ql_dbg(ql_dbg_init, vha, 0x0093, + "Firmware image Signature does not match (0x%x).\n", + be32_to_cpu(dword[0])); + return QLA_FUNCTION_FAILED; + } + if (be32_to_cpu(dword[2]) != LD_FL_HEADER_VERSION) { + ql_dbg(ql_dbg_init, vha, 0x0093, + "Firmware image Version does not match (0x%x).\n", + be32_to_cpu(dword[2])); + return QLA_FUNCTION_FAILED; + } + return QLA_SUCCESS; +} + +static int qla29xx_process_rd_image(struct scsi_qla_host *vha, + struct fcop_header *header, + __be32 *fwcode, int section, + uint32_t risc_addr, uint32_t section_size) +{ + int rval = QLA_SUCCESS; + uint32_t *dcode = NULL; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + int num_segments = 0, segment = 0; + int chunks_per_segment = 0, chunk = 0; + int is_first_chunk = 0; + int is_last_chunk = 0; + int is_first_segment = 0; + int is_last_segment = 0; + int opt = 0; + uint32_t size = 0; + uint32_t size_remainder = 0; + uint32_t seg_size_remainder = 0; + int i = 0; + + dcode = (uint32_t *)req->ring; + + if (section == TIM) { + if (section_size > req->length * qla_req_entry_size(ha)) { + ql_log(ql_log_fatal, vha, 0x0098, + "TIM section too large (0x%x bytes, ring 0x%lx bytes).\n", + section_size, + req->length * qla_req_entry_size(ha)); + return QLA_FUNCTION_FAILED; + } + + opt = BIT_15 | BIT_2 | BIT_1 | BIT_0; + for (i = 0; i < (section_size >> 2); i++) + dcode[i] = swab32((__force u32)fwcode[i]); + + ql_dbg(ql_dbg_init, vha, 0x0098, + "TIM : process_rd_image [opt 0x%x]\n", opt); + rval = qla2x00_load_ram(vha, req->dma, risc_addr, + section_size >> 2, opt); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "-> Failed load TIM\n"); + return QLA_FUNCTION_FAILED; + } + } else { + size_remainder = section_size; + num_segments += (section_size % header->segment_size == 0) ? + (section_size / header->segment_size) : + (section_size / header->segment_size) + 1; + chunks_per_segment = + (header->segment_size % CHUNK_SIZE == 0) ? + (header->segment_size / CHUNK_SIZE) : + (header->segment_size / CHUNK_SIZE) + 1; + + ql_dbg(ql_dbg_init, vha, 0x0098, + "num seg 0x%x chunk per seg 0x%x\n", + num_segments, chunks_per_segment); + + for (segment = 0; segment < num_segments; segment++) { + for (chunk = 0; chunk < chunks_per_segment; chunk++) { + is_first_chunk = (chunk == 0); + if (chunk == 0) { + if (size_remainder >= + header->segment_size) + seg_size_remainder = + header->segment_size; + else + seg_size_remainder = + size_remainder; + } + + is_first_segment = + (section == ARR1 && segment == 0); + is_last_segment = + (section == ARR2 && + (segment == (num_segments - 1))); + is_last_chunk = + (chunk == (chunks_per_segment - 1) || + size_remainder <= CHUNK_SIZE); + + if (seg_size_remainder < CHUNK_SIZE) + size = seg_size_remainder % CHUNK_SIZE; + else + size = CHUNK_SIZE; + + ql_dbg(ql_dbg_init, vha, 0x0098, + "[%d]chunk 0x%x segment 0x%x first_segment 0x%x first_chunk 0x%x last_segment 0x%x last_chunk 0x%x\n", + __LINE__, chunk, segment, + is_first_segment, is_first_chunk, + is_last_segment, is_last_chunk); + + opt = BIT_2; + if (is_first_chunk) + opt |= BIT_0; + if (is_last_chunk) + opt |= BIT_1; + if (is_first_segment) + opt |= BIT_3; + if (is_last_segment) + opt |= BIT_4; + + for (i = 0; i < (size >> 2); i++) + dcode[i] = + swab32((__force u32)fwcode[i]); + + ql_dbg(ql_dbg_init, vha, 0x0098, + "ARR[0x%x] : opt 0x%x\n", size, opt); + rval = qla2x00_load_ram(vha, req->dma, + risc_addr, + size >> 2, opt); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "-> Failed load ram\n"); + return QLA_FUNCTION_FAILED; + } + + fwcode += size >> 2; + size_remainder -= size; + seg_size_remainder -= size; + if (size_remainder == 0) + break; + } + risc_addr += header->segment_size >> 2; + } + } + return rval; +} + +static int +qla29xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + struct fcop_header *header; + uint32_t *dcode = NULL; + struct fw_blob *blob; + __be32 *fwcode, *array1_addr, *array3_addr; + uint templates; + uint32_t array_size, risc_attr = 0; + ulong i; + uint j; + ulong dlen, array3_offset; + size_t max_dwords; + struct qla_hw_data *ha = vha->hw; + struct fwdt *fwdt = ha->fwdt; + + header = vmalloc(sizeof(*header)); + if (!header) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_init, vha, 0x0090, + "-> FW: Loading via request-firmware.\n"); + + blob = qla2x00_request_firmware(vha); + if (!blob) { + ql_log(ql_log_warn, vha, 0x0092, + "-> Firmware file not found.\n"); + vfree(header); + return QLA_FUNCTION_FAILED; + } + + fwcode = (__force __be32 *)blob->fw->data; + + if (blob->fw->size < sizeof(*header)) { + ql_log(ql_log_fatal, vha, 0x0093, + "Firmware image too small (%zu bytes, need %zu).\n", + blob->fw->size, sizeof(*header)); + vfree(header); + return QLA_FUNCTION_FAILED; + } + + rval = qla29xx_validate_firmware_image(vha, fwcode); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x0093, + "Unable to verify integrity of firmware image\n"); + vfree(header); + return QLA_FUNCTION_FAILED; + } + + /* byte-swap big-endian firmware header to CPU endian */ + for (i = 0; i < sizeof(*header) / sizeof(__be32); i++) + ((uint32_t *)header)[i] = be32_to_cpu(fwcode[i]); + + /* + * Section lengths are dword counts from the firmware image. Reject + * values that would overflow the u32 fields on conversion to bytes + * (<< 2) or that exceed the image, before using them below. + */ + max_dwords = blob->fw->size >> 2; + if (header->header_length > max_dwords || + header->tim_length > max_dwords || + header->array1_length > max_dwords || + header->array2_length > max_dwords || + header->array3_length > max_dwords || + header->array4_length > max_dwords) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image has invalid section length.\n"); + goto img_corrupt; + } + + header->header_length <<= 2; + header->tim_length <<= 2; + header->array1_length <<= 2; + header->array2_length <<= 2; + header->array3_length <<= 2; + header->array4_length <<= 2; + + if (!header->segment_size) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image has zero segment_size.\n"); + goto img_corrupt; + } + + { + size_t fw_size = blob->fw->size; + size_t offset; + + /* TIM */ + offset = header->header_length; + if (offset + header->tim_length > fw_size) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image too small for TIM (%#zx > %#zx).\n", + offset + header->tim_length, fw_size); + goto img_corrupt; + } + rval = qla29xx_process_rd_image(vha, header, + (__be32 *)((char *)fwcode + offset), + TIM, TIM_DEST_ADDR, header->tim_length); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "-> Failed load TIM\n"); + goto img_corrupt; + } + + /* Array 1 */ + offset += header->tim_length; + if (offset + header->array1_length > fw_size) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image too small for Arr1 (%#zx > %#zx).\n", + offset + header->array1_length, fw_size); + goto img_corrupt; + } + rval = qla29xx_process_rd_image(vha, header, + (__be32 *)((char *)fwcode + offset), + ARR1, header->array1_destination_addr, + header->array1_length); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "-> Failed load Arr1\n"); + goto img_corrupt; + } + + /* Array 2 */ + offset += header->array1_length; + if (offset + header->array2_length > fw_size) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image too small for Arr2 (%#zx > %#zx).\n", + offset + header->array2_length, fw_size); + goto img_corrupt; + } + rval = qla29xx_process_rd_image(vha, header, + (__be32 *)((char *)fwcode + offset), + ARR2, header->array2_destination_addr, + header->array2_length); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "-> Failed load Arr2\n"); + goto img_corrupt; + } + + /* Array 3: FW_DUMP template */ + if (offset + 10 * sizeof(__be32) > fw_size) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image too small for risc_attr.\n"); + goto img_corrupt; + } + array1_addr = (__be32 *)((char *)fwcode + + header->header_length + + header->tim_length); + risc_attr = be32_to_cpu(array1_addr[9]); + } + templates = (risc_attr & BIT_9) ? 2 : 1; + ql_dbg(ql_dbg_init, vha, 0x0160, + "-> templates = %u\n", templates); + + array3_offset = header->header_length + header->tim_length + + header->array1_length + header->array2_length; + if (array3_offset > blob->fw->size) { + ql_log(ql_log_fatal, vha, 0x0098, + "FW image too small for Arr3 (%#lx > %#zx).\n", + array3_offset, blob->fw->size); + goto img_corrupt; + } + + array3_addr = (__be32 *)(((char *)fwcode) + array3_offset); + for (j = 0; j < templates; j++, fwdt++) { + vfree(fwdt->template); + fwdt->template = NULL; + fwdt->length = 0; + + if ((char *)&array3_addr[7] > + (char *)blob->fw->data + blob->fw->size) { + ql_log(ql_log_warn, vha, 0x0169, + "-> fwdt%u template header exceeds firmware blob.\n", + j); + goto failed; + } + + array_size = be32_to_cpu(array3_addr[2]); + ql_dbg(ql_dbg_init, vha, 0x0161, + "-> fwdt%u template array at %p (%#x dwords)\n", + j, array3_addr, array_size); + + if (!array_size || !~array_size || array_size <= 8) { + ql_dbg(ql_dbg_init, vha, 0x0162, + "-> fwdt%u failed to read array\n", j); + goto failed; + } + + array3_addr += 7; + array_size -= 8; + + if ((char *)&array3_addr[array_size] > + (char *)blob->fw->data + blob->fw->size) { + ql_log(ql_log_warn, vha, 0x0163, + "-> fwdt%u template array exceeds firmware blob.\n", + j); + goto failed; + } + + ql_dbg(ql_dbg_init, vha, 0x0163, + "-> fwdt%u template allocate template %#x words...\n", + j, array_size); + fwdt->template = vmalloc_array(array_size, sizeof(*dcode)); + if (!fwdt->template) { + ql_log(ql_log_warn, vha, 0x0164, + "-> fwdt%u failed allocate template.\n", j); + goto failed; + } + + dcode = fwdt->template; + for (i = 0; i < array_size; i++) + dcode[i] = (__force u32)array3_addr[i]; + + if (!qla27xx_fwdt_template_valid(dcode)) { + ql_log(ql_log_warn, vha, 0x0165, + "-> fwdt%u failed template validate\n", j); + goto failed; + } + + dlen = qla27xx_fwdt_template_size(dcode); + ql_dbg(ql_dbg_init, vha, 0x0166, + "-> fwdt%u template size %#lx bytes (%#lx words)\n", + j, dlen, dlen / sizeof(*dcode)); + if (dlen > array_size * sizeof(*dcode)) { + ql_log(ql_log_warn, vha, 0x0167, + "-> fwdt%u template exceeds array (%-lu bytes)\n", + j, dlen - array_size * sizeof(*dcode)); + goto failed; + } + + fwdt->length = dlen; + ql_dbg(ql_dbg_init, vha, 0x0168, + "-> fwdt%u loaded template ok\n", j); + + array3_addr += array_size + 1; + } + vfree(header); + return QLA_SUCCESS; + +failed: + vfree(fwdt->template); + fwdt->template = NULL; + fwdt->length = 0; +img_corrupt: + vfree(header); + return QLA_FUNCTION_FAILED; +} + +static int +qla29xx_load_fw_template(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct fwdt *fwdt = ha->fwdt; + uint templates = 1; + void *fw_dump_tmplt; + uint32_t *dcode; + void *buf = NULL; + uint32_t len; + uint32_t template_size; + uint j = 0; + int rval = 0; + + ql_dbg(ql_dbg_init, vha, 0x01bf, + "-> templates = %u fw_dump_tmplt_len = 0x%x\n", + templates, ha->fw_dump_tmplt_len); + + if (!ha->fw_dump_tmplt_len) { + ql_dbg(ql_dbg_init, vha, 0x01bf, + "no fw dump template available\n"); + return QLA_SUCCESS; + } + + fw_dump_tmplt = kzalloc(ha->fw_dump_tmplt_len, GFP_KERNEL); + if (!fw_dump_tmplt) { + ql_log(ql_log_info, vha, 0x0013, + "Unable to allocate memory\n"); + return QLA_FUNCTION_FAILED; + } + + buf = qla29xx_read_optrom_data(vha, FLT_REG_FW_DUMP_TMPLT, 0, + fw_dump_tmplt, 0, + ha->fw_dump_tmplt_len); + if (!buf) { + ql_log(ql_log_info, vha, 0x0013, + "Unable to read fw dump temp info.\n"); + goto free_fw_dump; + } + + ql_dump_buffer(ql_dbg_init, vha, 0x006b, fw_dump_tmplt, + min_t(uint32_t, ha->fw_dump_tmplt_len, 1024)); + + for (j = 0; j < templates; j++, fwdt++) { + vfree(fwdt->template); + fwdt->template = NULL; + fwdt->length = 0; + + template_size = le32_to_cpu(((__le32 *)fw_dump_tmplt)[2]); + + ql_dbg(ql_dbg_init, vha, 0x0161, + "-> fwdt%u template array at %p (0x%x bytes)\n", + j, fw_dump_tmplt, template_size); + + if (!template_size || !~template_size || + template_size > ha->fw_dump_tmplt_len) { + ql_dbg(ql_dbg_init, vha, 0x0162, + "-> fwdt%u failed to read array\n", j); + goto failed; + } + + fwdt->template = vmalloc(template_size); + if (!fwdt->template) { + ql_log(ql_log_warn, vha, 0x0164, + "-> fwdt%u failed allocate template.\n", j); + goto failed; + } + + dcode = fwdt->template; + memcpy((char *)dcode, (char *)fw_dump_tmplt, template_size); + + if (!qla27xx_fwdt_template_valid(dcode)) { + ql_log(ql_log_warn, vha, 0x0165, + "-> fwdt%u failed template validate (rval %x)\n", + j, rval); + goto failed; + } + + len = qla27xx_fwdt_template_size(dcode); + if (len > template_size) { + ql_log(ql_log_warn, vha, 0x0167, + "-> fwdt%u template exceeds array (%-u bytes)\n", + j, len - template_size); + goto failed; + } + + fwdt->length = len; + } + + kfree(fw_dump_tmplt); + return QLA_SUCCESS; + +failed: + if (fwdt->template) { + vfree(fwdt->template); + fwdt->template = NULL; + fwdt->length = 0; + } +free_fw_dump: + kfree(fw_dump_tmplt); + return QLA_FUNCTION_FAILED; +} + +int +qla29xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + + *srisc_addr = 0x100000; + + if (ql2xfwloadbin == 2) { + rval = qla29xx_load_risc_blob(vha, srisc_addr); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x019e, + "Failed to load firmware from file.\n"); + return QLA_FUNCTION_FAILED; + } + } else { + rval = qla28xx_load_flash_firmware(vha); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0x019e, + "Failed to load flash firmware.\n"); + return QLA_FUNCTION_FAILED; + } + + rval = qla29xx_load_fw_template(vha); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x01bd, + "failed to read firmware template\n"); + return QLA_FUNCTION_FAILED; + } + } + + return rval; +} + void qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha) { @@ -9306,35 +9936,98 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) if (IS_P3P_TYPE(ha) || IS_QLA8031(ha)) ha->vpd_size = FA_VPD_SIZE_82XX; - if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) - qla28xx_get_aux_images(vha, &active_regions); - /* Get VPD data into cache */ ha->vpd = ha->nvram + VPD_OFFSET; - faddr = ha->flt_region_vpd; - if (IS_QLA28XX(ha)) { - if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE) - faddr = ha->flt_region_vpd_sec; + if (IS_QLA29XX(ha)) { + uint16_t fw_options = 0, r_code; + uint32_t vpd_r[] = {FLT_REG_VPD_0, FLT_REG_VPD_1, + FLT_REG_VPD_2, FLT_REG_VPD_3}; + uint32_t nvram_r[] = {FLT_REG_NVRAM_0, FLT_REG_NVRAM_1, + FLT_REG_NVRAM_2, FLT_REG_NVRAM_3}; + void *buf; + + BUILD_BUG_ON((VPD_OFFSET + FA_NVRAM_VPD_SIZE + + sizeof(struct qla_flash_memo_block)) > + MAX_NVRAM_SIZE); + + ha->fiv = (struct qla_flash_memo_block *) + ((char *)ha->vpd + ha->vpd_size); + + buf = qla29xx_read_optrom_data(vha, FLT_REG_FMB_PRI, + fw_options, ha->fiv, 0, + sizeof(struct qla_flash_memo_block)); + if (!buf) { + ql_log(ql_log_info, vha, 0x01be, + "Unable to read Flash Image Version.\n"); + } else if (ha->fiv->signature != QLFC_FMB_SIG) { + ql_log(ql_log_warn, vha, 0x01bf, + "Invalid FMB signature %#x, expected %#x.\n", + le32_to_cpu(ha->fiv->signature), + le32_to_cpu(QLFC_FMB_SIG)); + ha->fiv = NULL; + } else { + ql_log(ql_log_info, vha, 0x0024, + "Flash Image Version %u.%02u.%02u\n", + ha->fiv->mbi_ver.major, + ha->fiv->mbi_ver.minor, + ha->fiv->mbi_ver.sub); + } + + if (ha->port_no >= ARRAY_SIZE(vpd_r)) { + ql_log(ql_log_warn, vha, 0x002e, + "Invalid port number %u, skipping VPD/NVRAM read.\n", + ha->port_no); + goto out_29xx; + } + + r_code = vpd_r[ha->port_no]; + buf = qla29xx_read_optrom_data(vha, r_code, fw_options, + ha->vpd, 0, ha->vpd_size); + if (!buf) + ql_log(ql_log_info, vha, 0x002d, + "Unable to read VPD info.\n"); + + r_code = nvram_r[ha->port_no]; + buf = qla29xx_read_optrom_data(vha, r_code, fw_options, + ha->nvram, 0, ha->nvram_size); + if (!buf) + ql_log(ql_log_info, vha, 0x0013, + "Unable to read nvram config info.\n"); + } else { + if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) + qla28xx_get_aux_images(vha, &active_regions); + + faddr = ha->flt_region_vpd; + if (IS_QLA28XX(ha)) { + if (active_regions.aux.vpd_nvram == + QLA27XX_SECONDARY_IMAGE) + faddr = ha->flt_region_vpd_sec; + ql_dbg(ql_dbg_init, vha, 0x0110, + "Loading %s nvram image.\n", + active_regions.aux.vpd_nvram == + QLA27XX_PRIMARY_IMAGE ? + "primary" : "secondary"); + } + ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2, + ha->vpd_size); + + /* Get NVRAM data into cache and calculate checksum. */ + faddr = ha->flt_region_nvram; + if (IS_QLA28XX(ha)) { + if (active_regions.aux.vpd_nvram == + QLA27XX_SECONDARY_IMAGE) + faddr = ha->flt_region_nvram_sec; + } ql_dbg(ql_dbg_init, vha, 0x0110, "Loading %s nvram image.\n", active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ? "primary" : "secondary"); + ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2, + ha->nvram_size); } - ha->isp_ops->read_optrom(vha, ha->vpd, faddr << 2, ha->vpd_size); - - /* Get NVRAM data into cache and calculate checksum. */ - faddr = ha->flt_region_nvram; - if (IS_QLA28XX(ha)) { - if (active_regions.aux.vpd_nvram == QLA27XX_SECONDARY_IMAGE) - faddr = ha->flt_region_nvram_sec; - } - ql_dbg(ql_dbg_init, vha, 0x0110, - "Loading %s nvram image.\n", - active_regions.aux.vpd_nvram == QLA27XX_PRIMARY_IMAGE ? - "primary" : "secondary"); - ha->isp_ops->read_optrom(vha, ha->nvram, faddr << 2, ha->nvram_size); +out_29xx: dptr = (__force __le32 *)nv; for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++, dptr++) chksum += le32_to_cpu(*dptr); @@ -9386,7 +10079,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) nv->login_timeout = cpu_to_le16(0); nv->firmware_options_1 = cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1); - nv->firmware_options_2 = cpu_to_le32(2 << 4); + if (IS_QLA29XX(ha)) + nv->firmware_options_2 = cpu_to_le32(BIT_4); + else + nv->firmware_options_2 = cpu_to_le32(BIT_5); nv->firmware_options_2 |= cpu_to_le32(BIT_12); nv->firmware_options_3 = cpu_to_le32(2 << 13); nv->host_p = cpu_to_le32(BIT_11|BIT_10); @@ -9468,9 +10164,13 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) icb->node_name[0] &= 0xF0; } - if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) { - if ((nv->enhanced_features & BIT_7) == 0) + /* SCM Enabled in NVRAM */ + if (IS_QLA29XX(ha) || IS_QLA28XX(ha) || IS_QLA27XX(ha)) { + if ((le16_to_cpu(nv->enhanced_features) & BIT_7) == 0) { + ql_log(ql_log_info, vha, 0x0062, + "USCM enabled in NVRAM\n"); ha->flags.scm_supported_a = 1; + } } /* Set host adapter parameters. */ @@ -9546,7 +10246,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) /* if not running MSI-X we need handshaking on interrupts */ if (!vha->hw->flags.msix_enabled && - (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))) + (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha))) icb->firmware_options_2 |= cpu_to_le32(BIT_22); /* Enable ZIO. */ @@ -10026,11 +10727,28 @@ int qla2xxx_delete_qpair(struct scsi_qla_host *vha, struct qla_qpair *qpair) { int ret = QLA_FUNCTION_FAILED; struct qla_hw_data *ha = qpair->hw; + struct rsp_que *rsp = qpair->rsp; qpair->delete_in_progress = 1; qla_free_buf_pool(qpair); + /* + * The response-queue interrupt schedules qla_do_work(), which + * dereferences qpair->rsp->req. Release the interrupt and flush + * any pending work before the request queue is freed below so a + * late completion cannot touch the freed request queue. The + * firmware queue-delete order (request then response) is kept. + */ + if (rsp && rsp->msix && rsp->msix->have_irq) { + free_irq(rsp->msix->vector, rsp->msix->handle); + rsp->msix->have_irq = 0; + rsp->msix->in_use = 0; + rsp->msix->handle = NULL; + } + if (rsp && ha->wq) + cancel_work_sync(&qpair->q_work); + ret = qla25xx_delete_req_que(vha, qpair->req); if (ret != QLA_SUCCESS) goto fail; diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 47fbd830ff16..90da4bad8e6e 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -54,6 +54,103 @@ qla2x00_debounce_register(volatile __le16 __iomem *addr) return (first); } +/** + * qla29xx_calc_iocbs() - Determine number of Command-Type and Continuation + * IOCBs to allocate for the 29xx extended (128-byte) IOCB ring. + * @vha: HA context + * @dsds: number of data segment descriptors needed + * @iocb_dsds: number of DSDs embedded in the first (command) IOCB. The + * remaining DSDs ride on Continuation Type 1 Ext IOCBs which hold + * NUM_CONT1_DSDS (10) each. + * + * Returns the total number of IOCB entries needed to carry @dsds. + */ +static inline uint16_t +qla29xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds, uint8_t iocb_dsds) +{ + uint16_t iocbs = 1; + + if (dsds > iocb_dsds) { + iocbs += (dsds - iocb_dsds) / NUM_CONT1_DSDS; + if ((dsds - iocb_dsds) % NUM_CONT1_DSDS) + iocbs++; + } + return iocbs; +} + +/** + * qla_req_entry_size() - request-ring entry stride. + * @ha: HBA pointer + * + * Returns sizeof(struct request_ext) (128) on 29xx, sizeof(request_t) (64) + * everywhere else. + */ +static inline size_t +qla_req_entry_size(struct qla_hw_data *ha) +{ + return IS_QLA29XX(ha) ? sizeof(struct request_ext) : sizeof(request_t); +} + +/** + * qla_rsp_entry_size() - response-ring entry stride. + * @ha: HBA pointer + * + * Counterpart of qla_req_entry_size() for the response ring. + */ +static inline size_t +qla_rsp_entry_size(struct qla_hw_data *ha) +{ + return IS_QLA29XX(ha) ? sizeof(struct response_ext) : sizeof(response_t); +} + +/** + * qla_sts_cont_data_size() - status-continuation IOCB data payload size. + * @ha: HBA pointer + * + * sts_cont_entry_t and struct sts_cont_entry_ext share the same header and + * data offset; only the trailing data[] size differs (60 vs 124 bytes). + * Returns that size so callers need not branch on the adapter type. + */ +static inline u32 +qla_sts_cont_data_size(struct qla_hw_data *ha) +{ + return IS_QLA29XX(ha) ? + sizeof_field(struct sts_cont_entry_ext, data) : + sizeof_field(sts_cont_entry_t, data); +} + +/** + * qla_logio_set_vp_index() - write vp_index into a login/logout IOCB. + * @ha: HBA pointer + * @pkt: logio IOCB (logio_entry_24xx or logio_entry_24xx_ext) + * @vp_idx: virtual port index + * + * vp_index widens from u8 (logio_entry_24xx) to __le16 + * (logio_entry_24xx_ext) on 29xx; write the field at the right width. + */ +static inline void +qla_logio_set_vp_index(struct qla_hw_data *ha, void *pkt, u16 vp_idx) +{ + if (IS_QLA29XX(ha)) + ((struct logio_entry_24xx_ext *)pkt)->vp_index = + cpu_to_le16(vp_idx); + else + ((struct logio_entry_24xx *)pkt)->vp_index = vp_idx; +} + +static inline u8 +qla_calc_queue_count(u16 msix_count) +{ + /* + * Request/response queues are bounded by the MSI-X vector count less + * the mailbox vector. These counters are u8, so a board advertising + * e.g. 257 vectors would truncate msix_count - 1 (256) to 0 and hand + * kzalloc_objs() a zero count (ZERO_SIZE_PTR), faulting on the first + * ha->req_q_map[0] store. Clamp into [1, QLA_MAX_QUEUES - 1]. + */ + return clamp_t(u16, msix_count - 1, 1, QLA_MAX_QUEUES - 1); +} + static inline void qla2x00_poll(struct rsp_que *rsp) { @@ -358,17 +455,132 @@ static inline void qla_83xx_start_iocbs(struct qla_qpair *qpair) { struct req_que *req = qpair->req; + struct qla_hw_data *ha = qpair->vha->hw; + /* + * 29xx uses the 128-byte-strided extended request ring; advance the + * matching ring_ext_ptr so the next IOCB allocator sees the correct + * slot. All other 83xx-family generations (83xx/27xx/28xx) keep the + * 64-byte ring_ptr. + */ req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; + if (IS_QLA29XX(ha)) { + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ext_ptr = req->ring_ext; + } else { + req->ring_ext_ptr++; + } + } else { + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else { + req->ring_ptr++; + } + } wrt_reg_dword(req->req_q_in, req->ring_index); } +/** + * qla_rsp_ring_advance() - Advance the response queue consumer pointer + * to the next IOCB slot, handling both 24xx (64-byte) and 29xx (128-byte) + * ring strides. + * + * On 29xx, ring_ext_ptr is the authoritative slot pointer (correct 128-byte + * pitch) and ring_ptr is kept in sync as a response_t view of the same slot + * so existing 24xx-shaped reads (rsp->ring_ptr->signature, + * (struct sts_entry_24xx *)rsp->ring_ptr, etc.) keep working unchanged; the + * first 64 bytes of struct response_ext are layout-compatible with response_t. + */ +static inline void +qla_rsp_ring_advance(struct rsp_que *rsp) +{ + rsp->ring_index++; + if (rsp->ring_index == rsp->length) { + rsp->ring_index = 0; + rsp->ring_ptr = rsp->ring; + if (rsp->hw && IS_QLA29XX(rsp->hw)) + rsp->ring_ext_ptr = rsp->ring_ext; + } else if (rsp->hw && IS_QLA29XX(rsp->hw)) { + rsp->ring_ext_ptr++; + rsp->ring_ptr = (response_t *)rsp->ring_ext_ptr; + } else { + rsp->ring_ptr++; + } +} + +/** + * qla_req_ring_slot() - return the current request-ring producer slot. + * @ha: HBA pointer + * @req: request queue + * + * On 29xx the firmware-visible ring uses 128-byte-strided entries + * referenced by ring_ext_ptr; on earlier adapters the 64-byte ring + * referenced by ring_ptr is used. The returned pointer is + * layout-compatible with request_t for common header writes; callers + * needing 29xx-specific fields should cast to struct request_ext. + */ +static inline void * +qla_req_ring_slot(struct qla_hw_data *ha, struct req_que *req) +{ + return IS_QLA29XX(ha) ? (void *)req->ring_ext_ptr + : (void *)req->ring_ptr; +} + +/** + * qla_req_ring_advance() - advance request-ring producer pointer. + * @ha: HBA pointer + * @req: request queue + * + * Mirrors qla_rsp_ring_advance(). Does NOT publish the new producer + * index to firmware; callers that need to do so should follow with a + * wrt_reg_dword or qla_83xx_start_iocbs(). + */ +static inline void +qla_req_ring_advance(struct qla_hw_data *ha, struct req_que *req) +{ + req->ring_index++; + if (IS_QLA29XX(ha)) { + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ext_ptr = req->ring_ext; + } else { + req->ring_ext_ptr++; + } + } else { + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else { + req->ring_ptr++; + } + } +} + +/** + * qla_rsp_ring_rewind_to() - Restore the response queue consumer pointer + * to a previously-observed slot (used when we need to defer processing an + * IOCB whose continuation entries have not yet arrived). + * @rsp: response queue + * @pkt: 64-byte view of the slot to rewind to (captured from a prior read + * of rsp->ring_ptr) + * @idx: matching ring_index value (also captured before the advance) + * + * On 29xx, pkt was originally obtained as (response_t *)rsp->ring_ext_ptr, + * so casting back to struct response_ext * recovers the 128-byte-stride slot + * pointer. + */ +static inline void +qla_rsp_ring_rewind_to(struct rsp_que *rsp, response_t *pkt, uint16_t idx) +{ + rsp->ring_ptr = pkt; + rsp->ring_index = idx; + if (rsp->hw && IS_QLA29XX(rsp->hw)) + rsp->ring_ext_ptr = (struct response_ext *)pkt; +} + static inline int qla2xxx_get_fc4_priority(struct scsi_qla_host *vha) { @@ -638,3 +850,76 @@ static inline bool val_is_in_range(u32 val, u32 start, u32 end) else return false; } + +/* + * Common fields extracted from FWI2 status IOCBs. Populated once so + * callers avoid duplicated IS_QLA29XX() branches for every field access. + */ +struct qla_sts_fwi2 { + u8 *data; + u32 data_sz; + u16 scsi_status; + u16 sts_qual; + u32 sense_len; + u32 rsp_data_len; + u32 rsp_residual_count; +}; + +static inline void +qla_sts_fwi2_extract(struct qla_hw_data *ha, void *pkt, + struct qla_sts_fwi2 *sf) +{ + if (IS_QLA29XX(ha)) { + struct sts_entry_24xx_ext *s = pkt; + + sf->scsi_status = le16_to_cpu(s->u2.scsi_status); + sf->sts_qual = le16_to_cpu(s->u2.retry_delay_timer); + sf->sense_len = le32_to_cpu(s->u2.sense_len); + sf->rsp_data_len = le32_to_cpu(s->u2.rsp_data_len_ndma); + sf->rsp_residual_count = le32_to_cpu(s->u2.rsp_residual_count); + sf->data = s->u2.data; + sf->data_sz = sizeof(s->u2.data); + host_to_fcp_swap(s->u2.data, sizeof(s->u2.data)); + host_to_fcp_swap(s->act_dif, sizeof(s->act_dif)); + host_to_fcp_swap(s->exp_dif, sizeof(s->exp_dif)); + } else { + struct sts_entry_24xx *s = pkt; + + sf->scsi_status = le16_to_cpu(s->scsi_status); + sf->sts_qual = le16_to_cpu(s->status_qualifier); + sf->sense_len = le32_to_cpu(s->sense_len); + sf->rsp_data_len = le32_to_cpu(s->rsp_data_len); + sf->rsp_residual_count = le32_to_cpu(s->rsp_residual_count); + sf->data = s->data; + sf->data_sz = sizeof(s->data); + host_to_fcp_swap(s->data, sizeof(s->data)); + } +} + +/* + * qla_els_set_vp_sof() - write the vp_index / sof_type pair into an ELS + * pass-through IOCB (els_entry_24xx{,_ext}). + * + * Both layouts have the same 16-bit slot at offset 14, but it is encoded + * differently: + * - 24xx: separate u8 vp_index + u8 sof_type with EST_SOFI3 (1 << 4) + * - 29xx: __le16 vp_index_sof with bits [8:0]=VP index, [15:12]=SOF type + * and ELS_EXT_EST_SOFI3 + * so this is the single point in the driver that knows about that + * encoding split. + */ +static inline void +qla_els_set_vp_sof(struct scsi_qla_host *vha, void *pkt, u16 vp_idx) +{ + if (IS_QLA29XX(vha->hw)) { + struct els_entry_24xx_ext *ext = pkt; + + ext->vp_index_sof = + qla_ext_build_vp_sof(vp_idx, ELS_EXT_EST_SOFI3); + } else { + struct els_entry_24xx *e = pkt; + + e->vp_index = vp_idx; + e->sof_type = EST_SOFI3; + } +} diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index dbe3cd4e274c..88bae1166a24 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -12,6 +12,19 @@ #include static int qla_start_scsi_type6(srb_t *sp); + +/* + * 29xx extended IOCB builders, implemented later in this file. Forward + * declared here because the unified 24xx/29xx fast paths dispatch to them + * via IS_QLA29XX() branches. + */ +static void qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt, + uint16_t tot_dsds, struct req_que *req); +static void qla29xx_build_scsi_type_6_iocbs(srb_t *sp, + struct cmd_type_6_ext *cmd_pkt, uint16_t tot_dsds); +static int qla29xx_build_scsi_crc_2_iocbs(srb_t *sp, + struct cmd_type_crc_2_ext *cmd_pkt, uint16_t tot_dsds, + uint16_t tot_prot_dsds, uint16_t fw_prot_opts); /** * qla2x00_get_cmd_direction() - Determine control_flag data direction. * @sp: SCSI command @@ -115,25 +128,21 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha) /** * qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB. * @vha: HA context + * @ha: pointer to adapter hardware data * @req: request queue * * Returns a pointer to the continuation type 1 IOCB packet. */ cont_a64_entry_t * -qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req) +qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct qla_hw_data *ha, + struct req_que *req) { cont_a64_entry_t *cont_pkt; /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else { - req->ring_ptr++; - } + qla_req_ring_advance(ha, req); - cont_pkt = (cont_a64_entry_t *)req->ring_ptr; + cont_pkt = (cont_a64_entry_t *)qla_req_ring_slot(ha, req); /* Load packet defaults. */ put_unaligned_le32(IS_QLAFX00(vha->hw) ? CONTINUE_A64_TYPE_FX00 : @@ -284,7 +293,8 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + vha->hw, vha->req); cur_dsd = cont_pkt->dsd; avail_dsds = ARRAY_SIZE(cont_pkt->dsd); } @@ -464,16 +474,16 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) if (IS_P3P_TYPE(ha)) { qla82xx_start_iocbs(vha); } else { - /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; + /* + * Adjust ring index. 29xx uses a 128-byte-strided ring, so + * advance the extended pointer; all other generations advance + * the 64-byte ring_ptr. + */ + qla_req_ring_advance(ha, req); /* Set chip new ring index. */ - if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (ha->mqenable || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { wrt_reg_dword(req->req_q_in, req->ring_index); } else if (IS_QLA83XX(ha)) { wrt_reg_dword(req->req_q_in, req->ring_index); @@ -509,36 +519,46 @@ static int __qla2x00_marker(struct scsi_qla_host *vha, struct qla_qpair *qpair, uint16_t loop_id, uint64_t lun, uint8_t type) { - mrk_entry_t *mrk; - struct mrk_entry_24xx *mrk24 = NULL; - struct req_que *req = qpair->req; struct qla_hw_data *ha = vha->hw; + struct mrk_entry_24xx *mrk24; + struct req_que *req = qpair->req; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - mrk = (mrk_entry_t *)__qla2x00_alloc_iocbs(qpair, NULL); - if (mrk == NULL) { + /* + * FWI2-capable adapters use the 24xx marker layout; 29xx uses the + * extended marker IOCB (128 bytes) whose first 64 bytes overlay + * mrk_entry_24xx through 'lun', so the common header is written + * through a struct mrk_entry_24xx * view and only vp_index (u8 vs + * __le16) needs a stride-aware branch. Pre-FWI2 adapters + * (ISP21xx/22xx/23xx) use the legacy mrk_entry_t layout with a + * 2-byte target ID and a 16-bit LUN. + */ + mrk24 = __qla2x00_alloc_iocbs(qpair, NULL); + if (!mrk24) { ql_log(ql_log_warn, base_vha, 0x3026, "Failed to allocate Marker IOCB.\n"); - - return (QLA_FUNCTION_FAILED); + return QLA_FUNCTION_FAILED; } - mrk24 = (struct mrk_entry_24xx *)mrk; - - mrk->entry_type = MARKER_TYPE; - mrk->modifier = type; + mrk24->entry_type = MARKER_TYPE; + mrk24->modifier = type; if (type != MK_SYNC_ALL) { if (IS_FWI2_CAPABLE(ha)) { mrk24->nport_handle = cpu_to_le16(loop_id); int_to_scsilun(lun, (struct scsi_lun *)&mrk24->lun); host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun)); - mrk24->vp_index = vha->vp_idx; + if (IS_QLA29XX(ha)) + ((struct mrk_entry_24xx_ext *)mrk24)->vp_index = + cpu_to_le16(vha->vp_idx); + else + mrk24->vp_index = vha->vp_idx; } else { + mrk_entry_t *mrk = (mrk_entry_t *)mrk24; + SET_TARGET_ID(ha, mrk->target, loop_id); mrk->lun = cpu_to_le16((uint16_t)lun); } } - if (IS_FWI2_CAPABLE(ha)) mrk24->handle = QLA_SKIP_HANDLE; @@ -747,7 +767,8 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + vha->hw, req); cur_dsd = cont_pkt->dsd; avail_dsds = ARRAY_SIZE(cont_pkt->dsd); } @@ -1620,46 +1641,84 @@ qla24xx_start_scsi(srb_t *sp) cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; - cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; - cmd_pkt->handle = make_handle(req->id, handle); + if (IS_QLA29XX(ha)) { + /* + * cmd_type_7_ext diverges from cmd_type_7 past offset 48 + * (port_id[3]+vp_index vs ctrl_flags_2+__le16 vp_index), + * so the packet-field writes can't be shared via a common + * pointer type. + */ + struct cmd_type_7_ext *cmd_pkt_ext = + (struct cmd_type_7_ext *)req->ring_ext_ptr; - /* Zero out remaining portion of packet. */ - /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ - clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); - cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + cmd_pkt_ext->handle = make_handle(req->id, handle); - /* Set NPORT-ID and LUN number*/ - cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->vha->vp_idx; + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt_ext + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8); + cmd_pkt_ext->dseg_count = cpu_to_le16(tot_dsds); - int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); - host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + /* Set NPORT-ID and LUN number. */ + cmd_pkt_ext->nport_handle = + cpu_to_le16(sp->fcport->loop_id); + cmd_pkt_ext->vp_index = + cpu_to_le16(sp->fcport->vha->vp_idx); - cmd_pkt->task = TSK_SIMPLE; + int_to_scsilun(cmd->device->lun, &cmd_pkt_ext->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt_ext->lun, sizeof(cmd_pkt_ext->lun)); - /* Load SCSI command packet. */ - memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); - host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); + cmd_pkt_ext->task = TSK_SIMPLE; - cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + /* Load SCSI command packet. */ + memcpy(cmd_pkt_ext->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt_ext->fcp_cdb, sizeof(cmd_pkt_ext->fcp_cdb)); - /* Build IOCB segments */ - qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req); + cmd_pkt_ext->byte_count = + cpu_to_le32((uint32_t)scsi_bufflen(cmd)); - /* Set total data segment count. */ - cmd_pkt->entry_count = (uint8_t)req_cnt; + /* Build IOCB segments */ + qla29xx_build_scsi_iocbs(sp, cmd_pkt_ext, tot_dsds, req); + + /* Set total data segment count. */ + cmd_pkt_ext->entry_count = (uint8_t)req_cnt; + } else { + cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; + cmd_pkt->handle = make_handle(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number*/ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->vha->vp_idx; + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + + cmd_pkt->task = TSK_SIMPLE; + + /* Load SCSI command packet. */ + memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); + + cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + /* Build IOCB segments */ + qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req); + + /* Set total data segment count. */ + cmd_pkt->entry_count = (uint8_t)req_cnt; + } wmb(); /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; + qla_req_ring_advance(ha, req); sp->qpair->cmd_cnt++; sp->flags |= SRB_DMA_VALID; @@ -1798,7 +1857,20 @@ qla24xx_dif_start_scsi(srb_t *sp) sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; sp->iores.exch_cnt = 1; - sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + /* + * Unlike cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS + * inline DSDs), cmd_type_crc_2_ext carries only a single inline + * data_dsd; the remaining DSDs live in the separate CRC-context DMA. + * Size the firmware IOCB-pool reservation against the CRC_2 ext inline + * capacity (NUM_CRC2_EXT_INLINE_DSDS) so it mirrors the 24xx path + * (qla24xx_calc_iocbs assumes 1 inline DSD for cmd_type_crc_2) and + * doesn't under-count continuations expected by firmware. + */ + if (IS_QLA29XX(ha)) + sp->iores.iocb_cnt = qla29xx_calc_iocbs(vha, tot_dsds, + NUM_CRC2_EXT_INLINE_DSDS); + else + sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds); if (qla_get_fw_resources(sp->qpair, &sp->iores)) goto queuing_error; @@ -1829,17 +1901,24 @@ qla24xx_dif_start_scsi(srb_t *sp) req->cnt -= req_cnt; /* Fill-in common area */ - cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr; + cmd_pkt = (struct cmd_type_crc_2 *)qla_req_ring_slot(ha, req); cmd_pkt->handle = make_handle(req->id, handle); clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + memset(clr_ptr, 0, qla_req_entry_size(ha) - 8); /* Set NPORT-ID and LUN number*/ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + if (!IS_QLA29XX(ha)) { + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + } + /* + * vp_index: 24xx uses a uint8_t field at offset 51 (in port_id + * group); 29xx replaces that region with control_flags_2/vp_index as + * __le16s starting at offset 48. + */ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); @@ -1848,10 +1927,22 @@ qla24xx_dif_start_scsi(srb_t *sp) cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); /* Build IOCB segments and adjust for data protection segments */ - if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *) - req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) != - QLA_SUCCESS) - goto queuing_error; + if (IS_QLA29XX(ha)) { + struct cmd_type_crc_2_ext *cmd_pkt_ext = + (struct cmd_type_crc_2_ext *)cmd_pkt; + + cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx); + + if (qla29xx_build_scsi_crc_2_iocbs(sp, cmd_pkt_ext, tot_dsds, + tot_prot_dsds, fw_prot_opts) != + QLA_SUCCESS) + goto queuing_error; + } else { + if (qla24xx_build_scsi_crc_2_iocbs(sp, cmd_pkt, tot_dsds, + tot_prot_dsds, fw_prot_opts) != + QLA_SUCCESS) + goto queuing_error; + } cmd_pkt->entry_count = (uint8_t)req_cnt; /* Specify response queue number where completion should happen */ @@ -1860,12 +1951,7 @@ qla24xx_dif_start_scsi(srb_t *sp) wmb(); /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; + qla_req_ring_advance(ha, req); sp->qpair->cmd_cnt++; /* Set chip new ring index. */ @@ -1954,7 +2040,17 @@ qla2xxx_start_scsi_mq(srb_t *sp) nseg = 0; tot_dsds = nseg; - req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + /* + * 29xx posts into the 128-byte extended IOCB ring (cmd_type_7_ext, + * NUM_CMD67_DSDS inline DSDs) via ring_ext_ptr; all other FWI2-capable + * generations use the 64-byte cmd_type_7 / ring_ptr path. The common + * skeleton (marker/handle/sg-map/resource-check/ring advance) is + * shared; only the cmd_pkt field-writes and ring stride differ. + */ + if (IS_QLA29XX(ha)) + req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_CMD67_DSDS); + else + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; sp->iores.exch_cnt = 1; @@ -1987,46 +2083,84 @@ qla2xxx_start_scsi_mq(srb_t *sp) cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; - cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; - cmd_pkt->handle = make_handle(req->id, handle); + if (IS_QLA29XX(ha)) { + /* + * cmd_type_7_ext diverges from cmd_type_7 past offset 48 + * (port_id[3]+vp_index vs ctrl_flags_2+__le16 vp_index), + * so the packet-field writes can't be shared via a common + * pointer type. + */ + struct cmd_type_7_ext *cmd_pkt_ext = + (struct cmd_type_7_ext *)req->ring_ext_ptr; - /* Zero out remaining portion of packet. */ - /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ - clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); - cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + cmd_pkt_ext->handle = make_handle(req->id, handle); - /* Set NPORT-ID and LUN number*/ - cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt_ext + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8); + cmd_pkt_ext->dseg_count = cpu_to_le16(tot_dsds); - int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); - host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); + /* Set NPORT-ID and LUN number. */ + cmd_pkt_ext->nport_handle = + cpu_to_le16(sp->fcport->loop_id); + cmd_pkt_ext->vp_index = + cpu_to_le16(sp->fcport->vha->vp_idx); - cmd_pkt->task = TSK_SIMPLE; + int_to_scsilun(cmd->device->lun, &cmd_pkt_ext->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt_ext->lun, + sizeof(cmd_pkt_ext->lun)); - /* Load SCSI command packet. */ - memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); - host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); + cmd_pkt_ext->task = TSK_SIMPLE; - cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + memcpy(cmd_pkt_ext->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt_ext->fcp_cdb, + sizeof(cmd_pkt_ext->fcp_cdb)); - /* Build IOCB segments */ - qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req); + cmd_pkt_ext->byte_count = + cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + qla29xx_build_scsi_iocbs(sp, cmd_pkt_ext, tot_dsds, req); + + cmd_pkt_ext->entry_count = (uint8_t)req_cnt; + } else { + cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; + cmd_pkt->handle = make_handle(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); + + /* Set NPORT-ID and LUN number. */ + cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + + int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); + host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, + sizeof(cmd_pkt->lun)); + + cmd_pkt->task = TSK_SIMPLE; + + memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); + host_to_fcp_swap(cmd_pkt->fcp_cdb, + sizeof(cmd_pkt->fcp_cdb)); + + cmd_pkt->byte_count = + cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, req); + + cmd_pkt->entry_count = (uint8_t)req_cnt; + } - /* Set total data segment count. */ - cmd_pkt->entry_count = (uint8_t)req_cnt; wmb(); /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; + qla_req_ring_advance(ha, req); sp->qpair->cmd_cnt++; sp->flags |= SRB_DMA_VALID; @@ -2034,8 +2168,12 @@ qla2xxx_start_scsi_mq(srb_t *sp) /* Set chip new ring index. */ wrt_reg_dword(req->req_q_in, req->ring_index); - /* Manage unprocessed RIO/ZIO commands in response queue. */ - if (vha->flags.process_response_queue && + /* + * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips + * this optimization until rsp_que is made 128-byte-stride aware; + * completions still arrive via the regular ISR path. + */ + if (!IS_QLA29XX(ha) && vha->flags.process_response_queue && rsp->ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(vha, rsp); @@ -2180,7 +2318,20 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp) sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; sp->iores.exch_cnt = 1; - sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + /* + * Unlike cmd_type_6_ext / cmd_type_7_ext (which carry NUM_CMD67_DSDS + * inline DSDs), cmd_type_crc_2_ext carries only a single inline + * data_dsd; the remaining DSDs live in the separate CRC-context DMA. + * Size the firmware IOCB-pool reservation against the CRC_2 ext inline + * capacity (NUM_CRC2_EXT_INLINE_DSDS) so it mirrors the 24xx path + * (qla24xx_calc_iocbs assumes 1 inline DSD for cmd_type_crc_2) and + * doesn't under-count continuations expected by firmware. + */ + if (IS_QLA29XX(ha)) + sp->iores.iocb_cnt = qla29xx_calc_iocbs(vha, tot_dsds, + NUM_CRC2_EXT_INLINE_DSDS); + else + sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds); if (qla_get_fw_resources(sp->qpair, &sp->iores)) goto queuing_error; @@ -2211,18 +2362,35 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp) cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; - /* Fill-in common area */ - cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr; + /* + * cmd_type_crc_2 and cmd_type_crc_2_ext share an identical header + * layout through byte_count (offset 44); the trailing port_id / + * vp_index / CRC-context fields diverge. The common header writes + * (handle, memset, nport_handle, lun, dseg_count, entry_count, + * timeout) are performed via the 24xx-typed pointer in both paths; + * the divergent tail and builder use hardware-specific handling. + */ + if (IS_QLA29XX(ha)) + cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ext_ptr; + else + cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr; cmd_pkt->handle = make_handle(req->id, handle); clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + memset(clr_ptr, 0, qla_req_entry_size(ha) - 8); /* Set NPORT-ID and LUN number*/ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + if (!IS_QLA29XX(ha)) { + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + } + /* + * vp_index: 24xx uses a uint8_t field at offset 51 (in port_id + * group); 29xx replaces that region with control_flags_2/vp_index as + * __le16s starting at offset 48. + */ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); @@ -2231,29 +2399,41 @@ qla2xxx_dif_start_scsi_mq(srb_t *sp) cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); /* Build IOCB segments and adjust for data protection segments */ - if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *) - req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) != - QLA_SUCCESS) - goto queuing_error; + if (IS_QLA29XX(ha)) { + struct cmd_type_crc_2_ext *cmd_pkt_ext = + (struct cmd_type_crc_2_ext *)cmd_pkt; + + cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx); + + if (qla29xx_build_scsi_crc_2_iocbs(sp, cmd_pkt_ext, tot_dsds, + tot_prot_dsds, + fw_prot_opts) != + QLA_SUCCESS) + goto queuing_error; + } else { + if (qla24xx_build_scsi_crc_2_iocbs(sp, cmd_pkt, tot_dsds, + tot_prot_dsds, + fw_prot_opts) != + QLA_SUCCESS) + goto queuing_error; + } cmd_pkt->entry_count = (uint8_t)req_cnt; cmd_pkt->timeout = cpu_to_le16(0); wmb(); - /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else - req->ring_ptr++; + /* Adjust ring index. 29xx uses the 128-byte extended ring pointer. */ + qla_req_ring_advance(ha, req); sp->qpair->cmd_cnt++; /* Set chip new ring index. */ wrt_reg_dword(req->req_q_in, req->ring_index); - /* Manage unprocessed RIO/ZIO commands in response queue. */ - if (vha->flags.process_response_queue && + /* + * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips + * this optimization until rsp_que is made 128-byte-stride aware. + */ + if (!IS_QLA29XX(ha) && vha->flags.process_response_queue && rsp->ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(vha, rsp); @@ -2286,10 +2466,9 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp) struct req_que *req = qpair->req; device_reg_t *reg = ISP_QUE_REG(ha, req->id); uint32_t handle; - request_t *pkt; uint16_t cnt, req_cnt; + request_t *pkt = NULL; - pkt = NULL; req_cnt = 1; handle = 0; @@ -2303,7 +2482,7 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp) if (qpair->use_shadow_reg) cnt = *req->out_ptr; else if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) cnt = rd_reg_dword(®->isp25mq.req_q_out); else if (IS_P3P_TYPE(ha)) cnt = rd_reg_dword(reg->isp82.req_q_out); @@ -2343,14 +2522,21 @@ __qla2x00_alloc_iocbs(struct qla_qpair *qpair, srb_t *sp) sp->handle = handle; } - /* Prep packet */ + /* + * Prep packet. 29xx posts into a 128-byte-strided ring via + * ring_ext_ptr; the first 8 bytes of struct request_ext overlay the + * common request_t header, so entry_count/handle writes are + * layout-compatible once we return the pkt as request_t *. + */ req->cnt -= req_cnt; - pkt = req->ring_ptr; - memset(pkt, 0, REQUEST_ENTRY_SIZE); + pkt = qla_req_ring_slot(ha, req); if (IS_QLAFX00(ha)) { + memset_io((void __iomem __force *)pkt, 0, + qla_req_entry_size(ha)); wrt_reg_byte((u8 __force __iomem *)&pkt->entry_count, req_cnt); wrt_reg_dword((__le32 __force __iomem *)&pkt->handle, handle); } else { + memset(pkt, 0, qla_req_entry_size(ha)); pkt->entry_count = req_cnt; pkt->handle = handle; } @@ -2380,8 +2566,9 @@ qla2x00_alloc_iocbs(struct scsi_qla_host *vha, srb_t *sp) } static void -qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio) +qla24xx_prli_iocb(srb_t *sp, void *pkt) { + struct logio_entry_24xx *logio = pkt; struct srb_iocb *lio = &sp->u.iocb_cmd; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; @@ -2408,12 +2595,13 @@ qla24xx_prli_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[2] = sp->fcport->d_id.b.domain; - logio->vp_index = sp->vha->vp_idx; + qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx); } static void -qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) +qla24xx_login_iocb(srb_t *sp, void *pkt) { + struct logio_entry_24xx *logio = pkt; struct srb_iocb *lio = &sp->u.iocb_cmd; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; @@ -2438,7 +2626,7 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[2] = sp->fcport->d_id.b.domain; - logio->vp_index = sp->vha->vp_idx; + qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx); } static void @@ -2466,9 +2654,11 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) } static void -qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) +qla24xx_logout_iocb(srb_t *sp, void *pkt) { + struct logio_entry_24xx *logio = pkt; u16 control_flags = LCF_COMMAND_LOGO; + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; if (sp->fcport->explicit_logout) { @@ -2485,7 +2675,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[2] = sp->fcport->d_id.b.domain; - logio->vp_index = sp->vha->vp_idx; + qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx); } static void @@ -2507,12 +2697,14 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx) } static void -qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio) +qla24xx_adisc_iocb(srb_t *sp, void *pkt) { + struct logio_entry_24xx *logio = pkt; + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC); logio->nport_handle = cpu_to_le16(sp->fcport->loop_id); - logio->vp_index = sp->vha->vp_idx; + qla_logio_set_vp_index(sp->vha->hw, pkt, sp->vha->vp_idx); } static void @@ -2537,7 +2729,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx) } static void -qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) +qla24xx_tm_iocb(srb_t *sp, void *pkt) { uint32_t flags; uint64_t lun; @@ -2546,26 +2738,39 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) struct qla_hw_data *ha = vha->hw; struct srb_iocb *iocb = &sp->u.iocb_cmd; struct req_que *req = sp->qpair->req; + struct tsk_mgmt_entry *tsk; flags = iocb->u.tmf.flags; lun = iocb->u.tmf.lun; + /* + * tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags + * (offsets 0-27 are byte-identical), so the common header writes + * go through one struct tsk_mgmt_entry * view. The ext layout + * has no port_id and uses a wider __le16 vp_index at a different + * offset, so port_id / vp_index assignments diverge per stride. + */ + tsk = pkt; tsk->entry_type = TSK_MGMT_IOCB_TYPE; tsk->entry_count = 1; tsk->handle = make_handle(req->id, tsk->handle); tsk->nport_handle = cpu_to_le16(fcport->loop_id); tsk->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); tsk->control_flags = cpu_to_le32(flags); - tsk->port_id[0] = fcport->d_id.b.al_pa; - tsk->port_id[1] = fcport->d_id.b.area; - tsk->port_id[2] = fcport->d_id.b.domain; - tsk->vp_index = fcport->vha->vp_idx; + if (IS_QLA29XX(ha)) { + ((struct tsk_mgmt_entry_ext *)pkt)->vp_index = + cpu_to_le16(fcport->vha->vp_idx); + } else { + tsk->port_id[0] = fcport->d_id.b.al_pa; + tsk->port_id[1] = fcport->d_id.b.area; + tsk->port_id[2] = fcport->d_id.b.domain; + tsk->vp_index = fcport->vha->vp_idx; + } - if (flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET| - TCF_CLEAR_TASK_SET|TCF_CLEAR_ACA)) { + if (flags & (TCF_LUN_RESET | TCF_ABORT_TASK_SET | + TCF_CLEAR_TASK_SET | TCF_CLEAR_ACA)) { int_to_scsilun(lun, &tsk->lun); - host_to_fcp_swap((uint8_t *)&tsk->lun, - sizeof(tsk->lun)); + host_to_fcp_swap((uint8_t *)&tsk->lun, sizeof(tsk->lun)); } } @@ -2791,8 +2996,9 @@ qla24xx_els_dcmd_iocb(scsi_qla_host_t *vha, int els_opcode, } static void -qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) +qla24xx_els_logo_iocb(srb_t *sp, void *pkt) { + struct els_entry_24xx *els_iocb = pkt; scsi_qla_host_t *vha = sp->vha; struct srb_iocb *elsio = &sp->u.iocb_cmd; @@ -2803,11 +3009,11 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->handle = sp->handle; els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); els_iocb->tx_dsd_count = cpu_to_le16(1); - els_iocb->vp_index = vha->vp_idx; - els_iocb->sof_type = EST_SOFI3; els_iocb->rx_dsd_count = 0; els_iocb->opcode = elsio->u.els_logo.els_cmd; + qla_els_set_vp_sof(vha, pkt, vha->vp_idx); + els_iocb->d_id[0] = sp->fcport->d_id.b.al_pa; els_iocb->d_id[1] = sp->fcport->d_id.b.area; els_iocb->d_id[2] = sp->fcport->d_id.b.domain; @@ -3138,9 +3344,10 @@ qla24xx_els_dcmd2_iocb(scsi_qla_host_t *vha, int els_opcode, /* it is assume qpair lock is held */ void qla_els_pt_iocb(struct scsi_qla_host *vha, - struct els_entry_24xx *els_iocb, - struct qla_els_pt_arg *a) + void *pkt, struct qla_els_pt_arg *a) { + struct els_entry_24xx *els_iocb = pkt; + els_iocb->entry_type = ELS_IOCB_TYPE; els_iocb->entry_count = 1; els_iocb->sys_define = 0; @@ -3149,11 +3356,11 @@ void qla_els_pt_iocb(struct scsi_qla_host *vha, els_iocb->nport_handle = a->nport_handle; els_iocb->rx_xchg_address = a->rx_xchg_address; els_iocb->tx_dsd_count = cpu_to_le16(1); - els_iocb->vp_index = a->vp_idx; - els_iocb->sof_type = EST_SOFI3; els_iocb->rx_dsd_count = cpu_to_le16(0); els_iocb->opcode = a->els_opcode; + qla_els_set_vp_sof(vha, pkt, a->vp_idx); + els_iocb->d_id[0] = a->did.b.al_pa; els_iocb->d_id[1] = a->did.b.area; els_iocb->d_id[2] = a->did.b.domain; @@ -3174,8 +3381,9 @@ void qla_els_pt_iocb(struct scsi_qla_host *vha, } static void -qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) +qla24xx_els_iocb(srb_t *sp, void *pkt) { + struct els_entry_24xx *els_iocb = pkt; struct bsg_job *bsg_job = sp->u.bsg_job; struct fc_bsg_request *bsg_request = bsg_job->request; @@ -3186,8 +3394,8 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) els_iocb->handle = sp->handle; els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); els_iocb->tx_dsd_count = cpu_to_le16(bsg_job->request_payload.sg_cnt); - els_iocb->vp_index = sp->vha->vp_idx; - els_iocb->sof_type = EST_SOFI3; + + qla_els_set_vp_sof(sp->vha, pkt, sp->vha->vp_idx); els_iocb->rx_dsd_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt); els_iocb->opcode = @@ -3269,7 +3477,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) * Type 1 IOCB. */ cont_pkt = qla2x00_prep_cont_type1_iocb(vha, - vha->hw->req_q_map[0]); + ha, ha->req_q_map[0]); cur_dsd = cont_pkt->dsd; avail_dsds = 5; entry_count++; @@ -3284,7 +3492,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) } static void -qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) +qla24xx_ct_iocb(srb_t *sp, void *pkt) { uint16_t avail_dsds; struct dsd64 *cur_dsd; @@ -3295,41 +3503,67 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) struct qla_hw_data *ha = vha->hw; struct bsg_job *bsg_job = sp->u.bsg_job; int entry_count = 1; - cont_a64_entry_t *cont_pkt = NULL; - - ct_iocb->entry_type = CT_IOCB_TYPE; - ct_iocb->entry_status = 0; - ct_iocb->sys_define = 0; - ct_iocb->handle = sp->handle; - - ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); - ct_iocb->vp_index = sp->vha->vp_idx; - ct_iocb->comp_status = cpu_to_le16(0); cmd_dsds = bsg_job->request_payload.sg_cnt; rsp_dsds = bsg_job->reply_payload.sg_cnt; - ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds); - ct_iocb->timeout = 0; - ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds); - ct_iocb->cmd_byte_count = - cpu_to_le32(bsg_job->request_payload.payload_len); + if (IS_QLA29XX(ha)) { + struct ct_entry_24xx_ext *ct_iocb = pkt; + + ct_iocb->entry_type = CT_IOCB_TYPE; + ct_iocb->entry_status = 0; + ct_iocb->sys_define = 0; + ct_iocb->handle = sp->handle; + ct_iocb->nport_handle = + cpu_to_le16(sp->fcport->loop_id); + ct_iocb->vp_index = cpu_to_le16(sp->vha->vp_idx); + ct_iocb->comp_status = cpu_to_le16(0); + ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds); + ct_iocb->timeout = 0; + ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds); + ct_iocb->cmd_byte_count = + cpu_to_le32(bsg_job->request_payload.payload_len); + avail_dsds = NUM_CT_DSDS; + cur_dsd = ct_iocb->dsd; + } else { + struct ct_entry_24xx *ct_iocb = pkt; + + ct_iocb->entry_type = CT_IOCB_TYPE; + ct_iocb->entry_status = 0; + ct_iocb->sys_define = 0; + ct_iocb->handle = sp->handle; + ct_iocb->nport_handle = + cpu_to_le16(sp->fcport->loop_id); + ct_iocb->vp_index = sp->vha->vp_idx; + ct_iocb->comp_status = cpu_to_le16(0); + ct_iocb->cmd_dsd_count = cpu_to_le16(cmd_dsds); + ct_iocb->timeout = 0; + ct_iocb->rsp_dsd_count = cpu_to_le16(rsp_dsds); + ct_iocb->cmd_byte_count = + cpu_to_le32(bsg_job->request_payload.payload_len); + avail_dsds = 2; + cur_dsd = ct_iocb->dsd; + } - avail_dsds = 2; - cur_dsd = ct_iocb->dsd; index = 0; for_each_sg(bsg_job->request_payload.sg_list, sg, cmd_dsds, index) { - /* Allocate additional continuation packets? */ if (avail_dsds == 0) { - /* - * Five DSDs are available in the Cont. - * Type 1 IOCB. - */ - cont_pkt = qla2x00_prep_cont_type1_iocb( - vha, ha->req_q_map[0]); - cur_dsd = cont_pkt->dsd; - avail_dsds = 5; + if (IS_QLA29XX(ha)) { + struct cont_a64_entry_ext *cont_pkt; + + cont_pkt = qla2900_prep_cont_type1_iocb(vha, + ha->req_q_map[0]); + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); + } else { + cont_a64_entry_t *cont_pkt; + + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + ha, ha->req_q_map[0]); + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); + } entry_count++; } @@ -3340,23 +3574,33 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) index = 0; for_each_sg(bsg_job->reply_payload.sg_list, sg, rsp_dsds, index) { - /* Allocate additional continuation packets? */ if (avail_dsds == 0) { - /* - * Five DSDs are available in the Cont. - * Type 1 IOCB. - */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha, - ha->req_q_map[0]); - cur_dsd = cont_pkt->dsd; - avail_dsds = 5; + if (IS_QLA29XX(ha)) { + struct cont_a64_entry_ext *cont_pkt; + + cont_pkt = qla2900_prep_cont_type1_iocb(vha, + ha->req_q_map[0]); + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); + } else { + cont_a64_entry_t *cont_pkt; + + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + ha, ha->req_q_map[0]); + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); + } entry_count++; } append_dsd64(&cur_dsd, sg); avail_dsds--; } - ct_iocb->entry_count = entry_count; + + if (IS_QLA29XX(ha)) + ((struct ct_entry_24xx_ext *)pkt)->entry_count = entry_count; + else + ((struct ct_entry_24xx *)pkt)->entry_count = entry_count; } /* @@ -3684,32 +3928,50 @@ qla82xx_start_scsi(srb_t *sp) } static void -qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) +qla24xx_abort_iocb(srb_t *sp, void *pkt) { struct srb_iocb *aio = &sp->u.iocb_cmd; scsi_qla_host_t *vha = sp->vha; + struct qla_hw_data *ha = vha->hw; struct req_que *req = sp->qpair->req; srb_t *orig_sp = sp->cmd_sp; + struct abort_entry_24xx *abt = pkt; - memset(abt_iocb, 0, sizeof(struct abort_entry_24xx)); - abt_iocb->entry_type = ABORT_IOCB_TYPE; - abt_iocb->entry_count = 1; - abt_iocb->handle = make_handle(req->id, sp->handle); - if (sp->fcport) { - abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); - abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; - abt_iocb->port_id[1] = sp->fcport->d_id.b.area; - abt_iocb->port_id[2] = sp->fcport->d_id.b.domain; - } - abt_iocb->handle_to_abort = - make_handle(le16_to_cpu(aio->u.abt.req_que_no), - aio->u.abt.cmd_hndl); - abt_iocb->vp_index = vha->vp_idx; - abt_iocb->req_que_no = aio->u.abt.req_que_no; + /* + * abort_entry_24xx_ext overlays abort_entry_24xx through + * req_que_no (offsets 0-17). After that the layouts diverge: + * the 24xx variant has 30 bytes of reserved_1 followed by + * port_id[3] and a u8 vp_index at offsets 48-51, while the + * ext variant places a __le16 vp_index at offset 18 and has + * no port_id field. Common-header writes go through one + * struct abort_entry_24xx * view; only port_id / vp_index + * branch on stride. + */ + memset(pkt, 0, qla_req_entry_size(ha)); + abt->entry_type = ABORT_IOCB_TYPE; + abt->entry_count = 1; + abt->handle = make_handle(req->id, sp->handle); + if (sp->fcport) + abt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + abt->handle_to_abort = + make_handle(le16_to_cpu(aio->u.abt.req_que_no), + aio->u.abt.cmd_hndl); + abt->req_que_no = aio->u.abt.req_que_no; + if (IS_QLA29XX(ha)) { + ((struct abort_entry_24xx_ext *)pkt)->vp_index = + cpu_to_le16(vha->vp_idx); + } else { + if (sp->fcport) { + abt->port_id[0] = sp->fcport->d_id.b.al_pa; + abt->port_id[1] = sp->fcport->d_id.b.area; + abt->port_id[2] = sp->fcport->d_id.b.domain; + } + abt->vp_index = vha->vp_idx; + } /* need to pass original sp */ if (orig_sp) - qla_nvme_abort_set_option(abt_iocb, orig_sp); + qla_nvme_abort_set_option(pkt, orig_sp); /* Send the command to the firmware */ wmb(); @@ -3729,11 +3991,15 @@ qla2x00_mb_iocb(srb_t *sp, struct mbx_24xx_entry *mbx) } static void -qla2x00_ctpthru_cmd_iocb(srb_t *sp, struct ct_entry_24xx *ct_pkt) +qla2x00_ctpthru_cmd_iocb(srb_t *sp, void *pkt) { - sp->u.iocb_cmd.u.ctarg.iocb = ct_pkt; + sp->u.iocb_cmd.u.ctarg.iocb = pkt; qla24xx_prep_ms_iocb(sp->vha, &sp->u.iocb_cmd.u.ctarg); - ct_pkt->handle = sp->handle; + + if (IS_QLA29XX(sp->vha->hw)) + ((struct ct_entry_24xx_ext *)pkt)->handle = sp->handle; + else + ((struct ct_entry_24xx *)pkt)->handle = sp->handle; } static void qla2x00_send_notify_ack_iocb(srb_t *sp, @@ -3775,63 +4041,107 @@ static void qla2x00_send_notify_ack_iocb(srb_t *sp, } /* - * Build NVME LS request + * Build NVME LS request. + * + * pt_ls4_request_ext overlays pt_ls4_request through exchange_address + * (offsets 0-27 are byte-identical), so the common-header writes go + * through one struct pt_ls4_request * view. The ext layout has a wider + * __le16 vp_index and places rx_/tx_byte_count and dsd[] at different + * offsets, so those assignments diverge per stride. */ static void -qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt) +qla_nvme_ls(srb_t *sp, void *cmd_pkt) { - struct srb_iocb *nvme; + struct srb_iocb *nvme = &sp->u.iocb_cmd; + struct qla_hw_data *ha = sp->vha->hw; + struct pt_ls4_request *pkt = cmd_pkt; - nvme = &sp->u.iocb_cmd; - cmd_pkt->entry_type = PT_LS4_REQUEST; - cmd_pkt->entry_count = 1; - cmd_pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec); - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + pkt->entry_type = PT_LS4_REQUEST; + pkt->entry_count = 1; + pkt->timeout = cpu_to_le16(nvme->u.nvme.timeout_sec); + pkt->tx_dseg_count = cpu_to_le16(1); if (sp->unsol_rsp) { - cmd_pkt->control_flags = - cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT); - cmd_pkt->nport_handle = nvme->u.nvme.nport_handle; - cmd_pkt->exchange_address = nvme->u.nvme.exchange_address; + pkt->control_flags = + cpu_to_le16(CF_LS4_RESPONDER << CF_LS4_SHIFT); + pkt->nport_handle = nvme->u.nvme.nport_handle; + pkt->exchange_address = nvme->u.nvme.exchange_address; } else { - cmd_pkt->control_flags = - cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT); - cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->rx_dseg_count = cpu_to_le16(1); - cmd_pkt->rx_byte_count = nvme->u.nvme.rsp_len; - cmd_pkt->dsd[1].length = nvme->u.nvme.rsp_len; - put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address); + pkt->control_flags = + cpu_to_le16(CF_LS4_ORIGINATOR << CF_LS4_SHIFT); + pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); + pkt->rx_dseg_count = cpu_to_le16(1); } - cmd_pkt->tx_dseg_count = cpu_to_le16(1); - cmd_pkt->tx_byte_count = nvme->u.nvme.cmd_len; - cmd_pkt->dsd[0].length = nvme->u.nvme.cmd_len; - put_unaligned_le64(nvme->u.nvme.cmd_dma, &cmd_pkt->dsd[0].address); + if (IS_QLA29XX(ha)) { + struct pt_ls4_request_ext *ext = cmd_pkt; + + ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx); + ext->tx_byte_count = nvme->u.nvme.cmd_len; + ext->dsd[0].length = nvme->u.nvme.cmd_len; + put_unaligned_le64(nvme->u.nvme.cmd_dma, + &ext->dsd[0].address); + if (!sp->unsol_rsp) { + ext->rx_byte_count = nvme->u.nvme.rsp_len; + ext->dsd[1].length = nvme->u.nvme.rsp_len; + put_unaligned_le64(nvme->u.nvme.rsp_dma, + &ext->dsd[1].address); + } + } else { + pkt->vp_index = sp->fcport->vha->vp_idx; + pkt->tx_byte_count = nvme->u.nvme.cmd_len; + pkt->dsd[0].length = nvme->u.nvme.cmd_len; + put_unaligned_le64(nvme->u.nvme.cmd_dma, + &pkt->dsd[0].address); + if (!sp->unsol_rsp) { + pkt->rx_byte_count = nvme->u.nvme.rsp_len; + pkt->dsd[1].length = nvme->u.nvme.rsp_len; + put_unaligned_le64(nvme->u.nvme.rsp_dma, + &pkt->dsd[1].address); + } + } } static void -qla25xx_ctrlvp_iocb(srb_t *sp, struct vp_ctrl_entry_24xx *vce) +qla25xx_ctrlvp_iocb(srb_t *sp, void *pkt) { + /* + * vp_ctrl_entry_24xx_ext is layout-identical to vp_ctrl_entry_24xx + * for all fields touched here (entry_type, handle, entry_count, + * command, vp_count, vp_idx_map) -- they all sit at the same + * offsets and types in both structs, and the ext layout merely + * tacks on flags/id/hopct/reserved at offset 32+. So no + * IS_QLA29XX(ha) dispatch is needed on the issue path. + */ + struct vp_ctrl_entry_24xx *vce = pkt; int map, pos; - vce->entry_type = VP_CTRL_IOCB_TYPE; - vce->handle = sp->handle; - vce->entry_count = 1; - vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd); - vce->vp_count = cpu_to_le16(1); - /* * index map in firmware starts with 1; decrement index * this is ok as we never use index 0 */ map = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) / 8; pos = (sp->u.iocb_cmd.u.ctrlvp.vp_index - 1) & 7; + + vce->entry_type = VP_CTRL_IOCB_TYPE; + vce->handle = sp->handle; + vce->entry_count = 1; + vce->command = cpu_to_le16(sp->u.iocb_cmd.u.ctrlvp.cmd); + vce->vp_count = cpu_to_le16(1); + if (map >= ARRAY_SIZE(vce->vp_idx_map)) { + ql_log(ql_log_warn, sp->vha, 0x307c, + "ctrlvp: vp_index %u exceeds vp_idx_map capacity\n", + sp->u.iocb_cmd.u.ctrlvp.vp_index); + return; + } vce->vp_idx_map[map] |= 1 << pos; } static void -qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio) +qla24xx_prlo_iocb(srb_t *sp, void *pkt) { + struct logio_entry_24xx *logio = pkt; + logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_PRLO|LCF_IMPL_PRLO); @@ -3840,7 +4150,7 @@ qla24xx_prlo_iocb(srb_t *sp, struct logio_entry_24xx *logio) logio->port_id[0] = sp->fcport->d_id.b.al_pa; logio->port_id[1] = sp->fcport->d_id.b.area; logio->port_id[2] = sp->fcport->d_id.b.domain; - logio->vp_index = sp->fcport->vha->vp_idx; + qla_logio_set_vp_index(sp->vha->hw, pkt, sp->fcport->vha->vp_idx); } static int qla_get_iocbs_resource(struct srb *sp) @@ -3903,16 +4213,31 @@ static int qla_get_iocbs_resource(struct srb *sp) } static void -qla_marker_iocb(srb_t *sp, struct mrk_entry_24xx *mrk) +qla_marker_iocb(srb_t *sp, void *pkt) { + struct qla_hw_data *ha = sp->vha->hw; + struct mrk_entry_24xx *mrk = pkt; + + /* + * mrk_entry_24xx_ext overlays mrk_entry_24xx through 'lun', so + * the common-header writes go through the 24xx-typed pointer; + * only vp_index (u8 in 24xx, __le16 in the ext layout) needs a + * stride-aware branch. + */ mrk->entry_type = MARKER_TYPE; mrk->modifier = sp->u.iocb_cmd.u.tmf.modifier; mrk->handle = make_handle(sp->qpair->req->id, sp->handle); if (sp->u.iocb_cmd.u.tmf.modifier != MK_SYNC_ALL) { - mrk->nport_handle = cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id); - int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun, (struct scsi_lun *)&mrk->lun); + mrk->nport_handle = + cpu_to_le16(sp->u.iocb_cmd.u.tmf.loop_id); + int_to_scsilun(sp->u.iocb_cmd.u.tmf.lun, + (struct scsi_lun *)&mrk->lun); host_to_fcp_swap(mrk->lun, sizeof(mrk->lun)); - mrk->vp_index = sp->u.iocb_cmd.u.tmf.vp_index; + if (IS_QLA29XX(ha)) + ((struct mrk_entry_24xx_ext *)pkt)->vp_index = + cpu_to_le16(sp->u.iocb_cmd.u.tmf.vp_index); + else + mrk->vp_index = sp->u.iocb_cmd.u.tmf.vp_index; } } @@ -3964,6 +4289,11 @@ qla2x00_start_sp(srb_t *sp) break; case SRB_ELS_CMD_HST_NOLOGIN: qla_els_pt_iocb(sp->vha, pkt, &sp->u.bsg_cmd.u.els_arg); + /* + * els_entry_24xx::handle and els_entry_24xx_ext::handle are + * both u32 at offset 4, so a 24xx-view write is layout- + * compatible with both strides. + */ ((struct els_entry_24xx *)pkt)->handle = sp->handle; break; case SRB_CT_CMD: @@ -4098,7 +4428,8 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, /* Continuation type 1 IOCB can accomodate * 5 DSDS */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + vha->hw, vha->req); cur_dsd = cont_pkt->dsd; avail_dsds = 5; entry_count++; @@ -4120,7 +4451,8 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, /* Continuation type 1 IOCB can accomodate * 5 DSDS */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, + vha->hw, vha->req); cur_dsd = cont_pkt->dsd; avail_dsds = 5; entry_count++; @@ -4152,6 +4484,21 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds) rsp = ha->rsp_q_map[0]; req = vha->req; + /* + * 29xx uses a 128-byte extended IOCB ring, but no extended + * COMMAND_BIDIRECTIONAL IOCB layout has been defined and no + * firmware-spec'd cmd_bidir_ext exists. Posting the 64-byte + * struct cmd_bidir against req->ring_ptr would overlap the next + * ring slot on 29xx HW (see BUILD_BUG_ON(sizeof(struct cmd_bidir) + * != 64) in qla_os.c), so refuse BSG BIDIR on 29xx rather than + * corrupt the request ring. + */ + if (IS_QLA29XX(ha)) { + ql_log(ql_log_warn, vha, 0x70af, + "BSG BIDIR not supported on 29xx adapters.\n"); + return EXT_STATUS_INVALID_PARAM; + } + /* Send marker if required */ if (vha->marker_needed != 0) { if (qla2x00_marker(vha, ha->base_qpair, @@ -4289,8 +4636,15 @@ qla_start_scsi_type6(srb_t *sp) tot_dsds = nseg; - /* eventhough driver only need 1 T6 IOCB, FW still convert DSD to Continueation IOCB */ - req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + /* + * Even though the driver only needs 1 T6 IOCB, FW still converts the + * DSD list into continuation IOCBs for reservation purposes. 29xx + * follows the same rule against its extended-IOCB inline DSD count. + */ + if (IS_QLA29XX(ha)) + req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_CMD67_DSDS); + else + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; sp->iores.exch_cnt = 1; @@ -4394,23 +4748,44 @@ qla_start_scsi_type6(srb_t *sp) cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; - cmd_pkt = (struct cmd_type_6 *)req->ring_ptr; + /* + * cmd_type_6 and cmd_type_6_ext share an identical header layout + * from offset 0 through byte_count (offset 48), so the common + * header/nport/lun writes go through the 24xx-typed cmd_pkt even on + * 29xx. Only the divergent port_id/vp_index tail and the DSD layout + * need hardware-specific handling. + */ + if (IS_QLA29XX(ha)) + cmd_pkt = (struct cmd_type_6 *)req->ring_ext_ptr; + else + cmd_pkt = (struct cmd_type_6 *)req->ring_ptr; cmd_pkt->handle = make_handle(req->id, handle); /* tagged queuing modifier -- default is TSK_SIMPLE (0). */ clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + memset(clr_ptr, 0, qla_req_entry_size(ha) - 8); cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); /* Set NPORT-ID and LUN number */ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->vha->vp_idx; - /* Build IOCB segments */ - qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds); + if (IS_QLA29XX(ha)) { + struct cmd_type_6_ext *cmd_pkt_ext = + (struct cmd_type_6_ext *)cmd_pkt; + + cmd_pkt_ext->vp_index = cpu_to_le16(sp->vha->vp_idx); + + /* Build IOCB segments (dsd[] in 29xx extended layout). */ + qla29xx_build_scsi_type_6_iocbs(sp, cmd_pkt_ext, tot_dsds); + } else { + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->vha->vp_idx; + + /* Build IOCB segments (24xx fcp_dsd single-DSD layout). */ + qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds); + } int_to_scsilun(cmd->device->lun, &cmd_pkt->lun); host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun)); @@ -4445,14 +4820,8 @@ qla_start_scsi_type6(srb_t *sp) cmd_pkt->entry_count = (uint8_t)req_cnt; wmb(); - /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else { - req->ring_ptr++; - } + /* Adjust ring index. 29xx uses the 128-byte extended ring pointer.*/ + qla_req_ring_advance(ha, req); sp->qpair->cmd_cnt++; sp->flags |= SRB_DMA_VALID; @@ -4460,8 +4829,11 @@ qla_start_scsi_type6(srb_t *sp) /* Set chip new ring index. */ wrt_reg_dword(req->req_q_in, req->ring_index); - /* Manage unprocessed RIO/ZIO commands in response queue. */ - if (vha->flags.process_response_queue && + /* + * Manage unprocessed RIO/ZIO commands in response queue. 29xx skips + * this optimization until rsp_que is made 128-byte-stride aware. + */ + if (!IS_QLA29XX(ha) && vha->flags.process_response_queue && rsp->ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(vha, rsp); @@ -4487,3 +4859,492 @@ qla_start_scsi_type6(srb_t *sp) return QLA_FUNCTION_FAILED; } + +/* + * --------------------------------------------------------------------------- + * 29xx extended (128-byte) IOCB fast-path helpers. + * + * These helpers drive the extended IOCB ring used by 29xx-class adapters and + * operate on the *_ext IOCB variants declared in qla_fw29.h + * (cmd_type_6_ext, cmd_type_7_ext, cmd_type_crc_2_ext, struct cont_a64_entry_ext). + * + * They are not yet wired into any dispatch path; the top-level entry points + * (e.g. qla29xx_start_scsi / qla29xx_dif_start_scsi_mq) and isp_ops hooks are + * added in subsequent merge phases. Until then the top-of-tree builder + * functions are marked __maybe_unused so the build stays warning-clean. + * + * Ported from the out-of-tree qla29xx driver. Signatures have been adapted + * to the Mach qla2xxx single-srb_t model: + * struct qla_io_srb * --> srb_t * + * sp->qcb.qpair --> sp->qpair + * sp->qcb.iores --> sp->iores + * sp->qcb.handle --> sp->handle + * qla2900_hba_err_chk_enabled() --> qla2x00_hba_err_chk_enabled() + * --------------------------------------------------------------------------- + */ + +static void qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt, + struct crc_context *crc_ctx_pkt, uint8_t dif_bundling); + +/** + * qla2900_prep_cont_type1_iocb() - Initialize a 29xx-ext Continuation + * Type 1 IOCB on the extended IOCB ring. + * @vha: HA context + * @req: request queue + * + * Returns a pointer to the continuation type 1 IOCB packet. + */ +struct cont_a64_entry_ext * +qla2900_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req) +{ + struct cont_a64_entry_ext *cont_pkt; + + /* + * 29xx uses the 128-byte extended IOCB ring. Advance via ring_ext_ptr + * so the pointer arithmetic matches the on-ring stride. + */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ext_ptr = req->ring_ext; + } else { + req->ring_ext_ptr++; + } + + cont_pkt = (struct cont_a64_entry_ext *)req->ring_ext_ptr; + + /* Load packet defaults. */ + put_unaligned_le32(CONTINUE_A64_TYPE, &cont_pkt->entry_type); + + return cont_pkt; +} + +/* + * Note: the 29xx extended path reuses qla24xx_configure_prot_mode() -- the + * SCSI-prot-op to PO_MODE_DIF_* translation is independent of the IOCB + * generation. + */ + +/** + * qla29xx_build_scsi_type_6_iocbs() - Build IOCB command utilizing Command + * Type 6 IOCB types on the 29xx extended ring. + * @sp: SRB command to process + * @cmd_pkt: Command type 6 extended IOCB + * @tot_dsds: Total number of segments to transfer + */ +static void +qla29xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6_ext *cmd_pkt, + uint16_t tot_dsds) +{ + struct dsd64 *cur_dsd = NULL, *next_dsd; + struct scsi_cmnd *cmd; + struct scatterlist *cur_seg; + uint8_t avail_dsds; + uint8_t first_iocb = 1; + uint32_t dsd_list_len; + struct dsd_dma *dsd_ptr; + struct ct6_dsd *ctx; + struct qla_qpair *qpair = sp->qpair; + + cmd = GET_CMD_SP(sp); + + /* Update entry type to indicate Command Type 6 IOCB */ + put_unaligned_le32(COMMAND_TYPE_6, &cmd_pkt->entry_type); + + /* No data transfer */ + if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = cpu_to_le32(0); + goto function_end; + } + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA); + qpair->counters.output_bytes += scsi_bufflen(cmd); + qpair->counters.output_requests++; + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA); + qpair->counters.input_bytes += scsi_bufflen(cmd); + qpair->counters.input_requests++; + } + + cur_seg = scsi_sglist(cmd); + ctx = &sp->u.scmd.ct6_ctx; + + while (tot_dsds) { + avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ? + QLA_DSDS_PER_IOCB : tot_dsds; + tot_dsds -= avail_dsds; + dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE; + + dsd_ptr = list_first_entry(&qpair->dsd_list, struct dsd_dma, + list); + next_dsd = dsd_ptr->dsd_addr; + list_del(&dsd_ptr->list); + qpair->dsd_avail--; + list_add_tail(&dsd_ptr->list, &ctx->dsd_list); + ctx->dsd_use_cnt++; + qpair->dsd_inuse++; + + if (first_iocb) { + first_iocb = 0; + put_unaligned_le64(dsd_ptr->dsd_list_dma, + &cmd_pkt->dsd[0].address); + cmd_pkt->dsd[0].length = cpu_to_le32(dsd_list_len); + } else { + put_unaligned_le64(dsd_ptr->dsd_list_dma, + &cur_dsd->address); + cur_dsd->length = cpu_to_le32(dsd_list_len); + cur_dsd++; + } + cur_dsd = next_dsd; + while (avail_dsds) { + append_dsd64(&cur_dsd, cur_seg); + cur_seg = sg_next(cur_seg); + avail_dsds--; + } + } + + /* Null termination */ + if (cur_dsd) { + cur_dsd->address = 0; + cur_dsd->length = 0; + cur_dsd++; + } + cmd_pkt->control_flags |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); +function_end: + return; +} + +/* + * Note: the 29xx extended path reuses qla24xx_calc_dsd_lists() -- the + * DSD-list-per-IOCB arithmetic (based on QLA_DSDS_PER_IOCB) is generation- + * agnostic. + */ + +/** + * qla29xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7 + * IOCB types on the 29xx extended ring. + * @sp: SRB command to process + * @cmd_pkt: Command type 7 extended IOCB + * @tot_dsds: Total number of segments to transfer + * @req: pointer to request queue + */ +static void __maybe_unused +qla29xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_ext *cmd_pkt, + uint16_t tot_dsds, struct req_que *req) +{ + uint8_t avail_dsds; + struct dsd64 *cur_dsd; + scsi_qla_host_t *vha; + struct scsi_cmnd *cmd; + struct scatterlist *sg; + int i; + struct qla_qpair *qpair = sp->qpair; + + cmd = GET_CMD_SP(sp); + + /* Update entry type to indicate Command Type 7 IOCB */ + put_unaligned_le32(COMMAND_TYPE_7, &cmd_pkt->entry_type); + + /* No data transfer */ + if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = cpu_to_le32(0); + return; + } + + vha = sp->vha; + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_WRITE_DATA); + qpair->counters.output_bytes += scsi_bufflen(cmd); + qpair->counters.output_requests++; + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_READ_DATA); + qpair->counters.input_bytes += scsi_bufflen(cmd); + qpair->counters.input_requests++; + } + + /* NUM_CMD67_DSDS DSDs available in the Command Type 7 ext IOCB */ + avail_dsds = NUM_CMD67_DSDS; + cur_dsd = &cmd_pkt->dsd[0]; + + scsi_for_each_sg(cmd, sg, tot_dsds, i) { + struct cont_a64_entry_ext *cont_pkt; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + /* + * NUM_CONT1_DSDS DSDs are available in the Continuation + * Type 1 extended IOCB. + */ + cont_pkt = qla2900_prep_cont_type1_iocb(vha, req); + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); + } + + append_dsd64(&cur_dsd, sg); + avail_dsds--; + } +} + +/* + * Note: the 29xx extended path reuses qla24xx_set_t10dif_tags() (defined + * earlier in this file) -- the T10-DIF ref/app tag layout is identical + * between the 24xx and 29xx extended CRC_2 IOCBs (both share the same + * fw_dif_context layout inside struct crc_context). + */ + +/* + * Note: the 29xx extended CRC_2 path reuses the 24xx SG walkers + * (qla24xx_get_one_block_sg, qla24xx_walk_and_build_sglist_no_difb, + * qla24xx_walk_and_build_sglist, qla24xx_walk_and_build_prot_sglist). + * The DSD-chain / CRC-context layout they produce is generation-agnostic; + * only the IOCB that points at the chain (Type-6/7/CRC_2 vs their _ext + * variants) differs between generations, and that is handled by the + * separate qla29xx_build_* builders. + * + * The walkers that take a struct qla_tc_param *tc are invoked with tc=NULL + * here because the 29xx fast-path does not use the tape/copy offload. + */ + +/** + * qla29xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command + * Type CRC_2 IOCB types on the 29xx extended ring. + * @sp: SRB command to process + * @cmd_pkt: Command type CRC_2 extended IOCB + * @tot_dsds: Total number of segments to transfer + * @tot_prot_dsds: Total number of segments with protection information + * @fw_prot_opts: Protection options to be passed to firmware + * + * Returns 0 if successful, 1 otherwise. + */ +static int +qla29xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2_ext *cmd_pkt, + uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts) +{ + struct dsd64 *cur_dsd; + __be32 *fcp_dl; + scsi_qla_host_t *vha; + struct scsi_cmnd *cmd; + uint32_t total_bytes = 0; + uint32_t data_bytes; + uint32_t dif_bytes; + uint8_t bundling = 1; + uint16_t blk_size; + struct crc_context *crc_ctx_pkt = NULL; + struct qla_hw_data *ha; + uint8_t additional_fcpcdb_len; + uint16_t fcp_cmnd_len; + struct fcp_cmnd *fcp_cmnd; + dma_addr_t crc_ctx_dma; + + cmd = GET_CMD_SP(sp); + + /* Update entry type to indicate Command Type CRC_2 IOCB */ + put_unaligned_le32(COMMAND_TYPE_CRC_2, &cmd_pkt->entry_type); + + vha = sp->vha; + ha = vha->hw; + + /* No data transfer */ + data_bytes = scsi_bufflen(cmd); + if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { + cmd_pkt->byte_count = cpu_to_le32(0); + return QLA_SUCCESS; + } + + cmd_pkt->vp_index = cpu_to_le16(sp->vha->vp_idx); + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) + cmd_pkt->control_flags_1 = cpu_to_le16(CF_WRITE_DATA); + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + cmd_pkt->control_flags_1 = cpu_to_le16(CF_READ_DATA); + + if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) || + (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) || + (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) || + (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT)) + bundling = 0; + + /* Allocate CRC context from global pool */ + crc_ctx_pkt = sp->u.scmd.crc_ctx = + dma_pool_zalloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma); + + if (!crc_ctx_pkt) + goto crc_queuing_error; + + crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma; + + sp->flags |= SRB_CRC_CTX_DMA_VALID; + + /* Set handle */ + crc_ctx_pkt->handle = cmd_pkt->handle; + + INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list); + + qla24xx_set_t10dif_tags(sp, (struct fw_dif_context *) + &crc_ctx_pkt->ref_tag, tot_prot_dsds); + + /* Determine SCSI command length -- align to 4 byte boundary */ + if (cmd->cmd_len > 16) { + additional_fcpcdb_len = cmd->cmd_len - 16; + if ((cmd->cmd_len % 4) != 0) { + /* SCSI cmd > 16 bytes must be multiple of 4 */ + goto crc_queuing_error; + } + fcp_cmnd_len = 12 + cmd->cmd_len + 4; + } else { + additional_fcpcdb_len = 0; + fcp_cmnd_len = 12 + 16 + 4; + } + + fcp_cmnd = &crc_ctx_pkt->fcp_cmnd; + + fcp_cmnd->additional_cdb_len = additional_fcpcdb_len; + if (cmd->sc_data_direction == DMA_TO_DEVICE) + fcp_cmnd->additional_cdb_len |= 1; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + fcp_cmnd->additional_cdb_len |= 2; + + int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun); + memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len); + cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len); + put_unaligned_le64(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF, + &cmd_pkt->fcp_cmnd_dseg_address); + fcp_cmnd->task_management = 0; + fcp_cmnd->task_attribute = TSK_SIMPLE; + + cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */ + + /* Compute dif len and adjust data len to include protection */ + dif_bytes = 0; + blk_size = cmd->device->sector_size; + dif_bytes = (data_bytes / blk_size) * 8; + + switch (scsi_get_prot_op(GET_CMD_SP(sp))) { + case SCSI_PROT_READ_INSERT: + case SCSI_PROT_WRITE_STRIP: + total_bytes = data_bytes; + data_bytes += dif_bytes; + break; + + case SCSI_PROT_READ_STRIP: + case SCSI_PROT_WRITE_INSERT: + case SCSI_PROT_READ_PASS: + case SCSI_PROT_WRITE_PASS: + total_bytes = data_bytes + dif_bytes; + break; + default: + WARN_ON_ONCE(1); + return 1; + } + + if (!qla2x00_hba_err_chk_enabled(sp)) + fw_prot_opts |= 0x10; /* Disable Guard tag checking */ + /* HBA error checking enabled */ + else { + if ((scsi_get_prot_type(GET_CMD_SP(sp)) == SCSI_PROT_DIF_TYPE1) + || (scsi_get_prot_type(GET_CMD_SP(sp)) == + SCSI_PROT_DIF_TYPE2)) + fw_prot_opts |= BIT_10; + else if (scsi_get_prot_type(GET_CMD_SP(sp)) == + SCSI_PROT_DIF_TYPE3) + fw_prot_opts |= BIT_11; + } + + if (!bundling) { + cur_dsd = &crc_ctx_pkt->u.nobundling.data_dsd[0]; + } else { + /* + * Configure Bundling if we need to fetch interleaving + * protection PCI accesses + */ + fw_prot_opts |= PO_ENABLE_DIF_BUNDLING; + crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes); + crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds - + tot_prot_dsds); + cur_dsd = &crc_ctx_pkt->u.bundling.data_dsd[0]; + } + + /* Finish the common fields of CRC pkt */ + crc_ctx_pkt->blk_size = cpu_to_le16(blk_size); + crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts); + crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes); + crc_ctx_pkt->guard_seed = cpu_to_le16(0); + /* Fibre channel byte count */ + cmd_pkt->byte_count = cpu_to_le32(total_bytes); + fcp_dl = (__be32 *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 + + additional_fcpcdb_len); + *fcp_dl = htonl(total_bytes); + + /* Walks data segments */ + + cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); + + if (!bundling && tot_prot_dsds) { + if (qla24xx_walk_and_build_sglist_no_difb(ha, sp, + cur_dsd, tot_dsds, NULL)) + goto crc_queuing_error; + } else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd, + (tot_dsds - tot_prot_dsds), NULL)) + goto crc_queuing_error; + + if (bundling && tot_prot_dsds) { + /* Walks dif segments */ + cmd_pkt->control_flags_1 |= cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE); + cur_dsd = &crc_ctx_pkt->u.bundling.dif_dsd; + if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd, + tot_prot_dsds, NULL)) + goto crc_queuing_error; + } + qla29xx_copy_dif_iocb_data(cmd_pkt, crc_ctx_pkt, bundling); + + return QLA_SUCCESS; + +crc_queuing_error: + /* Cleanup will be performed by the caller */ + return QLA_FUNCTION_FAILED; +} + +/** + * qla29xx_copy_dif_iocb_data() - Populate the DIF-related fields of an + * extended CRC_2 command IOCB from a freshly populated crc_context. + * @cmd_pkt: Extended CRC_2 command IOCB + * @crc_ctx_pkt: Source CRC context + * @dif_bundling: DIF bundling flag + */ +static void +qla29xx_copy_dif_iocb_data(struct cmd_type_crc_2_ext *cmd_pkt, + struct crc_context *crc_ctx_pkt, uint8_t dif_bundling) +{ + cmd_pkt->ref_tag = crc_ctx_pkt->ref_tag; + cmd_pkt->app_tag = crc_ctx_pkt->app_tag; + memcpy(cmd_pkt->ref_tag_mask, crc_ctx_pkt->ref_tag_mask, + sizeof(cmd_pkt->ref_tag_mask)); + memcpy(cmd_pkt->app_tag_mask, crc_ctx_pkt->app_tag_mask, + sizeof(cmd_pkt->app_tag_mask)); + cmd_pkt->blk_size = crc_ctx_pkt->blk_size; + cmd_pkt->prot_opts = crc_ctx_pkt->prot_opts; + cmd_pkt->tot_byte_count = crc_ctx_pkt->byte_count; + if (dif_bundling) { + cmd_pkt->u.bundling.guard_seed = crc_ctx_pkt->guard_seed; + cmd_pkt->u.bundling.dif_byte_count = + crc_ctx_pkt->u.bundling.dif_byte_count; + cmd_pkt->u.bundling.dseg_count = + crc_ctx_pkt->u.bundling.dseg_count; + memcpy(cmd_pkt->u.bundling.data_dsd, + crc_ctx_pkt->u.bundling.data_dsd, + sizeof(cmd_pkt->u.bundling.data_dsd)); + memcpy(&cmd_pkt->u.bundling.dif_dsd, + &crc_ctx_pkt->u.bundling.dif_dsd, + sizeof(cmd_pkt->u.bundling.dif_dsd)); + } else { + cmd_pkt->u.nobundling.guard_seed = crc_ctx_pkt->guard_seed; + memcpy(cmd_pkt->u.nobundling.data_dsd, + crc_ctx_pkt->u.nobundling.data_dsd, + sizeof(cmd_pkt->u.nobundling.data_dsd)); + } +} diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 33776330956c..63b70d4abbf0 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -19,7 +19,7 @@ static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t); static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *); -static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *); +static void qla2x00_status_cont_entry(struct rsp_que *, void *, u32); static int qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *, sts_entry_t *); static void qla27xx_process_purex_fpin(struct scsi_qla_host *vha, @@ -72,51 +72,130 @@ static inline void display_Laser_info(scsi_qla_host_t *vha, mb3); } +/* + * Type-generic helpers for qla24xx_process_abts(). + * Both abts_entry_24xx and abts_entry_24xx_ext share field names for all + * accessed fields, so these macros expand correctly for either type. + */ +#define QLA_LOG_ABTS_RCV(vha, abts_ptr) do { \ + ql_log(ql_log_warn, (vha), 0x0287, \ + "Processing ABTS xchg=%#x oxid=%#x rxid=%#x " \ + "seqid=%#x seqcnt=%#x\n", \ + (abts_ptr)->rx_xch_addr_to_abort, (abts_ptr)->ox_id, \ + (abts_ptr)->rx_id, (abts_ptr)->seq_id, \ + (abts_ptr)->seq_cnt); \ + ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287, \ + "-------- ABTS RCV -------\n"); \ + ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x0287, \ + (uint8_t *)(abts_ptr), sizeof(*(abts_ptr))); \ +} while (0) + +#define QLA_BUILD_ABTS_BA_ACC(rsp, src, fctl) do { \ + memset((rsp), 0, sizeof(*(rsp))); \ + (rsp)->entry_type = ABTS_RSP_TYPE; \ + (rsp)->entry_count = 1; \ + (rsp)->nport_handle = (src)->nport_handle; \ + (rsp)->rx_xch_addr = (src)->rx_xch_addr; \ + (rsp)->d_id[0] = (src)->s_id[0]; \ + (rsp)->d_id[1] = (src)->s_id[1]; \ + (rsp)->d_id[2] = (src)->s_id[2]; \ + (rsp)->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC; \ + (rsp)->s_id[0] = (src)->d_id[0]; \ + (rsp)->s_id[1] = (src)->d_id[1]; \ + (rsp)->s_id[2] = (src)->d_id[2]; \ + (rsp)->cs_ctl = (src)->cs_ctl; \ + (fctl) = ~((src)->f_ctl[2] | 0x7F) << 16 | \ + FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT; \ + (rsp)->f_ctl[0] = (fctl) >> 0 & 0xff; \ + (rsp)->f_ctl[1] = (fctl) >> 8 & 0xff; \ + (rsp)->f_ctl[2] = (fctl) >> 16 & 0xff; \ + (rsp)->type = FC_TYPE_BLD; \ + (rsp)->rx_id = (src)->rx_id; \ + (rsp)->ox_id = (src)->ox_id; \ + (rsp)->payload.ba_acc.aborted_rx_id = (src)->rx_id; \ + (rsp)->payload.ba_acc.aborted_ox_id = (src)->ox_id; \ + (rsp)->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0); \ + (rsp)->rx_xch_addr_to_abort = (src)->rx_xch_addr_to_abort; \ +} while (0) + +#define QLA_LOG_ISSUE_ABTS_RSP(vha, rsp, dma, rval) do { \ + ql_dbg(ql_dbg_init, (vha), 0x028b, \ + "Sending BA ACC response to ABTS %#x...\n", \ + (rsp)->rx_xch_addr_to_abort); \ + ql_dbg(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b, \ + "-------- ELS RSP -------\n"); \ + ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, (vha), 0x028b, \ + (uint8_t *)(rsp), sizeof(*(rsp))); \ + (rval) = qla2x00_issue_iocb((vha), (rsp), (dma), 0); \ + if (rval) { \ + ql_log(ql_log_warn, (vha), 0x028c, \ + "%s: iocb failed to execute -> %x\n", \ + __func__, (rval)); \ + } else if ((rsp)->comp_status) { \ + ql_log(ql_log_warn, (vha), 0x028d, \ + "%s: iocb failed to complete -> " \ + "completion=%#x subcode=(%#x,%#x)\n", \ + __func__, (rsp)->comp_status, \ + (rsp)->payload.error.subcode1, \ + (rsp)->payload.error.subcode2); \ + } else { \ + ql_dbg(ql_dbg_init, (vha), 0x028ea, \ + "%s: done.\n", __func__); \ + } \ +} while (0) + static void qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt) { - struct abts_entry_24xx *abts = - (struct abts_entry_24xx *)&pkt->iocb; struct qla_hw_data *ha = vha->hw; + struct abts_entry_24xx *abts = NULL; + struct abts_entry_24xx_ext *abts_ext = NULL; struct els_entry_24xx *rsp_els; - struct abts_entry_24xx *abts_rsp; dma_addr_t dma; + __le32 rx_xch_addr_to_abort; uint32_t fctl; int rval; + void *rsp_pkt; + size_t rsp_sz; ql_dbg(ql_dbg_init, vha, 0x0286, "%s: entered.\n", __func__); - ql_log(ql_log_warn, vha, 0x0287, - "Processing ABTS xchg=%#x oxid=%#x rxid=%#x seqid=%#x seqcnt=%#x\n", - abts->rx_xch_addr_to_abort, abts->ox_id, abts->rx_id, - abts->seq_id, abts->seq_cnt); - ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0287, - "-------- ABTS RCV -------\n"); - ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0287, - (uint8_t *)abts, sizeof(*abts)); + if (IS_QLA29XX(ha)) { + abts_ext = (struct abts_entry_24xx_ext *)&pkt->iocb; + QLA_LOG_ABTS_RCV(vha, abts_ext); + rsp_sz = sizeof(struct els_entry_24xx_ext); + rx_xch_addr_to_abort = abts_ext->rx_xch_addr_to_abort; + } else { + abts = (struct abts_entry_24xx *)&pkt->iocb; + QLA_LOG_ABTS_RCV(vha, abts); + rsp_sz = sizeof(struct els_entry_24xx); + rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort; + } - rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els), &dma, + rsp_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_sz, &dma, GFP_KERNEL); - if (!rsp_els) { + if (!rsp_pkt) { ql_log(ql_log_warn, vha, 0x0287, "Failed allocate dma buffer ABTS/ELS RSP.\n"); return; } + rsp_els = rsp_pkt; /* terminate exchange */ + memset(rsp_pkt, 0, rsp_sz); rsp_els->entry_type = ELS_IOCB_TYPE; rsp_els->entry_count = 1; rsp_els->nport_handle = cpu_to_le16(~0); - rsp_els->rx_xchg_address = abts->rx_xch_addr_to_abort; + rsp_els->rx_xchg_address = rx_xch_addr_to_abort; rsp_els->control_flags = cpu_to_le16(EPD_RX_XCHG); ql_dbg(ql_dbg_init, vha, 0x0283, "Sending ELS Response to terminate exchange %#x...\n", - abts->rx_xch_addr_to_abort); + rx_xch_addr_to_abort); ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0283, "-------- ELS RSP -------\n"); ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0283, - (uint8_t *)rsp_els, sizeof(*rsp_els)); - rval = qla2x00_issue_iocb(vha, rsp_els, dma, 0); + (uint8_t *)rsp_pkt, rsp_sz); + rval = qla2x00_issue_iocb(vha, rsp_pkt, dma, 0); if (rval) { ql_log(ql_log_warn, vha, 0x0288, "%s: iocb failed to execute -> %x\n", __func__, rval); @@ -131,60 +210,30 @@ qla24xx_process_abts(struct scsi_qla_host *vha, struct purex_item *pkt) } /* send ABTS response */ - abts_rsp = (void *)rsp_els; - memset(abts_rsp, 0, sizeof(*abts_rsp)); - abts_rsp->entry_type = ABTS_RSP_TYPE; - abts_rsp->entry_count = 1; - abts_rsp->nport_handle = abts->nport_handle; - abts_rsp->vp_idx = abts->vp_idx; - abts_rsp->sof_type = abts->sof_type & 0xf0; - abts_rsp->rx_xch_addr = abts->rx_xch_addr; - abts_rsp->d_id[0] = abts->s_id[0]; - abts_rsp->d_id[1] = abts->s_id[1]; - abts_rsp->d_id[2] = abts->s_id[2]; - abts_rsp->r_ctl = FC_ROUTING_BLD | FC_R_CTL_BLD_BA_ACC; - abts_rsp->s_id[0] = abts->d_id[0]; - abts_rsp->s_id[1] = abts->d_id[1]; - abts_rsp->s_id[2] = abts->d_id[2]; - abts_rsp->cs_ctl = abts->cs_ctl; - /* include flipping bit23 in fctl */ - fctl = ~(abts->f_ctl[2] | 0x7F) << 16 | - FC_F_CTL_LAST_SEQ | FC_F_CTL_END_SEQ | FC_F_CTL_SEQ_INIT; - abts_rsp->f_ctl[0] = fctl >> 0 & 0xff; - abts_rsp->f_ctl[1] = fctl >> 8 & 0xff; - abts_rsp->f_ctl[2] = fctl >> 16 & 0xff; - abts_rsp->type = FC_TYPE_BLD; - abts_rsp->rx_id = abts->rx_id; - abts_rsp->ox_id = abts->ox_id; - abts_rsp->payload.ba_acc.aborted_rx_id = abts->rx_id; - abts_rsp->payload.ba_acc.aborted_ox_id = abts->ox_id; - abts_rsp->payload.ba_acc.high_seq_cnt = cpu_to_le16(~0); - abts_rsp->rx_xch_addr_to_abort = abts->rx_xch_addr_to_abort; - ql_dbg(ql_dbg_init, vha, 0x028b, - "Sending BA ACC response to ABTS %#x...\n", - abts->rx_xch_addr_to_abort); - ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x028b, - "-------- ELS RSP -------\n"); - ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x028b, - (uint8_t *)abts_rsp, sizeof(*abts_rsp)); - rval = qla2x00_issue_iocb(vha, abts_rsp, dma, 0); - if (rval) { - ql_log(ql_log_warn, vha, 0x028c, - "%s: iocb failed to execute -> %x\n", __func__, rval); - } else if (abts_rsp->comp_status) { - ql_log(ql_log_warn, vha, 0x028d, - "%s: iocb failed to complete -> completion=%#x subcode=(%#x,%#x)\n", - __func__, abts_rsp->comp_status, - abts_rsp->payload.error.subcode1, - abts_rsp->payload.error.subcode2); + if (IS_QLA29XX(ha)) { + struct abts_entry_24xx_ext *rsp_ext = rsp_pkt; + + QLA_BUILD_ABTS_BA_ACC(rsp_ext, abts_ext, fctl); + rsp_ext->vp_idx_sof = qla_ext_build_vp_sof( + qla_ext_get_vp_index(abts_ext->vp_idx_sof), + qla_ext_get_sof_type(abts_ext->vp_idx_sof)); + QLA_LOG_ISSUE_ABTS_RSP(vha, rsp_ext, dma, rval); } else { - ql_dbg(ql_dbg_init, vha, 0x028ea, - "%s: done.\n", __func__); + struct abts_entry_24xx *abts_rsp = rsp_pkt; + + QLA_BUILD_ABTS_BA_ACC(abts_rsp, abts, fctl); + abts_rsp->vp_idx = abts->vp_idx; + abts_rsp->sof_type = abts->sof_type & 0xf0; + QLA_LOG_ISSUE_ABTS_RSP(vha, abts_rsp, dma, rval); } - dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), rsp_els, dma); + dma_free_coherent(&ha->pdev->dev, rsp_sz, rsp_pkt, dma); } +#undef QLA_LOG_ABTS_RCV +#undef QLA_BUILD_ABTS_BA_ACC +#undef QLA_LOG_ISSUE_ABTS_RSP + /** * __qla_consume_iocb - this routine is used to tell fw driver has processed * or consumed the head IOCB along with the continuation IOCB's from the @@ -202,20 +251,30 @@ void __qla_consume_iocb(struct scsi_qla_host *vha, struct rsp_que *rsp_q = *rsp; response_t *new_pkt; uint16_t entry_count_remaining; + /* + * entry_count is u8 at offset 1 in both purex_entry_24xx and + * purex_entry_24xx_ext, so the 24xx view is layout-compatible with + * either stride. + */ struct purex_entry_24xx *purex = *pkt; entry_count_remaining = purex->entry_count; + + /* + * The caller already advanced ring_ptr past the head IOCB, so mark + * the head processed and account for it here, then consume only the + * continuation IOCBs that follow. + */ + ((response_t *)purex)->signature = RESPONSE_PROCESSED; + /* flush signature */ + wmb(); + --entry_count_remaining; + while (entry_count_remaining > 0) { new_pkt = rsp_q->ring_ptr; *pkt = new_pkt; - rsp_q->ring_index++; - if (rsp_q->ring_index == rsp_q->length) { - rsp_q->ring_index = 0; - rsp_q->ring_ptr = rsp_q->ring; - } else { - rsp_q->ring_ptr++; - } + qla_rsp_ring_advance(rsp_q); new_pkt->signature = RESPONSE_PROCESSED; /* flush signature */ @@ -236,17 +295,30 @@ void __qla_consume_iocb(struct scsi_qla_host *vha, int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len) { + /* + * purex_entry_24xx_ext overlays purex_entry_24xx for entry_count + * (offset 1), frame_size (offset 12) and els_frame_payload (offset + * 44, base address only -- the array size grows from 20 to 84 + * bytes). Header fields are read through the 24xx view; the + * initial payload memcpy uses a purex_entry_24xx_ext pointer on + * 29xx so that FORTIFY_SOURCE sees the correct 84-byte source. + */ struct purex_entry_24xx *purex = *pkt; + struct qla_hw_data *ha = vha->hw; struct rsp_que *rsp_q = *rsp; - sts_cont_entry_t *new_pkt; + size_t payload_size = IS_QLA29XX(ha) ? + sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) : + sizeof_field(struct purex_entry_24xx, els_frame_payload); + u8 *data; + u32 data_sz; uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0; uint16_t buffer_copy_offset = 0; uint16_t entry_count_remaining; u16 tpad; entry_count_remaining = purex->entry_count; - total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) - - PURX_ELS_HEADER_SIZE; + total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) - + PURX_ELS_HEADER_SIZE; /* * end of payload may not end in 4bytes boundary. Need to @@ -263,41 +335,47 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha, } pending_bytes = total_bytes = tpad; - no_bytes = (pending_bytes > sizeof(purex->els_frame_payload)) ? - sizeof(purex->els_frame_payload) : pending_bytes; + no_bytes = (pending_bytes > payload_size) ? + payload_size : pending_bytes; + if (IS_QLA29XX(ha)) { + struct purex_entry_24xx_ext *purex_ext = *pkt; - memcpy(buf, &purex->els_frame_payload[0], no_bytes); + memcpy(buf, &purex_ext->els_frame_payload[0], no_bytes); + } else { + memcpy(buf, &purex->els_frame_payload[0], no_bytes); + } buffer_copy_offset += no_bytes; pending_bytes -= no_bytes; --entry_count_remaining; + /* + * response_t::signature and struct response_ext::signature are both u32 + * at offset 60 (handle:4 + data[52]:60), so the 24xx view writes + * the right slot regardless of stride. + */ ((response_t *)purex)->signature = RESPONSE_PROCESSED; /* flush signature */ wmb(); do { while ((total_bytes > 0) && (entry_count_remaining > 0)) { - new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr; - *pkt = new_pkt; + *pkt = rsp_q->ring_ptr; + data = ((sts_cont_entry_t *)*pkt)->data; + data_sz = qla_sts_cont_data_size(ha); - if (new_pkt->entry_type != STATUS_CONT_TYPE) { + if (((sts_cont_entry_t *)*pkt)->entry_type != + STATUS_CONT_TYPE) { ql_log(ql_log_warn, vha, 0x507a, "Unexpected IOCB type, partial data 0x%x\n", buffer_copy_offset); break; } - rsp_q->ring_index++; - if (rsp_q->ring_index == rsp_q->length) { - rsp_q->ring_index = 0; - rsp_q->ring_ptr = rsp_q->ring; - } else { - rsp_q->ring_ptr++; - } - no_bytes = (pending_bytes > sizeof(new_pkt->data)) ? - sizeof(new_pkt->data) : pending_bytes; + qla_rsp_ring_advance(rsp_q); + no_bytes = (pending_bytes > data_sz) ? + data_sz : pending_bytes; if ((buffer_copy_offset + no_bytes) <= total_bytes) { - memcpy((buf + buffer_copy_offset), new_pkt->data, + memcpy((buf + buffer_copy_offset), data, no_bytes); buffer_copy_offset += no_bytes; pending_bytes -= no_bytes; @@ -306,11 +384,11 @@ int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha, ql_log(ql_log_warn, vha, 0x5044, "Attempt to copy more that we got, optimizing..%x\n", buffer_copy_offset); - memcpy((buf + buffer_copy_offset), new_pkt->data, + memcpy((buf + buffer_copy_offset), data, total_bytes - buffer_copy_offset); } - ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED; + ((response_t *)*pkt)->signature = RESPONSE_PROCESSED; /* flush signature */ wmb(); } @@ -664,7 +742,7 @@ const char * qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed) { static const char *const link_speeds[] = { - "1", "2", "?", "4", "8", "16", "32", "64", "10" + "1", "2", "?", "4", "8", "16", "32", "64", "128", "10" }; #define QLA_LAST_SPEED (ARRAY_SIZE(link_speeds) - 1) @@ -854,15 +932,18 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp, bool is_purls, bool byte_order) { + struct purex_entry_24xx_ext *purex_ext = NULL; struct purex_entry_24xx *purex = NULL; struct pt_ls4_rx_unsol *purls = NULL; + struct qla_hw_data *ha = vha->hw; struct rsp_que *rsp_q = *rsp; - sts_cont_entry_t *new_pkt; uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0; uint16_t buffer_copy_offset = 0, payload_size = 0; uint16_t entry_count, entry_count_remaining; struct purex_item *item; void *iocb_pkt = NULL; + u8 *data; + u32 data_sz; if (is_purls) { purls = *pkt; @@ -870,6 +951,13 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, PURX_ELS_HEADER_SIZE; entry_count = entry_count_remaining = purls->entry_count; payload_size = sizeof(purls->payload); + } else if (IS_QLA29XX(ha)) { + purex_ext = *pkt; + total_bytes = (le16_to_cpu(purex_ext->frame_size) & 0x0FFF) - + PURX_ELS_HEADER_SIZE; + entry_count = entry_count_remaining = + purex_ext->entry_count; + payload_size = sizeof(purex_ext->els_frame_payload); } else { purex = *pkt; total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) - @@ -878,8 +966,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, payload_size = sizeof(purex->els_frame_payload); } - if (total_bytes > sizeof(item->iocb.iocb)) - total_bytes = sizeof(item->iocb.iocb); + if (total_bytes > QLA_MAX_IOCB_SIZE) + total_bytes = QLA_MAX_IOCB_SIZE; pending_bytes = total_bytes; no_bytes = (pending_bytes > payload_size) ? payload_size : @@ -896,6 +984,8 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, if (is_purls) memcpy(iocb_pkt, &purls->payload[0], no_bytes); + else if (IS_QLA29XX(ha)) + memcpy(iocb_pkt, &purex_ext->els_frame_payload[0], no_bytes); else memcpy(iocb_pkt, &purex->els_frame_payload[0], no_bytes); buffer_copy_offset += no_bytes; @@ -904,42 +994,32 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, if (is_purls) ((response_t *)purls)->signature = RESPONSE_PROCESSED; + else if (IS_QLA29XX(ha)) + ((struct response_ext *)purex_ext)->signature = RESPONSE_PROCESSED; else ((response_t *)purex)->signature = RESPONSE_PROCESSED; wmb(); do { while ((total_bytes > 0) && (entry_count_remaining > 0)) { - if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) { - ql_dbg(ql_dbg_async, vha, 0x5084, - "Ran out of IOCBs, partial data 0x%x\n", - buffer_copy_offset); - cpu_relax(); - continue; - } - - new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr; - *pkt = new_pkt; + *pkt = rsp_q->ring_ptr; + data = ((sts_cont_entry_t *)*pkt)->data; + data_sz = qla_sts_cont_data_size(ha); - if (new_pkt->entry_type != STATUS_CONT_TYPE) { + if (((sts_cont_entry_t *)*pkt)->entry_type != + STATUS_CONT_TYPE) { ql_log(ql_log_warn, vha, 0x507a, "Unexpected IOCB type, partial data 0x%x\n", buffer_copy_offset); break; } - rsp_q->ring_index++; - if (rsp_q->ring_index == rsp_q->length) { - rsp_q->ring_index = 0; - rsp_q->ring_ptr = rsp_q->ring; - } else { - rsp_q->ring_ptr++; - } - no_bytes = (pending_bytes > sizeof(new_pkt->data)) ? - sizeof(new_pkt->data) : pending_bytes; + qla_rsp_ring_advance(rsp_q); + no_bytes = (pending_bytes > data_sz) ? + data_sz : pending_bytes; if ((buffer_copy_offset + no_bytes) <= total_bytes) { - memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset), - new_pkt->data, no_bytes); + memcpy(((uint8_t *)iocb_pkt + + buffer_copy_offset), data, no_bytes); buffer_copy_offset += no_bytes; pending_bytes -= no_bytes; --entry_count_remaining; @@ -947,12 +1027,12 @@ qla27xx_copy_multiple_pkt(struct scsi_qla_host *vha, void **pkt, ql_log(ql_log_warn, vha, 0x5044, "Attempt to copy more that we got, optimizing..%x\n", buffer_copy_offset); - memcpy(((uint8_t *)iocb_pkt + buffer_copy_offset), - new_pkt->data, - total_bytes - buffer_copy_offset); + memcpy(((uint8_t *)iocb_pkt + + buffer_copy_offset), data, + total_bytes - buffer_copy_offset); } - ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED; + ((response_t *)*pkt)->signature = RESPONSE_PROCESSED; wmb(); } @@ -1082,9 +1162,9 @@ qla24xx_alloc_purex_item(scsi_qla_host_t *vha, uint16_t size) struct purex_item *item = NULL; uint8_t item_hdr_size = sizeof(*item); - if (size > QLA_DEFAULT_PAYLOAD_SIZE) { + if (size > QLA_MAX_IOCB_SIZE) { item = kzalloc(item_hdr_size + - (size - QLA_DEFAULT_PAYLOAD_SIZE), GFP_ATOMIC); + (size - QLA_MAX_IOCB_SIZE), GFP_ATOMIC); } else { if (atomic_inc_return(&vha->default_item.in_use) == 1) { item = &vha->default_item; @@ -1133,14 +1213,20 @@ qla24xx_queue_purex_item(scsi_qla_host_t *vha, struct purex_item *pkt, static struct purex_item *qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt) { + struct qla_hw_data *ha = vha->hw; struct purex_item *item; + u16 copy_sz; + + if (IS_QLA29XX(ha)) + copy_sz = sizeof(struct purex_entry_24xx_ext); + else + copy_sz = QLA_DEFAULT_PAYLOAD_SIZE; - item = qla24xx_alloc_purex_item(vha, - QLA_DEFAULT_PAYLOAD_SIZE); + item = qla24xx_alloc_purex_item(vha, copy_sz); if (!item) return item; - memcpy(&item->iocb, pkt, sizeof(item->iocb)); + memcpy(&item->iocb, pkt, copy_sz); return item; } @@ -1156,8 +1242,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp) { struct purex_entry_24xx *purex = *pkt; + struct qla_hw_data *ha = vha->hw; struct rsp_que *rsp_q = *rsp; - sts_cont_entry_t *new_pkt; + size_t payload_size = IS_QLA29XX(ha) ? + sizeof_field(struct purex_entry_24xx_ext, els_frame_payload) : + sizeof_field(struct purex_entry_24xx, els_frame_payload); + u8 *data; + u32 data_sz; uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0; uint16_t buffer_copy_offset = 0; uint16_t entry_count, entry_count_remaining; @@ -1167,13 +1258,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt, total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF) - PURX_ELS_HEADER_SIZE; - if (total_bytes > sizeof(item->iocb.iocb)) - total_bytes = sizeof(item->iocb.iocb); + if (total_bytes > QLA_MAX_IOCB_SIZE) + total_bytes = QLA_MAX_IOCB_SIZE; pending_bytes = total_bytes; entry_count = entry_count_remaining = purex->entry_count; - no_bytes = (pending_bytes > sizeof(purex->els_frame_payload)) ? - sizeof(purex->els_frame_payload) : pending_bytes; + no_bytes = (pending_bytes > payload_size) ? + payload_size : pending_bytes; ql_log(ql_log_info, vha, 0x509a, "FPIN ELS, frame_size 0x%x, entry count %d\n", total_bytes, entry_count); @@ -1184,7 +1275,13 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt, fpin_pkt = &item->iocb; - memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes); + if (IS_QLA29XX(ha)) { + struct purex_entry_24xx_ext *purex_ext = *pkt; + + memcpy(fpin_pkt, &purex_ext->els_frame_payload[0], no_bytes); + } else { + memcpy(fpin_pkt, &purex->els_frame_payload[0], no_bytes); + } buffer_copy_offset += no_bytes; pending_bytes -= no_bytes; --entry_count_remaining; @@ -1194,36 +1291,24 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt, do { while ((total_bytes > 0) && (entry_count_remaining > 0)) { - if (rsp_q->ring_ptr->signature == RESPONSE_PROCESSED) { - ql_dbg(ql_dbg_async, vha, 0x5084, - "Ran out of IOCBs, partial data 0x%x\n", - buffer_copy_offset); - cpu_relax(); - continue; - } + *pkt = rsp_q->ring_ptr; + data = ((sts_cont_entry_t *)*pkt)->data; + data_sz = qla_sts_cont_data_size(ha); - new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr; - *pkt = new_pkt; - - if (new_pkt->entry_type != STATUS_CONT_TYPE) { + if (((sts_cont_entry_t *)*pkt)->entry_type != + STATUS_CONT_TYPE) { ql_log(ql_log_warn, vha, 0x507a, "Unexpected IOCB type, partial data 0x%x\n", buffer_copy_offset); break; } - rsp_q->ring_index++; - if (rsp_q->ring_index == rsp_q->length) { - rsp_q->ring_index = 0; - rsp_q->ring_ptr = rsp_q->ring; - } else { - rsp_q->ring_ptr++; - } - no_bytes = (pending_bytes > sizeof(new_pkt->data)) ? - sizeof(new_pkt->data) : pending_bytes; + qla_rsp_ring_advance(rsp_q); + no_bytes = (pending_bytes > data_sz) ? + data_sz : pending_bytes; if ((buffer_copy_offset + no_bytes) <= total_bytes) { memcpy(((uint8_t *)fpin_pkt + - buffer_copy_offset), new_pkt->data, + buffer_copy_offset), data, no_bytes); buffer_copy_offset += no_bytes; pending_bytes -= no_bytes; @@ -1233,11 +1318,11 @@ qla27xx_copy_fpin_pkt(struct scsi_qla_host *vha, void **pkt, "Attempt to copy more that we got, optimizing..%x\n", buffer_copy_offset); memcpy(((uint8_t *)fpin_pkt + - buffer_copy_offset), new_pkt->data, + buffer_copy_offset), data, total_bytes - buffer_copy_offset); } - ((response_t *)new_pkt)->signature = RESPONSE_PROCESSED; + ((response_t *)*pkt)->signature = RESPONSE_PROCESSED; wmb(); } @@ -2307,8 +2392,25 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, static void qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, - struct sts_entry_24xx *pkt, int iocb_type) + void *pkt, int iocb_type) { + /* + * els_sts_entry_24xx_ext (29xx) overlays els_sts_entry_24xx for every + * field touched in this completion handler: comp_status (offset 8), + * total_byte_count (32), error_subcode_1 (36), error_subcode_2 (40), + * d_id[]/s_id[] (24..29), control_flags (30) all sit at byte-identical + * offsets in both layouts (only vp_index/sof_type at offset 14-15 are + * bit-packed differently, and that field is write-only on the issue + * path -- we never read it here). All reads in this function are + * therefore stride-agnostic and go through a single struct + * els_sts_entry_24xx * view; the trailing reserved_4[] of the extended + * layout is irrelevant on completion. + * + * Likewise els_entry_24xx_ext overlays els_entry_24xx through + * control_flags (offset 30), so the SRB_ELS_CMD_HST_NOLOGIN ctl_flags + * read below also goes through the 24xx view. + */ + struct sts_entry_24xx *sts24 = pkt; struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt; const char func[] = "ELS_CT_IOCB"; const char *type; @@ -2331,9 +2433,9 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, type = NULL; - comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status); - fw_status[1] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_1); - fw_status[2] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_2); + comp_status = fw_status[0] = le16_to_cpu(sts24->comp_status); + fw_status[1] = le32_to_cpu(ese->error_subcode_1); + fw_status[2] = le32_to_cpu(ese->error_subcode_2); switch (sp->type) { case SRB_ELS_CMD_RPT: @@ -2343,17 +2445,18 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, case SRB_ELS_CMD_HST_NOLOGIN: type = "els"; { - struct els_entry_24xx *els = (void *)pkt; + __le16 ctl_flags = + ((struct els_entry_24xx *)pkt)->control_flags; struct qla_bsg_auth_els_request *p = (struct qla_bsg_auth_els_request *)bsg_job->request; ql_dbg(ql_dbg_user, vha, 0x700f, - "%s %s. portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n", + "%s %s complete portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n", __func__, sc_to_str(p->e.sub_cmd), e->d_id[2], e->d_id[1], e->d_id[0], comp_status, p->e.extra_rx_xchg_address, bsg_job); - if (!(le16_to_cpu(els->control_flags) & ECF_PAYLOAD_DESCR_MASK)) { + if (!(le16_to_cpu(ctl_flags) & ECF_PAYLOAD_DESCR_MASK)) { if (sp->remap.remapped) { n = sg_copy_from_buffer(bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, @@ -2409,14 +2512,15 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, } else { if (comp_status == CS_DATA_UNDERRUN) { res = DID_OK << 16; - els->u.els_plogi.len = cpu_to_le16(le32_to_cpu( - ese->total_byte_count)); + els->u.els_plogi.len = cpu_to_le16( + le32_to_cpu(ese->total_byte_count)); if (sp->remap.remapped && ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_ACC) { ql_dbg(ql_dbg_user, vha, 0x503f, "%s IOCB Done LS_ACC %02x%02x%02x -> %02x%02x%02x", - __func__, e->s_id[0], e->s_id[2], e->s_id[1], + __func__, + e->s_id[0], e->s_id[2], e->s_id[1], e->d_id[2], e->d_id[1], e->d_id[0]); logit = 0; } @@ -2444,8 +2548,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, ql_dbg(ql_dbg_user, vha, 0x503f, "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n", fw_status[1], fw_status[2], - le32_to_cpu(((struct els_sts_entry_24xx *) - pkt)->total_byte_count), + le32_to_cpu(ese->total_byte_count), e->s_id[0], e->s_id[2], e->s_id[1], e->d_id[2], e->d_id[1], e->d_id[0]); } @@ -2462,8 +2565,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, ql_log(ql_log_info, vha, 0x503f, "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n", fw_status[1], fw_status[2], - le32_to_cpu(((struct els_sts_entry_24xx *) - pkt)->total_byte_count), + le32_to_cpu(ese->total_byte_count), e->s_id[0], e->s_id[2], e->s_id[1], e->d_id[2], e->d_id[1], e->d_id[0]); } @@ -2516,8 +2618,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, } static void -qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, - struct logio_entry_24xx *logio) +qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, void *pkt) { const char func[] = "LOGIO-IOCB"; const char *type; @@ -2527,8 +2628,20 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, uint16_t *data; uint32_t iop[2]; int logit = 1; + struct qla_hw_data *ha = vha->hw; + /* + * logio_entry_24xx_ext overlays logio_entry_24xx through + * io_parameter[10]: comp_status, io_parameter[0..10] and + * entry_status are at identical offsets and types in both layouts + * (only vp_index width differs, and that field is write-only on + * the issue path). So all reads in this completion handler are + * stride-agnostic and we read through a single struct + * logio_entry_24xx * view; the trailing reserved_2[64] of the + * extended layout is irrelevant here. + */ + struct logio_entry_24xx *logio = pkt; - sp = qla2x00_get_sp_from_handle(vha, func, req, logio); + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (!sp) return; @@ -2548,7 +2661,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, fcport->d_id.b.area, fcport->d_id.b.al_pa, logio->entry_status); ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x504d, - logio, sizeof(*logio)); + pkt, qla_rsp_entry_size(ha)); goto logio_done; } @@ -2559,7 +2672,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, type, sp->handle, fcport->d_id.b24, fcport->port_name, le32_to_cpu(logio->io_parameter[0])); - vha->hw->exch_starvation = 0; + ha->exch_starvation = 0; data[0] = MBS_COMMAND_COMPLETE; if (sp->type == SRB_PRLI_CMD) { @@ -2598,6 +2711,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, iop[0] = le32_to_cpu(logio->io_parameter[0]); iop[1] = le32_to_cpu(logio->io_parameter[1]); + lio->u.logio.iop[0] = iop[0]; lio->u.logio.iop[1] = iop[1]; switch (iop[0]) { @@ -2622,14 +2736,14 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, data[0] = MBS_COMMAND_ERROR; break; case LSC_SCODE_NOXCB: - vha->hw->exch_starvation++; - if (vha->hw->exch_starvation > 5) { + ha->exch_starvation++; + if (ha->exch_starvation > 5) { ql_log(ql_log_warn, vha, 0xd046, "Exchange starvation. Resetting RISC\n"); - vha->hw->exch_starvation = 0; + ha->exch_starvation = 0; - if (IS_P3P_TYPE(vha->hw)) + if (IS_P3P_TYPE(ha)) set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); else set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); @@ -2645,16 +2759,12 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, ql_log(ql_log_warn, sp->vha, 0x5037, "Async-%s failed: " "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n", type, sp->handle, fcport->d_id.b24, fcport->port_name, - le16_to_cpu(logio->comp_status), - le32_to_cpu(logio->io_parameter[0]), - le32_to_cpu(logio->io_parameter[1])); + le16_to_cpu(logio->comp_status), iop[0], iop[1]); else ql_dbg(ql_dbg_disc, sp->vha, 0x5037, "Async-%s failed: " "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n", type, sp->handle, fcport->d_id.b24, fcport->port_name, - le16_to_cpu(logio->comp_status), - le32_to_cpu(logio->io_parameter[0]), - le32_to_cpu(logio->io_parameter[1])); + le16_to_cpu(logio->comp_status), iop[0], iop[1]); logio_done: sp->done(sp, 0); @@ -2669,7 +2779,9 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) srb_t *sp; struct srb_iocb *iocb; struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk; + struct qla_hw_data *ha = vha->hw; u16 comp_status; + struct qla_sts_fwi2 sf; sp = qla2x00_get_sp_from_handle(vha, func, req, tsk); if (!sp) @@ -2691,18 +2803,19 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) "Async-%s error - hdl=%x completion status(%x).\n", type, sp->handle, comp_status); iocb->u.tmf.data = QLA_FUNCTION_FAILED; - } else if ((le16_to_cpu(sts->scsi_status) & - SS_RESPONSE_INFO_LEN_VALID)) { - host_to_fcp_swap(sts->data, sizeof(sts->data)); - if (le32_to_cpu(sts->rsp_data_len) < 4) { - ql_log(ql_log_warn, fcport->vha, 0x503b, - "Async-%s error - hdl=%x not enough response(%d).\n", - type, sp->handle, sts->rsp_data_len); - } else if (sts->data[3]) { - ql_log(ql_log_warn, fcport->vha, 0x503c, - "Async-%s error - hdl=%x response(%x).\n", - type, sp->handle, sts->data[3]); - iocb->u.tmf.data = QLA_FUNCTION_FAILED; + } else { + qla_sts_fwi2_extract(ha, tsk, &sf); + if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) { + if (sf.rsp_data_len < 4) { + ql_log(ql_log_warn, fcport->vha, 0x503b, + "Async-%s error - hdl=%x not enough response(%d).\n", + type, sp->handle, sf.rsp_data_len); + } else if (sf.data[3]) { + ql_log(ql_log_warn, fcport->vha, 0x503c, + "Async-%s error - hdl=%x response(%x).\n", + type, sp->handle, sf.data[3]); + iocb->u.tmf.data = QLA_FUNCTION_FAILED; + } } } @@ -2731,7 +2844,8 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) if (iocb->u.tmf.data != QLA_SUCCESS) ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, sp->vha, 0x5055, - sts, sizeof(*sts)); + tsk, IS_QLA29XX(ha) ? + sizeof(struct sts_entry_24xx_ext) : sizeof(*sts)); sp->done(sp, 0); } @@ -2742,6 +2856,8 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, fc_port_t *fcport; struct srb_iocb *iocb; struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk; + struct sts_entry_24xx_ext *stsext = (struct sts_entry_24xx_ext *)tsk; + struct qla_hw_data *ha = vha->hw; uint16_t state_flags; struct nvmefc_fcp_req *fd; uint16_t ret = QLA_SUCCESS; @@ -2793,7 +2909,10 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, uint32_t *inbuf, *outbuf; uint16_t iter; - inbuf = (uint32_t *)&sts->nvme_ersp_data; + if (IS_QLA29XX(ha)) + inbuf = (uint32_t *)stsext->u2.nvme_ersp_data; + else + inbuf = (uint32_t *)&sts->nvme_ersp_data; outbuf = (uint32_t *)fd->rspaddr; iocb->u.nvme.rsp_pyld_len = sts->nvme_rsp_pyld_len; if (unlikely(le16_to_cpu(iocb->u.nvme.rsp_pyld_len) > @@ -2877,13 +2996,19 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, } static void qla_ctrlvp_completed(scsi_qla_host_t *vha, struct req_que *req, - struct vp_ctrl_entry_24xx *vce) + void *pkt) { const char func[] = "CTRLVP-IOCB"; + /* + * vp_ctrl_entry_24xx_ext overlays vp_ctrl_entry_24xx for all + * fields read here (entry_status, comp_status, vp_idx_failed), + * so the read goes through one struct vp_ctrl_entry_24xx * view. + */ + struct vp_ctrl_entry_24xx *vce = pkt; srb_t *sp; int rval = QLA_SUCCESS; - sp = qla2x00_get_sp_from_handle(vha, func, req, vce); + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); if (!sp) return; @@ -2936,7 +3061,8 @@ static void qla2x00_process_response_entry(struct scsi_qla_host *vha, sts22_entry->handle[cnt]); break; case STATUS_CONT_TYPE: - qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt); + qla2x00_status_cont_entry(rsp, pkt, + qla_sts_cont_data_size(rsp->hw)); break; case MBX_IOCB_TYPE: qla2x00_mbx_iocb_entry(vha, rsp->req, (struct mbx_entry *)pkt); @@ -2973,13 +3099,7 @@ qla2x00_process_response_queue(struct rsp_que *rsp) while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) { pkt = (sts_entry_t *)rsp->ring_ptr; - rsp->ring_index++; - if (rsp->ring_index == rsp->length) { - rsp->ring_index = 0; - rsp->ring_ptr = rsp->ring; - } else { - rsp->ring_ptr++; - } + qla_rsp_ring_advance(rsp); if (pkt->entry_status != 0) { qla2x00_error_entry(vha, rsp, pkt); @@ -3043,16 +3163,26 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, * to indicate to the kernel that the HBA detected error. */ static inline int -qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) +qla2x00_handle_dif_error(srb_t *sp, void *pkt) { struct scsi_qla_host *vha = sp->vha; + struct qla_hw_data *ha = vha->hw; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - uint8_t *ap = &sts24->data[12]; - uint8_t *ep = &sts24->data[20]; + struct sts_entry_24xx *sts24 = pkt; + struct sts_entry_24xx_ext *stsext = pkt; + uint8_t *ap, *ep; uint32_t e_ref_tag, a_ref_tag; uint16_t e_app_tag, a_app_tag; uint16_t e_guard, a_guard; + if (IS_QLA29XX(ha)) { + ap = stsext->act_dif; + ep = stsext->exp_dif; + } else { + ap = &sts24->data[12]; + ep = &sts24->data[20]; + } + /* * swab32 of the "data" field in the beginning of qla2x00_status_entry() * would make guard field appear at offset 2 @@ -3065,7 +3195,7 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) e_ref_tag = get_unaligned_le32(ep + 4); ql_dbg(ql_dbg_io, vha, 0x3023, - "iocb(s) %p Returned STATUS.\n", sts24); + "iocb(s) %px Returned STATUS.\n", pkt); ql_dbg(ql_dbg_io, vha, 0x3024, "DIF ERROR in cmd 0x%x lba 0x%llx act ref" @@ -3197,7 +3327,9 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt, if (IS_FWI2_CAPABLE(ha)) { comp_status = le16_to_cpu(sts24->comp_status); - scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; + scsi_status = IS_QLA29XX(ha) ? + le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK : + le16_to_cpu(sts24->scsi_status) & SS_MASK; } else { comp_status = le16_to_cpu(sts->comp_status); scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; @@ -3333,10 +3465,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) int res = 0; uint16_t state_flags = 0; uint16_t sts_qual = 0; + struct qla_sts_fwi2 sf; if (IS_FWI2_CAPABLE(ha)) { comp_status = le16_to_cpu(sts24->comp_status); - scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; + scsi_status = IS_QLA29XX(ha) ? + le16_to_cpu(((struct sts_entry_24xx_ext *)pkt)->u2.scsi_status) & SS_MASK : + le16_to_cpu(sts24->scsi_status) & SS_MASK; state_flags = le16_to_cpu(sts24->state_flags); } else { comp_status = le16_to_cpu(sts->comp_status); @@ -3406,8 +3541,16 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) return; } + /* Everything below is the SCSI fast path; reject other SRB types. */ + if (sp->type != SRB_SCSI_CMD) { + ql_dbg(ql_dbg_io, vha, 0x303d, + "Unexpected SRB type %x for status IOCB, sp %p.\n", + sp->type, sp); + return; + } + /* Fast path completion. */ - qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24); + qla_chk_edif_rx_sa_delete_pending(vha, sp, pkt); sp->qpair->cmd_completion_cnt++; if (comp_status == CS_COMPLETE && scsi_status == 0) { @@ -3434,20 +3577,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) sense_len = par_sense_len = rsp_info_len = resid_len = fw_resid_len = 0; if (IS_FWI2_CAPABLE(ha)) { + qla_sts_fwi2_extract(ha, pkt, &sf); if (scsi_status & SS_SENSE_LEN_VALID) - sense_len = le32_to_cpu(sts24->sense_len); + sense_len = sf.sense_len; if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) - rsp_info_len = le32_to_cpu(sts24->rsp_data_len); + rsp_info_len = sf.rsp_data_len; if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) - resid_len = le32_to_cpu(sts24->rsp_residual_count); + resid_len = sf.rsp_residual_count; if (comp_status == CS_DATA_UNDERRUN) fw_resid_len = le32_to_cpu(sts24->residual_len); - rsp_info = sts24->data; - sense_data = sts24->data; - host_to_fcp_swap(sts24->data, sizeof(sts24->data)); + rsp_info = sf.data; + sense_data = sf.data; + par_sense_len = sf.data_sz; + sts_qual = sf.sts_qual; ox_id = le16_to_cpu(sts24->ox_id); - par_sense_len = sizeof(sts24->data); - sts_qual = le16_to_cpu(sts24->status_qualifier); } else { if (scsi_status & SS_SENSE_LEN_VALID) sense_len = le16_to_cpu(sts->req_sense_length); @@ -3463,6 +3606,18 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) { /* Sense data lies beyond any FCP RESPONSE data. */ if (IS_FWI2_CAPABLE(ha)) { + /* + * A hostile or buggy target may report an + * rsp_info_len larger than the IOCB data area. + * Clamp it so the par_sense_len subtraction cannot + * underflow and walk sense_data out of bounds. + */ + if (rsp_info_len > par_sense_len) { + ql_log(ql_log_warn, fcport->vha, 0x3107, + "Truncating bogus rsp_info_len 0x%x to 0x%x.\n", + rsp_info_len, par_sense_len); + rsp_info_len = par_sense_len; + } sense_data += rsp_info_len; par_sense_len -= rsp_info_len; } @@ -3647,7 +3802,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) break; case CS_DIF_ERROR: - logit = qla2x00_handle_dif_error(sp, sts24); + logit = qla2x00_handle_dif_error(sp, pkt); res = cp->result; break; @@ -3672,7 +3827,8 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ox_id, cp->cmnd, scsi_bufflen(cp), rsp_info_len, resid_len, fw_resid_len, sp, cp); ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee, - pkt, sizeof(*sts24)); + pkt, IS_QLA29XX(ha) ? + sizeof(struct sts_entry_24xx_ext) : sizeof(*sts24)); res = DID_ERROR << 16; vha->hw_err_cnt++; break; @@ -3702,11 +3858,12 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) * qla2x00_status_cont_entry() - Process a Status Continuations entry. * @rsp: response queue * @pkt: Entry pointer + * @data_sz: maximum sense-data payload size for this entry * * Extended sense data. */ static void -qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) +qla2x00_status_cont_entry(struct rsp_que *rsp, void *pkt, u32 data_sz) { uint8_t sense_sz = 0; struct qla_hw_data *ha = rsp->hw; @@ -3715,6 +3872,7 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) struct scsi_cmnd *cp; uint32_t sense_len; uint8_t *sense_ptr; + u8 *data = ((sts_cont_entry_t *)pkt)->data; if (!sp || !GET_CMD_SENSE_LEN(sp)) return; @@ -3731,15 +3889,15 @@ qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) return; } - if (sense_len > sizeof(pkt->data)) - sense_sz = sizeof(pkt->data); + if (sense_len > data_sz) + sense_sz = data_sz; else sense_sz = sense_len; /* Move sense data. */ if (IS_FWI2_CAPABLE(ha)) - host_to_fcp_swap(pkt->data, sizeof(pkt->data)); - memcpy(sense_ptr, pkt->data, sense_sz); + host_to_fcp_swap(data, data_sz); + memcpy(sense_ptr, data, sense_sz); ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c, sense_ptr, sense_sz); @@ -3778,10 +3936,12 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt) "iocb type %xh with error status %xh, handle %xh, rspq id %d\n", pkt->entry_type, pkt->entry_status, pkt->handle, rsp->id); - if (que >= ha->max_req_queues || !ha->req_q_map[que]) + if (que >= ha->max_req_queues) goto fatal; req = ha->req_q_map[que]; + if (!req) + goto fatal; if (pkt->entry_status & RF_BUSY) res = DID_BUS_BUSY << 16; @@ -3864,7 +4024,7 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) static void qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, - struct abort_entry_24xx *pkt) + void *pkt) { const char func[] = "ABT_IOCB"; srb_t *sp; @@ -3876,7 +4036,14 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, return; abt = &sp->u.iocb_cmd; - abt->u.abt.comp_status = pkt->comp_status; + /* + * abort_entry_24xx_ext overlays abort_entry_24xx through the + * nport_handle/comp_status union at offset 8, so reading + * comp_status is stride-agnostic and goes through the 24xx view. + */ + abt->u.abt.comp_status = + ((struct abort_entry_24xx *)pkt)->comp_status; + orig_sp = sp->cmd_sp; /* Need to pass original sp */ if (orig_sp) @@ -3886,7 +4053,7 @@ qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, } void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, - struct pt_ls4_request *pkt, struct req_que *req) + void *pkt, struct req_que *req) { srb_t *sp; const char func[] = "LS4_IOCB"; @@ -3896,7 +4063,9 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, if (!sp) return; - comp_status = le16_to_cpu(pkt->status); + /* status lives at the same offset (8) in both IOCB strides */ + comp_status = le16_to_cpu(((struct pt_ls4_request *)pkt)->status); + sp->done(sp, comp_status); } @@ -3941,9 +4110,10 @@ static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha, } static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, - struct mrk_entry_24xx *pkt) + void *pkt) { const char func[] = "MRK-IOCB"; + struct mrk_entry_24xx *mrk = pkt; srb_t *sp; int res = QLA_SUCCESS; @@ -3954,7 +4124,7 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, if (!sp) return; - if (pkt->entry_status) { + if (mrk->entry_status) { ql_dbg(ql_dbg_taskm, vha, 0x8025, "marker failure.\n"); res = QLA_COMMAND_ERROR; } @@ -3970,8 +4140,9 @@ static void qla_marker_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void qla24xx_process_response_queue(struct scsi_qla_host *vha, struct rsp_que *rsp) { - struct sts_entry_24xx *pkt; + void *pkt; struct qla_hw_data *ha = vha->hw; + struct purex_entry_24xx_ext *purex_entry_ext; struct purex_entry_24xx *purex_entry; struct purex_item *pure_item; struct pt_ls4_rx_unsol *p; @@ -4000,19 +4171,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, while (rsp->ring_index != rsp_in && rsp->ring_ptr->signature != RESPONSE_PROCESSED) { - pkt = (struct sts_entry_24xx *)rsp->ring_ptr; + pkt = (void *)rsp->ring_ptr; cur_ring_index = rsp->ring_index; - rsp->ring_index++; - if (rsp->ring_index == rsp->length) { - rsp->ring_index = 0; - rsp->ring_ptr = rsp->ring; - } else { - rsp->ring_ptr++; - } + qla_rsp_ring_advance(rsp); - if (pkt->entry_status != 0) { - if (qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt)) + if (((response_t *)pkt)->entry_status != 0) { + if (qla2x00_error_entry(vha, rsp, (sts_entry_t *)pkt)) goto process_err; ((response_t *)pkt)->signature = RESPONSE_PROCESSED; @@ -4021,20 +4186,19 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, } process_err: - switch (pkt->entry_type) { + switch (((response_t *)pkt)->entry_type) { case STATUS_TYPE: qla2x00_status_entry(vha, rsp, pkt); break; case STATUS_CONT_TYPE: - qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt); + qla2x00_status_cont_entry(rsp, pkt, + qla_sts_cont_data_size(rsp->hw)); break; case VP_RPT_ID_IOCB_TYPE: - qla24xx_report_id_acquisition(vha, - (struct vp_rpt_id_entry_24xx *)pkt); + qla24xx_report_id_acquisition(vha, pkt); break; case LOGINOUT_PORT_IOCB_TYPE: - qla24xx_logio_entry(vha, rsp->req, - (struct logio_entry_24xx *)pkt); + qla24xx_logio_entry(vha, rsp->req, pkt); break; case CT_IOCB_TYPE: qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE); @@ -4067,11 +4231,10 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, qlt_response_pkt_all_vps(vha, rsp, (response_t *)pkt); break; case PT_LS4_REQUEST: - qla24xx_nvme_ls4_iocb(vha, (struct pt_ls4_request *)pkt, - rsp->req); + qla24xx_nvme_ls4_iocb(vha, pkt, rsp->req); break; case NOTIFY_ACK_TYPE: - if (pkt->handle == QLA_TGT_SKIP_HANDLE) + if (((response_t *)pkt)->handle == QLA_TGT_SKIP_HANDLE) qlt_response_pkt_all_vps(vha, rsp, (response_t *)pkt); else @@ -4079,23 +4242,29 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, (struct nack_to_isp *)pkt); break; case MARKER_TYPE: - qla_marker_iocb_entry(vha, rsp->req, (struct mrk_entry_24xx *)pkt); + qla_marker_iocb_entry(vha, rsp->req, pkt); break; case ABORT_IOCB_TYPE: - qla24xx_abort_iocb_entry(vha, rsp->req, - (struct abort_entry_24xx *)pkt); + qla24xx_abort_iocb_entry(vha, rsp->req, pkt); break; case MBX_IOCB_TYPE: qla24xx_mbx_iocb_entry(vha, rsp->req, (struct mbx_24xx_entry *)pkt); break; case VP_CTRL_IOCB_TYPE: - qla_ctrlvp_completed(vha, rsp->req, - (struct vp_ctrl_entry_24xx *)pkt); + qla_ctrlvp_completed(vha, rsp->req, pkt); break; case PUREX_IOCB_TYPE: - purex_entry = (void *)pkt; - switch (purex_entry->els_frame_payload[3]) { + if (IS_QLA29XX(ha)) { + purex_entry_ext = (void *)pkt; + purex_entry = NULL; + } else { + purex_entry = (void *)pkt; + purex_entry_ext = NULL; + } + switch (IS_QLA29XX(ha) ? + purex_entry_ext->els_frame_payload[3] : + purex_entry->els_frame_payload[3]) { case ELS_RDP: pure_item = qla24xx_copy_std_pkt(vha, pkt); if (!pure_item) @@ -4109,9 +4278,24 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, "SCM not active for this port\n"); break; } + if (qla_chk_cont_iocb_avail(vha, rsp, + (response_t *)pkt, rsp_in)) { + /* + * ring_ptr and ring_index were + * pre-incremented above. Reset them + * back to current. Wait for next + * interrupt with all IOCBs to arrive + * and re-process. + */ + qla_rsp_ring_rewind_to(rsp, + (response_t *)pkt, cur_ring_index); + + ql_dbg(ql_dbg_init, vha, 0x5095, + "Defer processing FPIN...\n"); + return; + } pure_item = qla27xx_copy_fpin_pkt(vha, (void **)&pkt, &rsp); - __update_rsp_in(is_shadow_hba, rsp, rsp_in); if (!pure_item) break; qla24xx_queue_purex_item(vha, pure_item, @@ -4127,11 +4311,13 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, * interrupt with all IOCBs to arrive * and re-process. */ - rsp->ring_ptr = (response_t *)pkt; - rsp->ring_index = cur_ring_index; + qla_rsp_ring_rewind_to(rsp, + (response_t *)pkt, cur_ring_index); ql_dbg(ql_dbg_init, vha, 0x5091, "Defer processing ELS opcode %#x...\n", + IS_QLA29XX(ha) ? + purex_entry_ext->els_frame_payload[3] : purex_entry->els_frame_payload[3]); return; } @@ -4139,7 +4325,9 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, break; default: ql_log(ql_log_warn, vha, 0x509c, - "Discarding ELS Request opcode 0x%x\n", + "Discarding ELS Request opcode 0x%x...\n", + IS_QLA29XX(ha) ? + purex_entry_ext->els_frame_payload[3] : purex_entry->els_frame_payload[3]); } break; @@ -4150,8 +4338,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, case PT_LS4_UNSOL: p = (void *)pkt; if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) { - rsp->ring_ptr = (response_t *)pkt; - rsp->ring_index = cur_ring_index; + qla_rsp_ring_rewind_to(rsp, (response_t *)pkt, + cur_ring_index); ql_dbg(ql_dbg_init, vha, 0x2124, "Defer processing UNSOL LS req opcode %#x...\n", @@ -4164,7 +4352,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, /* Type Not Supported. */ ql_dbg(ql_dbg_async, vha, 0x5042, "Received unknown response pkt type 0x%x entry status=%x.\n", - pkt->entry_type, pkt->entry_status); + ((response_t *)pkt)->entry_type, + ((response_t *)pkt)->entry_status); break; } ((response_t *)pkt)->signature = RESPONSE_PROCESSED; @@ -4548,10 +4737,10 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) ha->msix_count = ret; /* Recalculate queue values */ if (ha->mqiobase && (ql2xmqsupport || ql2xnvmeenable)) { - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); /* ATIOQ needs 1 vector. That's 1 less QPair */ - if (QLA_TGT_MODE_ENABLED()) + if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1) ha->max_req_queues--; ha->max_rsp_queues = ha->max_req_queues; @@ -4663,7 +4852,8 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) /* If possible, enable MSI-X. */ if (ql2xenablemsix == 0 || (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) && - !IS_QLAFX00(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha))) + !IS_QLAFX00(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && + !IS_QLA29XX(ha))) goto skip_msi; if (ql2xenablemsix == 2) @@ -4702,7 +4892,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && !IS_QLA8001(ha) && !IS_P3P_TYPE(ha) && !IS_QLAFX00(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && !IS_QLA29XX(ha)) goto skip_msi; ret = pci_alloc_irq_vectors(ha->pdev, 1, 1, PCI_IRQ_MSI); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 44e310f1a370..59ec5605930b 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -618,7 +618,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) int qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr, - uint32_t risc_code_size) + uint32_t risc_code_size, int opt) { int rval; struct qla_hw_data *ha = vha->hw; @@ -651,7 +651,14 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr, mcp->out_mb |= MBX_4; } - mcp->in_mb = MBX_1|MBX_0; + if (IS_QLA29XX(ha)) { + mcp->mb[9] = opt; + mcp->out_mb |= MBX_9; + mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; + } else { + mcp->in_mb = MBX_1|MBX_0; + } + mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); @@ -692,6 +699,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) { int rval; struct qla_hw_data *ha = vha->hw; + struct nvram_81xx *nv = ha->nvram; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; u8 semaphore = 0; @@ -720,14 +728,13 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) ha->lr_distance << LR_DIST_FW_POS; } - if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha))) + if (ql2xnvmeenable && (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha))) mcp->mb[4] |= NVME_ENABLE_FLAG; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { - struct nvram_81xx *nv = ha->nvram; + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { /* set minimum speed if specified in nvram */ if (nv->min_supported_speed >= 2 && - nv->min_supported_speed <= 5) { + nv->min_supported_speed <= 7) { mcp->mb[4] |= BIT_4; mcp->mb[11] |= nv->min_supported_speed & 0xF; mcp->out_mb |= MBX_11; @@ -765,7 +772,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - if (IS_QLA28XX(ha) && rval == QLA_COMMAND_ERROR && + if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && rval == QLA_COMMAND_ERROR && mcp->mb[1] == 0x27 && retry) { semaphore = 1; retry--; @@ -793,17 +800,20 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) ql_dbg(ql_dbg_mbx, vha, 0x119a, "fw_ability_mask=%x.\n", ha->fw_ability_mask); ql_dbg(ql_dbg_mbx, vha, 0x1027, "exchanges=%x.\n", mcp->mb[1]); - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { - ha->max_supported_speed = mcp->mb[2] & (BIT_0|BIT_1); + + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { + ha->max_supported_speed = mcp->mb[2] & (BIT_0|BIT_1|BIT_2|BIT_3); ql_dbg(ql_dbg_mbx, vha, 0x119b, "max_supported_speed=%s.\n", ha->max_supported_speed == 0 ? "16Gps" : ha->max_supported_speed == 1 ? "32Gps" : - ha->max_supported_speed == 2 ? "64Gps" : "unknown"); + ha->max_supported_speed == 2 ? "64Gps" : + ha->max_supported_speed == 3 ? "128Gps" : "unknown"); if (vha->min_supported_speed) { ha->min_supported_speed = mcp->mb[5] & - (BIT_0 | BIT_1 | BIT_2); + (BIT_0 | BIT_1 | BIT_2 | BIT_3); ql_dbg(ql_dbg_mbx, vha, 0x119c, "min_supported_speed=%s.\n", + ha->min_supported_speed == 7 ? "128Gps" : ha->min_supported_speed == 6 ? "64Gps" : ha->min_supported_speed == 5 ? "32Gps" : ha->min_supported_speed == 4 ? "16Gps" : @@ -812,7 +822,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) } } - if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) { + if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && (mcp->mb[5] & EDIF_HW_SUPPORT)) { ha->flags.edif_hw = 1; ql_log(ql_log_info, vha, 0xffff, "%s: edif HW\n", __func__); @@ -846,15 +856,19 @@ qla28xx_load_flash_firmware(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA28XX(ha)) + if (!IS_QLA28XX(ha) && !IS_QLA29XX(ha)) return rval; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x11a6, "Entered %s.\n", __func__); mcp->mb[0] = MBC_LOAD_FLASH_FIRMWARE; + mcp->mb[1] = 0; mcp->out_mb = MBX_2 | MBX_1 | MBX_0; - mcp->in_mb = MBX_0; + if (IS_QLA29XX(ha)) + mcp->in_mb = MBX_1 | MBX_0; + else + mcp->in_mb = MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); @@ -1125,7 +1139,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8; if (IS_FWI2_CAPABLE(ha)) mcp->in_mb |= MBX_17|MBX_16|MBX_15; - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) mcp->in_mb |= MBX_25|MBX_24|MBX_23|MBX_22|MBX_21|MBX_20|MBX_19|MBX_18| MBX_14|MBX_13|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7; @@ -1201,7 +1215,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) vha->flags.nvme2_enabled = 1; } - if (IS_QLA28XX(ha) && ha->flags.edif_hw && ql2xsecenable && + if ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ha->flags.edif_hw && ql2xsecenable && (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_EDIF)) { ha->flags.edif_enabled = 1; ql_log(ql_log_info, vha, 0xffff, @@ -1209,7 +1223,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) } } - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { ha->serdes_version[0] = mcp->mb[7] & 0xff; ha->serdes_version[1] = mcp->mb[8] >> 8; ha->serdes_version[2] = mcp->mb[8] & 0xff; @@ -1223,7 +1237,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20]; ha->fw_ddr_ram_start = (mcp->mb[23] << 16) | mcp->mb[22]; ha->fw_ddr_ram_end = (mcp->mb[25] << 16) | mcp->mb[24]; - if (IS_QLA28XX(ha)) { + if (IS_QLA28XX(ha) || IS_QLA29XX(ha)) { if (mcp->mb[16] & BIT_10) ha->flags.secure_fw = 1; @@ -1758,7 +1772,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10; if (IS_FWI2_CAPABLE(vha->hw)) mcp->in_mb |= MBX_19|MBX_18|MBX_17|MBX_16; - if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) + if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw)) mcp->in_mb |= MBX_15|MBX_21|MBX_22|MBX_23; mcp->tov = MBX_TOV_SECONDS; @@ -1813,7 +1827,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, } } - if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) { + if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw) || IS_QLA29XX(vha->hw)) { vha->bbcr = mcp->mb[15]; if (mcp->mb[7] & SCM_EDC_ACC_RECEIVED) { ql_log(ql_log_info, vha, 0x11a4, @@ -1954,7 +1968,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) /* 1 and 2 should normally be captured. */ mcp->in_mb = MBX_2|MBX_1|MBX_0; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) /* mb3 is additional info about the installed SFP. */ mcp->in_mb |= MBX_3; mcp->buf_size = size; @@ -1978,7 +1992,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) 0x0104d, ha->ex_init_cb, sizeof(*ha->ex_init_cb)); } } else { - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { if (mcp->mb[2] == 6 || mcp->mb[3] == 2) ql_dbg(ql_dbg_mbx, vha, 0x119d, "Invalid SFP/Validation Failed\n"); @@ -2262,6 +2276,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) if (!ha->flags.fw_started) return QLA_FUNCTION_FAILED; + memset(&mc, 0, sizeof(mc)); + mcp->mb[0] = MBC_GET_FIRMWARE_STATE; mcp->out_mb = MBX_0; if (IS_FWI2_CAPABLE(vha->hw)) @@ -2269,7 +2285,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) else mcp->in_mb = MBX_1|MBX_0; - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { mcp->mb[12] = 0; mcp->out_mb |= MBX_12; mcp->in_mb |= MBX_12; @@ -2287,7 +2303,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) states[3] = mcp->mb[4]; states[4] = mcp->mb[5]; states[5] = mcp->mb[6]; /* DPORT status */ - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) states[11] = mcp->mb[12]; /* MPI state. */ } @@ -2295,7 +2311,7 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) /*EMPTY*/ ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval); } else { - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { if (mcp->mb[2] == 6 || mcp->mb[3] == 2) ql_dbg(ql_dbg_mbx, vha, 0x119e, "Invalid SFP/Validation Failed\n"); @@ -2559,7 +2575,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt) { int rval; - + void *lg_buf; struct logio_entry_24xx *lg; dma_addr_t lg_dma; uint32_t iop[2]; @@ -2574,12 +2590,13 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, else req = ha->req_q_map[0]; - lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); - if (lg == NULL) { + lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); + if (!lg_buf) { ql_log(ql_log_warn, vha, 0x1062, "Failed to allocate login IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } + lg = lg_buf; lg->entry_type = LOGINOUT_PORT_IOCB_TYPE; lg->entry_count = 1; @@ -2593,8 +2610,9 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; - lg->vp_index = vha->vp_idx; - rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0, + qla_logio_set_vp_index(ha, lg, vha->vp_idx); + + rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0, (ha->r_a_tov / 10 * 2) + 2); if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x1063, @@ -2664,7 +2682,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, */ } - dma_pool_free(ha->s_dma_pool, lg, lg_dma); + dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma); return rval; } @@ -2835,6 +2853,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, uint8_t area, uint8_t al_pa) { int rval; + void *lg_buf; struct logio_entry_24xx *lg; dma_addr_t lg_dma; struct qla_hw_data *ha = vha->hw; @@ -2843,12 +2862,13 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d, "Entered %s.\n", __func__); - lg = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); - if (lg == NULL) { + lg_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); + if (!lg_buf) { ql_log(ql_log_warn, vha, 0x106e, "Failed to allocate logout IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } + lg = lg_buf; req = vha->req; lg->entry_type = LOGINOUT_PORT_IOCB_TYPE; @@ -2861,8 +2881,9 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; - lg->vp_index = vha->vp_idx; - rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0, + qla_logio_set_vp_index(ha, lg, vha->vp_idx); + + rval = qla2x00_issue_iocb_timeout(vha, lg_buf, lg_dma, 0, (ha->r_a_tov / 10 * 2) + 2); if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x106f, @@ -2884,7 +2905,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, "Done %s.\n", __func__); } - dma_pool_free(ha->s_dma_pool, lg, lg_dma); + dma_pool_free(ha->s_dma_pool, lg_buf, lg_dma); return rval; } @@ -3079,7 +3100,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha) mcp->out_mb = MBX_0; mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || - IS_QLA27XX(ha) || IS_QLA28XX(ha)) + IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) mcp->in_mb |= MBX_12; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; @@ -3304,7 +3326,7 @@ qla24xx_abort_command(srb_t *sp) { int rval; unsigned long flags = 0; - + void *abt_buf; struct abort_entry_24xx *abt; dma_addr_t abt_dma; uint32_t handle; @@ -3336,28 +3358,42 @@ qla24xx_abort_command(srb_t *sp) return QLA_ERR_NOT_FOUND; } - abt = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma); - if (abt == NULL) { + abt_buf = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma); + if (abt_buf == NULL) { ql_log(ql_log_warn, vha, 0x108d, "Failed to allocate abort IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } + abt = abt_buf; + /* + * abort_entry_24xx_ext overlays abort_entry_24xx through + * req_que_no (offsets 0-17), and entry_status (offset 3) and + * comp_status (offset 8) sit at identical offsets in both, so + * the common header writes and the completion-status reads are + * stride-agnostic and go through the 24xx view. Only port_id + * (24xx-only) and vp_index width / offset diverge per stride. + */ abt->entry_type = ABORT_IOCB_TYPE; abt->entry_count = 1; abt->handle = make_handle(req->id, abt->handle); abt->nport_handle = cpu_to_le16(fcport->loop_id); abt->handle_to_abort = make_handle(req->id, handle); - abt->port_id[0] = fcport->d_id.b.al_pa; - abt->port_id[1] = fcport->d_id.b.area; - abt->port_id[2] = fcport->d_id.b.domain; - abt->vp_index = fcport->vha->vp_idx; - abt->req_que_no = cpu_to_le16(req->id); + if (IS_QLA29XX(ha)) { + ((struct abort_entry_24xx_ext *)abt)->vp_index = + cpu_to_le16(fcport->vha->vp_idx); + } else { + abt->port_id[0] = fcport->d_id.b.al_pa; + abt->port_id[1] = fcport->d_id.b.area; + abt->port_id[2] = fcport->d_id.b.domain; + abt->vp_index = fcport->vha->vp_idx; + } + /* Need to pass original sp */ - qla_nvme_abort_set_option(abt, sp); + qla_nvme_abort_set_option(abt_buf, sp); - rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0); + rval = qla2x00_issue_iocb(vha, abt_buf, abt_dma, 0); if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x108e, "Failed to issue IOCB (%x).\n", rval); @@ -3366,11 +3402,11 @@ qla24xx_abort_command(srb_t *sp) "Failed to complete IOCB -- error status (%x).\n", abt->entry_status); rval = QLA_FUNCTION_FAILED; - } else if (abt->nport_handle != cpu_to_le16(0)) { + } else if (abt->comp_status != cpu_to_le16(0)) { ql_dbg(ql_dbg_mbx, vha, 0x1090, "Failed to complete IOCB -- completion status (%x).\n", - le16_to_cpu(abt->nport_handle)); - if (abt->nport_handle == cpu_to_le16(CS_IOCB_ERROR)) + le16_to_cpu(abt->comp_status)); + if (abt->comp_status == cpu_to_le16(CS_IOCB_ERROR)) rval = QLA_FUNCTION_PARAMETER_ERROR; else rval = QLA_FUNCTION_FAILED; @@ -3378,12 +3414,13 @@ qla24xx_abort_command(srb_t *sp) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091, "Done %s.\n", __func__); } + if (rval == QLA_SUCCESS) - qla_nvme_abort_process_comp_status(abt, sp); + qla_nvme_abort_process_comp_status(abt_buf, sp); qla_wait_nvme_release_cmd_kref(sp); - dma_pool_free(ha->s_dma_pool, abt, abt_dma); + dma_pool_free(ha->s_dma_pool, abt_buf, abt_dma); return rval; } @@ -3391,7 +3428,9 @@ qla24xx_abort_command(srb_t *sp) struct tsk_mgmt_cmd { union { struct tsk_mgmt_entry tsk; + struct tsk_mgmt_entry_ext tsk_ext; struct sts_entry_24xx sts; + struct sts_entry_24xx_ext sts_ext; } p; }; @@ -3402,6 +3441,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, int rval, rval2; struct tsk_mgmt_cmd *tsk; struct sts_entry_24xx *sts; + struct qla_sts_fwi2 sf; dma_addr_t tsk_dma; scsi_qla_host_t *vha; struct qla_hw_data *ha; @@ -3428,16 +3468,25 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, return QLA_MEMORY_ALLOC_FAILED; } + /* + * tsk_mgmt_entry_ext overlays tsk_mgmt_entry through control_flags; + * the common-header writes go through tsk->p.tsk and only port_id + * (24xx-only) and vp_index width / offset diverge. + */ tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE; tsk->p.tsk.entry_count = 1; tsk->p.tsk.handle = make_handle(req->id, tsk->p.tsk.handle); tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id); tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2); tsk->p.tsk.control_flags = cpu_to_le32(type); - tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa; - tsk->p.tsk.port_id[1] = fcport->d_id.b.area; - tsk->p.tsk.port_id[2] = fcport->d_id.b.domain; - tsk->p.tsk.vp_index = fcport->vha->vp_idx; + if (IS_QLA29XX(ha)) { + tsk->p.tsk_ext.vp_index = cpu_to_le16(fcport->vha->vp_idx); + } else { + tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa; + tsk->p.tsk.port_id[1] = fcport->d_id.b.area; + tsk->p.tsk.port_id[2] = fcport->d_id.b.domain; + tsk->p.tsk.vp_index = fcport->vha->vp_idx; + } if (type == TCF_LUN_RESET) { int_to_scsilun(l, &tsk->p.tsk.lun); host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun, @@ -3459,18 +3508,20 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, "Failed to complete IOCB -- completion status (%x).\n", le16_to_cpu(sts->comp_status)); rval = QLA_FUNCTION_FAILED; - } else if (le16_to_cpu(sts->scsi_status) & - SS_RESPONSE_INFO_LEN_VALID) { - if (le32_to_cpu(sts->rsp_data_len) < 4) { - ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097, - "Ignoring inconsistent data length -- not enough " - "response info (%d).\n", - le32_to_cpu(sts->rsp_data_len)); - } else if (sts->data[3]) { - ql_dbg(ql_dbg_mbx, vha, 0x1098, - "Failed to complete IOCB -- response (%x).\n", - sts->data[3]); - rval = QLA_FUNCTION_FAILED; + } else { + qla_sts_fwi2_extract(ha, sts, &sf); + if (sf.scsi_status & SS_RESPONSE_INFO_LEN_VALID) { + if (sf.rsp_data_len < 4) { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, + 0x1097, + "Ignoring inconsistent data length -- not enough response info (%d).\n", + sf.rsp_data_len); + } else if (sf.data[3]) { + ql_dbg(ql_dbg_mbx, vha, 0x1098, + "Failed to complete IOCB -- response (%x).\n", + sf.data[3]); + rval = QLA_FUNCTION_FAILED; + } } } @@ -3551,7 +3602,8 @@ qla2x00_write_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t data) mbx_cmd_t *mcp = &mc; if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) && - !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw)) + !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw) && + !IS_QLA29XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1182, @@ -3590,7 +3642,8 @@ qla2x00_read_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t *data) mbx_cmd_t *mcp = &mc; if (!IS_QLA25XX(vha->hw) && !IS_QLA2031(vha->hw) && - !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw)) + !IS_QLA27XX(vha->hw) && !IS_QLA28XX(vha->hw) && + !IS_QLA29XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1185, @@ -3860,7 +3913,7 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma, if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) && - !IS_QLA28XX(vha->hw)) + !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw)) return QLA_FUNCTION_FAILED; if (unlikely(pci_channel_offline(vha->hw->pdev))) @@ -4035,15 +4088,18 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, } void -qla24xx_report_id_acquisition(scsi_qla_host_t *vha, - struct vp_rpt_id_entry_24xx *rptid_entry) +qla24xx_report_id_acquisition(scsi_qla_host_t *vha, void *pkt) { struct qla_hw_data *ha = vha->hw; + struct vp_rpt_id_entry_24xx *rptid_entry = pkt; + struct vp_rpt_id_entry_24xx_ext *rptid_entry_ext = pkt; scsi_qla_host_t *vp = NULL; unsigned long flags; int found; port_id_t id; struct fc_port *fcport; + u16 vp_idx; + u8 vp_status; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6, "Entered %s.\n", __func__); @@ -4051,6 +4107,16 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, if (rptid_entry->entry_status != 0) return; + if (IS_QLA29XX(ha)) { + vp_idx = le16_to_cpu(rptid_entry_ext->vp_idx_status) & + EXT_VP_STATUS_VP_INDEX_MASK; + vp_status = (le16_to_cpu(rptid_entry_ext->vp_idx_status) >> + EXT_VP_STATUS_VP_STATUS_SHIFT) & 0x7f; + } else { + vp_idx = rptid_entry->vp_idx; + vp_status = rptid_entry->vp_status; + } + id.b.domain = rptid_entry->port_id[2]; id.b.area = rptid_entry->port_id[1]; id.b.al_pa = rptid_entry->port_id[0]; @@ -4073,9 +4139,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, } else if (rptid_entry->format == 1) { /* fabric */ ql_dbg(ql_dbg_async, vha, 0x10b9, - "Format 1: VP[%d] enabled - status %d - with " - "port id %02x%02x%02x.\n", rptid_entry->vp_idx, - rptid_entry->vp_status, + "Format 1: VP[%d] enabled - status %d - with port id %02x%02x%02x.\n", + vp_idx, vp_status, rptid_entry->port_id[2], rptid_entry->port_id[1], rptid_entry->port_id[0]); ql_dbg(ql_dbg_async, vha, 0x5075, @@ -4170,10 +4235,11 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, ha->flags.gpsc_supported = 1; ha->current_topology = ISP_CFG_F; /* buffer to buffer credit flag */ - vha->flags.bbcr_enable = (rptid_entry->u.f1.bbcr & 0xf) != 0; + vha->flags.bbcr_enable = + (le16_to_cpu(rptid_entry->u.f1.bbcr) & 0xf) != 0; - if (rptid_entry->vp_idx == 0) { - if (rptid_entry->vp_status == VP_STAT_COMPL) { + if (vp_idx == 0) { + if (vp_status == VP_STAT_COMPL) { /* FA-WWN is only for physical port */ if (qla_ini_mode_enabled(vha) && ha->flags.fawwpn_enabled && @@ -4190,19 +4256,22 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags); set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags); } else { - if (rptid_entry->vp_status != VP_STAT_COMPL && - rptid_entry->vp_status != VP_STAT_ID_CHG) { + if (vp_status != VP_STAT_COMPL && + vp_status != VP_STAT_ID_CHG) { ql_dbg(ql_dbg_mbx, vha, 0x10ba, "Could not acquire ID for VP[%d].\n", - rptid_entry->vp_idx); + vp_idx); return; } found = 0; spin_lock_irqsave(&ha->vport_slock, flags); list_for_each_entry(vp, &ha->vp_list, list) { - if (rptid_entry->vp_idx == vp->vp_idx) { + if (vp_idx == vp->vp_idx) { + if (test_bit(VPORT_DELETE, &vp->dpc_flags)) + break; found = 1; + atomic_inc(&vp->vref_count); break; } } @@ -4211,7 +4280,9 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, if (!found) return; + spin_lock_irqsave(&ha->vport_slock, flags); qla_update_host_map(vp, id); + spin_unlock_irqrestore(&ha->vport_slock, flags); /* * Cannot configure here as we are still sitting on the @@ -4220,6 +4291,10 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, set_bit(VP_IDX_ACQUIRED, &vp->vp_flags); set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags); set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags); + + spin_lock_irqsave(&ha->vport_slock, flags); + atomic_dec(&vp->vref_count); + spin_unlock_irqrestore(&ha->vport_slock, flags); } set_bit(VP_DPC_NEEDED, &vha->dpc_flags); qla2xxx_wake_dpc(vha); @@ -4313,6 +4388,13 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) return QLA_MEMORY_ALLOC_FAILED; } + /* + * vp_config_entry_24xx_ext overlays vp_config_entry_24xx for the + * full 64-byte 24xx layout (the ext variant merely appends fields + * at offset 64+ which this helper never touches), so the IOCB is + * built and inspected through a single struct vp_config_entry_24xx + * pointer regardless of adapter stride. + */ vpmod->entry_type = VP_CONFIG_IOCB_TYPE; vpmod->entry_count = 1; vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS; @@ -4330,10 +4412,10 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x10bd, "Failed to issue VP config IOCB (%x).\n", rval); - } else if (vpmod->comp_status != 0) { + } else if (vpmod->entry_status != 0) { ql_dbg(ql_dbg_mbx, vha, 0x10be, "Failed to complete IOCB -- error status (%x).\n", - vpmod->comp_status); + vpmod->entry_status); rval = QLA_FUNCTION_FAILED; } else if (vpmod->comp_status != cpu_to_le16(CS_COMPLETE)) { ql_dbg(ql_dbg_mbx, vha, 0x10bf, @@ -4341,7 +4423,6 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) le16_to_cpu(vpmod->comp_status)); rval = QLA_FUNCTION_FAILED; } else { - /* EMPTY */ ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c0, "Done %s.\n", __func__); fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING); @@ -4584,7 +4665,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) mcp->mb[12] = req->qos; mcp->mb[11] = req->vp_idx; mcp->mb[13] = req->rid; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) mcp->mb[15] = 0; mcp->mb[4] = req->id; @@ -4599,9 +4681,10 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) mcp->tov = MBX_TOV_SECONDS * 2; if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) mcp->in_mb |= MBX_1; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { mcp->out_mb |= MBX_15; /* debug q create issue in SR-IOV */ mcp->in_mb |= MBX_9 | MBX_8 | MBX_7; @@ -4610,7 +4693,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) spin_lock_irqsave(&ha->hardware_lock, flags); if (!(req->options & BIT_0)) { wrt_reg_dword(req->req_q_in, 0); - if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && + !IS_QLA29XX(ha)) wrt_reg_dword(req->req_q_out, 0); } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -4654,7 +4738,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) mcp->mb[5] = rsp->length; mcp->mb[14] = rsp->msix->entry; mcp->mb[13] = rsp->rid; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) mcp->mb[15] = 0; mcp->mb[4] = rsp->id; @@ -4671,7 +4756,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) if (IS_QLA81XX(ha)) { mcp->out_mb |= MBX_12|MBX_11|MBX_10; mcp->in_mb |= MBX_1; - } else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + } else if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10; mcp->in_mb |= MBX_1; /* debug q create issue in SR-IOV */ @@ -4681,7 +4767,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) spin_lock_irqsave(&ha->hardware_lock, flags); if (!(rsp->options & BIT_0)) { wrt_reg_dword(rsp->rsp_q_out, 0); - if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && + !IS_QLA29XX(ha)) wrt_reg_dword(rsp->rsp_q_in, 0); } @@ -5064,7 +5151,8 @@ qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; if (!IS_QLA25XX(ha) && !IS_QLA2031(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) + !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && + !IS_QLA29XX(ha)) return QLA_SUCCESS; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1197, @@ -5469,10 +5557,10 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, mcp->in_mb = MBX_0; if (IS_CNA_CAPABLE(ha) || IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || - IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) mcp->in_mb |= MBX_1; if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) + IS_QLA28XX(ha) || IS_QLA29XX(ha)) mcp->in_mb |= MBX_3; mcp->tov = MBX_TOV_SECONDS; @@ -5643,6 +5731,8 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode) case PORT_SPEED_8GB: case PORT_SPEED_16GB: case PORT_SPEED_32GB: + case PORT_SPEED_64GB: + case PORT_SPEED_128GB: val = ha->set_data_rate; break; default: @@ -5659,7 +5749,7 @@ qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode) mcp->out_mb = MBX_2|MBX_1|MBX_0; mcp->in_mb = MBX_2|MBX_1|MBX_0; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) mcp->in_mb |= MBX_4|MBX_3; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; @@ -5697,7 +5787,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) mcp->mb[1] = QLA_GET_DATA_RATE; mcp->out_mb = MBX_1|MBX_0; mcp->in_mb = MBX_2|MBX_1|MBX_0; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) mcp->in_mb |= MBX_4|MBX_3; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; @@ -5706,10 +5796,11 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) ql_dbg(ql_dbg_mbx, vha, 0x1107, "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - if (mcp->mb[1] != 0x7) + if (mcp->mb[1] != 0x7 || IS_QLA28XX(ha) || IS_QLA29XX(ha)) ha->link_data_rate = mcp->mb[1]; - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) { if (mcp->mb[4] & BIT_0) ql_log(ql_log_info, vha, 0x11a2, "FEC=enabled (data rate).\n"); @@ -5717,8 +5808,6 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108, "Done %s.\n", __func__); - if (mcp->mb[1] != 0x7) - ha->link_data_rate = mcp->mb[1]; } return rval; @@ -6490,23 +6579,21 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; dma_addr_t dd_dma; + void *dd; if (!IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw) && - !IS_QLA28XX(vha->hw)) + !IS_QLA28XX(vha->hw) && !IS_QLA29XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f, "Entered %s.\n", __func__); - dd_dma = dma_map_single(&vha->hw->pdev->dev, - dd_buf, size, DMA_FROM_DEVICE); - if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) { - ql_log(ql_log_warn, vha, 0x1194, "Failed to map dma buffer.\n"); + dd = dma_alloc_coherent(&vha->hw->pdev->dev, size, &dd_dma, GFP_KERNEL); + if (!dd) { + ql_log(ql_log_warn, vha, 0x1194, "Failed to allocate dma buffer.\n"); return QLA_MEMORY_ALLOC_FAILED; } - memset(dd_buf, 0, size); - mcp->mb[0] = MBC_DPORT_DIAGNOSTICS; mcp->mb[1] = options; mcp->mb[2] = MSW(LSD(dd_dma)); @@ -6528,8 +6615,9 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha, "Done %s.\n", __func__); } - dma_unmap_single(&vha->hw->pdev->dev, dd_dma, - size, DMA_FROM_DEVICE); + memcpy(dd_buf, dd, size); + + dma_free_coherent(&vha->hw->pdev->dev, size, dd, dd_dma); return rval; } @@ -7254,3 +7342,192 @@ int qla_mpipt_validate_fw(scsi_qla_host_t *vha, u16 img_idx, uint16_t *state) return rval; } + +int qla29xx_flash_block_read(scsi_qla_host_t *vha, dma_addr_t req_dma, + uint32_t flash_addr, uint32_t flash_size, + uint16_t reg_code, uint16_t opt) +{ + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int rval = 0; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067, + "Entered %s options 0x%x.\n", __func__, opt); + + memset(mcp->mb, 0, sizeof(mcp->mb)); + + if (!is_flash_read(opt)) { + ql_log(ql_log_info, vha, 0x1068, + "%s: Invalid flash option 0x%x.\n", __func__, opt); + return -EINVAL; + } + + mcp->mb[0] = MBC_RD_WR_FLASH; + + /* mailbox option field : + * TIM img or Img : BIT_15 (1/0) + * Last seg img : BIT_11 (1) + * First seg img : BIT_10 (1) + * dword or block : BIT_9 (1/0) + * flash MBR update : BIT_8 (1/0) + * Secure or non-secure : BIT_7 (1/0) + * Write or Read : BIT_6 (1/0) + * Last block img : BIT_5 (1) + * First block img : BIT_4 (1) + * Request type : (BIT3 - BIT_0) + * - Normal + * - Normal, Force Sema + * - Initialize + * - Initialize, Force sema + * - Abort secured update + */ + mcp->mb[1] = opt; + mcp->mb[2] = reg_code; + + mcp->mb[3] = MSW(flash_size); + mcp->mb[4] = LSW(flash_size); + + mcp->mb[5] = MSW(req_dma); + mcp->mb[6] = LSW(req_dma); + mcp->mb[7] = MSW(MSD(req_dma)); + mcp->mb[8] = LSW(MSD(req_dma)); + + mcp->mb[9] = MSW(flash_addr); + mcp->mb[10] = LSW(flash_addr); + + mcp->out_mb = + MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x103f, + "Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1043, + "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__, + mcp->mb[0], mcp->mb[1], mcp->mb[2]); + } + + return rval; +} + +int qla29xx_flash_block_write(scsi_qla_host_t *vha, dma_addr_t req_dma, + uint32_t flash_addr, uint32_t flash_size, + uint16_t reg_code, uint16_t opt) +{ + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int rval = 0; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069, + "Entered %s options 0x%x.\n", __func__, opt); + + memset(mcp->mb, 0, sizeof(mcp->mb)); + + if (!is_flash_write(opt)) { + ql_log(ql_log_info, vha, 0x106a, + "%s: Invalid flash option 0x%x.\n", __func__, opt); + return -EINVAL; + } + + mcp->mb[0] = MBC_RD_WR_FLASH; + + /* mailbox option field : + * TIM img or Img : BIT_15 (1/0) + * Last seg img : BIT_11 (1) + * First seg img : BIT_10 (1) + * dword or block : BIT_9 (1/0) + * flash MBR update : BIT_8 (1/0) + * Secure or non-secure : BIT_7 (1/0) + * Write or Read : BIT_6 (1/0) + * Last block img : BIT_5 (1) + * First block img : BIT_4 (1) + * Request type : (BIT3 - BIT_0) + * - Normal + * - Normal, Force Sema + * - Initialize + * - Initialize, Force sema + * - Abort secured update + */ + mcp->mb[1] = opt; + mcp->mb[2] = reg_code; + + mcp->mb[3] = MSW(flash_size); + mcp->mb[4] = LSW(flash_size); + + mcp->mb[5] = MSW(req_dma); + mcp->mb[6] = LSW(req_dma); + mcp->mb[7] = MSW(MSD(req_dma)); + mcp->mb[8] = LSW(MSD(req_dma)); + + mcp->mb[9] = MSW(flash_addr); + mcp->mb[10] = LSW(flash_addr); + + mcp->out_mb = + MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x110a, + "Failed=%x mb=(0x%x,0x%x,0x%x,0x%x).\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b, + "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__, + mcp->mb[0], mcp->mb[1], mcp->mb[2]); + } + + return rval; +} + +int +qla29xx_load_dump_mpi(scsi_qla_host_t *vha, uint16_t opt, uint32_t mpi_addr, + uint32_t dlen, dma_addr_t req_dma) +{ + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int rval = 0; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xffff, + "Entered %s mpi_addr 0x%x len 0x%x opt 0x%x.\n", + __func__, mpi_addr, dlen, opt); + + memset(mcp->mb, 0, sizeof(mcp->mb)); + + mcp->mb[0] = MBC_LOAD_DUMP_MPI_RAM; + mcp->mb[9] = opt; + mcp->mb[1] = LSW(mpi_addr); + mcp->mb[8] = MSW(mpi_addr); + + mcp->mb[2] = MSW(req_dma); + mcp->mb[3] = LSW(req_dma); + mcp->mb[6] = MSW(MSD(req_dma)); + mcp->mb[7] = LSW(MSD(req_dma)); + + mcp->mb[4] = MSW(dlen); + mcp->mb[5] = LSW(dlen); + + mcp->out_mb = MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + + rval = qla2x00_mailbox_command(vha, mcp); + if (rval != QLA_SUCCESS) + ql_dbg(ql_dbg_mbx, vha, 0x110a, + "Failed=%x mb=(0x%x,0x%x).\n", + rval, mcp->mb[0], mcp->mb[1]); + else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b, + "Done %s mb=(0x%x,0x%x,0x%x).\n", __func__, + mcp->mb[0], mcp->mb[1], mcp->mb[2]); + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index c563133f751e..4ad23d206add 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -574,16 +574,19 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req) { struct qla_hw_data *ha = vha->hw; uint16_t que_id = req->id; + size_t req_entry_size = qla_req_entry_size(ha); - dma_free_coherent(&ha->pdev->dev, (req->length + 1) * - sizeof(request_t), req->ring, req->dma); + if (req->ring) + dma_free_coherent(&ha->pdev->dev, + (req->length + 1) * req_entry_size, + req->ring, req->dma); req->ring = NULL; req->dma = 0; if (que_id) { + mutex_lock(&ha->mq_lock); ha->req_q_map[que_id] = NULL; - mutex_lock(&ha->vport_lock); clear_bit(que_id, ha->req_qid_map); - mutex_unlock(&ha->vport_lock); + mutex_unlock(&ha->mq_lock); } kfree(req->outstanding_cmds); kfree(req); @@ -594,6 +597,7 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) { struct qla_hw_data *ha = vha->hw; uint16_t que_id = rsp->id; + size_t rsp_entry_size = qla_rsp_entry_size(ha); if (rsp->msix && rsp->msix->have_irq) { free_irq(rsp->msix->vector, rsp->msix->handle); @@ -601,15 +605,22 @@ qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) rsp->msix->in_use = 0; rsp->msix->handle = NULL; } - dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) * - sizeof(response_t), rsp->ring, rsp->dma); + + /* Flush any queued response work before freeing the queue/qpair. */ + if (rsp->qpair && ha->wq) + cancel_work_sync(&rsp->qpair->q_work); + + if (rsp->ring) + dma_free_coherent(&ha->pdev->dev, + (rsp->length + 1) * rsp_entry_size, + rsp->ring, rsp->dma); rsp->ring = NULL; rsp->dma = 0; if (que_id) { + mutex_lock(&ha->mq_lock); ha->rsp_q_map[que_id] = NULL; - mutex_lock(&ha->vport_lock); clear_bit(que_id, ha->rsp_qid_map); - mutex_unlock(&ha->vport_lock); + mutex_unlock(&ha->mq_lock); } kfree(rsp); } @@ -706,6 +717,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, uint16_t que_id = 0; device_reg_t *reg; uint32_t cnt; + size_t req_entry_size = qla_req_entry_size(ha); req = kzalloc_obj(struct req_que); if (req == NULL) { @@ -716,13 +728,17 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->length = REQUEST_ENTRY_CNT_24XX; req->ring = dma_alloc_coherent(&ha->pdev->dev, - (req->length + 1) * sizeof(request_t), + (req->length + 1) * req_entry_size, &req->dma, GFP_KERNEL); if (req->ring == NULL) { ql_log(ql_log_fatal, base_vha, 0x00da, "Failed to allocate memory for request_ring.\n"); goto que_failed; } + if (IS_QLA29XX(ha)) { + req->ring_ext = (struct request_ext *)req->ring; + req->ring_ext_ptr = req->ring_ext; + } ret = qla2x00_alloc_outstanding_cmds(ha, req); if (ret != QLA_SUCCESS) @@ -768,7 +784,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->outstanding_cmds[cnt] = NULL; req->current_outstanding_cmd = 1; + /* + * Re-anchor both the 24xx (ring_ptr) and 29xx (ring_ext_ptr) views + * at the start of the ring. Keeping them reset together here + * guarantees they stay in sync with ring_index=0 regardless of any + * prior allocator/fast-path advances against this queue. + */ req->ring_ptr = req->ring; + if (IS_QLA29XX(ha)) + req->ring_ext_ptr = req->ring_ext; req->ring_index = 0; req->cnt = req->length; req->id = que_id; @@ -776,7 +800,15 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->req_q_in = ®->isp25mq.req_q_in; req->req_q_out = ®->isp25mq.req_q_out; req->max_q_depth = ha->req_q_map[0]->max_q_depth; - req->out_ptr = (uint16_t *)(req->ring + req->length); + /* + * out_ptr sits in the scratch slot immediately after the ring. Use + * the 29xx-stride pointer when the ring is 128-byte-per-entry so the + * pointer arithmetic resolves to the correct byte offset. + */ + if (IS_QLA29XX(ha)) + req->out_ptr = (uint16_t *)(req->ring_ext + req->length); + else + req->out_ptr = (uint16_t *)(req->ring + req->length); mutex_unlock(&ha->mq_lock); ql_dbg(ql_dbg_multiq, base_vha, 0xc004, "ring_ptr=%p ring_index=%d, " @@ -794,9 +826,6 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, if (ret != QLA_SUCCESS) { ql_log(ql_log_fatal, base_vha, 0x00df, "%s failed.\n", __func__); - mutex_lock(&ha->mq_lock); - clear_bit(que_id, ha->req_qid_map); - mutex_unlock(&ha->mq_lock); goto que_failed; } vha->flags.qpairs_req_created = 1; @@ -833,6 +862,7 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev); uint16_t que_id = 0; device_reg_t *reg; + size_t rsp_entry_size = qla_rsp_entry_size(ha); rsp = kzalloc_obj(struct rsp_que); if (rsp == NULL) { @@ -843,13 +873,17 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, rsp->length = RESPONSE_ENTRY_CNT_MQ; rsp->ring = dma_alloc_coherent(&ha->pdev->dev, - (rsp->length + 1) * sizeof(response_t), + (rsp->length + 1) * rsp_entry_size, &rsp->dma, GFP_KERNEL); if (rsp->ring == NULL) { ql_log(ql_log_warn, base_vha, 0x00e1, "Failed to allocate memory for response ring.\n"); goto que_failed; } + if (IS_QLA29XX(ha)) { + rsp->ring_ext = (struct response_ext *)rsp->ring; + rsp->ring_ext_ptr = rsp->ring_ext; + } mutex_lock(&ha->mq_lock); que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues); @@ -888,7 +922,10 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, reg = ISP_QUE_REG(ha, que_id); rsp->rsp_q_in = ®->isp25mq.rsp_q_in; rsp->rsp_q_out = ®->isp25mq.rsp_q_out; - rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length); + if (IS_QLA29XX(ha)) + rsp->in_ptr = (uint16_t *)(rsp->ring_ext + rsp->length); + else + rsp->in_ptr = (uint16_t *)(rsp->ring + rsp->length); mutex_unlock(&ha->mq_lock); ql_dbg(ql_dbg_multiq, base_vha, 0xc00b, "options=%x id=%d rsp_q_in=%p rsp_q_out=%p\n", @@ -908,9 +945,6 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, if (ret != QLA_SUCCESS) { ql_log(ql_log_fatal, base_vha, 0x00e7, "%s failed.\n", __func__); - mutex_lock(&ha->mq_lock); - clear_bit(que_id, ha->rsp_qid_map); - mutex_unlock(&ha->mq_lock); goto que_failed; } vha->flags.qpairs_rsp_created = 1; @@ -957,6 +991,14 @@ int qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) if (vp_index == 0 || vp_index >= ha->max_npiv_vports) return QLA_PARAMETER_ERROR; + /* + * The VP_CTRL IOCB selects the target VP through a fixed 128-bit + * (16-byte) vp_idx_map bitmap, so vp_index must fit within it even + * if firmware advertises more NPIV vports. + */ + if (vp_index > VP_CTRL_IDX_MAP_BITS) + return QLA_PARAMETER_ERROR; + /* ref: INIT */ sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL); if (!sp) diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 2531e71c39dc..beccece1e7d9 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -257,7 +257,9 @@ static void qla_nvme_release_lsrsp_cmd_kref(struct kref *kref) fd_rsp = uctx->fd_rsp; + spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags); list_del(&uctx->elem); + spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags); fd_rsp->done(fd_rsp); kfree(uctx); @@ -374,6 +376,7 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport, srb_t *sp; int rval = QLA_FUNCTION_FAILED; uint8_t cnt = 0; + unsigned long flags; if (!fcport || fcport->deleted) goto out; @@ -440,7 +443,14 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport, a.vp_idx = vha->vp_idx; a.nport_handle = uctx->nport_handle; a.xchg_address = uctx->exchange_address; - qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true); + if (ha->flags.fw_started) { + spin_lock_irqsave(ha->base_qpair->qp_lock_ptr, flags); + qla_nvme_ls_reject_iocb(vha, ha->base_qpair, &a, true); + spin_unlock_irqrestore(ha->base_qpair->qp_lock_ptr, flags); + } + spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags); + list_del(&uctx->elem); + spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags); kfree(uctx); return rval; } @@ -463,8 +473,8 @@ static void qla_nvme_ls_abort(struct nvme_fc_local_port *lport, } spin_unlock_irqrestore(&priv->cmd_lock, flags); - INIT_WORK(&priv->abort_work, qla_nvme_abort_work); - schedule_work(&priv->abort_work); + if (!schedule_work(&priv->abort_work)) + kref_put(&priv->sp->cmd_kref, priv->sp->put_fn); } static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, @@ -501,6 +511,7 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, priv->sp = sp; kref_init(&sp->cmd_kref); spin_lock_init(&priv->cmd_lock); + INIT_WORK(&priv->abort_work, qla_nvme_abort_work); nvme = &sp->u.iocb_cmd; priv->fd = fd; nvme->u.nvme.desc = fd; @@ -545,8 +556,8 @@ static void qla_nvme_fcp_abort(struct nvme_fc_local_port *lport, } spin_unlock_irqrestore(&priv->cmd_lock, flags); - INIT_WORK(&priv->abort_work, qla_nvme_abort_work); - schedule_work(&priv->abort_work); + if (!schedule_work(&priv->abort_work)) + kref_put(&priv->sp->cmd_kref, priv->sp->put_fn); } static inline int qla2x00_start_nvme_mq(srb_t *sp) @@ -554,7 +565,8 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) unsigned long flags; uint32_t *clr_ptr; uint32_t handle; - struct cmd_nvme *cmd_pkt; + struct cmd_nvme *cmd_pkt = NULL; + struct cmd_nvme_ext *cmd_pkt_ext = NULL; uint16_t cnt, i; uint16_t req_cnt; uint16_t tot_dsds; @@ -584,7 +596,10 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) rval = -EBUSY; goto queuing_error; } - req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + if (IS_QLA29XX(ha)) + req_cnt = qla29xx_calc_iocbs(vha, tot_dsds, NUM_NVME_DSDS); + else + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); sp->iores.res_type = RESOURCE_IOCB | RESOURCE_EXCH; sp->iores.exch_cnt = 1; @@ -629,19 +644,62 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) sp->handle = handle; req->cnt -= req_cnt; - cmd_pkt = (struct cmd_nvme *)req->ring_ptr; + /* + * 29xx operates on the 128-byte extended IOCB ring; the header + * layout of struct cmd_nvme is identical to the head of struct + * cmd_nvme_ext through 'byte_count', so the common-field writes + * below go through cmd_pkt regardless of stride. Divergent tail + * fields (port_id/vp_index, DSD array) are handled via + * IS_QLA29XX(ha) branches. + * + * Enforce the layout contract at compile time so any future + * change to either struct that breaks the common-header overlap + * fails the build rather than silently corrupting IOCBs. + */ + BUILD_BUG_ON(offsetof(struct cmd_nvme, entry_type) != + offsetof(struct cmd_nvme_ext, entry_type)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, handle) != + offsetof(struct cmd_nvme_ext, handle)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, nport_handle) != + offsetof(struct cmd_nvme_ext, nport_handle)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, timeout) != + offsetof(struct cmd_nvme_ext, timeout)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, dseg_count) != + offsetof(struct cmd_nvme_ext, dseg_count)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_rsp_dsd_len) != + offsetof(struct cmd_nvme_ext, nvme_rsp_dsd_len)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, rsvd) != + offsetof(struct cmd_nvme_ext, rsvd)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, control_flags) != + offsetof(struct cmd_nvme_ext, control_flags)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_cmnd_dseg_len) != + offsetof(struct cmd_nvme_ext, nvme_cmnd_dseg_len)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_cmnd_dseg_address) != + offsetof(struct cmd_nvme_ext, nvme_cmnd_dseg_address)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, nvme_rsp_dseg_address) != + offsetof(struct cmd_nvme_ext, nvme_rsp_dseg_address)); + BUILD_BUG_ON(offsetof(struct cmd_nvme, byte_count) != + offsetof(struct cmd_nvme_ext, byte_count)); + BUILD_BUG_ON(sizeof_field(struct cmd_nvme, byte_count) != + sizeof_field(struct cmd_nvme_ext, byte_count)); + + if (IS_QLA29XX(ha)) + cmd_pkt = (struct cmd_nvme *)req->ring_ext_ptr; + else + cmd_pkt = (struct cmd_nvme *)req->ring_ptr; + cmd_pkt_ext = (struct cmd_nvme_ext *)cmd_pkt; cmd_pkt->handle = make_handle(req->id, handle); /* Zero out remaining portion of packet. */ clr_ptr = (uint32_t *)cmd_pkt + 2; - memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + if (IS_QLA29XX(ha)) + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE_EXT - 8); + else + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); cmd_pkt->entry_status = 0; - - /* Update entry type to indicate Command NVME IOCB */ cmd_pkt->entry_type = COMMAND_NVME; - /* No data transfer how do we check buffer len == 0?? */ if (fd->io_dir == NVMEFC_FCP_READ) { cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA); qpair->counters.input_bytes += fd->payload_length; @@ -666,18 +724,23 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) if (sp->fcport->edif.enable && fd->io_dir != 0) cmd_pkt->control_flags |= cpu_to_le16(CF_EN_EDIF); - /* Set BIT_13 of control flags for Async event */ if (vha->flags.nvme2_enabled && - cmd->sqe.common.opcode == nvme_admin_async_event) { + cmd->sqe.common.opcode == nvme_admin_async_event) cmd_pkt->control_flags |= cpu_to_le16(CF_ADMIN_ASYNC_EVENT); - } - /* Set NPORT-ID */ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); - cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; - cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; - cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; - cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + if (IS_QLA29XX(ha)) { + /* + * 29xx extended NVMe IOCB has no port_id[] field; vp_index is + * a 9-bit __le16 (see CMD_EXT_VP_INDEX_MASK). + */ + cmd_pkt_ext->vp_index = cpu_to_le16(sp->fcport->vha->vp_idx); + } else { + cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; + cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; + cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; + } /* NVME RSP IU */ cmd_pkt->nvme_rsp_dsd_len = cpu_to_le16(fd->rsplen); @@ -690,36 +753,51 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); cmd_pkt->byte_count = cpu_to_le32(fd->payload_length); - /* One DSD is available in the Command Type NVME IOCB */ - avail_dsds = 1; - cur_dsd = &cmd_pkt->nvme_dsd; + /* + * 24xx carries a single inline DSD in the NVMe command IOCB; 29xx + * carries NUM_NVME_DSDS inline DSDs in the extended IOCB. + */ + if (IS_QLA29XX(ha)) { + avail_dsds = NUM_NVME_DSDS; + cur_dsd = &cmd_pkt_ext->nvme_dsd[0]; + } else { + avail_dsds = 1; + cur_dsd = &cmd_pkt->nvme_dsd; + } sgl = fd->first_sgl; /* Load data segments */ for_each_sg(sgl, sg, tot_dsds, i) { - cont_a64_entry_t *cont_pkt; - /* Allocate additional continuation packets? */ if (avail_dsds == 0) { - /* - * Five DSDs are available in the Continuation - * Type 1 IOCB. - */ - - /* Adjust ring index */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; + if (IS_QLA29XX(ha)) { + struct cont_a64_entry_ext *cont_pkt; + + cont_pkt = qla2900_prep_cont_type1_iocb(vha, + req); + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); } else { - req->ring_ptr++; + cont_a64_entry_t *cont_pkt; + + /* + * Five DSDs are available in the 24xx + * Continuation Type 1 IOCB. + */ + req->ring_index++; + if (req->ring_index == req->length) { + req->ring_index = 0; + req->ring_ptr = req->ring; + } else { + req->ring_ptr++; + } + cont_pkt = (cont_a64_entry_t *)req->ring_ptr; + put_unaligned_le32(CONTINUE_A64_TYPE, + &cont_pkt->entry_type); + + cur_dsd = cont_pkt->dsd; + avail_dsds = ARRAY_SIZE(cont_pkt->dsd); } - cont_pkt = (cont_a64_entry_t *)req->ring_ptr; - put_unaligned_le32(CONTINUE_A64_TYPE, - &cont_pkt->entry_type); - - cur_dsd = cont_pkt->dsd; - avail_dsds = ARRAY_SIZE(cont_pkt->dsd); } append_dsd64(&cur_dsd, sg); @@ -731,13 +809,7 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) wmb(); /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else { - req->ring_ptr++; - } + qla_req_ring_advance(ha, req); /* ignore nvme async cmd due to long timeout */ if (!nvme->u.nvme.aen_op) @@ -746,7 +818,12 @@ static inline int qla2x00_start_nvme_mq(srb_t *sp) /* Set chip new ring index. */ wrt_reg_dword(req->req_q_in, req->ring_index); - if (vha->flags.process_response_queue && + /* + * 29xx inline response drain would require the 128-byte response ring + * view, which the 24xx qla24xx_process_response_queue() does not walk; + * skip here and rely on the normal ISR path. + */ + if (!IS_QLA29XX(ha) && vha->flags.process_response_queue && rsp->ring_ptr->signature != RESPONSE_PROCESSED) qla24xx_process_response_queue(vha, rsp); @@ -811,6 +888,7 @@ static int qla_nvme_post_cmd(struct nvme_fc_local_port *lport, kref_init(&sp->cmd_kref); spin_lock_init(&priv->cmd_lock); + INIT_WORK(&priv->abort_work, qla_nvme_abort_work); sp->priv = priv; priv->sp = sp; sp->type = SRB_NVME_CMD; @@ -1001,36 +1079,69 @@ int qla_nvme_register_hba(struct scsi_qla_host *vha) return ret; } -void qla_nvme_abort_set_option(struct abort_entry_24xx *abt, srb_t *orig_sp) +void qla_nvme_abort_set_option(void *pkt, srb_t *orig_sp) { struct qla_hw_data *ha; + struct abort_entry_24xx *abt = pkt; if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp))) return; ha = orig_sp->fcport->vha->hw; + /* + * abort_entry_24xx_ext overlays abort_entry_24xx through 'options' + * (offset 10), so options writes are stride-agnostic. The drv + * union sits at offset 56 in the 24xx layout but offset 24 in the + * ext layout, so the drv writes need a typed pointer. + */ WARN_ON_ONCE(abt->options & cpu_to_le16(BIT_0)); - /* Use Driver Specified Retry Count */ abt->options |= cpu_to_le16(AOF_ABTS_RTY_CNT); - abt->drv.abts_rty_cnt = cpu_to_le16(2); - /* Use specified response timeout */ abt->options |= cpu_to_le16(AOF_RSP_TIMEOUT); - /* set it to 2 * r_a_tov in secs */ - abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10)); + if (IS_QLA29XX(ha)) { + struct abort_entry_24xx_ext *abt_ext = pkt; + + abt_ext->drv.abts_rty_cnt = cpu_to_le16(2); + abt_ext->drv.rsp_timeout = + cpu_to_le16(2 * (ha->r_a_tov / 10)); + } else { + abt->drv.abts_rty_cnt = cpu_to_le16(2); + abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10)); + } } -void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *orig_sp) +void qla_nvme_abort_process_comp_status(void *pkt, srb_t *orig_sp) { u16 comp_status; struct scsi_qla_host *vha; + u8 rjt_vendor_unique, rjt_reason_expl, rjt_reason_code; + struct abort_entry_24xx *abt = pkt; if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp))) return; vha = orig_sp->fcport->vha; + /* + * comp_status sits at offset 8 in both layouts (the + * nport_handle/comp_status union), so the read is + * stride-agnostic. The fw union, like drv, lives at offset + * 56 in the 24xx layout and offset 24 in the ext layout, so + * those byte reads still need a typed pointer. + */ comp_status = le16_to_cpu(abt->comp_status); + if (IS_QLA29XX(vha->hw)) { + struct abort_entry_24xx_ext *abt_ext = pkt; + + rjt_vendor_unique = abt_ext->fw.ba_rjt_vendorUnique; + rjt_reason_expl = abt_ext->fw.ba_rjt_reasonCodeExpl; + rjt_reason_code = abt_ext->fw.ba_rjt_reasonCode; + } else { + rjt_vendor_unique = abt->fw.ba_rjt_vendorUnique; + rjt_reason_expl = abt->fw.ba_rjt_reasonCodeExpl; + rjt_reason_code = abt->fw.ba_rjt_reasonCode; + } + switch (comp_status) { case CS_RESET: /* reset event aborted */ case CS_ABORTED: /* IOCB was cleaned */ @@ -1050,11 +1161,8 @@ void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *ori /* BA_RJT was received for the ABTS */ case CS_REJECT_RECEIVED: ql_dbg(ql_dbg_async, vha, 0xf09e, - "BA_RJT was received for the ABTS rjt_vendorUnique = %u", - abt->fw.ba_rjt_vendorUnique); - ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e, - "ba_rjt_reasonCodeExpl = %u, ba_rjt_reasonCode = %u\n", - abt->fw.ba_rjt_reasonCodeExpl, abt->fw.ba_rjt_reasonCode); + "BA_RJT was received for the ABTS rjt_vendorUnique=%u, ba_rjt_reasonCodeExpl=%u, ba_rjt_reasonCode=%u\n", + rjt_vendor_unique, rjt_reason_expl, rjt_reason_code); break; case CS_COMPLETE: @@ -1101,37 +1209,61 @@ static void qla_nvme_fc_format_rjt(void *buf, u8 ls_cmd, u8 reason, rjt->rjt.vendor = vendor; } +/* + * pt_ls4_request_ext overlays pt_ls4_request through exchange_address + * (offsets 0-27 are byte-identical), so the common-header writes go + * through one struct pt_ls4_request * view. The ext layout has a wider + * __le16 vp_index and places tx_/rx_byte_count and dsd[] at different + * offsets, so those assignments diverge per stride. + */ static void qla_nvme_lsrjt_pt_iocb(struct scsi_qla_host *vha, - struct pt_ls4_request *lsrjt_iocb, + void *lsrjt_iocb, struct qla_nvme_lsrjt_pt_arg *a) { - lsrjt_iocb->entry_type = PT_LS4_REQUEST; - lsrjt_iocb->entry_count = 1; - lsrjt_iocb->sys_define = 0; - lsrjt_iocb->entry_status = 0; - lsrjt_iocb->handle = QLA_SKIP_HANDLE; - lsrjt_iocb->nport_handle = a->nport_handle; - lsrjt_iocb->exchange_address = a->xchg_address; - lsrjt_iocb->vp_index = a->vp_idx; - - lsrjt_iocb->control_flags = cpu_to_le16(a->control_flags); - - put_unaligned_le64(a->tx_addr, &lsrjt_iocb->dsd[0].address); - lsrjt_iocb->dsd[0].length = cpu_to_le32(a->tx_byte_count); - lsrjt_iocb->tx_dseg_count = cpu_to_le16(1); - lsrjt_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count); - - put_unaligned_le64(a->rx_addr, &lsrjt_iocb->dsd[1].address); - lsrjt_iocb->dsd[1].length = 0; - lsrjt_iocb->rx_dseg_count = 0; - lsrjt_iocb->rx_byte_count = 0; + struct qla_hw_data *ha = vha->hw; + struct pt_ls4_request *pkt = lsrjt_iocb; + + pkt->entry_type = PT_LS4_REQUEST; + pkt->entry_count = 1; + pkt->sys_define = 0; + pkt->entry_status = 0; + pkt->handle = QLA_SKIP_HANDLE; + pkt->nport_handle = a->nport_handle; + pkt->exchange_address = a->xchg_address; + pkt->control_flags = cpu_to_le16(a->control_flags); + pkt->tx_dseg_count = cpu_to_le16(1); + pkt->rx_dseg_count = 0; + + if (IS_QLA29XX(ha)) { + struct pt_ls4_request_ext *ext = lsrjt_iocb; + + ext->vp_index = cpu_to_le16(a->vp_idx); + ext->tx_byte_count = cpu_to_le32(a->tx_byte_count); + ext->rx_byte_count = 0; + put_unaligned_le64(a->tx_addr, &ext->dsd[0].address); + ext->dsd[0].length = cpu_to_le32(a->tx_byte_count); + put_unaligned_le64(a->rx_addr, &ext->dsd[1].address); + ext->dsd[1].length = 0; + } else { + pkt->vp_index = a->vp_idx; + pkt->tx_byte_count = cpu_to_le32(a->tx_byte_count); + pkt->rx_byte_count = 0; + put_unaligned_le64(a->tx_addr, &pkt->dsd[0].address); + pkt->dsd[0].length = cpu_to_le32(a->tx_byte_count); + put_unaligned_le64(a->rx_addr, &pkt->dsd[1].address); + pkt->dsd[1].length = 0; + } } +/* + * Allocates from and advances the request ring, so the caller must hold + * qp->qp_lock_ptr (the response-queue caller already holds it). + */ static int qla_nvme_ls_reject_iocb(struct scsi_qla_host *vha, struct qla_qpair *qp, struct qla_nvme_lsrjt_pt_arg *a, bool is_xchg_terminate) { - struct pt_ls4_request *lsrjt_iocb; + void *lsrjt_iocb; lsrjt_iocb = __qla2x00_alloc_iocbs(qp, NULL); if (!lsrjt_iocb) { @@ -1183,6 +1315,7 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item) { struct qla_nvme_unsol_ctx *uctx = item->purls_context; struct qla_nvme_lsrjt_pt_arg a; + unsigned long flags; int ret = 1; #if (IS_ENABLED(CONFIG_NVME_FC)) @@ -1195,8 +1328,17 @@ qla2xxx_process_purls_pkt(struct scsi_qla_host *vha, struct purex_item *item) a.vp_idx = vha->vp_idx; a.nport_handle = uctx->nport_handle; a.xchg_address = uctx->exchange_address; - qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, true); + if (vha->hw->flags.fw_started) { + spin_lock_irqsave(vha->hw->base_qpair->qp_lock_ptr, + flags); + qla_nvme_ls_reject_iocb(vha, vha->hw->base_qpair, &a, + true); + spin_unlock_irqrestore(vha->hw->base_qpair->qp_lock_ptr, + flags); + } + spin_lock_irqsave(&uctx->fcport->unsol_ctx_lock, flags); list_del(&uctx->elem); + spin_unlock_irqrestore(&uctx->fcport->unsol_ctx_lock, flags); kfree(uctx); } } @@ -1238,6 +1380,7 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp) struct purex_item *item; port_id_t d_id = {0}; port_id_t id = {0}; + unsigned long flags; u8 *opcode; bool xmt_reject = false; @@ -1303,7 +1446,9 @@ void qla2xxx_process_purls_iocb(void **pkt, struct rsp_que **rsp) uctx->ox_id = p->ox_id; qla_rport->uctx = uctx; INIT_LIST_HEAD(&uctx->elem); + spin_lock_irqsave(&fcport->unsol_ctx_lock, flags); list_add_tail(&uctx->elem, &fcport->unsol_ctx_head); + spin_unlock_irqrestore(&fcport->unsol_ctx_lock, flags); item->purls_context = (void *)uctx; ql_dbg(ql_dbg_unsol, vha, 0x2121, diff --git a/drivers/scsi/qla2xxx/qla_nvme.h b/drivers/scsi/qla2xxx/qla_nvme.h index a253ac55171b..e6a41d53cbe1 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.h +++ b/drivers/scsi/qla2xxx/qla_nvme.h @@ -144,7 +144,7 @@ struct pt_ls4_rx_unsol { int qla_nvme_register_hba(struct scsi_qla_host *); int qla_nvme_register_remote(struct scsi_qla_host *, struct fc_port *); void qla_nvme_delete(struct scsi_qla_host *); -void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *, struct pt_ls4_request *, - struct req_que *); +void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, void *pkt, + struct req_que *req); void qla24xx_async_gffid_sp_done(struct srb *sp, int); #endif diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index 298c060c1292..2366cca51bce 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -2663,7 +2663,7 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, __le32 *dwptr, ret = qla2x00_load_ram(vha, optrom_dma, (ha->flash_data_off | faddr), - OPTROM_BURST_DWORDS); + OPTROM_BURST_DWORDS, 0); if (ret != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0xb01e, "Unable to burst-write optrom segment " diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 72b1c28e4dae..c0efdbff5da7 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -429,7 +429,8 @@ static void qla_init_base_qpair(struct scsi_qla_host *vha, struct req_que *req, qla_cpu_update(rsp->qpair, raw_smp_processor_id()); ha->base_qpair->pdev = ha->pdev; - if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha)) + if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) ha->base_qpair->reqq_start_iocbs = qla_83xx_start_iocbs; } @@ -501,6 +502,8 @@ static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) { + size_t req_entry_size = qla_req_entry_size(ha); + if (IS_QLAFX00(ha)) { if (req && req->ring_fx00) dma_free_coherent(&ha->pdev->dev, @@ -508,8 +511,8 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) req->ring_fx00, req->dma_fx00); } else if (req && req->ring) dma_free_coherent(&ha->pdev->dev, - (req->length + 1) * sizeof(request_t), - req->ring, req->dma); + (req->length + 1) * req_entry_size, + req->ring, req->dma); if (req) kfree(req->outstanding_cmds); @@ -519,6 +522,8 @@ static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) { + size_t rsp_entry_size = qla_rsp_entry_size(ha); + if (IS_QLAFX00(ha)) { if (rsp && rsp->ring_fx00) dma_free_coherent(&ha->pdev->dev, @@ -526,8 +531,8 @@ static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) rsp->ring_fx00, rsp->dma_fx00); } else if (rsp && rsp->ring) { dma_free_coherent(&ha->pdev->dev, - (rsp->length + 1) * sizeof(response_t), - rsp->ring, rsp->dma); + (rsp->length + 1) * rsp_entry_size, + rsp->ring, rsp->dma); } kfree(rsp); } @@ -2127,7 +2132,7 @@ qla2x00_iospace_config(struct qla_hw_data *ha) ha->msix_count = msix + 1; /* Max queues are bounded by available msix vectors */ /* MB interrupt uses 1 vector */ - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); ha->max_rsp_queues = ha->max_req_queues; /* Queue pairs is the max value minus the base queue pair */ ha->max_qpairs = ha->max_rsp_queues - 1; @@ -2153,8 +2158,6 @@ qla2x00_iospace_config(struct qla_hw_data *ha) static int qla83xx_iospace_config(struct qla_hw_data *ha) { - uint16_t msix; - if (pci_request_selected_regions(ha->pdev, ha->bars, QLA2XXX_DRIVER_NAME)) { ql_log_pci(ql_log_fatal, ha->pdev, 0x0117, @@ -2203,20 +2206,28 @@ qla83xx_iospace_config(struct qla_hw_data *ha) ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2), pci_resource_len(ha->pdev, 2)); if (ha->msixbase) { + int msix_cnt; + /* Read MSIX vector size of the board */ - pci_read_config_word(ha->pdev, - QLA_83XX_PCI_MSIX_CONTROL, &msix); - ha->msix_count = (msix & PCI_MSIX_FLAGS_QSIZE) + 1; + msix_cnt = pci_msix_vec_count(ha->pdev); + if (msix_cnt <= 0) { + ql_log_pci(ql_log_warn, ha->pdev, 0x0120, + "Failed to read MSI-X count (%d), falling back to base vectors.\n", + msix_cnt); + goto mqiobase_exit; + } + ha->msix_count = msix_cnt; + /* * By default, driver uses at least two msix vectors * (default & rspq) */ if (ql2xmqsupport || ql2xnvmeenable) { /* MB interrupt uses 1 vector */ - ha->max_req_queues = ha->msix_count - 1; + ha->max_req_queues = qla_calc_queue_count(ha->msix_count); /* ATIOQ needs 1 vector. That's 1 less QPair */ - if (QLA_TGT_MODE_ENABLED()) + if (QLA_TGT_MODE_ENABLED() && ha->max_req_queues > 1) ha->max_req_queues--; ha->max_rsp_queues = ha->max_req_queues; @@ -2633,6 +2644,66 @@ static struct isp_operations qla27xx_isp_ops = { .initialize_adapter = qla2x00_initialize_adapter, }; +static void * +qla29xx_read_optrom_stub(struct scsi_qla_host *vha, void *buf, + uint32_t offset, uint32_t length) +{ + ql_dbg(ql_dbg_init, vha, 0x0191, + "read_optrom not supported on 29xx, use read_optrom_region.\n"); + return NULL; +} + +static int +qla29xx_write_optrom_stub(struct scsi_qla_host *vha, void *buf, + uint32_t offset, uint32_t length) +{ + ql_dbg(ql_dbg_init, vha, 0x0192, + "write_optrom not supported on 29xx, use write_optrom_region.\n"); + return QLA_FUNCTION_FAILED; +} + +static struct isp_operations qla29xx_isp_ops = { + .pci_config = qla25xx_pci_config, + .reset_chip = qla24xx_reset_chip, + .chip_diag = qla24xx_chip_diag, + .config_rings = qla24xx_config_rings, + .reset_adapter = qla24xx_reset_adapter, + .nvram_config = qla81xx_nvram_config, + .update_fw_options = qla24xx_update_fw_options, + .load_risc = qla29xx_load_risc, + .pci_info_str = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla24xx_intr_handler, + .enable_intrs = qla24xx_enable_intrs, + .disable_intrs = qla24xx_disable_intrs, + .abort_command = qla24xx_abort_command, + .target_reset = qla24xx_abort_target, + .lun_reset = qla24xx_lun_reset, + .fabric_login = qla24xx_login_fabric, + .fabric_logout = qla24xx_fabric_logout, + .calc_req_entries = NULL, + .build_iocbs = NULL, + .prep_ms_iocb = qla24xx_prep_ms_iocb, + .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb, + .read_nvram = NULL, + .write_nvram = NULL, + .fw_dump = qla27xx_fwdump, + .mpi_fw_dump = qla27xx_mpi_fwdump, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = qla83xx_beacon_blink, + .read_optrom = qla29xx_read_optrom_stub, + .write_optrom = qla29xx_write_optrom_stub, + .read_optrom_region = qla29xx_read_optrom_data, + .write_optrom_region = qla29xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qla24xx_dif_start_scsi, + .start_scsi_mq = qla2xxx_dif_start_scsi_mq, + .abort_isp = qla2x00_abort_isp, + .iospace_config = qla83xx_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, +}; + static inline void qla2x00_set_isp_flags(struct qla_hw_data *ha) { @@ -2796,6 +2867,20 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) ha->device_type |= DT_T10_PI; ha->fw_srisc_address = RISC_START_ADDRESS_2400; break; + case PCI_DEVICE_ID_QLOGIC_ISP2099: + case PCI_DEVICE_ID_QLOGIC_ISP2299: + case PCI_DEVICE_ID_QLOGIC_ISP2091: + case PCI_DEVICE_ID_QLOGIC_ISP2291: + ha->isp_type |= DT_ISP2299; + ha->isp_type |= DT_ISP2099; + ha->isp_type |= DT_ISP2091; + ha->isp_type |= DT_ISP2291; + ha->device_type |= DT_ZIO_SUPPORTED; + ha->device_type |= DT_FWI2; + ha->device_type |= DT_IIDMA; + ha->device_type |= DT_T10_PI; + ha->fw_srisc_address = RISC_START_ADDRESS_2400; + break; } if (IS_QLA82XX(ha)) @@ -2803,11 +2888,15 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) else { /* Get adapter physical port no from interrupt pin register. */ pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no); - if (IS_QLA25XX(ha) || IS_QLA2031(ha) || - IS_QLA27XX(ha) || IS_QLA28XX(ha)) + if (ha->port_no == 0) { + /* None of INT[A|B|C|D] may be virtualized by vfio */ + ha->port_no = PCI_FUNC(ha->pdev->devfn); + } else if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || + IS_QLA28XX(ha) || IS_QLA29XX(ha)) { ha->port_no--; - else + } else { ha->port_no = !(ha->port_no & 1); + } } ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b, @@ -2936,7 +3025,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2081 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2281 || pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2089 || - pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2289) { + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2289 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2099 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2299 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2091 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2291) { bars = pci_select_bars(pdev, IORESOURCE_MEM); mem_only = 1; ql_dbg_pci(ql_dbg_init, pdev, 0x0007, @@ -2998,7 +3091,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) /* Set EEH reset type to fundamental if required by hba */ if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) || - IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) + IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha) || + IS_QLA29XX(ha)) pdev->needs_freset = 1; ha->prev_topology = 0; @@ -3195,6 +3289,23 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flash_data_off = FARX_ACCESS_FLASH_DATA_28XX; ha->nvram_conf_off = ~0; ha->nvram_data_off = ~0; + } else if (IS_QLA29XX(ha)) { + ha->portnum = PCI_FUNC(ha->pdev->devfn); + ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; + ha->mbx_count = MAILBOX_REGISTER_COUNT; + req_length = REQUEST_ENTRY_CNT_83XX; + rsp_length = RESPONSE_ENTRY_CNT_83XX; + ha->max_loop_id = SNS_LAST_LOOP_ID_2300; + ha->init_cb_size = sizeof(struct mid_init_cb_81xx); + ha->gid_list_info_size = 8; + ha->optrom_size = OPTROM_SIZE_28XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + ha->isp_ops = &qla29xx_isp_ops; + ha->flash_conf_off = ~0; + ha->flash_data_off = ~0; + ha->nvram_conf_off = ~0; + ha->nvram_data_off = ~0; + ha->flt_segment_length = QLA_SEGMENT_LENGTH; } ql_dbg_pci(ql_dbg_init, pdev, 0x001e, @@ -3373,7 +3484,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in; rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out; if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) { + IS_QLA28XX(ha) || IS_QLA29XX(ha)) { req->req_q_in = &ha->mqiobase->isp25mq.req_q_in; req->req_q_out = &ha->mqiobase->isp25mq.req_q_out; rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in; @@ -3614,6 +3725,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (test_bit(UNLOADING, &base_vha->dpc_flags)) return -ENODEV; + /* + * FW dump can happens before sysfs nodes are created. If sysfs nodes + * are unavailable then udev script will not be able to read the fw dump. + * Notify udev to read again, now that sysfs nodes are available. + */ + if (ha->fw_dumped || ha->mpi_fw_dumped) + qla2x00_post_uevent_work(base_vha, QLA_UEVENT_CODE_FW_DUMP); + return 0; probe_failed: @@ -3733,7 +3852,7 @@ qla2x00_shutdown(struct pci_dev *pdev) qla2x00_disable_eft_trace(vha); if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) { + IS_QLA28XX(ha) || IS_QLA29XX(ha)) { if (ha->flags.fw_started) qla2x00_abort_isp_cleanup(vha); } else { @@ -3898,7 +4017,7 @@ qla2x00_remove_one(struct pci_dev *pdev) return; if (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) { + IS_QLA28XX(ha) || IS_QLA29XX(ha)) { if (ha->flags.fw_started) qla2x00_abort_isp_cleanup(base_vha); } else if (!IS_QLAFX00(ha)) { @@ -3933,8 +4052,6 @@ qla2x00_remove_one(struct pci_dev *pdev) qla2x00_dfs_remove(base_vha); - qla84xx_put_chip(base_vha); - /* Disable timer */ if (base_vha->timer_active) qla2x00_stop_timer(base_vha); @@ -3959,6 +4076,8 @@ qla2x00_remove_one(struct pci_dev *pdev) scsi_remove_host(base_vha->host); + qla84xx_put_chip(base_vha); + qla2x00_free_device(base_vha); qla2x00_clear_drv_active(ha); @@ -4147,6 +4266,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, { char name[16]; int rc; + size_t req_entry_size = qla_req_entry_size(ha); + size_t rsp_entry_size = qla_rsp_entry_size(ha); if (QLA_TGT_MODE_ENABLED() || EDIF_CAP(ha)) { ha->vp_map = kzalloc_objs(struct qla_vp_map, @@ -4176,7 +4297,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->srb_mempool) goto fail_free_gid_list; - if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) || (ql2xsecenable && IS_QLA28XX(ha))) { + if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) || + (ql2xsecenable && (IS_QLA28XX(ha) || IS_QLA29XX(ha)))) { /* Allocate cache for CT6 Ctx. */ if (!ctx_cachep) { ctx_cachep = kmem_cache_create("qla2xxx_ctx", @@ -4210,7 +4332,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n", ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool); - if (IS_P3P_TYPE(ha) || ql2xenabledif || (IS_QLA28XX(ha) && ql2xsecenable)) { + if (IS_P3P_TYPE(ha) || ql2xenabledif || + ((IS_QLA28XX(ha) || IS_QLA29XX(ha)) && ql2xsecenable)) { ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev, DSD_LIST_DMA_POOL_SIZE, 8, 0); if (!ha->dl_dma_pool) { @@ -4342,13 +4465,17 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, } (*req)->length = req_len; (*req)->ring = dma_alloc_coherent(&ha->pdev->dev, - ((*req)->length + 1) * sizeof(request_t), + ((*req)->length + 1) * req_entry_size, &(*req)->dma, GFP_KERNEL); if (!(*req)->ring) { ql_log_pci(ql_log_fatal, ha->pdev, 0x0029, "Failed to allocate memory for req_ring.\n"); goto fail_req_ring; } + if (IS_QLA29XX(ha)) { + (*req)->ring_ext = (struct request_ext *)(*req)->ring; + (*req)->ring_ext_ptr = (*req)->ring_ext; + } /* Allocate memory for response ring */ *rsp = kzalloc_obj(struct rsp_que); if (!*rsp) { @@ -4359,13 +4486,17 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, (*rsp)->hw = ha; (*rsp)->length = rsp_len; (*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev, - ((*rsp)->length + 1) * sizeof(response_t), + ((*rsp)->length + 1) * rsp_entry_size, &(*rsp)->dma, GFP_KERNEL); if (!(*rsp)->ring) { ql_log_pci(ql_log_fatal, ha->pdev, 0x002b, "Failed to allocate memory for rsp_ring.\n"); goto fail_rsp_ring; } + if (IS_QLA29XX(ha)) { + (*rsp)->ring_ext = (struct response_ext *)(*rsp)->ring; + (*rsp)->ring_ext_ptr = (*rsp)->ring_ext; + } (*req)->rsp = *rsp; (*rsp)->req = *req; ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c, @@ -4387,7 +4518,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, /* Get consistent memory allocated for EX-INIT-CB. */ if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || - IS_QLA28XX(ha)) { + IS_QLA28XX(ha) || IS_QLA29XX(ha)) { ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &ha->ex_init_cb_dma); if (!ha->ex_init_cb) @@ -4397,7 +4528,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, } /* Get consistent memory allocated for Special Features-CB. */ - if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { + if (IS_QLA27XX(ha) || IS_QLA28XX(ha) || IS_QLA29XX(ha)) { ha->sf_init_cb = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &ha->sf_init_cb_dma); if (!ha->sf_init_cb) @@ -4446,6 +4577,14 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, goto fail_flt_buffer; } + ha->flt_data = vzalloc(sizeof(struct qla_flash_layout) + + (sizeof(struct qla_flt_region_data) * FLT_MAX_REGIONS)); + if (!ha->flt_data) { + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001a, + "Unable to allocate memory for mini FLT data.\n"); + goto fail_flt; + } + /* allocate the purex dma pool */ ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev, ELS_MAX_PAYLOAD, 8, 0); @@ -4453,7 +4592,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->purex_dma_pool) { ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b, "Unable to allocate purex_dma_pool.\n"); - goto fail_flt; + goto fail_flt_data; } ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16; @@ -4484,36 +4623,56 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, fail_lsrjt: dma_free_coherent(&ha->pdev->dev, ha->elsrej.size, ha->elsrej.c, ha->elsrej.cdma); + ha->elsrej.c = NULL; + ha->elsrej.cdma = 0; fail_elsrej: dma_pool_destroy(ha->purex_dma_pool); + ha->purex_dma_pool = NULL; +fail_flt_data: + vfree(ha->flt_data); + ha->flt_data = NULL; fail_flt: dma_free_coherent(&ha->pdev->dev, sizeof(struct qla_flt_header) + FLT_REGIONS_SIZE, ha->flt, ha->flt_dma); + ha->flt = NULL; + ha->flt_dma = 0; fail_flt_buffer: dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE, ha->sfp_data, ha->sfp_data_dma); + ha->sfp_data = NULL; + ha->sfp_data_dma = 0; fail_sfp_data: kfree(ha->loop_id_map); + ha->loop_id_map = NULL; fail_loop_id_map: dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma); + ha->async_pd = NULL; + ha->async_pd_dma = 0; fail_async_pd: dma_pool_free(ha->s_dma_pool, ha->sf_init_cb, ha->sf_init_cb_dma); + ha->sf_init_cb = NULL; + ha->sf_init_cb_dma = 0; fail_sf_init_cb: dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); + ha->ex_init_cb = NULL; + ha->ex_init_cb_dma = 0; fail_ex_init_cb: kfree(ha->npiv_info); + ha->npiv_info = NULL; fail_npiv_info: - dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) * - sizeof(response_t), (*rsp)->ring, (*rsp)->dma); + dma_free_coherent(&ha->pdev->dev, + ((*rsp)->length + 1) * rsp_entry_size, + (*rsp)->ring, (*rsp)->dma); (*rsp)->ring = NULL; (*rsp)->dma = 0; fail_rsp_ring: kfree(*rsp); *rsp = NULL; fail_rsp: - dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) * - sizeof(request_t), (*req)->ring, (*req)->dma); + dma_free_coherent(&ha->pdev->dev, + ((*req)->length + 1) * req_entry_size, + (*req)->ring, (*req)->dma); (*req)->ring = NULL; (*req)->dma = 0; fail_req_ring: @@ -4551,12 +4710,12 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, } fail_dif_bundl_dma_pool: - if (IS_QLA82XX(ha) || ql2xenabledif) { + if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) { dma_pool_destroy(ha->fcp_cmnd_dma_pool); ha->fcp_cmnd_dma_pool = NULL; } fail_dl_dma_pool: - if (IS_QLA82XX(ha) || ql2xenabledif) { + if (IS_QLA82XX(ha) || IS_QLA29XX(ha) || ql2xenabledif) { dma_pool_destroy(ha->dl_dma_pool); ha->dl_dma_pool = NULL; } @@ -4566,6 +4725,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, fail_free_nvram: kfree(ha->nvram); ha->nvram = NULL; + ha->fiv = NULL; fail_free_ctx_mempool: mempool_destroy(ha->ctx_mempool); ha->ctx_mempool = NULL; @@ -4921,6 +5081,10 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->flt = NULL; ha->flt_dma = 0; + if (ha->flt_data) + vfree(ha->flt_data); + ha->flt_data = NULL; + if (ha->ms_iocb) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); ha->ms_iocb = NULL; @@ -5027,6 +5191,7 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->optrom_buffer = NULL; kfree(ha->nvram); ha->nvram = NULL; + ha->fiv = NULL; kfree(ha->npiv_info); ha->npiv_info = NULL; kfree(ha->swl); @@ -6000,13 +6165,15 @@ qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id) } static bool -qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha, - struct purex_entry_24xx *purex) +qla25xx_rdp_rsp_reduce_size(struct scsi_qla_host *vha, void *pkt) { + struct purex_entry_24xx *purex = pkt; char fwstr[16]; - u32 sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0]; + u32 sid; struct port_database_24xx *pdb; + sid = purex->s_id[2] << 16 | purex->s_id[1] << 8 | purex->s_id[0]; + /* Domain Controller is always logged-out. */ /* if RDP request is not from Domain Controller: */ if (sid != 0xfffc01) @@ -6071,15 +6238,28 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha, uint8_t *sfp = NULL; uint16_t sfp_flags = 0; uint rsp_payload_length = sizeof(*rsp_payload); + u16 vp_idx; + size_t purex_sz; + size_t rsp_els_sz; + void *rsp_els_pkt = NULL; int rval; ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0180, "%s: Enter\n", __func__); + if (IS_QLA29XX(ha)) { + vp_idx = le16_to_cpu( + ((struct purex_entry_24xx_ext *)purex)->vp_idx); + purex_sz = sizeof(struct purex_entry_24xx_ext); + } else { + vp_idx = purex->vp_idx; + purex_sz = sizeof(*purex); + } + ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0181, "-------- ELS REQ -------\n"); ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0182, - purex, sizeof(*purex)); + purex, purex_sz); if (qla25xx_rdp_rsp_reduce_size(vha, purex)) { rsp_payload_length = @@ -6089,13 +6269,19 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha, rsp_payload_length); } - rsp_els = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_els), + if (IS_QLA29XX(ha)) + rsp_els_sz = sizeof(struct els_entry_24xx_ext); + else + rsp_els_sz = sizeof(struct els_entry_24xx); + + rsp_els_pkt = dma_alloc_coherent(&ha->pdev->dev, rsp_els_sz, &rsp_els_dma, GFP_KERNEL); - if (!rsp_els) { + if (!rsp_els_pkt) { ql_log(ql_log_warn, vha, 0x0183, - "Failed allocate dma buffer ELS RSP.\n"); + "Failed to allocate dma buffer ELS RSP.\n"); goto dealloc; } + rsp_els = rsp_els_pkt; rsp_payload = dma_alloc_coherent(&ha->pdev->dev, sizeof(*rsp_payload), &rsp_payload_dma, GFP_KERNEL); @@ -6119,12 +6305,12 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha, rsp_els->handle = 0; rsp_els->nport_handle = purex->nport_handle; rsp_els->tx_dsd_count = cpu_to_le16(1); - rsp_els->vp_index = purex->vp_idx; - rsp_els->sof_type = EST_SOFI3; rsp_els->rx_xchg_address = purex->rx_xchg_addr; rsp_els->rx_dsd_count = 0; rsp_els->opcode = purex->els_frame_payload[0]; + qla_els_set_vp_sof(vha, rsp_els_pkt, vp_idx); + rsp_els->d_id[0] = purex->s_id[0]; rsp_els->d_id[1] = purex->s_id[1]; rsp_els->d_id[2] = purex->s_id[2]; @@ -6424,13 +6610,13 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha, ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0184, "-------- ELS RSP -------\n"); ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0185, - rsp_els, sizeof(*rsp_els)); + rsp_els_pkt, rsp_els_sz); ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x0186, "-------- ELS RSP PAYLOAD -------\n"); ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x0187, rsp_payload, rsp_payload_length); - rval = qla2x00_issue_iocb(vha, rsp_els, rsp_els_dma, 0); + rval = qla2x00_issue_iocb(vha, rsp_els_pkt, rsp_els_dma, 0); if (rval) { ql_log(ql_log_warn, vha, 0x0188, @@ -6454,9 +6640,9 @@ void qla24xx_process_purex_rdp(struct scsi_qla_host *vha, if (rsp_payload) dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_payload), rsp_payload, rsp_payload_dma); - if (rsp_els) - dma_free_coherent(&ha->pdev->dev, sizeof(*rsp_els), - rsp_els, rsp_els_dma); + if (rsp_els_pkt) + dma_free_coherent(&ha->pdev->dev, rsp_els_sz, + rsp_els_pkt, rsp_els_dma); } void @@ -6828,8 +7014,6 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) qla2x00_dfs_remove(base_vha); - qla84xx_put_chip(base_vha); - if (base_vha->timer_active) qla2x00_stop_timer(base_vha); @@ -6847,6 +7031,8 @@ qla2x00_disable_board_on_pci_error(struct work_struct *work) scsi_remove_host(base_vha->host); + qla84xx_put_chip(base_vha); + base_vha->flags.init_done = 0; qla25xx_delete_queues(base_vha); qla2x00_free_fcports(base_vha); @@ -7616,6 +7802,7 @@ qla2x00_timer(struct timer_list *t) #define FW_ISP8031 9 #define FW_ISP27XX 10 #define FW_ISP28XX 11 +#define FW_ISP29XX 12 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" @@ -7629,6 +7816,7 @@ qla2x00_timer(struct timer_list *t) #define FW_FILE_ISP8031 "ql8300_fw.bin" #define FW_FILE_ISP27XX "ql2700_fw.bin" #define FW_FILE_ISP28XX "ql2800_fw.bin" +#define FW_FILE_ISP29XX "ql2900_fw.bin" static DEFINE_MUTEX(qla_fw_lock); @@ -7646,6 +7834,7 @@ static struct fw_blob qla_fw_blobs[] = { { .name = FW_FILE_ISP8031, }, { .name = FW_FILE_ISP27XX, }, { .name = FW_FILE_ISP28XX, }, + { .name = FW_FILE_ISP29XX, }, { .name = NULL, }, }; @@ -7679,6 +7868,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) blob = &qla_fw_blobs[FW_ISP27XX]; } else if (IS_QLA28XX(ha)) { blob = &qla_fw_blobs[FW_ISP28XX]; + } else if (IS_QLA29XX(ha)) { + blob = &qla_fw_blobs[FW_ISP29XX]; } else { return NULL; } @@ -8155,6 +8346,10 @@ static const struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2281) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2089) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2289) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2099) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2299) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2091) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2291) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); @@ -8190,17 +8385,21 @@ qla2x00_module_init(void) BUILD_BUG_ON(sizeof(ms_iocb_entry_t) != 64); BUILD_BUG_ON(sizeof(request_t) != 64); BUILD_BUG_ON(sizeof(struct abort_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct abort_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct abort_iocb_entry_fx00) != 64); BUILD_BUG_ON(sizeof(struct abts_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct abts_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct access_chip_84xx) != 64); BUILD_BUG_ON(sizeof(struct access_chip_rsp_84xx) != 64); BUILD_BUG_ON(sizeof(struct cmd_bidir) != 64); BUILD_BUG_ON(sizeof(struct cmd_nvme) != 64); + BUILD_BUG_ON(sizeof(struct cmd_nvme_ext) != 128); BUILD_BUG_ON(sizeof(struct cmd_type_6) != 64); BUILD_BUG_ON(sizeof(struct cmd_type_7) != 64); BUILD_BUG_ON(sizeof(struct cmd_type_7_fx00) != 64); BUILD_BUG_ON(sizeof(struct cmd_type_crc_2) != 64); BUILD_BUG_ON(sizeof(struct ct_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct ct_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct ct_fdmi1_hba_attributes) != 2604); BUILD_BUG_ON(sizeof(struct ct_fdmi2_hba_attributes) != 4424); BUILD_BUG_ON(sizeof(struct ct_fdmi2_port_attributes) != 4164); @@ -8214,20 +8413,26 @@ qla2x00_module_init(void) BUILD_BUG_ON(sizeof(struct device_reg_82xx) != 1288); BUILD_BUG_ON(sizeof(struct device_reg_fx00) != 216); BUILD_BUG_ON(sizeof(struct els_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct els_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct els_sts_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct fxdisc_entry_fx00) != 64); BUILD_BUG_ON(sizeof(struct imm_ntfy_from_isp) != 64); BUILD_BUG_ON(sizeof(struct init_cb_24xx) != 128); BUILD_BUG_ON(sizeof(struct init_cb_81xx) != 128); BUILD_BUG_ON(sizeof(struct logio_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct logio_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct mbx_entry) != 64); BUILD_BUG_ON(sizeof(struct mid_init_cb_24xx) != 5252); BUILD_BUG_ON(sizeof(struct mrk_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct mrk_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct nvram_24xx) != 512); BUILD_BUG_ON(sizeof(struct nvram_81xx) != 512); BUILD_BUG_ON(sizeof(struct pt_ls4_request) != 64); + BUILD_BUG_ON(sizeof(struct pt_ls4_request_ext) != 128); BUILD_BUG_ON(sizeof(struct pt_ls4_rx_unsol) != 64); BUILD_BUG_ON(sizeof(struct purex_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct purex_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct qla2100_fw_dump) != 123634); BUILD_BUG_ON(sizeof(struct qla2300_fw_dump) != 136100); BUILD_BUG_ON(sizeof(struct qla24xx_fw_dump) != 37976); @@ -8251,17 +8456,23 @@ qla2x00_module_init(void) BUILD_BUG_ON(sizeof(struct rdp_rsp_payload) != 336); BUILD_BUG_ON(sizeof(struct sns_cmd_pkt) != 2064); BUILD_BUG_ON(sizeof(struct sts_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct sts_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry) != 64); + BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_ext) != 128); BUILD_BUG_ON(sizeof(struct tsk_mgmt_entry_fx00) != 64); BUILD_BUG_ON(sizeof(struct verify_chip_entry_84xx) != 64); BUILD_BUG_ON(sizeof(struct verify_chip_rsp_84xx) != 52); BUILD_BUG_ON(sizeof(struct vf_evfp_entry_24xx) != 56); BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct vp_config_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct vp_ctrl_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx) != 64); + BUILD_BUG_ON(sizeof(struct vp_rpt_id_entry_24xx_ext) != 128); BUILD_BUG_ON(sizeof(sts21_entry_t) != 64); BUILD_BUG_ON(sizeof(sts22_entry_t) != 64); BUILD_BUG_ON(sizeof(sts_cont_entry_t) != 64); + BUILD_BUG_ON(sizeof(struct sts_cont_entry_ext) != 128); BUILD_BUG_ON(sizeof(sts_entry_t) != 64); BUILD_BUG_ON(sizeof(sw_info_t) != 32); BUILD_BUG_ON(sizeof(target_id_t) != 2); @@ -8381,3 +8592,4 @@ MODULE_FIRMWARE(FW_FILE_ISP2300); MODULE_FIRMWARE(FW_FILE_ISP2322); MODULE_FIRMWARE(FW_FILE_ISP24XX); MODULE_FIRMWARE(FW_FILE_ISP25XX); +MODULE_FIRMWARE(FW_FILE_ISP29XX); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index b6c36a8a2d60..56ef2b4c7c26 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -10,6 +10,655 @@ #include #include +/** + * qla29xx_get_flt_layout - Retrieve the flash layout table (FLT) for QLA29xx. + * @vha: Pointer to SCSI QLogic host structure. + * + * This function reads and validates the FLT structure from the flash memory. + * It extracts region information and updates the hardware data structure. + */ +static void +qla29xx_get_flt_layout(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_flash_layout *flt_layout = ha->flt_data; + struct qla_flt_region_header *flt_header = &flt_layout->flt_header; + struct qla_flt_region_data *region = &flt_layout->region[0]; + uint32_t flt_options = 0; + uint32_t flt_size; + uint32_t cnt, chksum; + uint16_t reg_cnt, i; + __le32 *wptr; + void *buf = NULL; + + flt_size = sizeof(struct qla_flt_region_header); + wptr = (__force __le32 *)ha->flt_data; + + buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options, + ha->flt_data, 0, flt_size); + if (!buf) { + ql_log(ql_log_warn, vha, 0x007b, + "Failed to read FLT information.\n"); + goto exit_flt; + } + + ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111, + "Contents of Flash Layout (0x%x):\n", flt_size); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112, + ha->flt_data, flt_size); + + /* check flt version checksum and other info */ + if ((le32_to_cpu(*wptr) == 0xffffffff) || + flt_header->version != cpu_to_le32(FLT_HDR_VERSION)) { + ql_log(ql_log_warn, vha, 0x007c, + "Unsupported FLT detected: version=0x%x length=0x%x signature=0x%x region_count=0x%x checksum=0x%x.\n", + le32_to_cpu(flt_header->version), + le32_to_cpu(flt_header->length), + le32_to_cpu(flt_header->signature), + le16_to_cpu(flt_header->region_count), + le32_to_cpu(flt_header->checksum)); + goto exit_flt; + } + + reg_cnt = le16_to_cpu(flt_header->region_count); + if (reg_cnt > FLT_MAX_REGIONS) + goto exit_flt; + + if (le16_to_cpu(flt_header->region_size) != + sizeof(struct qla_flt_region_data)) { + ql_log(ql_log_warn, vha, 0x0082, + "Unsupported FLT region size 0x%x (expected 0x%zx).\n", + le16_to_cpu(flt_header->region_size), + sizeof(struct qla_flt_region_data)); + goto exit_flt; + } + + flt_size = sizeof(struct qla_flash_layout) + + (reg_cnt * le16_to_cpu(flt_header->region_size)); + if (flt_size > sizeof(struct qla_flash_layout) + + sizeof(struct qla_flt_region_data) * FLT_DATA_MAX_REGIONS) { + ql_log(ql_log_warn, vha, 0x007d, + "FLT size 0x%x exceeds buffer, region_size=0x%x.\n", + flt_size, le16_to_cpu(flt_header->region_size)); + goto exit_flt; + } + + buf = qla29xx_read_optrom_data(vha, FLT_REG_MINI_FLT, flt_options, + ha->flt_data, 0, flt_size); + if (!buf) { + ql_log(ql_log_warn, vha, 0x007b, + "Failed to read FLT information.\n"); + goto exit_flt; + } + + cnt = le32_to_cpu(flt_header->length) / sizeof(*wptr); + if (cnt > flt_size / sizeof(*wptr)) { + ql_log(ql_log_warn, vha, 0x007e, + "FLT length 0x%x exceeds read size 0x%x, rejecting.\n", + le32_to_cpu(flt_header->length), flt_size); + goto exit_flt; + } + for (chksum = 0; cnt--; wptr++) + chksum += le32_to_cpu(*wptr); + if (chksum) { + ql_log(ql_log_fatal, vha, 0x007f, + "Inconsistent FLT detected: version=0x%x length=0x%x signature=0x%x checksum=0x%x.\n", + le32_to_cpu(flt_header->version), + le32_to_cpu(flt_header->length), + le32_to_cpu(flt_header->signature), + le32_to_cpu(flt_header->checksum)); + goto exit_flt; + } + + ql_dbg(ql_dbg_init, vha, 0x007f, + "FLT detected: version=0x%08x length=0x%08x signature=0x%08x region_count=0x%04x region_len=0x%04x segment_len=0x%08x checksum=0x%08x.\n", + le32_to_cpu(flt_header->version), + le32_to_cpu(flt_header->length), + le32_to_cpu(flt_header->signature), + le16_to_cpu(flt_header->region_count), + le16_to_cpu(flt_header->region_size), + le32_to_cpu(flt_header->segment_size), + le32_to_cpu(flt_header->checksum)); + + for (i = 0; reg_cnt; i++, reg_cnt--) { + region = &flt_layout->region[i]; + ql_dbg(ql_dbg_init, vha, 0x0080, + "FLT[%03x]: len=0x%08x version=0x%08x attr=0x%02x.\n", + le16_to_cpu(region->region_code), + le32_to_cpu(region->image_length), + le32_to_cpu(region->version), + le32_to_cpu(region->attribute)); + if (le16_to_cpu(region->region_code) == FLT_REG_FW_DUMP_TMPLT) { + ql_dbg(ql_dbg_init, vha, 0x0080, + "%s: %d: Found FW dump template", __func__, __LINE__); + ha->fw_dump_tmplt_len = le32_to_cpu(region->image_length); + } + } + + if (le32_to_cpu(flt_header->segment_size) >= sizeof(uint32_t)) { + ha->flt_segment_length = le32_to_cpu(flt_header->segment_size); + } else { + ql_log(ql_log_warn, vha, 0x0081, + "FLT segment size 0x%x too small, using default 0x%x.\n", + le32_to_cpu(flt_header->segment_size), QLA_SEGMENT_LENGTH); + ha->flt_segment_length = QLA_SEGMENT_LENGTH; + } + ha->flags.valid_flt = true; + return; + +exit_flt: + ha->flt_segment_length = QLA_SEGMENT_LENGTH; +} + +/** + * qla29xx_get_fdt_info - Retrieve flash descriptor table (FDT) information. + * @vha: Pointer to SCSI QLogic host structure. + * + * This function reads and validates the FDT structure from the flash memory. + * It extracts manufacturer and device-specific information and updates + * the hardware data structure. + */ +static void +qla29xx_get_fdt_info(scsi_qla_host_t *vha) +{ +#define FLASH_BLK_SIZE_4K 0x1000 +#define FLASH_BLK_SIZE_32K 0x8000 +#define FLASH_BLK_SIZE_64K 0x10000 + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + uint16_t cnt, chksum; + __le16 *wptr = (__force __le16 *)req->ring; + struct qla_fdt_layout *fdt = (struct qla_fdt_layout *)req->ring; + uint32_t fdt_options = 0; + void *buf = NULL; + + buf = qla29xx_read_optrom_data(vha, FLT_REG_FDT, fdt_options, + fdt, 0, sizeof(*fdt)); + if (!buf) { + ql_log(ql_log_warn, vha, 0x0047, + "Failed to read FLT information.\n"); + return; + } + + if (le16_to_cpu(*wptr) == 0xffff) + return; + if (memcmp(fdt->sig, "QLID", 4)) + return; + + for (cnt = 0, chksum = 0; cnt < sizeof(*fdt) >> 1; cnt++, wptr++) + chksum += le16_to_cpu(*wptr); + if (chksum) { + ql_dbg(ql_dbg_init, vha, 0x004c, + "Inconsistent FDT detected: checksum=0x%x id=%c version0x%x.\n", + chksum, fdt->sig[0], le16_to_cpu(fdt->version)); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113, + fdt, sizeof(*fdt)); + return; + } +} + +/** + * qla29xx_get_flash_region - Retrieve flash region information for QLA29xx adapters. + * @vha: Pointer to SCSI QLogic host structure. + * @code: Region code to identify the flash region. + * @region: Pointer to store the retrieved flash region information. + * + * This function retrieves the flash region information for QLA29xx adapters + * based on the specified region code. + * + * Returns QLA_SUCCESS on success or QLA_FUNCTION_FAILED on failure. + */ +static int +qla29xx_get_flash_region(struct scsi_qla_host *vha, uint32_t code, + struct qla_flt_region_data *region) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_flash_layout *flt = ha->flt_data; + struct qla_flt_region_header *flt_hdr = &flt->flt_header; + struct qla_flt_region_data *flt_reg = &flt->region[0]; + uint16_t cnt; + int rval = QLA_FUNCTION_FAILED; + + if (!ha->flt_data || !ha->flags.valid_flt) + return QLA_FUNCTION_FAILED; + + cnt = le16_to_cpu(flt_hdr->region_count); + if (cnt > FLT_MAX_REGIONS) + return QLA_FUNCTION_FAILED; + /* + * flt_reg++ strides by sizeof(struct qla_flt_region_data). This is + * safe because valid_flt is set only after qla29xx_get_flt_layout() + * has verified the firmware region_size equals that struct size. + */ + for (; cnt; cnt--, flt_reg++) { + if (le16_to_cpu(flt_reg->region_code) == code) { + memcpy((uint8_t *)region, flt_reg, + sizeof(struct qla_flt_region_data)); + rval = QLA_SUCCESS; + break; + } + } + + return rval; +} + +/** + * set_segment_bits - Set segment-related bits in the options field. + * @options: Pointer to the options field. + * @segment_index: Index of the current segment. + * @total: Total number of segments. + * + * This function sets the appropriate bits in the options field to indicate + * whether the current segment is the first, last, or a single segment. + */ +static void set_segment_bits(uint16_t *options, int segment_index, int total) +{ + /* - Single segment complete image. + * - 1st Segment of an image. + * - Last segment of an image. + */ + if (total == 1) + *options |= (1 << 10) | (1 << 11); + else if (segment_index == 0) + *options |= (1 << 10); + else if (segment_index == total - 1) + *options |= (1 << 11); +} + +/** + * set_chunk_bits - Set chunk-related bits in the options field. + * @options: Pointer to the options field. + * @count: Index of the current chunk. + * @total: Total number of chunks. + * + * This function sets the appropriate bits in the options field to indicate + * whether the current chunk is the first, last, or a single chunk. + */ +static void set_chunk_bits(uint16_t *options, int count, int total) +{ + /* - Single chunk complete segment + * - 1st chunk of a segment + * - Last chunk of a segment + */ + if (total == 1) + *options |= (1 << 4) | (1 << 5); + else if (count == 0) + *options |= (1 << 4); + else if (count == total - 1) + *options |= (1 << 5); +} + +/** + * qla29xx_write_optrom_data - Write data to the optrom for QLA29xx adapters. + * @vha: Pointer to SCSI QLogic host structure. + * @reg_code: Region code to write to. + * @opts: Options for the write operation. + * @buf: Buffer containing the data to write. + * @offset: Offset within the region to start writing. + * @length: Length of data to write. + * + * This function writes data to the specified optrom region for QLA29xx adapters. + * + * Returns 0 on success or a negative error code on failure. + */ +int +qla29xx_write_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code, + uint16_t opts, void *buf, uint32_t offset, + uint32_t length) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_flt_region_data region; + dma_addr_t optrom_dma; + uint32_t faddr, left, burst; + uint32_t img_len, seg_dlen; + uint32_t region_len, region_dlen; + void *optrom; + uint8_t *pbuf; + int rval = -EINVAL; + uint16_t total_segments, segment_index = 0; + uint16_t chunk_index = 0; + + memset(®ion, 0, sizeof(region)); + + optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, + &optrom_dma, GFP_KERNEL); + if (!optrom) { + ql_log(ql_log_warn, vha, 0x0090, + "Unable to allocate memory for optrom burst read (%x KB).\n", + OPTROM_BURST_SIZE / 1024); + return -ENOMEM; + } + + if (ha->flags.valid_flt && length == 0) { + /* Get image length and segment length from FLT */ + rval = qla29xx_get_flash_region(vha, reg_code, ®ion); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x0092, + "Invalid address %x - not a region start address\n", + reg_code); + goto free_buf; + } + img_len = le32_to_cpu(region.image_length); + } else { + img_len = length; + } + + seg_dlen = ha->flt_segment_length >> 2; + region_len = (length > 0) ? length : img_len; + region_dlen = (region_len >> 2); + total_segments = (region_dlen + seg_dlen - 1) / seg_dlen; + + faddr = offset >> 2; + left = region_dlen; + burst = OPTROM_BURST_DWORDS; + pbuf = buf; + + while (region_dlen > 0) { + uint32_t segment_size, total_chunks; + uint16_t options = 0; + + segment_size = (region_dlen > seg_dlen) ? seg_dlen : region_dlen; + total_chunks = (segment_size + OPTROM_BURST_DWORDS - 1) / + OPTROM_BURST_DWORDS; + + burst = OPTROM_BURST_DWORDS; + if (burst > left) + burst = left; + if (burst > segment_size - chunk_index * OPTROM_BURST_DWORDS) + burst = segment_size - chunk_index * OPTROM_BURST_DWORDS; + + set_segment_bits(&options, segment_index, total_segments); + set_chunk_bits(&options, chunk_index, total_chunks); + + /* flash block write operations */ + SET_FW_BIT(options, BIT_6); + CLEAR_FW_BIT(options, BIT_9); + + check_and_set_mbc_bits(opts, options, BIT_15, BIT_15); + check_and_set_mbc_bits(opts, options, BIT_7, BIT_7); + + if (segment_index == total_segments - 1 && + chunk_index == total_chunks - 1) + check_and_set_mbc_bits(opts, options, BIT_8, BIT_8); + + memcpy(optrom, pbuf, burst * 4); + + /* faddr is offset relative to region code */ + rval = qla29xx_flash_block_write(vha, optrom_dma, + faddr, burst, reg_code, options); + if (rval) { + ql_log(ql_log_warn, vha, 0x0095, + "Unable to burst-write optrom segment (%x/%x/%llx).\n", + rval, faddr, (unsigned long long)optrom_dma); + + dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, + optrom, optrom_dma); + goto exit_write; + } + + left -= burst; + faddr += burst; + pbuf += burst * 4; + chunk_index++; + if (chunk_index >= total_chunks) { + chunk_index = 0; + segment_index++; + region_dlen -= segment_size; + } + } + +free_buf: + dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom, + optrom_dma); + return rval; + +exit_write: + return rval; +} + +/** + * qla29xx_read_optrom_data - Read data from the optrom for QLA29xx adapters. + * @vha: Pointer to SCSI QLogic host structure. + * @reg_code: Region code to read from. + * @opts: Options for the read operation. + * @buf: Buffer to store the read data. + * @offset: Offset within the region to start reading. + * @length: Length of data to read. + * + * This function reads data from the specified optrom region for QLA29xx adapters. + * + * Returns a pointer to the buffer on success or NULL on failure. + */ +void * +qla29xx_read_optrom_data(struct scsi_qla_host *vha, uint16_t reg_code, + uint16_t opts, void *buf, uint32_t offset, + uint32_t length) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_flt_region_data region; + dma_addr_t optrom_dma; + uint32_t faddr, left, burst; + uint32_t img_len, seg_dlen; + uint32_t region_len, region_dlen; + void *optrom; + uint8_t *pbuf; + uint16_t total_segments, segment_index = 0; + uint16_t chunk_index = 0; + int rval; + + memset(®ion, 0, sizeof(region)); + + optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, + &optrom_dma, GFP_KERNEL); + if (!optrom) { + ql_log(ql_log_warn, vha, 0x0093, + "Unable to allocate memory for optrom burst read (%x KB).\n", + OPTROM_BURST_SIZE / 1024); + return NULL; + } + + if (ha->flags.valid_flt && length == 0) { + /* Get image length and segment length from FLT */ + rval = qla29xx_get_flash_region(vha, reg_code, ®ion); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7033, + "Invalid address %x - not a region start address\n", + reg_code); + goto free_buf; + } + img_len = le32_to_cpu(region.image_length); + } else { + img_len = length; + } + + seg_dlen = ha->flt_segment_length >> 2; + region_len = (length > 0) ? length : img_len; + region_dlen = (region_len >> 2); + total_segments = (region_dlen + seg_dlen - 1) / seg_dlen; + + faddr = offset >> 2; + left = region_dlen; + burst = OPTROM_BURST_DWORDS; + pbuf = buf; + + ql_log(ql_log_info, vha, 0x0096, + "Reg[0x%x]: options=0x%x length=0x%x offset=0x%x segments=%u\n", + reg_code, opts, region_len, offset, total_segments); + + while (region_dlen > 0) { + uint32_t segment_size, total_chunks; + uint16_t options = 0; + + segment_size = (region_dlen > seg_dlen) ? seg_dlen : region_dlen; + total_chunks = (segment_size + OPTROM_BURST_DWORDS - 1) / + OPTROM_BURST_DWORDS; + + burst = OPTROM_BURST_DWORDS; + if (burst > left) + burst = left; + if (burst > segment_size - chunk_index * OPTROM_BURST_DWORDS) + burst = segment_size - chunk_index * OPTROM_BURST_DWORDS; + + set_segment_bits(&options, segment_index, total_segments); + set_chunk_bits(&options, chunk_index, total_chunks); + + /* flash block read operations */ + CLEAR_FW_BIT(options, BIT_9); + CLEAR_FW_BIT(options, BIT_6); + + options |= opts; + + /* faddr is offset relative to region code */ + rval = qla29xx_flash_block_read(vha, optrom_dma, + faddr, burst, reg_code, options); + if (rval) { + ql_log(ql_log_warn, vha, 0x0097, + "Unable to burst-read optrom segment (%x/%x/%llx).\n", + rval, faddr, (unsigned long long)optrom_dma); + goto free_buf; + } + + memcpy(pbuf, optrom, burst * 4); + + left -= burst; + faddr += burst; + pbuf += burst * 4; + chunk_index++; + if (chunk_index >= total_chunks) { + chunk_index = 0; + segment_index++; + region_dlen -= segment_size; + } + } + + dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom, + optrom_dma); + return buf; + +free_buf: + dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom, + optrom_dma); + return NULL; +} + +static void set_chunk_mpi_bits(uint16_t *options, int count, int total) +{ + /* - Single chunk complete segment/image + * - 1st chunk of a segment/image + * - Last chunk of a segment/image + */ + if (total == 1) + *options |= BIT_2 | BIT_1; + else if (count == 0) + *options |= BIT_1; + else if (count == total - 1) + *options |= BIT_2; +} + +/** + * qla29xx_mpi_optrom_data - Dump/load MPI optrom data for 29xx. + * @vha: Pointer to SCSI QLogic host structure. + * @opts: Options for the operation. + * @buf: Buffer to read from/write to. + * @offset: MPI RAM address, in 32-bit words (MBC_LOAD_DUMP_MPI_RAM is + * word-addressed; not a byte offset). + * @length: Length of data, in bytes (converted internally to a word count). + * @op: Operation, either QLA29XX_MPI_OP_DUMP or QLA29XX_MPI_OP_LOAD. + * + * Returns: + * QLA_SUCCESS on success or an error code on failure. + */ +int +qla29xx_mpi_optrom_data(struct scsi_qla_host *vha, uint16_t opts, void *buf, + uint32_t offset, uint32_t length, enum qla29xx_mpi_optrom_op op) +{ + struct qla_hw_data *ha = vha->hw; + dma_addr_t optrom_dma; + void *optrom; + uint32_t mpi_addr, mpi_size, burst = 0; + uint32_t total_chunks = 0; + uint32_t *dcode, *fwcode; + uint32_t chunk_count = 0; + int rval = -EINVAL; + + optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, + &optrom_dma, GFP_KERNEL); + if (!optrom) { + ql_log(ql_log_warn, vha, 0x0090, + "Unable to allocate memory for optrom burst read (%x KB).\n", + OPTROM_BURST_SIZE / 1024); + rval = -ENOMEM; + goto exit_mpi_op; + } + + mpi_addr = offset; + dcode = (uint32_t *)optrom; + memset(dcode, 0, OPTROM_BURST_SIZE); + + fwcode = (uint32_t *)buf; + mpi_size = length >> 2; + burst = OPTROM_BURST_SIZE >> 2; + total_chunks = (mpi_size + burst - 1) / burst; + + while (mpi_size > 0) { + uint16_t options = 0; + + if (burst > mpi_size) + burst = mpi_size; + + set_chunk_mpi_bits(&options, chunk_count, total_chunks); + + if (op == QLA29XX_MPI_OP_DUMP) { + options |= (BIT_0); + options |= opts; + + ql_log(ql_log_info, vha, 0x008b, + "-> %s (DUMP): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n", + __func__, mpi_addr, burst, options, chunk_count, + total_chunks); + + memcpy(dcode, fwcode, burst * 4); + + rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst, + optrom_dma); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "Failed dump mpi firmware.\n"); + goto free_buf; + } + } else if (op == QLA29XX_MPI_OP_LOAD) { + options |= opts; + + ql_log(ql_log_info, vha, 0x008b, + "-> %s (LOAD): %#x <-(%#x words) (0x%x options) chunk (0x%x, 0x%x)\n", + __func__, mpi_addr, burst, options, chunk_count, + total_chunks); + + rval = qla29xx_load_dump_mpi(vha, options, mpi_addr, burst, + optrom_dma); + if (rval) { + ql_log(ql_log_fatal, vha, 0x0098, + "Failed load mpi firmware.\n"); + goto free_buf; + } + memcpy(fwcode, dcode, burst * 4); + } + + chunk_count++; + fwcode += burst; + mpi_addr += burst; + mpi_size -= burst; + } + + rval = QLA_SUCCESS; +free_buf: + dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom, + optrom_dma); + +exit_mpi_op: + return rval; +} + /* * NVRAM support routines */ @@ -1117,10 +1766,18 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) uint32_t flt_addr; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && - !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) && - !IS_QLA27XX(ha) && !IS_QLA28XX(ha)) - return QLA_SUCCESS; + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_CNA_CAPABLE(ha) && + !IS_QLA2031(ha) && !IS_QLA27XX(ha) && !IS_QLA28XX(ha) && + !IS_QLA29XX(ha)) + goto done; + + if (IS_QLA29XX(ha)) { + mutex_lock(&ha->optrom_mutex); + qla29xx_get_flt_layout(vha); + qla29xx_get_fdt_info(vha); + mutex_unlock(&ha->optrom_mutex); + goto done; + } if (IS_QLA28XX(ha) && !qla28xx_validate_mcu_signature(vha)) ha->flags.secure_mcu = 1; @@ -1132,7 +1789,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) qla2xxx_get_flt_info(vha, flt_addr); qla2xxx_get_fdt_info(vha); qla2xxx_get_idc_param(vha); - +done: return QLA_SUCCESS; } @@ -1374,7 +2031,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, __le32 *dwptr, uint32_t faddr, ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095, "Write burst (%#lx dwords)...\n", dburst); ret = qla2x00_load_ram(vha, optrom_dma, - flash_data_addr(ha, faddr), dburst); + flash_data_addr(ha, faddr), dburst, 0); if (!ret) { liter += dburst - 1; faddr += dburst - 1; @@ -2974,7 +3631,7 @@ qla28xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, ql_log(ql_log_warn + ql_dbg_verbose, vha, 0x7095, "Write burst (%#lx dwords)...\n", dburst); rval = qla2x00_load_ram(vha, optrom_dma, - flash_data_addr(ha, faddr), dburst); + flash_data_addr(ha, faddr), dburst, 0); if (rval != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x7097, "Failed burst write at %x (%p/%#llx)...\n", @@ -3448,6 +4105,86 @@ qla82xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) return ret; } +/* + * Read the boot BIOS/FCODE/EFI version for 29xx adapters. + * + * 29xx reads flash through an FLT region code plus a region-relative + * offset. The firmware only honours such a read at the region base + * (offset 0) and rejects a non-zero offset with MBS_COMMAND_ERROR. The + * PCI expansion ROM header and the PCI data structure it points to both + * live within the first dwords of FLT_REG_BOOT_CODE, so read one bounded + * block at offset 0 and parse both structures from memory instead of + * issuing a second read at the PCI-data-structure offset. + */ +#define QLA29XX_BOOT_PROBE_LEN 0x100 + +static void +qla29xx_get_boot_version(scsi_qla_host_t *vha, void *mbuf) +{ + struct qla_hw_data *ha = vha->hw; + uint8_t *bcode = mbuf; + uint32_t pcids; + uint8_t code_type; + + if (!qla29xx_read_optrom_data(vha, FLT_REG_BOOT_CODE, 0, mbuf, 0, + QLA29XX_BOOT_PROBE_LEN)) { + ql_log(ql_log_info, vha, 0x018e, + "Unable to read PCI Data Structure.\n"); + return; + } + + /* Verify PCI expansion ROM header. */ + if (memcmp(bcode, "\x55\xaa", 2)) { + ql_log(ql_log_info, vha, 0x0059, + "No matching ROM signature.\n"); + return; + } + + /* Locate the PCI data structure within the buffer. */ + pcids = (bcode[0x19] << 8) | bcode[0x18]; + if (pcids + 0x16 > QLA29XX_BOOT_PROBE_LEN) + return; + + bcode += pcids; + + /* Validate signature of PCI data structure. */ + if (memcmp(bcode, "PCIR", 4)) { + ql_log(ql_log_info, vha, 0x005a, + "PCI data struct not found pcir_adr=%x.\n", pcids); + return; + } + + code_type = bcode[0x14]; + switch (code_type) { + case ROM_CODE_TYPE_BIOS: + /* Intel x86, PC-AT compatible. */ + ha->bios_revision[0] = bcode[0x12]; + ha->bios_revision[1] = bcode[0x13]; + ql_dbg(ql_dbg_init, vha, 0x005b, "Read BIOS %d.%d.\n", + ha->bios_revision[1], ha->bios_revision[0]); + break; + case ROM_CODE_TYPE_FCODE: + /* Open Firmware standard for PCI (FCode). */ + ha->fcode_revision[0] = bcode[0x12]; + ha->fcode_revision[1] = bcode[0x13]; + ql_dbg(ql_dbg_init, vha, 0x005c, "Read FCODE %d.%d.\n", + ha->fcode_revision[1], ha->fcode_revision[0]); + break; + case ROM_CODE_TYPE_EFI: + /* Extensible Firmware Interface (EFI). */ + ha->efi_revision[0] = bcode[0x12]; + ha->efi_revision[1] = bcode[0x13]; + ql_dbg(ql_dbg_init, vha, 0x005d, "Read EFI %d.%d.\n", + ha->efi_revision[1], ha->efi_revision[0]); + break; + default: + ql_log(ql_log_warn, vha, 0x005e, + "Unrecognized code type %x at pcids %x.\n", + code_type, pcids); + break; + } +} + int qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) { @@ -3472,12 +4209,31 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); + /* ISP29xx: get FW version from FLT region metadata */ + if (IS_QLA29XX(ha)) { + struct qla_flt_region_data region; + + ret = qla29xx_get_flash_region(vha, FLT_REG_FW, ®ion); + if (ret != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x7033, + "Invalid region %x\n", FLT_REG_FW); + return ret; + } + + ha->fw_revision[0] = (le32_to_cpu(region.version) >> 16) & 0xff; + ha->fw_revision[1] = (le32_to_cpu(region.version) >> 8) & 0xff; + ha->fw_revision[2] = le32_to_cpu(region.version) & 0xff; + + /* BIOS/FCODE/EFI version from the boot-code region. */ + qla29xx_get_boot_version(vha, mbuf); + return ret; + } + pcihdr = ha->flt_region_boot << 2; if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) { qla27xx_get_active_image(vha, &active_regions); - if (active_regions.global == QLA27XX_SECONDARY_IMAGE) { + if (active_regions.global == QLA27XX_SECONDARY_IMAGE) pcihdr = ha->flt_region_boot_sec << 2; - } } do { @@ -3486,7 +4242,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) if (ret) { ql_log(ql_log_info, vha, 0x017d, "Unable to read PCI EXP Rom Header(%x).\n", ret); - return QLA_FUNCTION_FAILED; + break; } bcode = mbuf + (pcihdr % 4); @@ -3494,7 +4250,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) /* No signature */ ql_log(ql_log_fatal, vha, 0x0059, "No matching ROM signature.\n"); - return QLA_FUNCTION_FAILED; + break; } /* Locate PCI data structure. */ @@ -3504,7 +4260,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) if (ret) { ql_log(ql_log_info, vha, 0x018e, "Unable to read PCI Data Structure (%x).\n", ret); - return QLA_FUNCTION_FAILED; + break; } bcode = mbuf + (pcihdr % 4); @@ -3515,7 +4271,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) ql_log(ql_log_fatal, vha, 0x005a, "PCI data struct not found pcir_adr=%x.\n", pcids); ql_dump_buffer(ql_dbg_init, vha, 0x0059, dcode, 32); - return QLA_FUNCTION_FAILED; + break; } /* Read version */ diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index e47da45e93a0..53a505df8da0 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -1738,8 +1738,8 @@ static int qlt_build_abts_resp_iocb(struct qla_tgt_mgmt_cmd *mcmd) return -EAGAIN; } - resp = (struct abts_resp_to_24xx *)qpair->req->ring_ptr; - memset(resp, 0, sizeof(*resp)); + resp = (struct abts_resp_to_24xx *)qla_req_ring_slot(ha, qpair->req); + memset(resp, 0, qla_req_entry_size(ha)); h = qlt_make_handle(qpair); if (unlikely(h == QLA_TGT_NULL_HANDLE)) { @@ -2490,17 +2490,13 @@ static int qlt_check_reserve_free_req(struct qla_qpair *qpair, /* * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire */ -static inline void *qlt_get_req_pkt(struct req_que *req) +static inline void *qlt_get_req_pkt(struct qla_hw_data *ha, + struct req_que *req) { /* Adjust ring index. */ - req->ring_index++; - if (req->ring_index == req->length) { - req->ring_index = 0; - req->ring_ptr = req->ring; - } else { - req->ring_ptr++; - } - return (cont_entry_t *)req->ring_ptr; + qla_req_ring_advance(ha, req); + + return qla_req_ring_slot(ha, req); } /* ha->hardware_lock supposed to be held on entry */ @@ -2549,9 +2545,9 @@ static int qlt_24xx_build_ctio_pkt(struct qla_qpair *qpair, uint16_t temp; struct qla_tgt_cmd *cmd = prm->cmd; - pkt = (struct ctio7_to_24xx *)qpair->req->ring_ptr; + pkt = (struct ctio7_to_24xx *)qla_req_ring_slot(qpair->hw, qpair->req); prm->pkt = pkt; - memset(pkt, 0, sizeof(*pkt)); + memset(pkt, 0, qla_req_entry_size(qpair->hw)); pkt->entry_type = CTIO_TYPE7; pkt->entry_count = (uint8_t)prm->req_cnt; @@ -2600,11 +2596,12 @@ static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm) { int cnt; struct dsd64 *cur_dsd; + struct qla_hw_data *ha = prm->cmd->qpair->hw; /* Build continuation packets */ while (prm->seg_cnt > 0) { cont_a64_entry_t *cont_pkt64 = - (cont_a64_entry_t *)qlt_get_req_pkt( + (cont_a64_entry_t *)qlt_get_req_pkt(ha, prm->cmd->qpair->req); /* @@ -2614,7 +2611,7 @@ static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm) * that. */ - memset(cont_pkt64, 0, sizeof(*cont_pkt64)); + memset(cont_pkt64, 0, qla_req_entry_size(ha)); cont_pkt64->entry_count = 1; cont_pkt64->sys_define = 0; @@ -3012,9 +3009,9 @@ qlt_build_ctio_crc2_pkt(struct qla_qpair *qpair, struct qla_tgt_prm *prm) ha = vha->hw; - pkt = (struct ctio_crc2_to_fw *)qpair->req->ring_ptr; + pkt = (struct ctio_crc2_to_fw *)qla_req_ring_slot(ha, qpair->req); prm->pkt = pkt; - memset(pkt, 0, sizeof(*pkt)); + memset(pkt, 0, qla_req_entry_size(ha)); ql_dbg_qp(ql_dbg_tgt, cmd->qpair, 0xe071, "qla_target(%d):%s: se_cmd[%p] CRC2 prot_op[0x%x] cmd prot sg:cnt[%p:%x] lba[%llu]\n", @@ -3306,7 +3303,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, */ struct ctio7_to_24xx *ctio = (struct ctio7_to_24xx *)qlt_get_req_pkt( - qpair->req); + vha->hw, qpair->req); ql_dbg_qp(ql_dbg_tgt, qpair, 0x305e, "Building additional status packet 0x%p.\n", @@ -3316,6 +3313,7 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, * T10Dif: ctio_crc2_to_fw overlay ontop of * ctio7_to_24xx */ + memset(ctio, 0, qla_req_entry_size(vha->hw)); memcpy(ctio, pkt, sizeof(*ctio)); /* reset back to CTIO7 */ ctio->entry_count = 1; diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c index b0a74b036cf4..fd3984127497 100644 --- a/drivers/scsi/qla2xxx/qla_tmpl.c +++ b/drivers/scsi/qla2xxx/qla_tmpl.c @@ -306,6 +306,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha, goto done; } + if (vha->hw->flags.t262_fail) { + ql_dbg(ql_dbg_misc, vha, 0xd045, + "%s: failed previously\n", __func__); + qla27xx_skip_entry(ent, buf); + goto done; + } dwords = end - start + 1; if (buf) { buf += *len; @@ -314,7 +320,12 @@ qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha, ql_dbg(ql_dbg_async, vha, 0xffff, "%s: dump ram MB failed. Area %xh start %lxh end %lxh\n", __func__, area, start, end); - return INVALID_ENTRY; + + if (rc == QLA_FUNCTION_TIMEOUT) + vha->hw->flags.t262_fail = 1; + + qla27xx_skip_entry(ent, buf); + goto done; } } *len += dwords * sizeof(uint32_t); @@ -536,13 +547,12 @@ qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha, { ql_dbg(ql_dbg_misc, vha, 0xd20d, "%s: scratch [%lx]\n", __func__, *len); - qla27xx_insert32(0xaaaaaaaa, buf, len); - qla27xx_insert32(0xbbbbbbbb, buf, len); - qla27xx_insert32(0xcccccccc, buf, len); - qla27xx_insert32(0xdddddddd, buf, len); - qla27xx_insert32(*len + sizeof(uint32_t), buf, len); + + /* The data format is based on entry type t260. */ + qla27xx_insert32(offsetof(struct device_reg_24xx, mailbox0), buf, len); + qla27xx_insertbuf(vha->hw->mbregs, sizeof(vha->hw->mbregs), buf, len); if (buf) - ent->t269.scratch_size = 5 * sizeof(uint32_t); + ent->t269.scratch_size = sizeof(uint32_t) + sizeof(vha->hw->mbregs); return qla27xx_next_entry(ent); } @@ -589,17 +599,37 @@ qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha, { ulong dwords = le32_to_cpu(ent->t272.count); ulong start = le32_to_cpu(ent->t272.addr); + int rc; ql_dbg(ql_dbg_misc, vha, 0xd210, "%s: rdremram [%lx]\n", __func__, *len); + + if (vha->hw->flags.t272_fail) { + ql_dbg(ql_dbg_misc, vha, 0xd04f, + "%s: failed previously\n", __func__); + qla27xx_skip_entry(ent, buf); + goto done; + } + if (buf) { ql_dbg(ql_dbg_misc, vha, 0xd02c, "%s: @%lx -> (%lx dwords)\n", __func__, start, dwords); buf += *len; - qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf); + rc = qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf); + if (rc != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xd01b, + "%s: dump mpi MB failed. Start %lxh dwords %lxh\n", + __func__, start, dwords); + + if (rc == QLA_FUNCTION_TIMEOUT) + vha->hw->flags.t272_fail = 1; + + qla27xx_skip_entry(ent, buf); + goto done; + } } *len += dwords * sizeof(uint32_t); - +done: return qla27xx_next_entry(ent); } diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 9564beafdab7..ef9dc88221c9 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -6,9 +6,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "10.02.10.100-k" +#define QLA2XXX_VERSION "12.00.00.2607b2" -#define QLA_DRIVER_MAJOR_VER 10 -#define QLA_DRIVER_MINOR_VER 02 -#define QLA_DRIVER_PATCH_VER 10 -#define QLA_DRIVER_BETA_VER 100 +#define QLA_DRIVER_MAJOR_VER 12 +#define QLA_DRIVER_MINOR_VER 00 +#define QLA_DRIVER_PATCH_VER 00 +#define QLA_DRIVER_BETA_VER 2607 diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 9d1c9c41d0f9..4fb2dc403962 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -4310,8 +4310,8 @@ static bool comp_write_worker(struct sdeb_store_info *sip, u64 lba, u32 num, if (!res) return res; if (rest) - res = memcmp(fsp, arr + ((num - rest) * lb_size), - rest * lb_size); + res = !memcmp(fsp, arr + ((num - rest) * lb_size), + rest * lb_size); if (!res) return res; if (compare_only) @@ -5890,6 +5890,7 @@ static int resp_report_zones(struct scsi_cmnd *scp, u32 alloc_len, rep_opts, rep_len; bool partial; u64 lba, zs_lba; + u64 arr_len; u8 *arr = NULL, *desc; u8 *cmd = scp->cmnd; struct sdeb_zone_state *zsp = NULL; @@ -5911,9 +5912,12 @@ static int resp_report_zones(struct scsi_cmnd *scp, return check_condition_result; } - rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD); + rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> + ilog2(RZONES_DESC_HD); + rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones); + arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); - arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN); + arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN); if (!arr) { mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 147127fb4db9..b729407348f7 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -73,6 +73,26 @@ void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy) } } +void scsi_rcu_eh_wakeup(struct work_struct *work) +{ + struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work); + unsigned long flags; + unsigned int busy; + + /* + * Ensure any running scsi_dec_host_busy has completed its rcu section + * so changes to host state and host_eh_scheduled are visible to all + * future calls of scsi_dec_host_busy + */ + synchronize_rcu(); + + busy = scsi_host_busy(shost); + + spin_lock_irqsave(shost->host_lock, flags); + scsi_eh_wakeup(shost, busy); + spin_unlock_irqrestore(shost->host_lock, flags); +} + /** * scsi_schedule_eh - schedule EH for SCSI host * @shost: SCSI host to invoke error handling on. @@ -88,7 +108,7 @@ void scsi_schedule_eh(struct Scsi_Host *shost) if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 || scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) { shost->host_eh_scheduled++; - scsi_eh_wakeup(shost, scsi_host_busy(shost)); + queue_work(shost->tmf_work_q, &shost->eh_work); } spin_unlock_irqrestore(shost->host_lock, flags); @@ -2141,7 +2161,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) struct scsi_cmnd *scmd; struct request *req; - req = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_IN, 0); + req = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_IN, BLK_MQ_REQ_NOWAIT); if (IS_ERR(req)) return; scmd = blk_mq_rq_to_pdu(req); @@ -2342,6 +2362,7 @@ static void scsi_unjam_host(struct Scsi_Host *shost) int scsi_error_handler(void *data) { struct Scsi_Host *shost = data; + bool eh_noresume; /* * We use TASK_INTERRUPTIBLE so that the thread is not @@ -2383,7 +2404,8 @@ int scsi_error_handler(void *data) * what we need to do to get it up and online again (if we can). * If we fail, we end up taking the thing offline. */ - if (!shost->eh_noresume && scsi_autopm_get_host(shost) != 0) { + eh_noresume = READ_ONCE(shost->eh_noresume); + if (!eh_noresume && scsi_autopm_get_host(shost) != 0) { SCSI_LOG_ERROR_RECOVERY(1, shost_printk(KERN_ERR, shost, "scsi_eh_%d: unable to autoresume\n", @@ -2407,7 +2429,7 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - if (!shost->eh_noresume) + if (!eh_noresume) scsi_autopm_put_host(shost); } __set_current_state(TASK_RUNNING); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 22e2e3223440..480a0bf2aff3 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1187,8 +1187,10 @@ blk_status_t scsi_alloc_sgtables(struct scsi_cmnd *cmd) if (blk_rq_bytes(rq) & rq->q->limits.dma_pad_mask) { unsigned int pad_len = (rq->q->limits.dma_pad_mask & ~blk_rq_bytes(rq)) + 1; + unsigned int data_len = last_sg->length; last_sg->length += pad_len; + sg_zero_buffer(last_sg, 1, pad_len, data_len); cmd->extra_len += pad_len; } @@ -2224,14 +2226,6 @@ struct scsi_device *scsi_device_from_queue(struct request_queue *q) return sdev; } -/* - * pktcdvd should have been integrated into the SCSI layers, but for historical - * reasons like the old IDE driver it isn't. This export allows it to safely - * probe if a given device is a SCSI one and only attach to that. - */ -#ifdef CONFIG_CDROM_PKTCDVD_MODULE -EXPORT_SYMBOL_GPL(scsi_device_from_queue); -#endif /** * scsi_block_requests - Utility function used by low-level drivers to prevent diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 7a193cc04e5b..3dbf2ca59536 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -91,6 +91,7 @@ extern enum blk_eh_timer_return scsi_timeout(struct request *req); extern int scsi_error_handler(void *host); extern enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *cmd); extern void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy); +extern void scsi_rcu_eh_wakeup(struct work_struct *work); extern void scsi_eh_scmd_add(struct scsi_cmnd *); void scsi_eh_ready_devs(struct Scsi_Host *shost, struct list_head *work_q, @@ -102,6 +103,7 @@ void scsi_eh_done(struct scsi_cmnd *scmd); /* scsi_lib.c */ extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd); +extern struct scsi_device *scsi_device_from_queue(struct request_queue *q); extern void scsi_queue_insert(struct scsi_cmnd *cmd, enum scsi_qc_status reason); extern void scsi_io_completion(struct scsi_cmnd *, unsigned int); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 599e75f33334..a1b21ea14e54 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -958,6 +958,21 @@ static unsigned char sd_setup_protect_cmnd(struct scsi_cmnd *scmd, return protect; } +static void sd_uninit_command(struct scsi_cmnd *cmd) +{ + struct request *rq = scsi_cmd_to_rq(cmd); + struct scsi_device *sdp = cmd->device; + + if (!(rq->rq_flags & RQF_SPECIAL_PAYLOAD)) + return; + + if (sdp->sector_size > PAGE_SIZE) + mempool_free(rq->special_vec.bv_page, sd_large_page_pool); + else + mempool_free(rq->special_vec.bv_page, sd_page_pool); + rq->rq_flags &= ~RQF_SPECIAL_PAYLOAD; +} + static void *sd_set_special_bvec(struct scsi_cmnd *cmd, unsigned int data_len) { struct page *page; @@ -990,6 +1005,7 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); unsigned int data_len = 24; char *buf; + blk_status_t ret; buf = sd_set_special_bvec(cmd, data_len); if (!buf) @@ -1008,7 +1024,10 @@ static blk_status_t sd_setup_unmap_cmnd(struct scsi_cmnd *cmd) cmd->transfersize = data_len; rq->timeout = SD_TIMEOUT; - return scsi_alloc_sgtables(cmd); + ret = scsi_alloc_sgtables(cmd); + if (ret != BLK_STS_OK) + sd_uninit_command(cmd); + return ret; } static void sd_config_atomic(struct scsi_disk *sdkp, struct queue_limits *lim) @@ -1079,6 +1098,7 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); u32 data_len = sdp->sector_size; + blk_status_t ret; if (!sd_set_special_bvec(cmd, data_len)) return BLK_STS_RESOURCE; @@ -1094,7 +1114,10 @@ static blk_status_t sd_setup_write_same16_cmnd(struct scsi_cmnd *cmd, cmd->transfersize = data_len; rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT; - return scsi_alloc_sgtables(cmd); + ret = scsi_alloc_sgtables(cmd); + if (ret != BLK_STS_OK) + sd_uninit_command(cmd); + return ret; } static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, @@ -1106,6 +1129,7 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, u64 lba = sectors_to_logical(sdp, blk_rq_pos(rq)); u32 nr_blocks = sectors_to_logical(sdp, blk_rq_sectors(rq)); u32 data_len = sdp->sector_size; + blk_status_t ret; if (!sd_set_special_bvec(cmd, data_len)) return BLK_STS_RESOURCE; @@ -1121,7 +1145,10 @@ static blk_status_t sd_setup_write_same10_cmnd(struct scsi_cmnd *cmd, cmd->transfersize = data_len; rq->timeout = unmap ? SD_TIMEOUT : SD_WRITE_SAME_TIMEOUT; - return scsi_alloc_sgtables(cmd); + ret = scsi_alloc_sgtables(cmd); + if (ret != BLK_STS_OK) + sd_uninit_command(cmd); + return ret; } static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd) @@ -1550,20 +1577,6 @@ static blk_status_t sd_init_command(struct scsi_cmnd *cmd) } } -static void sd_uninit_command(struct scsi_cmnd *SCpnt) -{ - struct request *rq = scsi_cmd_to_rq(SCpnt); - struct scsi_device *sdp = SCpnt->device; - unsigned sector_size = sdp->sector_size; - - if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) { - if (sector_size > PAGE_SIZE) - mempool_free(rq->special_vec.bv_page, sd_large_page_pool); - else - mempool_free(rq->special_vec.bv_page, sd_page_pool); - } -} - static bool sd_need_revalidate(struct gendisk *disk, struct scsi_disk *sdkp) { if (sdkp->device->removable || sdkp->write_prot) { @@ -2406,8 +2419,8 @@ static int sd_done(struct scsi_cmnd *SCpnt) } sdkp->medium_access_timed_out = 0; - if (!scsi_status_is_check_condition(result) && - (!sense_valid || sense_deferred)) + if (!scsi_status_is_check_condition(result) || + !sense_valid || sense_deferred) goto out; switch (sshdr.sense_key) { @@ -4089,7 +4102,9 @@ static int sd_probe(struct scsi_device *sdp) if (sdp->sector_size > PAGE_SIZE) { if (sd_large_pool_create()) { error = -ENOMEM; - goto out_free_index; + device_unregister(&sdkp->disk_dev); + put_disk(gd); + goto out; } } diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 74cd4e8a61c2..5408f002e6c0 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -863,10 +863,9 @@ sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) if (val >= SG_MAX_QUEUE) break; rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = - srp->header.masked_status & - srp->header.host_status & - srp->header.driver_status; + rinfo[val].problem = srp->header.masked_status || + srp->header.host_status || + srp->header.driver_status; if (srp->done) rinfo[val].duration = srp->header.duration; diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 5ec583dc2e7d..140e8a6addbe 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -34,11 +34,11 @@ #define BUILD_TIMESTAMP #endif -#define DRIVER_VERSION "2.1.36-026" +#define DRIVER_VERSION "2.1.42-011" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 -#define DRIVER_RELEASE 36 -#define DRIVER_REVISION 26 +#define DRIVER_RELEASE 42 +#define DRIVER_REVISION 11 #define DRIVER_NAME "Microchip SmartPQI Driver (v" \ DRIVER_VERSION BUILD_TIMESTAMP ")" @@ -66,6 +66,12 @@ static struct pqi_cmd_priv *pqi_cmd_priv(struct scsi_cmnd *cmd) return scsi_cmd_priv(cmd); } +static int pqi_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd) +{ + memset(pqi_cmd_priv(cmd), 0, sizeof(struct pqi_cmd_priv)); + return 0; +} + static void pqi_verify_structures(void); static void pqi_take_ctrl_offline(struct pqi_ctrl_info *ctrl_info, enum pqi_ctrl_shutdown_reason ctrl_shutdown_reason); @@ -5958,6 +5964,17 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd) struct pqi_scsi_dev *device; struct completion *wait; + /* + * Clear the AIO-retry marker on final completion so the tag + * starts clean on its next dispatch. On DID_IMM_RETRY leave + * it intact: pqi_aio_io_complete() sets DID_IMM_RETRY and + * bumps the marker to steer the requeue onto the RAID path, + * and pqi_process_raid_io_error() consumes the non-zero + * marker to offline a misbehaving drive. + */ + if (host_byte(scmd->result) != DID_IMM_RETRY) + pqi_cmd_priv(scmd)->this_residual = 0; + if (!scmd->device) { set_host_byte(scmd, DID_NO_CONNECT); return; @@ -6918,6 +6935,144 @@ static int pqi_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) return rc; } +static int pqi_big_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) +{ + int rc; + char *kernel_buffer = NULL; + u16 iu_length; + size_t sense_data_length; + BIG_IOCTL_Command_struct iocommand; + struct pqi_raid_path_request request; + struct pqi_raid_error_info pqi_error_info; + struct ciss_error_info ciss_error_info; + + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + if (pqi_ofa_in_progress(ctrl_info) && pqi_ctrl_blocked(ctrl_info)) + return -EBUSY; + if (!arg) + return -EINVAL; + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + if (copy_from_user(&iocommand, arg, sizeof(iocommand))) + return -EFAULT; + if (iocommand.buf_size < 1 && + iocommand.Request.Type.Direction != XFER_NONE) + return -EINVAL; + if (iocommand.Request.CDBLen > sizeof(request.cdb)) + return -EINVAL; + if (iocommand.Request.Type.Type != TYPE_CMD) + return -EINVAL; + + switch (iocommand.Request.Type.Direction) { + case XFER_NONE: + case XFER_WRITE: + case XFER_READ: + case XFER_READ | XFER_WRITE: + break; + default: + return -EINVAL; + } + + if (iocommand.buf_size > 0) { + kernel_buffer = kmalloc(iocommand.buf_size, GFP_KERNEL); + if (!kernel_buffer) + return -ENOMEM; + if (iocommand.Request.Type.Direction & XFER_WRITE) { + if (copy_from_user(kernel_buffer, iocommand.buf, + iocommand.buf_size)) { + rc = -EFAULT; + goto out; + } + } else { + memset(kernel_buffer, 0, iocommand.buf_size); + } + } + + memset(&request, 0, sizeof(request)); + + request.header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO; + iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) - + PQI_REQUEST_HEADER_LENGTH; + memcpy(request.lun_number, iocommand.LUN_info.LunAddrBytes, sizeof(request.lun_number)); + memcpy(request.cdb, iocommand.Request.CDB, iocommand.Request.CDBLen); + request.additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0; + + switch (iocommand.Request.Type.Direction) { + case XFER_NONE: + request.data_direction = SOP_NO_DIRECTION_FLAG; + break; + case XFER_WRITE: + request.data_direction = SOP_WRITE_FLAG; + break; + case XFER_READ: + request.data_direction = SOP_READ_FLAG; + break; + case XFER_READ | XFER_WRITE: + request.data_direction = SOP_BIDIRECTIONAL; + break; + } + + request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE; + + if (iocommand.buf_size > 0) { + put_unaligned_le32(iocommand.buf_size, &request.buffer_length); + + rc = pqi_map_single(ctrl_info->pci_dev, + &request.sg_descriptors[0], kernel_buffer, + iocommand.buf_size, DMA_BIDIRECTIONAL); + if (rc) + goto out; + + iu_length += sizeof(request.sg_descriptors[0]); + } + + put_unaligned_le16(iu_length, &request.header.iu_length); + + if (ctrl_info->raid_iu_timeout_supported) + put_unaligned_le32(iocommand.Request.Timeout, &request.timeout); + + rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header, + PQI_SYNC_FLAGS_INTERRUPTABLE, &pqi_error_info); + + if (iocommand.buf_size > 0) + pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, DMA_BIDIRECTIONAL); + + memset(&iocommand.error_info, 0, sizeof(iocommand.error_info)); + + if (rc == 0) { + pqi_error_info_to_ciss(&pqi_error_info, &ciss_error_info); + iocommand.error_info.ScsiStatus = ciss_error_info.scsi_status; + iocommand.error_info.CommandStatus = ciss_error_info.command_status; + sense_data_length = ciss_error_info.sense_data_length; + if (sense_data_length) { + if (sense_data_length > sizeof(iocommand.error_info.SenseInfo)) + sense_data_length = sizeof(iocommand.error_info.SenseInfo); + memcpy(iocommand.error_info.SenseInfo, + pqi_error_info.data, sense_data_length); + iocommand.error_info.SenseLen = sense_data_length; + } + } + + if (copy_to_user(arg, &iocommand, sizeof(iocommand))) { + rc = -EFAULT; + goto out; + } + + if (rc == 0 && iocommand.buf_size > 0 && + (iocommand.Request.Type.Direction & XFER_READ)) { + if (copy_to_user(iocommand.buf, kernel_buffer, + iocommand.buf_size)) { + rc = -EFAULT; + } + } + +out: + kfree(kernel_buffer); + + return rc; +} + static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd, void __user *arg) { @@ -6941,6 +7096,12 @@ static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd, case CCISS_PASSTHRU: rc = pqi_passthru_ioctl(ctrl_info, arg); break; + case CCISS_BIG_PASSTHRU: + rc = pqi_big_passthru_ioctl(ctrl_info, arg); + break; + case CCISS_BIG_PASSTHRU_SUPPORTED: + rc = 0; + break; default: rc = -EINVAL; break; @@ -7612,6 +7773,7 @@ static const struct scsi_host_template pqi_driver_template = { .sdev_groups = pqi_sdev_groups, .shost_groups = pqi_shost_groups, .cmd_size = sizeof(struct pqi_cmd_priv), + .init_cmd_priv = pqi_init_cmd_priv, }; static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info) @@ -10152,10 +10314,30 @@ static const struct pci_device_id pqi_pci_id_table[] = { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x207d, 0x4240) }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x207d, 0x4246) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x207d, 0x4256) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x207d, 0x4356) + }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x207d, 0x4840) }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x207d, 0x4940) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x207d, 0x4a46) + }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, PCI_VENDOR_ID_ADVANTECH, 0x8312) @@ -10340,6 +10522,18 @@ static const struct pci_device_id pqi_pci_id_table[] = { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x1cf2, 0x544f) }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1cf2, 0x5451) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1cf2, 0x5452) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1cf2, 0x5453) + }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x1cf2, 0x54da) @@ -10380,6 +10574,10 @@ static const struct pci_device_id pqi_pci_id_table[] = { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x1f3f, 0x0610) }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1f3f, 0x0670) + }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, PCI_VENDOR_ID_LENOVO, 0x0220) diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 571ea549152b..58b6ae96d03f 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -520,6 +520,36 @@ static void storvsc_host_scan(struct work_struct *work) scsi_scan_host(host); } +#if IS_ENABLED(CONFIG_SCSI_FC_ATTRS) +static int storvsc_user_scan(struct Scsi_Host *host, + unsigned int channel, + unsigned int id, + u64 lun) +{ + unsigned int first_channel, last_channel; + unsigned int first_id, end_id; + unsigned int ch, target; + + if ((channel != SCAN_WILD_CARD && channel > host->max_channel) || + (id != SCAN_WILD_CARD && id >= host->max_id) || + (lun != SCAN_WILD_CARD && lun >= host->max_lun)) + return -EINVAL; + + first_channel = channel == SCAN_WILD_CARD ? 0 : channel; + last_channel = channel == SCAN_WILD_CARD ? host->max_channel : channel; + first_id = id == SCAN_WILD_CARD ? 0 : id; + end_id = id == SCAN_WILD_CARD ? host->max_id : id + 1; + + for (ch = first_channel; ch <= last_channel; ch++) { + for (target = first_id; target < end_id; target++) + scsi_scan_target(&host->shost_gendev, ch, target, lun, + SCSI_SCAN_MANUAL); + } + + return 0; +} +#endif + static void storvsc_remove_lun(struct work_struct *work) { struct storvsc_scan_work *wrk; @@ -2232,6 +2262,8 @@ static int __init storvsc_drv_init(void) fc_transport_template = fc_attach_transport(&fc_transport_functions); if (!fc_transport_template) return -ENODEV; + + fc_transport_template->user_scan = storvsc_user_scan; #endif ret = vmbus_driver_register(&storvsc_drv); diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 5fdaa71f0652..35731b18c519 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -122,10 +122,11 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf) struct virtio_scsi_cmd *cmd = buf; struct scsi_cmnd *sc = cmd->sc; struct virtio_scsi_cmd_resp *resp = &cmd->resp.cmd; + unsigned sense_len = virtio32_to_cpu(vscsi->vdev, resp->sense_len); dev_dbg(&sc->device->sdev_gendev, "cmd %p response %u status %#02x sense_len %u\n", - sc, resp->response, resp->status, resp->sense_len); + sc, resp->response, resp->status, sense_len); sc->result = resp->status; virtscsi_compute_resid(sc, virtio32_to_cpu(vscsi->vdev, resp->resid)); @@ -166,13 +167,10 @@ static void virtscsi_complete_cmd(struct virtio_scsi *vscsi, void *buf) break; } - WARN_ON(virtio32_to_cpu(vscsi->vdev, resp->sense_len) > - VIRTIO_SCSI_SENSE_SIZE); + WARN_ON(sense_len > VIRTIO_SCSI_SENSE_SIZE); if (resp->sense_len) { memcpy(sc->sense_buffer, resp->sense, - min_t(u32, - virtio32_to_cpu(vscsi->vdev, resp->sense_len), - VIRTIO_SCSI_SENSE_SIZE)); + min_t(u32, sense_len, VIRTIO_SCSI_SENSE_SIZE)); } scsi_done(sc); @@ -288,8 +286,9 @@ static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi, struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); unsigned int target = event->lun[1]; unsigned int lun = (event->lun[2] << 8) | event->lun[3]; + unsigned int reason = virtio32_to_cpu(vscsi->vdev, event->reason); - switch (virtio32_to_cpu(vscsi->vdev, event->reason)) { + switch (reason) { case VIRTIO_SCSI_EVT_RESET_RESCAN: if (lun == 0) { scsi_scan_target(&shost->shost_gendev, 0, target, @@ -309,7 +308,7 @@ static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi, } break; default: - pr_info("Unsupported virtio scsi event reason %x\n", event->reason); + pr_info("Unsupported virtio scsi event reason %x\n", reason); } } @@ -409,7 +408,8 @@ static void virtscsi_handle_event(struct work_struct *work) virtscsi_handle_param_change(vscsi, event); break; default: - pr_err("Unsupported virtio scsi event %x\n", event->event); + pr_err("Unsupported virtio scsi event %x\n", + virtio32_to_cpu(vscsi->vdev, event->event)); } virtscsi_kick_event(vscsi, event_node); } diff --git a/drivers/scsi/zorro7xx.c b/drivers/scsi/zorro7xx.c index 6aca9897b231..21c769dc1ecb 100644 --- a/drivers/scsi/zorro7xx.c +++ b/drivers/scsi/zorro7xx.c @@ -50,25 +50,25 @@ static struct zorro_driver_data { static struct zorro_device_id zorro7xx_zorro_tbl[] = { { .id = ZORRO_PROD_PHASE5_BLIZZARD_603E_PLUS, - .driver_data = (unsigned long)&zorro7xx_driver_data[0], + .driver_data_ptr = &zorro7xx_driver_data[0], }, { .id = ZORRO_PROD_MACROSYSTEMS_WARP_ENGINE_40xx, - .driver_data = (unsigned long)&zorro7xx_driver_data[1], + .driver_data_ptr = &zorro7xx_driver_data[1], }, { .id = ZORRO_PROD_CBM_A4091_1, - .driver_data = (unsigned long)&zorro7xx_driver_data[2], + .driver_data_ptr = &zorro7xx_driver_data[2], }, { .id = ZORRO_PROD_CBM_A4091_2, - .driver_data = (unsigned long)&zorro7xx_driver_data[2], + .driver_data_ptr = &zorro7xx_driver_data[2], }, { .id = ZORRO_PROD_GVP_GFORCE_040_060, - .driver_data = (unsigned long)&zorro7xx_driver_data[3], + .driver_data_ptr = &zorro7xx_driver_data[3], }, - { 0 } + { } }; MODULE_DEVICE_TABLE(zorro, zorro7xx_zorro_tbl); @@ -77,11 +77,11 @@ static int zorro7xx_init_one(struct zorro_dev *z, { struct Scsi_Host *host; struct NCR_700_Host_Parameters *hostdata; - struct zorro_driver_data *zdd; + const struct zorro_driver_data *zdd; unsigned long board, ioaddr; board = zorro_resource_start(z); - zdd = (struct zorro_driver_data *)ent->driver_data; + zdd = ent->driver_data_ptr; if (zdd->absolute) { ioaddr = zdd->offset; diff --git a/drivers/scsi/zorro_esp.c b/drivers/scsi/zorro_esp.c index 1622285c9aec..178d46140674 100644 --- a/drivers/scsi/zorro_esp.c +++ b/drivers/scsi/zorro_esp.c @@ -706,7 +706,7 @@ static const struct zorro_device_id zorro_esp_zorro_tbl[] = { .id = ZORRO_ID(PHASE5, 0x19, 0), .driver_data = ZORRO_CYBERII, }, - { 0 } + { } }; MODULE_DEVICE_TABLE(zorro, zorro_esp_zorro_tbl); diff --git a/drivers/target/target_core_fabric_lib.c b/drivers/target/target_core_fabric_lib.c index 87c5d26a5089..2853b95b2c59 100644 --- a/drivers/target/target_core_fabric_lib.c +++ b/drivers/target/target_core_fabric_lib.c @@ -290,13 +290,24 @@ static void sbp_parse_pr_out_transport_id(char *buf, char *i_str) static bool iscsi_parse_pr_out_transport_id( struct se_portal_group *se_tpg, char *buf, + u32 buf_len, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) { char *p; + u32 tid_len; int i; - u8 format_code = (buf[0] & 0xc0); + u8 format_code; + + /* + * The 4-byte iSCSI TransportID header (FORMAT CODE + 2-byte ADDITIONAL + * LENGTH) must be present before any of it can be parsed. + */ + if (buf_len < 4) + return false; + + format_code = buf[0] & 0xc0; /* * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6: * @@ -316,15 +327,17 @@ static bool iscsi_parse_pr_out_transport_id( return false; } /* - * If the caller wants the TransportID Length, we set that value for the - * entire iSCSI Tarnsport ID now. + * Reconstruct the self-described TransportID length from the ADDITIONAL + * LENGTH field plus the 4-byte header. Reject it if it is below the + * spc4r17 section 7.5.4.6 minimum (ADDITIONAL LENGTH shall be at least + * 20) or if it runs past the bytes actually received, so that every + * access below stays inside the TransportID. */ - if (out_tid_len) { - /* The shift works thanks to integer promotion rules */ - *out_tid_len = get_unaligned_be16(&buf[2]); - /* Add four bytes for iSCSI Transport ID header */ - *out_tid_len += 4; - } + tid_len = get_unaligned_be16(&buf[2]) + 4; + if (tid_len < 24 || tid_len > buf_len) + return false; + if (out_tid_len) + *out_tid_len = tid_len; /* * Check for ',i,0x' separator between iSCSI Name and iSCSI Initiator @@ -332,16 +345,32 @@ static bool iscsi_parse_pr_out_transport_id( * format. */ if (format_code == 0x40) { - p = strstr(&buf[4], ",i,0x"); + p = strnstr(&buf[4], ",i,0x", tid_len - 4); if (!p) { - pr_err("Unable to locate \",i,0x\" separator" - " for Initiator port identifier: %s\n", - &buf[4]); + pr_err("Unable to locate \",i,0x\" separator in iSCSI TransportID\n"); + return false; + } + /* + * The iSCSI name runs from &buf[4] up to the separator; reject it + * if it cannot fit in i_str[TRANSPORT_IQN_LEN]. + */ + if (p - &buf[4] >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); return false; } *p = '\0'; /* Terminate iSCSI Name */ p += 5; /* Skip over ",i,0x" separator */ + /* + * The ISID must follow the separator. A ",i,0x" sitting at the + * very end of the TransportID leaves no ISID and would point the + * port nexus at buf + tid_len, i.e. past the descriptor, which + * the registration code then reads as the ISID string. + */ + if (p >= buf + tid_len) { + pr_err("Missing ISID in iSCSI Initiator port TransportID\n"); + return false; + } *port_nexus_ptr = p; /* * Go ahead and do the lower case conversion of the received @@ -349,7 +378,7 @@ static bool iscsi_parse_pr_out_transport_id( * for comparison against the running iSCSI session's ISID from * iscsi_target.c:lio_sess_get_initiator_sid() */ - for (i = 0; i < 12; i++) { + for (i = 0; i < 12 && p < buf + tid_len; i++) { /* * The first ISCSI INITIATOR SESSION ID field byte * containing an ASCII null character terminates the @@ -367,10 +396,22 @@ static bool iscsi_parse_pr_out_transport_id( *p = tolower(*p); p++; } - } else + strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + } else { *port_nexus_ptr = NULL; - - strscpy(i_str, &buf[4], TRANSPORT_IQN_LEN); + /* + * FORMAT CODE 00b: the name occupies buf[4..tid_len-1]. The + * declared length tid_len - 4 must fit in i_str[TRANSPORT_IQN_LEN]. + * (For 01b the same tid_len bound would be over-restrictive: the + * descriptor also carries the separator and ISID, so a legal + * <=223-byte name gives tid_len up to 244.) + */ + if (tid_len - 4 >= TRANSPORT_IQN_LEN) { + pr_err("iSCSI Initiator port name too long in TransportID\n"); + return false; + } + strscpy(i_str, &buf[4], tid_len - 4); + } return true; } @@ -420,8 +461,16 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, } bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str) + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str) { + /* + * The fixed-length SAS/SRP/FCP/SBP TransportIDs are 24 bytes; the iSCSI + * format is variable and bounds itself against buf_len below. + */ + if (tpg->proto_id != SCSI_PROTOCOL_ISCSI && buf_len < 24) + return false; + switch (tpg->proto_id) { case SCSI_PROTOCOL_SAS: /* @@ -440,8 +489,8 @@ bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, sbp_parse_pr_out_transport_id(buf, i_str); break; case SCSI_PROTOCOL_ISCSI: - return iscsi_parse_pr_out_transport_id(tpg, buf, out_tid_len, - port_nexus_ptr, i_str); + return iscsi_parse_pr_out_transport_id(tpg, buf, buf_len, + out_tid_len, port_nexus_ptr, i_str); default: pr_err("Unknown proto_id: 0x%02x\n", tpg->proto_id); return false; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 1087d1d17c36..ea65d39fef88 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, break; case PRO_PREEMPT: case PRO_PREEMPT_AND_ABORT: - if (!ops->pr_clear) { + if (!ops->pr_preempt) { pr_err("block_device does not support pr_preempt.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } @@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, sa == PRO_PREEMPT_AND_ABORT); break; case PRO_RELEASE: - if (!ops->pr_clear) { - pr_err("block_device does not support pr_pclear.\n"); + if (!ops->pr_release) { + pr_err("block_device does not support pr_release.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 763e6d26e187..f0886ea29034 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h @@ -104,7 +104,8 @@ int target_get_pr_transport_id(struct se_node_acl *nacl, struct t10_pr_registration *pr_reg, int *format_code, unsigned char *buf); bool target_parse_pr_out_transport_id(struct se_portal_group *tpg, - char *buf, u32 *out_tid_len, char **port_nexus_ptr, char *i_str); + char *buf, u32 buf_len, u32 *out_tid_len, + char **port_nexus_ptr, char *i_str); /* target_core_hba.c */ struct se_hba *core_alloc_hba(const char *, u32, u32); diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 11790f2c5d80..1a77b4bb62b0 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1573,7 +1573,7 @@ core_scsi3_decode_spec_i_port( iport_ptr = NULL; tid_found = target_parse_pr_out_transport_id(tmp_tpg, - ptr, &tid_len, &iport_ptr, i_str); + ptr, tpdl, &tid_len, &iport_ptr, i_str); if (!tid_found) continue; /* @@ -3285,7 +3285,7 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } tid_found = target_parse_pr_out_transport_id(dest_se_tpg, - &buf[24], &tmp_tid_len, &iport_ptr, initiator_str); + &buf[24], tid_len, &tmp_tid_len, &iport_ptr, initiator_str); if (!tid_found) { pr_err("SPC-3 PR REGISTER_AND_MOVE: Unable to locate" " initiator_str from Transport ID\n"); @@ -3293,9 +3293,6 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, goto out; } - transport_kunmap_data_sg(cmd); - buf = NULL; - pr_debug("SPC-3 PR [%s] Extracted initiator %s identifier: %s" " %s\n", dest_tf_ops->fabric_name, (iport_ptr != NULL) ? "port" : "device", initiator_str, (iport_ptr != NULL) ? @@ -3532,6 +3529,11 @@ core_scsi3_emulate_pro_register_and_move(struct se_cmd *cmd, u64 res_key, core_scsi3_update_and_write_aptpl(cmd->se_dev, aptpl); core_scsi3_put_pr_reg(dest_pr_reg); + /* + * iport_ptr aliases the PR-OUT parameter list mapped above, so the + * buffer is unmapped only here on success (and at out: on error). + */ + transport_kunmap_data_sg(cmd); return 0; out: if (buf) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index fad03a15c969..dcfe94594916 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, u32 data_length, int task_attr, int data_dir, int flags) { struct se_portal_group *se_tpg; + int ret; se_tpg = se_sess->se_tpg; BUG_ON(!se_tpg); @@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second * kref_put() to happen during fabric packet acknowledgement. */ - return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) + se_cmd->cmd_cnt = NULL; + + return ret; } EXPORT_SYMBOL_GPL(target_init_cmd); @@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, * allocation failure. */ ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); - if (ret < 0) + if (ret < 0) { + se_cmd->cmd_cnt = NULL; return -ENOMEM; + } if (tm_type == TMR_ABORT_TASK) se_cmd->se_tmr_req->ref_task_tag = tag; @@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, /* See target_submit_cmd for commentary */ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); if (ret) { + se_cmd->cmd_cnt = NULL; core_tmr_release_req(se_cmd->se_tmr_req); return ret; } diff --git a/drivers/ufs/core/ufs-debugfs.c b/drivers/ufs/core/ufs-debugfs.c index e3dd81d6fe82..be527209540d 100644 --- a/drivers/ufs/core/ufs-debugfs.c +++ b/drivers/ufs/core/ufs-debugfs.c @@ -165,7 +165,7 @@ static ssize_t ufs_saved_err_write(struct file *file, const char __user *buf, char val_str[16] = { }; int val, ret; - if (count > sizeof(val_str)) + if (count >= sizeof(val_str)) return -EINVAL; if (copy_from_user(val_str, buf, count)) return -EFAULT; diff --git a/drivers/ufs/core/ufs-mcq.c b/drivers/ufs/core/ufs-mcq.c index 13b60a2d06db..8106d55f4041 100644 --- a/drivers/ufs/core/ufs-mcq.c +++ b/drivers/ufs/core/ufs-mcq.c @@ -296,14 +296,20 @@ static int ufshcd_mcq_get_tag(struct ufs_hba *hba, struct cq_entry *cqe) if (hba->ufs_version >= ufshci_version(4, 1)) return cqe->task_tag; - /* sizeof(struct utp_transfer_cmd_desc) must be a multiple of 128 */ + /* Both UCD types must have a size that is a multiple of 128 bytes */ BUILD_BUG_ON(sizeof(struct utp_transfer_cmd_desc) & GENMASK(6, 0)); + BUILD_BUG_ON(sizeof(struct utp_devman_cmd_desc) & GENMASK(6, 0)); /* Bits 63:7 UCD base address, 6:5 are reserved, 4:0 is SQ ID */ - addr = (le64_to_cpu(cqe->command_desc_base_addr) & CQE_UCD_BA) - - hba->ucdl_dma_addr; + addr = le64_to_cpu(cqe->command_desc_base_addr) & CQE_UCD_BA; - return div_u64(addr, ufshcd_get_ucd_size(hba)); + /* The devman UCD is outside the pool; return its reserved tag. */ + if (unlikely(addr == hba->devman_ucd_dma_addr)) + return hba->dev_cmd.tag; + + /* Pool entries follow the reserved tags. */ + return div_u64(addr - hba->ucdl_dma_addr, ufshcd_get_ucd_size(hba)) + + UFSHCD_NUM_RESERVED; } static void ufshcd_mcq_process_cqe(struct ufs_hba *hba, diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c index ffad049872b9..aa925cbb07e8 100644 --- a/drivers/ufs/core/ufs-rpmb.c +++ b/drivers/ufs/core/ufs-rpmb.c @@ -69,7 +69,11 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l hba = ufs_rpmb->hba; - req_type = be16_to_cpu(frm_out->req_resp); + /* req_resp is at the end of an RPMB frame. */ + if (req_len < sizeof(*frm_out)) + return -EINVAL; + + req_type = get_unaligned_be16(&frm_out->req_resp); switch (req_type) { case RPMB_PROGRAM_KEY: @@ -107,7 +111,7 @@ static int ufs_rpmb_route_frames(struct device *dev, u8 *req, unsigned int req_l struct rpmb_frame *frm_resp = (struct rpmb_frame *)resp; memset(frm_resp, 0, sizeof(*frm_resp)); - frm_resp->req_resp = cpu_to_be16(RPMB_RESULT_READ); + put_unaligned_be16(RPMB_RESULT_READ, &frm_resp->req_resp); ret = ufs_sec_submit(hba, protocol_id, resp, resp_len, true); if (ret) { dev_err(dev, "Result read request failed with ret=%d\n", ret); @@ -152,8 +156,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba) return -EINVAL; } - INIT_LIST_HEAD(&hba->rpmbs); - struct rpmb_descr descr = { .type = RPMB_TYPE_UFS, .route_frames = ufs_rpmb_route_frames, diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c index d9dc4cc3452e..63e670d1a9d9 100644 --- a/drivers/ufs/core/ufs-sysfs.c +++ b/drivers/ufs/core/ufs-sysfs.c @@ -54,6 +54,7 @@ static const char *ufs_hs_gear_to_string(enum ufs_hs_gear_tag gear) case UFS_HS_G3: return "HS_GEAR3"; case UFS_HS_G4: return "HS_GEAR4"; case UFS_HS_G5: return "HS_GEAR5"; + case UFS_HS_G6: return "HS_GEAR6"; default: return "UNKNOWN"; } } diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c index aa64f2bf4f1e..fa5f539632eb 100644 --- a/drivers/ufs/core/ufs-txeq.c +++ b/drivers/ufs/core/ufs-txeq.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include "ufshcd-priv.h" @@ -481,7 +482,8 @@ static void ufshcd_evaluate_tx_eqtr_fom(struct ufs_hba *hba, * @h_iter: host TX EQTR iterator data structure * @d_iter: device TX EQTR iterator data structure * - * Returns 0 on success, negative error code otherwise + * Returns 0 on success, negative error code if get_rx_fom vops fails. + * RX_FOM DME get failures are logged and treated as 0 FOM for that lane. */ static int ufshcd_get_rx_fom(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode, @@ -496,8 +498,12 @@ static int ufshcd_get_rx_fom(struct ufs_hba *hba, ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB_SEL(RX_FOM, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), &fom); - if (ret) - return ret; + if (ret) { + h_iter->fom[lane] = 0; + dev_dbg(hba->dev, "Failed to get FOM for Host TX Lane %d: %d\n", + lane, ret); + continue; + } h_iter->fom[lane] = (u8)fom; } @@ -507,8 +513,12 @@ static int ufshcd_get_rx_fom(struct ufs_hba *hba, ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_FOM, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), &fom); - if (ret) - return ret; + if (ret) { + d_iter->fom[lane] = 0; + dev_dbg(hba->dev, "Failed to get FOM for Device TX Lane %d: %d\n", + lane, ret); + continue; + } d_iter->fom[lane] = (u8)fom; } @@ -1216,14 +1226,26 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba, struct ufs_pa_layer_attr *pwr_mode) { struct ufs_pa_layer_attr old_pwr_info; + unsigned int noio_flag; + int notify_ret; int ret; + /* + * ufshcd_tx_eqtr() is called from a power-mode-change context where + * I/O is suspended. Use memalloc_noio_save() to propagate GFP_NOIO + * to all allocations in the call tree instead of tagging each call + * site individually. + */ + noio_flag = memalloc_noio_save(); + if (!params->eqtr_record) { params->eqtr_record = devm_kzalloc(hba->dev, sizeof(*params->eqtr_record), GFP_KERNEL); - if (!params->eqtr_record) - return -ENOMEM; + if (!params->eqtr_record) { + ret = -ENOMEM; + goto out_noio_restore; + } } memcpy(&old_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr)); @@ -1231,23 +1253,31 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba, ret = ufshcd_tx_eqtr_prepare(hba, pwr_mode); if (ret) { dev_err(hba->dev, "Failed to prepare TX EQTR: %d\n", ret); - goto out; + goto out_unprepare; } ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode); - if (ret) - goto out; + if (ret) { + dev_err(hba->dev, "TX EQTR PRE_CHANGE notify failed: %d\n", ret); + goto out_unprepare; + } ret = __ufshcd_tx_eqtr(hba, params, pwr_mode); - if (ret) - goto out; - ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode); + notify_ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode); + if (notify_ret) + dev_err(hba->dev, "TX EQTR POST_CHANGE notify failed: %d\n", notify_ret); -out: + if (!ret) + ret = notify_ret; + +out_unprepare: if (ret) ufshcd_tx_eqtr_unprepare(hba, &old_pwr_info); +out_noio_restore: + memalloc_noio_restore(noio_flag); + return ret; } @@ -1301,7 +1331,13 @@ int ufshcd_config_tx_eq_settings(struct ufs_hba *hba, } params = &hba->tx_eq_params[gear - 1]; - if (!params->is_valid || force_tx_eqtr) { + /* + * TX EQTR must run for the following cases: + * 1. TX EQ settings are invalid. + * 2. TX EQ settings are from Device Tree. + * 3. TX EQTR procedure is forced. + */ + if (!params->is_valid || params->from_dt || force_tx_eqtr) { int ret; ret = ufshcd_tx_eqtr(hba, params, pwr_mode); @@ -1314,6 +1350,8 @@ int ufshcd_config_tx_eq_settings(struct ufs_hba *hba, /* Mark TX Equalization settings as valid */ params->is_valid = true; params->is_trained = true; + /* TX EQTR succeeds, clear from_dt flag */ + params->from_dt = false; params->is_applied = false; } @@ -1499,6 +1537,11 @@ static void ufshcd_extract_tx_eq_settings_attrs(struct ufs_hba *hba, u8 gear) } params->is_valid = true; + /* + * Optimal TX EQ settings are retrieved from UFS device attributes, + * clear from_dt flag to avoid TX EQTR procedure. + */ + params->from_dt = false; } void ufshcd_retrieve_tx_eq_settings(struct ufs_hba *hba) diff --git a/drivers/ufs/core/ufs_bsg.c b/drivers/ufs/core/ufs_bsg.c index 58b506eac6dc..7a66917b3fc0 100644 --- a/drivers/ufs/core/ufs_bsg.c +++ b/drivers/ufs/core/ufs_bsg.c @@ -13,37 +13,31 @@ #include #include "ufshcd-priv.h" -static int ufs_bsg_get_query_desc_size(struct ufs_hba *hba, int *desc_len, - struct utp_upiu_query *qr) -{ - int desc_size = be16_to_cpu(qr->length); - - if (desc_size <= 0) - return -EINVAL; - - *desc_len = min_t(int, QUERY_DESC_MAX_SIZE, desc_size); - - return 0; -} - static int ufs_bsg_alloc_desc_buffer(struct ufs_hba *hba, struct bsg_job *job, - uint8_t **desc_buff, int *desc_len, + u8 **desc_buff, u16 *desc_len, enum query_opcode desc_op) { struct ufs_bsg_request *bsg_request = job->request; struct utp_upiu_query *qr; + u16 max_desc_len; u8 *descp; if (desc_op != UPIU_QUERY_OPCODE_WRITE_DESC && - desc_op != UPIU_QUERY_OPCODE_READ_DESC) + desc_op != UPIU_QUERY_OPCODE_READ_DESC && + desc_op != UPIU_QUERY_OPCODE_AGGREGATED_READ) goto out; qr = &bsg_request->upiu_req.qr; - if (ufs_bsg_get_query_desc_size(hba, desc_len, qr)) { + *desc_len = be16_to_cpu(qr->length); + if (*desc_len == 0) { dev_err(hba->dev, "Illegal desc size\n"); return -EINVAL; } + max_desc_len = desc_op == UPIU_QUERY_OPCODE_AGGREGATED_READ ? + QUERY_AGGREGATED_MAX_SIZE : QUERY_DESC_MAX_SIZE; + *desc_len = min(*desc_len, max_desc_len); + if (*desc_len > job->request_payload.payload_len) { dev_err(hba->dev, "Illegal desc size\n"); return -EINVAL; @@ -108,6 +102,9 @@ static int ufs_bsg_exec_advanced_rpmb_req(struct ufs_hba *hba, struct bsg_job *j if (!payload->payload_len || !payload->sg_cnt) return -EINVAL; + if (payload->sg_cnt > UFSHCD_DEVMAN_SG_ENTRIES) + return -EINVAL; + sg_cnt = dma_map_sg(hba->host->dma_dev, payload->sg_list, payload->sg_cnt, dir); if (unlikely(!sg_cnt)) return -ENOMEM; @@ -136,8 +133,9 @@ static int ufs_bsg_request(struct bsg_job *job) struct ufs_hba *hba = shost_priv(dev_to_shost(job->dev->parent)); struct uic_command uc = {}; int msgcode; - uint8_t *buff = NULL; - int desc_len = 0; + u8 *buff = NULL; + u16 desc_len = 0; + int buff_len; enum query_opcode desc_op = UPIU_QUERY_OPCODE_NOP; int ret; bool rpmb = false; @@ -156,12 +154,15 @@ static int ufs_bsg_request(struct bsg_job *job) fallthrough; case UPIU_TRANSACTION_NOP_OUT: case UPIU_TRANSACTION_TASK_REQ: + buff_len = desc_len; ret = ufshcd_exec_raw_upiu_cmd(hba, &bsg_request->upiu_req, &bsg_reply->upiu_rsp, msgcode, - buff, &desc_len, desc_op); + buff, &buff_len, desc_op); + desc_len = buff_len; if (ret) dev_err(hba->dev, "exe raw upiu: error code %d\n", ret); - else if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len) { + else if ((desc_op == UPIU_QUERY_OPCODE_READ_DESC || + desc_op == UPIU_QUERY_OPCODE_AGGREGATED_READ) && desc_len) { bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(job->request_payload.sg_list, job->request_payload.sg_cnt, diff --git a/drivers/ufs/core/ufs_trace.h b/drivers/ufs/core/ufs_trace.h index 309ae51b4906..377a3c54b9f5 100644 --- a/drivers/ufs/core/ufs_trace.h +++ b/drivers/ufs/core/ufs_trace.h @@ -89,16 +89,18 @@ TRACE_EVENT(ufshcd_clk_gating, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, state) ), TP_fast_assign( + __assign_str(dev_name); __entry->hba = hba; __entry->state = state; ), TP_printk("%s: gating state changed to %s", - dev_name(__entry->hba->dev), + __get_str(dev_name), __print_symbolic(__entry->state, UFSCHD_CLK_GATING_STATES)) ); @@ -111,6 +113,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) __string(clk, clk) __field(u32, prev_state) @@ -119,6 +122,7 @@ TRACE_EVENT(ufshcd_clk_scaling, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); __assign_str(clk); __entry->prev_state = prev_state; @@ -126,7 +130,7 @@ TRACE_EVENT(ufshcd_clk_scaling, ), TP_printk("%s: %s %s from %u to %u Hz", - dev_name(__entry->hba->dev), __get_str(state), __get_str(clk), + __get_str(dev_name), __get_str(state), __get_str(clk), __entry->prev_state, __entry->curr_state) ); @@ -138,16 +142,18 @@ TRACE_EVENT(ufshcd_auto_bkops_state, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(state, state) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(state); ), TP_printk("%s: auto bkops - %s", - dev_name(__entry->hba->dev), __get_str(state)) + __get_str(dev_name), __get_str(state)) ); DECLARE_EVENT_CLASS(ufshcd_profiling_template, @@ -158,6 +164,7 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __string(profile_info, profile_info) __field(s64, time_us) __field(int, err) @@ -165,13 +172,14 @@ DECLARE_EVENT_CLASS(ufshcd_profiling_template, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __assign_str(profile_info); __entry->time_us = time_us; __entry->err = err; ), TP_printk("%s: %s: took %lld usecs, err %d", - dev_name(__entry->hba->dev), __get_str(profile_info), + __get_str(dev_name), __get_str(profile_info), __entry->time_us, __entry->err) ); @@ -200,6 +208,7 @@ DECLARE_EVENT_CLASS(ufshcd_template, __field(s64, usecs) __field(int, err) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(int, dev_state) __field(int, link_state) ), @@ -208,13 +217,14 @@ DECLARE_EVENT_CLASS(ufshcd_template, __entry->usecs = usecs; __entry->err = err; __entry->hba = hba; + __assign_str(dev_name); __entry->dev_state = dev_state; __entry->link_state = link_state; ), TP_printk( "%s: took %lld usecs, dev_state: %s, link_state: %s, err %d", - dev_name(__entry->hba->dev), + __get_str(dev_name), __entry->usecs, __print_symbolic(__entry->dev_state, UFS_PWR_MODES), __print_symbolic(__entry->link_state, UFS_LINK_STATES), @@ -279,6 +289,7 @@ TRACE_EVENT(ufshcd_command, TP_STRUCT__entry( __field(struct scsi_device *, sdev) __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(&sdev->sdev_dev)) __field(enum ufs_trace_str_t, str_t) __field(unsigned int, tag) __field(u32, doorbell) @@ -291,6 +302,7 @@ TRACE_EVENT(ufshcd_command, ), TP_fast_assign( + __assign_str(dev_name); __entry->sdev = sdev; __entry->hba = hba; __entry->str_t = str_t; @@ -307,7 +319,7 @@ TRACE_EVENT(ufshcd_command, TP_printk( "%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x (%s), group_id: 0x%x, hwq_id: %d", show_ufs_cmd_trace_str(__entry->str_t), - dev_name(&__entry->sdev->sdev_dev), __entry->tag, + __get_str(dev_name), __entry->tag, __entry->doorbell, __entry->transfer_len, __entry->intr, __entry->lba, (u32)__entry->opcode, str_opcode(__entry->opcode), (u32)__entry->group_id, __entry->hwq_id @@ -322,6 +334,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __field(u32, cmd) __field(u32, arg1) @@ -331,6 +344,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; __entry->cmd = cmd; __entry->arg1 = arg1; @@ -340,7 +354,7 @@ TRACE_EVENT(ufshcd_uic_command, TP_printk( "%s: %s: cmd: 0x%x, arg1: 0x%x, arg2: 0x%x, arg3: 0x%x", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __entry->cmd, __entry->arg1, __entry->arg2, __entry->arg3 ) ); @@ -353,6 +367,7 @@ TRACE_EVENT(ufshcd_upiu, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(enum ufs_trace_str_t, str_t) __array(unsigned char, hdr, 12) __array(unsigned char, tsf, 16) @@ -361,6 +376,7 @@ TRACE_EVENT(ufshcd_upiu, TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->str_t = str_t; memcpy(__entry->hdr, hdr, sizeof(__entry->hdr)); memcpy(__entry->tsf, tsf, sizeof(__entry->tsf)); @@ -369,7 +385,7 @@ TRACE_EVENT(ufshcd_upiu, TP_printk( "%s: %s: HDR:%s, %s:%s", - show_ufs_cmd_trace_str(__entry->str_t), dev_name(__entry->hba->dev), + show_ufs_cmd_trace_str(__entry->str_t), __get_str(dev_name), __print_hex(__entry->hdr, sizeof(__entry->hdr)), show_ufs_cmd_trace_tsf(__entry->tsf_t), __print_hex(__entry->tsf, sizeof(__entry->tsf)) @@ -384,16 +400,18 @@ TRACE_EVENT(ufshcd_exception_event, TP_STRUCT__entry( __field(struct ufs_hba *, hba) + __string(dev_name, dev_name(hba->dev)) __field(u16, status) ), TP_fast_assign( __entry->hba = hba; + __assign_str(dev_name); __entry->status = status; ), TP_printk("%s: status 0x%x", - dev_name(__entry->hba->dev), __entry->status + __get_str(dev_name), __entry->status ) ); diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index d3044a3089b5..69d92387e9d2 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -2506,7 +2506,10 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba) hba->nutmrs = ((hba->capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1; - hba->nortt = FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1; + if (hba->vops && hba->vops->get_hba_nortt) + hba->nortt = hba->vops->get_hba_nortt(hba); + else + hba->nortt = FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1; /* Read crypto capabilities */ err = ufshcd_hba_init_crypto_capabilities(hba); @@ -2944,23 +2947,44 @@ static void ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct scsi_cmnd *cmd) static void ufshcd_init_lrb(struct ufs_hba *hba, struct scsi_cmnd *cmd) { const int i = scsi_cmd_to_rq(cmd)->tag; - struct utp_transfer_cmd_desc *cmd_descp = - (void *)hba->ucdl_base_addr + i * ufshcd_get_ucd_size(hba); struct utp_transfer_req_desc *utrdlp = hba->utrdl_base_addr; - dma_addr_t cmd_desc_element_addr = - hba->ucdl_dma_addr + i * ufshcd_get_ucd_size(hba); u16 response_offset = le16_to_cpu(utrdlp[i].response_upiu_offset); u16 prdt_offset = le16_to_cpu(utrdlp[i].prd_table_offset); struct ufshcd_lrb *lrb = scsi_cmd_priv(cmd); + u8 *command_upiu, *response_upiu, *prd_table; + dma_addr_t cmd_desc_element_addr; + + /* The reserved tag uses a dedicated UCD outside the pool. */ + if (unlikely(blk_mq_is_reserved_rq(scsi_cmd_to_rq(cmd)))) { + struct utp_devman_cmd_desc *cmd_descp = hba->devman_ucd_base_addr; + + cmd_desc_element_addr = hba->devman_ucd_dma_addr; + command_upiu = cmd_descp->command_upiu; + response_upiu = cmd_descp->response_upiu; + prd_table = cmd_descp->prd_table; + } else { + int slot = i - UFSHCD_NUM_RESERVED; + struct utp_transfer_cmd_desc *cmd_descp; + + /* Non-reserved tags start at UFSHCD_NUM_RESERVED, so slot >= 0. */ + WARN_ON_ONCE(slot < 0); + cmd_descp = (void *)hba->ucdl_base_addr + slot * ufshcd_get_ucd_size(hba); + + cmd_desc_element_addr = + hba->ucdl_dma_addr + slot * ufshcd_get_ucd_size(hba); + command_upiu = cmd_descp->command_upiu; + response_upiu = cmd_descp->response_upiu; + prd_table = cmd_descp->prd_table; + } lrb->utr_descriptor_ptr = utrdlp + i; lrb->utrd_dma_addr = hba->utrdl_dma_addr + i * sizeof(struct utp_transfer_req_desc); - lrb->ucd_req_ptr = (struct utp_upiu_req *)cmd_descp->command_upiu; + lrb->ucd_req_ptr = (struct utp_upiu_req *)command_upiu; lrb->ucd_req_dma_addr = cmd_desc_element_addr; - lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp->response_upiu; + lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)response_upiu; lrb->ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset; - lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)cmd_descp->prd_table; + lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)prd_table; lrb->ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset; } @@ -3157,6 +3181,7 @@ static void ufshcd_setup_dev_cmd(struct ufs_hba *hba, struct scsi_cmnd *cmd, __ufshcd_setup_cmd(hba, cmd, lun, tag); lrbp->intr_cmd = true; /* No interrupt aggregation */ hba->dev_cmd.type = cmd_type; + hba->dev_cmd.tag = tag; } /* @@ -3865,7 +3890,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u { struct uc_string_id *uc_str; u8 *str; - int ret; + int ret, uc_len; if (!buf) return -EINVAL; @@ -3890,11 +3915,19 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u goto out; } + uc_len = uc_str->len - QUERY_DESC_HDR_SIZE; + if (uc_len % sizeof(*uc_str->uc)) { + dev_err(hba->dev, "String Desc has an odd UTF-16 payload length\n"); + str = NULL; + ret = -EINVAL; + goto out; + } + if (fmt == SD_ASCII_STD) { ssize_t ascii_len; int i; - /* remove header and divide by 2 to move from UTF16 to UTF8 */ - ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1; + /* Allow up to three UTF-8 bytes per UTF-16 code unit plus a NUL. */ + ascii_len = uc_len / sizeof(*uc_str->uc) * 3 + 1; str = kzalloc(ascii_len, GFP_KERNEL); if (!str) { ret = -ENOMEM; @@ -3906,7 +3939,7 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u * we need to convert to utf-8 so it can be displayed */ ret = utf16s_to_utf8s(uc_str->uc, - uc_str->len - QUERY_DESC_HDR_SIZE, + uc_len / sizeof(*uc_str->uc), UTF16_BIG_ENDIAN, str, ascii_len - 1); /* replace non-printable or non-ASCII characters with spaces */ @@ -3916,11 +3949,17 @@ int ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index, u8 **buf, enum u str[ret++] = '\0'; } else { - str = kmemdup(uc_str->uc, uc_str->len, GFP_KERNEL); + /* + * Keep the bLength-sized raw output for the RPMB device ID ABI. + * The two bytes beyond the UTF-16 payload are explicitly zeroed + * instead of being read past the descriptor buffer. + */ + str = kzalloc(uc_str->len, GFP_KERNEL); if (!str) { ret = -ENOMEM; goto out; } + memcpy(str, uc_str->uc, uc_len); ret = uc_str->len; } out: @@ -3998,8 +4037,8 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) { size_t utmrdl_size, utrdl_size, ucdl_size; - /* Allocate memory for UTP command descriptors */ - ucdl_size = ufshcd_get_ucd_size(hba) * hba->nutrs; + /* The reserved tag uses the dedicated UCD below, not this pool. */ + ucdl_size = ufshcd_get_ucd_size(hba) * (hba->nutrs - UFSHCD_NUM_RESERVED); hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev, ucdl_size, &hba->ucdl_dma_addr, @@ -4015,6 +4054,21 @@ static int ufshcd_memory_alloc(struct ufs_hba *hba) goto out; } + /* Dedicated UCD for the reserved tag; allocate once (survives MCQ re-init). */ + if (!hba->devman_ucd_base_addr) { + hba->devman_ucd_base_addr = + dmam_alloc_coherent(hba->dev, + ufshcd_get_devman_ucd_size(hba), + &hba->devman_ucd_dma_addr, + GFP_KERNEL); + if (!hba->devman_ucd_base_addr || + WARN_ON(hba->devman_ucd_dma_addr & (128 - 1))) { + dev_err(hba->dev, + "Devman Command Descriptor Memory allocation failed\n"); + goto out; + } + } + /* * Allocate memory for UTP Transfer descriptors * UFSHCI requires 1KB alignment of UTRD @@ -4081,23 +4135,38 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) dma_addr_t cmd_desc_element_addr; u16 response_offset; u16 prdt_offset; + u16 response_len; int cmd_desc_size; int i; utrdlp = hba->utrdl_base_addr; - response_offset = - offsetof(struct utp_transfer_cmd_desc, response_upiu); - prdt_offset = - offsetof(struct utp_transfer_cmd_desc, prd_table); - cmd_desc_size = ufshcd_get_ucd_size(hba); cmd_desc_dma_addr = hba->ucdl_dma_addr; for (i = 0; i < hba->nutrs; i++) { + /* + * Reserved tags (low end) use the dedicated devman UCD with a + * larger response area; other tags index the pool at i - RESERVED. + */ + if (i < UFSHCD_NUM_RESERVED) { + cmd_desc_element_addr = hba->devman_ucd_dma_addr; + response_offset = offsetof(struct utp_devman_cmd_desc, + response_upiu); + prdt_offset = offsetof(struct utp_devman_cmd_desc, + prd_table); + response_len = ALIGNED_DEVMAN_RSP_SIZE; + } else { + cmd_desc_element_addr = cmd_desc_dma_addr + + cmd_desc_size * (i - UFSHCD_NUM_RESERVED); + response_offset = offsetof(struct utp_transfer_cmd_desc, + response_upiu); + prdt_offset = offsetof(struct utp_transfer_cmd_desc, + prd_table); + response_len = ALIGNED_UPIU_SIZE; + } + /* Configure UTRD with command descriptor base address */ - cmd_desc_element_addr = - (cmd_desc_dma_addr + (cmd_desc_size * i)); utrdlp[i].command_desc_base_addr = cpu_to_le64(cmd_desc_element_addr); @@ -4108,14 +4177,14 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) utrdlp[i].prd_table_offset = cpu_to_le16(prdt_offset); utrdlp[i].response_upiu_length = - cpu_to_le16(ALIGNED_UPIU_SIZE); + cpu_to_le16(response_len); } else { utrdlp[i].response_upiu_offset = cpu_to_le16(response_offset >> 2); utrdlp[i].prd_table_offset = cpu_to_le16(prdt_offset >> 2); utrdlp[i].response_upiu_length = - cpu_to_le16(ALIGNED_UPIU_SIZE >> 2); + cpu_to_le16(response_len >> 2); } } } @@ -4715,7 +4784,9 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &pwr_info->lane_tx); - if (!pwr_info->lane_rx || !pwr_info->lane_tx) { + if (!pwr_info->lane_rx || !pwr_info->lane_tx || + pwr_info->lane_rx > UFS_MAX_LANES || + pwr_info->lane_tx > UFS_MAX_LANES) { dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n", __func__, pwr_info->lane_rx, @@ -5846,8 +5917,8 @@ void ufshcd_compl_one_cqe(struct ufs_hba *hba, int task_tag, struct ufshcd_lrb *lrbp = scsi_cmd_priv(cmd); enum utp_ocs ocs; - if (WARN_ONCE(!cmd, "cqe->command_desc_base_addr = %#llx\n", - le64_to_cpu(cqe->command_desc_base_addr))) + if (WARN_ONCE(!cmd, "invalid completion tag %d, cqe->command_desc_base_addr = %#llx\n", + task_tag, cqe ? le64_to_cpu(cqe->command_desc_base_addr) : 0ULL)) return; if (hba->monitor.enabled) { @@ -6415,8 +6486,8 @@ static bool ufshcd_wb_curr_buff_threshold_check(struct ufs_hba *hba, } if (!cur_buf) { - dev_info(hba->dev, "dCurWBBuf: %d WB disabled until free-space is available\n", - cur_buf); + dev_warn_once(hba->dev, "dCurWBBuf: %d WB disabled until free-space is available\n", + cur_buf); return false; } /* Let it continue to flush when available buffer exceeds threshold */ @@ -7357,7 +7428,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) } /** - * ufshcd_threaded_intr - Threaded interrupt service routine + * ufshcd_intr - Main interrupt service routine * @irq: irq number * @__hba: pointer to adapter instance * @@ -7365,7 +7436,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ -static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) +static irqreturn_t ufshcd_intr(int irq, void *__hba) { u32 last_intr_status, intr_status, enabled_intr_status = 0; irqreturn_t retval = IRQ_NONE; @@ -7404,38 +7475,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) return retval; } -/** - * ufshcd_intr - Main interrupt service routine - * @irq: irq number - * @__hba: pointer to adapter instance - * - * Return: - * IRQ_HANDLED - If interrupt is valid - * IRQ_WAKE_THREAD - If handling is moved to threaded handled - * IRQ_NONE - If invalid interrupt - */ -static irqreturn_t ufshcd_intr(int irq, void *__hba) -{ - struct ufs_hba *hba = __hba; - u32 intr_status, enabled_intr_status; - - /* - * Handle interrupt in thread if MCQ or ESI is disabled, - * and no active UIC command. - */ - if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && - !hba->active_uic_cmd) - return IRQ_WAKE_THREAD; - - intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); - - /* Directly handle interrupts since MCQ ESI handlers does the hard job */ - return ufshcd_sl_intr(hba, enabled_intr_status); -} - static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) { int err = 0; @@ -7638,7 +7677,8 @@ static int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba, /* just copy the upiu response as it is */ memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu)); - if (desc_buff && desc_op == UPIU_QUERY_OPCODE_READ_DESC) { + if (desc_buff && (desc_op == UPIU_QUERY_OPCODE_READ_DESC || + desc_op == UPIU_QUERY_OPCODE_AGGREGATED_READ)) { u8 *descp = (u8 *)lrbp->ucd_rsp_ptr + sizeof(*rsp_upiu); u16 resp_len = be16_to_cpu(lrbp->ucd_rsp_ptr->header .data_segment_length); @@ -7810,10 +7850,7 @@ int ufshcd_advanced_rpmb_req_handler(struct ufs_hba *hba, struct utp_upiu_req *r * Message is 02h */ if (ehs_len == 2 && rsp_ehs) { - /* - * ucd_rsp_ptr points to a buffer with a length of 512 bytes - * (ALIGNED_UPIU_SIZE = 512), and the EHS data just starts from byte32 - */ + /* EHS data starts from byte32 of the devman UCD response area. */ ehs_data = (u8 *)lrbp->ucd_rsp_ptr + EHS_OFFSET_IN_RESPONSE; memcpy(rsp_ehs, ehs_data, ehs_len * 32); } @@ -8611,8 +8648,6 @@ static void ufshcd_set_rtt(struct ufs_hba *hba) struct ufs_dev_info *dev_info = &hba->dev_info; u32 rtt = 0; u32 dev_rtt = 0; - int host_rtt_cap = hba->vops && hba->vops->max_num_rtt ? - hba->vops->max_num_rtt : hba->nortt; /* RTT override makes sense only for UFS-4.0 and above */ if (dev_info->wspecversion < 0x400) @@ -8628,7 +8663,7 @@ static void ufshcd_set_rtt(struct ufs_hba *hba) if (dev_rtt != DEFAULT_MAX_NUM_RTT) return; - rtt = min_t(int, dev_info->rtt_cap, host_rtt_cap); + rtt = min_t(int, dev_info->rtt_cap, hba->nortt); if (rtt == dev_rtt) return; @@ -9239,7 +9274,7 @@ static void ufshcd_release_sdb_queue(struct ufs_hba *hba, int nutrs) { size_t ucdl_size, utrdl_size; - ucdl_size = ufshcd_get_ucd_size(hba) * nutrs; + ucdl_size = ufshcd_get_ucd_size(hba) * (nutrs - UFSHCD_NUM_RESERVED); dmam_free_coherent(hba->dev, ucdl_size, hba->ucdl_base_addr, hba->ucdl_dma_addr); @@ -10048,7 +10083,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, * we are functional while we are here, skip host resume in error * handling context. */ - hba->host->eh_noresume = 1; + WRITE_ONCE(hba->host->eh_noresume, 1); /* * Current function would be generally called from the power management @@ -10070,7 +10105,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba, } scsi_device_put(sdp); - hba->host->eh_noresume = 0; + WRITE_ONCE(hba->host->eh_noresume, 0); return ret; } @@ -10269,6 +10304,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) req_link_state == UIC_LINK_ACTIVE_STATE) { ufshcd_disable_auto_bkops(hba); flush_work(&hba->eeh_work); + cancel_delayed_work_sync(&hba->ufs_rtc_update_work); goto vops_suspend; } @@ -10478,10 +10514,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (ret) goto set_old_link_state; ufshcd_set_timestamp_attr(hba); - schedule_delayed_work(&hba->ufs_rtc_update_work, - msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); } + schedule_delayed_work(&hba->ufs_rtc_update_work, + msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) ufshcd_enable_auto_bkops(hba); else @@ -10988,6 +11025,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) hba->nop_out_timeout = NOP_OUT_TIMEOUT; ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry)); INIT_LIST_HEAD(&hba->clk_list_head); + INIT_LIST_HEAD(&hba->rpmbs); spin_lock_init(&hba->outstanding_lock); *hba_handle = hba; @@ -11236,8 +11274,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_readl(hba, REG_INTERRUPT_ENABLE); /* IRQ registration */ - err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr, - IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba); + err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { dev_err(hba->dev, "request irq failed\n"); goto out_disable; diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index 3991a51263a6..814c1b7343b9 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -2183,6 +2183,16 @@ static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up, return 0; } +static int ufs_mtk_get_hba_nortt(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + if (host->legacy_ip_ver || host->ip_ver < IP_VER_MT6995_B0) + return MTK_MAX_NUM_RTT_LEGACY; + + return FIELD_GET(MASK_NUMBER_OUTSTANDING_RTT, hba->capabilities) + 1; +} + static int ufs_mtk_get_hba_mac(struct ufs_hba *hba) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); @@ -2266,10 +2276,8 @@ static int ufs_mtk_config_mcq_irq(struct ufs_hba *hba) dev_dbg(hba->dev, "request irq %d intr %s\n", irq, ret ? "failed" : ""); - if (ret) { - dev_err(hba->dev, "Cannot request irq %d\n", ret); + if (ret) return ret; - } } host->is_mcq_intr_enabled = true; @@ -2322,7 +2330,6 @@ static void ufs_mtk_config_scsi_dev(struct scsi_device *sdev) */ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = { .name = "mediatek.ufshci", - .max_num_rtt = MTK_MAX_NUM_RTT, .init = ufs_mtk_init, .get_ufs_hci_version = ufs_mtk_get_ufs_hci_version, .setup_clocks = ufs_mtk_setup_clocks, @@ -2339,6 +2346,7 @@ static const struct ufs_hba_variant_ops ufs_hba_mtk_vops = { .event_notify = ufs_mtk_event_notify, .config_scaling_param = ufs_mtk_config_scaling_param, .clk_scale_notify = ufs_mtk_clk_scale_notify, + .get_hba_nortt = ufs_mtk_get_hba_nortt, /* mcq vops */ .get_hba_mac = ufs_mtk_get_hba_mac, .op_runtime_config = ufs_mtk_op_runtime_config, diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 8547a6f04990..73cdc726f290 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -203,8 +203,8 @@ struct ufs_mtk_host { /* MTK delay of autosuspend: 500 ms */ #define MTK_RPM_AUTOSUSPEND_DELAY_MS 500 -/* MTK RTT support number */ -#define MTK_MAX_NUM_RTT 2 +/* MTK RTT support number for platforms before MT6995 B0 */ +#define MTK_MAX_NUM_RTT_LEGACY 2 /* UFSHCI MTK ip version value */ enum { diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index 291c43448764..62396212a0a7 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -2431,8 +2431,6 @@ static int ufs_qcom_config_esi(struct ufs_hba *hba) ret = devm_request_irq(hba->dev, qi[idx].irq, ufs_qcom_mcq_esi_handler, IRQF_SHARED, "qcom-mcq-esi", qi + idx); if (ret) { - dev_err(hba->dev, "%s: Failed to request IRQ for %d, err = %d\n", - __func__, qi[idx].irq, ret); /* Free previously allocated IRQs */ for (int j = 0; j < idx; j++) devm_free_irq(hba->dev, qi[j].irq, qi + j); @@ -2794,7 +2792,7 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba, if (ret) { dev_err(hba->dev, "%s: Failed to apply TX EQ settings for HS-G%u: %d\n", __func__, gear, ret); - return ret; + goto link_recover_and_restore; } /* Force PMC to target HS Gear to use new TX Equalization settings. */ @@ -2802,16 +2800,15 @@ static int ufs_qcom_get_rx_fom(struct ufs_hba *hba, if (ret) { dev_err(hba->dev, "%s: Failed to change power mode to HS-G%u, Rate-%s: %d\n", __func__, gear, ufs_hs_rate_to_str(rate), ret); - return ret; + goto link_recover_and_restore; } ret = ufs_qcom_host_sw_rx_fom(hba, pwr_mode->lane_rx, fom); - if (ret) { + if (ret) dev_err(hba->dev, "Failed to get SW FOM of TX (PreShoot: %u, DeEmphasis: %u): %d\n", d_iter->preshoot, d_iter->deemphasis, ret); - return ret; - } +link_recover_and_restore: /* Restore Device's TX Equalization settings. */ ret = ufshcd_apply_tx_eq_settings(hba, &hba->tx_eq_params[gear - 1], gear); if (ret) { diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c index c2dafb583cf5..5ac7afe75934 100644 --- a/drivers/ufs/host/ufshcd-pltfrm.c +++ b/drivers/ufs/host/ufshcd-pltfrm.c @@ -210,6 +210,158 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) } } +static int ufshcd_parse_tx_precode_enable(struct ufs_hba *hba, + bool host_precode_en[UFS_MAX_LANES], + bool device_precode_en[UFS_MAX_LANES]) +{ + const char *prop_name = "tx-precode-enable-g6"; + u32 num_elems = 2 * hba->lanes_per_direction; + const u32 lpd = hba->lanes_per_direction; + u32 precode[UFS_MAX_LANES * 2]; + struct device *dev = hba->dev; + int count, err, i; + + count = of_property_count_u32_elems(dev->of_node, prop_name); + if (count == -EINVAL || count == -ENODATA) + return 0; + + if (count < 0) + return count; + + if (count != num_elems) { + dev_err(dev, "Property %s has invalid count (%d), expecting %u\n", + prop_name, count, num_elems); + return -EINVAL; + } + + err = of_property_read_u32_array(dev->of_node, prop_name, precode, num_elems); + if (err) { + dev_err(dev, "Failed to read %s property, %d\n", prop_name, err); + return err; + } + + for (i = 0; i < num_elems; i++) { + if (precode[i] > 1) { + dev_err(dev, "Invalid TX precode value (%u) in %s property\n", + precode[i], prop_name); + return -EINVAL; + } + } + + for (i = 0; i < lpd; i++) { + host_precode_en[i] = precode[i * 2]; + device_precode_en[i] = precode[i * 2 + 1]; + } + + return 0; +} + +static int ufshcd_parse_tx_eq_value_array(struct ufs_hba *hba, + const char *prop_name, + const u32 max_value, + u32 values[UFS_MAX_LANES * 2]) +{ + u32 num_elems = 2 * hba->lanes_per_direction; + struct device *dev = hba->dev; + int count, err, i; + + count = of_property_count_u32_elems(dev->of_node, prop_name); + if (count < 0) + return count; + + if (count != num_elems) { + dev_err(dev, "Property %s has invalid count (%d), expecting %u\n", + prop_name, count, num_elems); + return -EINVAL; + } + + err = of_property_read_u32_array(dev->of_node, prop_name, values, num_elems); + if (err) { + dev_err(dev, "Failed to read %s property, %d\n", prop_name, err); + return err; + } + + for (i = 0; i < num_elems; i++) { + if (values[i] >= max_value) { + dev_err(dev, "Invalid TX EQ value (%u) in %s property\n", + values[i], prop_name); + return -EINVAL; + } + } + + return 0; +} + +/** + * ufshcd_parse_tx_eq_settings_for_gear - Parse static TX EQ DT settings for one gear + * @hba: per adapter instance + * @gear: target HS gear + * + * Reads the txeq-preshoot-gN, txeq-deemphasis-gN, and (for G6) + * tx-precode-enable-g6 device-tree properties. + * If all present values are valid, stores them as static TX Equalization + * settings for the given gear. + */ +static void ufshcd_parse_tx_eq_settings_for_gear(struct ufs_hba *hba, int gear) +{ + bool device_precode_en[UFS_MAX_LANES] = { false }; + bool host_precode_en[UFS_MAX_LANES] = { false }; + const u32 lpd = hba->lanes_per_direction; + struct ufshcd_tx_eq_params *params; + u32 deemphasis[UFS_MAX_LANES * 2]; + u32 preshoot[UFS_MAX_LANES * 2]; + char prop_name[MAX_PROP_SIZE]; + int err, lane; + + snprintf(prop_name, MAX_PROP_SIZE, "txeq-preshoot-g%d", gear); + err = ufshcd_parse_tx_eq_value_array(hba, prop_name, TX_HS_NUM_PRESHOOT, preshoot); + if (err) + return; + + snprintf(prop_name, MAX_PROP_SIZE, "txeq-deemphasis-g%d", gear); + err = ufshcd_parse_tx_eq_value_array(hba, prop_name, TX_HS_NUM_DEEMPHASIS, deemphasis); + if (err) + return; + + if (gear == UFS_HS_G6) { + err = ufshcd_parse_tx_precode_enable(hba, host_precode_en, device_precode_en); + if (err) + return; + } + + params = &hba->tx_eq_params[gear - 1]; + for (lane = 0; lane < lpd; lane++) { + params->host[lane].preshoot = preshoot[lane * 2]; + params->host[lane].deemphasis = deemphasis[lane * 2]; + params->host[lane].precode_en = host_precode_en[lane]; + + params->device[lane].preshoot = preshoot[lane * 2 + 1]; + params->device[lane].deemphasis = deemphasis[lane * 2 + 1]; + params->device[lane].precode_en = device_precode_en[lane]; + } + + params->is_valid = true; + params->from_dt = true; +} + +static void ufshcd_parse_static_tx_eq_settings(struct ufs_hba *hba) +{ + const u32 lpd = hba->lanes_per_direction; + int gear; + + if (!lpd) + return; + + if (lpd > UFS_MAX_LANES) { + dev_warn(hba->dev, "lanes_per_direction (%u) exceeds UFS_MAX_LANES (%u)\n", + lpd, UFS_MAX_LANES); + return; + } + + for (gear = UFS_HS_G1; gear <= UFS_HS_GEAR_MAX; gear++) + ufshcd_parse_tx_eq_settings_for_gear(hba, gear); +} + /** * ufshcd_parse_clock_min_max_freq - Parse MIN and MAX clocks freq * @hba: per adapter instance @@ -528,6 +680,8 @@ int ufshcd_pltfrm_init(struct platform_device *pdev, ufshcd_init_lanes_per_dir(hba); + ufshcd_parse_static_tx_eq_settings(hba); + err = ufshcd_parse_operating_points(hba); if (err) { dev_err(dev, "%s: OPP parse failed %d\n", __func__, err); diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index e33f95c91b09..c7036e0e41bd 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -611,6 +611,25 @@ static void scsiback_disconnect(struct vscsibk_info *info) xenbus_unmap_ring_vfree(info->dev, info->ring.sring); } +/* + * Send the error response for a request that did not reach the target core + * and return its tag. Free the tag before the response drops the v2p + * reference that keeps the session alive, and snapshot what the response + * needs since returning the tag can let the slot be reused. + */ +static void scsiback_resp_and_free(struct vscsibk_pend *pending_req, + int32_t result) +{ + struct vscsibk_info *info = pending_req->info; + struct v2p_entry *v2p = pending_req->v2p; + struct se_session *se_sess = v2p->tpg->tpg_nexus->tvn_se_sess; + u16 rqid = pending_req->rqid; + + target_free_tag(se_sess, &pending_req->se_cmd); + scsiback_send_response(info, NULL, result, 0, rqid); + kref_put(&v2p->kref, scsiback_free_translation_entry); +} + static void scsiback_device_action(struct vscsibk_pend *pending_req, enum tcm_tmreq_table act, int tag) { @@ -639,7 +658,7 @@ static void scsiback_device_action(struct vscsibk_pend *pending_req, return; err: - scsiback_do_resp_with_sense(NULL, err, 0, pending_req); + scsiback_resp_and_free(pending_req, err); } /* @@ -792,9 +811,8 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, case VSCSIIF_ACT_SCSI_CDB: if (scsiback_gnttab_data_map(&ring_req, pending_req)) { scsiback_fast_flush_area(pending_req); - scsiback_do_resp_with_sense(NULL, - DID_ERROR << 16, 0, pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, + DID_ERROR << 16); } else { scsiback_cmd_exec(pending_req); } @@ -808,9 +826,7 @@ static int scsiback_do_cmd_fn(struct vscsibk_info *info, break; default: pr_err_ratelimited("invalid request\n"); - scsiback_do_resp_with_sense(NULL, DID_ERROR << 16, 0, - pending_req); - transport_generic_free_cmd(&pending_req->se_cmd, 0); + scsiback_resp_and_free(pending_req, DID_ERROR << 16); break; } diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 3b0c9a251a2e..2673a1bd82c4 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -640,7 +640,11 @@ struct mdio_device_id { struct zorro_device_id { __u32 id; /* Device ID or ZORRO_WILDCARD */ - kernel_ulong_t driver_data; /* Data private to the driver */ + union { + /* Data private to the driver */ + kernel_ulong_t driver_data; + const void *driver_data_ptr; + }; }; #define ZORRO_WILDCARD (0xffffffff) /* not official */ diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 163f23c92b41..36d4cb567837 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -680,7 +680,6 @@ extern int sas_register_ha(struct sas_ha_struct *); extern int sas_unregister_ha(struct sas_ha_struct *); extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); extern void sas_resume_ha(struct sas_ha_struct *sas_ha); -extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); int sas_phy_reset(struct sas_phy *phy, int hard_reset); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 029f5115b2ea..8694eeadd753 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -408,7 +408,6 @@ void scsi_attach_vpd(struct scsi_device *sdev); void scsi_cdl_check(struct scsi_device *sdev); int scsi_cdl_enable(struct scsi_device *sdev, bool enable); -extern struct scsi_device *scsi_device_from_queue(struct request_queue *q); extern int __must_check scsi_device_get(struct scsi_device *); extern void scsi_device_put(struct scsi_device *); extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *, diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 7e2011830ba4..98b0ccf0813e 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -664,6 +664,9 @@ struct Scsi_Host { /* Asynchronous scan in progress */ bool async_scan __guarded_by(&scan_mutex); + /* Don't resume host in EH */ + bool eh_noresume; + unsigned active_mode:2; /* @@ -682,9 +685,6 @@ struct Scsi_Host { /* Task mgmt function in progress */ unsigned tmf_in_progress:1; - /* Don't resume host in EH */ - unsigned eh_noresume:1; - /* The controller does not support WRITE SAME */ unsigned no_write_same:1; @@ -750,6 +750,9 @@ struct Scsi_Host { */ struct device *dma_dev; + /* Used for an rcu-synchronizing eh wakeup */ + struct work_struct eh_work; + /* Delay for runtime autosuspend */ int rpm_autosuspend_delay; diff --git a/include/uapi/linux/cciss_ioctl.h b/include/uapi/linux/cciss_ioctl.h index 562230199461..8295f1bf3bf3 100644 --- a/include/uapi/linux/cciss_ioctl.h +++ b/include/uapi/linux/cciss_ioctl.h @@ -85,5 +85,6 @@ typedef struct _LogvolInfo_struct{ #define CCISS_RESCANDISK _IO(CCISS_IOC_MAGIC, 16) #define CCISS_GETLUNINFO _IOR(CCISS_IOC_MAGIC, 17, LogvolInfo_struct) #define CCISS_BIG_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 18, BIG_IOCTL_Command_struct) +#define CCISS_BIG_PASSTHRU_SUPPORTED _IO(CCISS_IOC_MAGIC, 19) #endif /* _UAPICCISS_IOCTLH */ diff --git a/include/ufs/ufs.h b/include/ufs/ufs.h index 0d48e137d66d..afbb32654fab 100644 --- a/include/ufs/ufs.h +++ b/include/ufs/ufs.h @@ -25,6 +25,11 @@ static_assert(sizeof(struct utp_upiu_query) == 20); #define GENERAL_UPIU_REQUEST_SIZE (sizeof(struct utp_upiu_req)) #define QUERY_DESC_MAX_SIZE 255 +/* + * Max aggregated read data segment: the devman response area + * (ALIGNED_DEVMAN_RSP_SIZE) minus the fixed UPIU header it follows. + */ +#define QUERY_AGGREGATED_MAX_SIZE (4096 - GENERAL_UPIU_REQUEST_SIZE) #define QUERY_DESC_MIN_SIZE 2 #define QUERY_DESC_HDR_SIZE 2 #define QUERY_OSF_SIZE (GENERAL_UPIU_REQUEST_SIZE - \ @@ -464,6 +469,7 @@ enum query_opcode { UPIU_QUERY_OPCODE_SET_FLAG = 0x6, UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7, UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, + UPIU_QUERY_OPCODE_AGGREGATED_READ = 0x9, }; /* bRefClkFreq attribute values */ diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index 248d0a5bef40..dfd302f2dc7c 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -163,7 +163,6 @@ struct ufs_pm_lvl_states { * @ucd_req_dma_addr: UPIU request dma address for debug * @scsi_status: SCSI status of the command * @command_type: SCSI, UFS, Query. - * @task_tag: Task tag of the command * @lun: LUN of the command * @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation) * @req_abort_skip: skip request abort task flag @@ -212,8 +211,7 @@ struct ufs_query_req { }; /** - * struct ufs_query_resp - UPIU QUERY - * @response: device response code + * struct ufs_query_res - UPIU QUERY * @upiu_res: query response data */ struct ufs_query_res { @@ -237,11 +235,13 @@ struct ufs_query { * @type: device management command type - Query, NOP OUT * @lock: lock to allow one command at a time * @query: Device management query information + * @tag: tag of the reserved request in use */ struct ufs_dev_cmd { enum dev_cmd_type type; struct mutex lock; struct ufs_query query; + u8 tag; }; /** @@ -359,6 +359,7 @@ struct ufshcd_tx_eqtr_record { * @is_valid: True if parameter contains valid TX Equalization settings * @is_applied: True if settings have been applied to UniPro of both sides * @is_trained: True if parameters obtained from TX EQTR procedure + * @from_dt: True if settings are from Device Tree */ struct ufshcd_tx_eq_params { struct ufshcd_tx_eq_settings host[UFS_MAX_LANES]; @@ -367,12 +368,12 @@ struct ufshcd_tx_eq_params { bool is_valid; bool is_applied; bool is_trained; + bool from_dt; }; /** * struct ufs_hba_variant_ops - variant specific callbacks * @name: variant name - * @max_num_rtt: maximum RTT supported by the host * @init: called when the driver is initialized * @exit: called to cleanup everything done in init * @set_dma_mask: For setting another DMA mask than indicated by the 64AS @@ -417,10 +418,11 @@ struct ufshcd_tx_eq_params { * @get_rx_fom: called to get Figure of Merit (FOM) value. * @tx_eqtr_notify: called before and after TX Equalization Training procedure * to allow platform vendor specific configs to take place. + * @get_hba_nortt: called to get maximum number of outstanding RTTs supported by + * the controller. */ struct ufs_hba_variant_ops { const char *name; - int max_num_rtt; int (*init)(struct ufs_hba *); void (*exit)(struct ufs_hba *); u32 (*get_ufs_hci_version)(struct ufs_hba *); @@ -479,6 +481,7 @@ struct ufs_hba_variant_ops { int (*tx_eqtr_notify)(struct ufs_hba *hba, enum ufs_notify_change_status status, struct ufs_pa_layer_attr *pwr_mode); + int (*get_hba_nortt)(struct ufs_hba *hba); }; /* clock gating state */ @@ -552,6 +555,7 @@ struct ufs_clk_gating { * @is_initialized: Indicates whether clock scaling is initialized or not * @is_busy_started: tracks if busy period has started or not * @is_suspended: tracks if devfreq is suspended or not + * @suspend_on_no_request: suspend clock scaling only when no request */ struct ufs_clk_scaling { struct workqueue_struct *workq; @@ -952,9 +956,13 @@ enum ufshcd_mcq_opr { * @ucdl_base_addr: UFS Command Descriptor base address * @utrdl_base_addr: UTP Transfer Request Descriptor base address * @utmrdl_base_addr: UTP Task Management Descriptor base address + * @devman_ucd_base_addr: UFS Command Descriptor base address for the reserved + * device management tag (has a larger response area) * @ucdl_dma_addr: UFS Command Descriptor DMA address * @utrdl_dma_addr: UTRDL DMA address * @utmrdl_dma_addr: UTMRDL DMA address + * @devman_ucd_dma_addr: UFS Command Descriptor DMA address for the reserved + * device management tag * @host: Scsi_Host instance of the driver * @dev: device handle * @ufs_device_wlun: WLUN that controls the entire UFS device. @@ -973,7 +981,7 @@ enum ufshcd_mcq_opr { * @capabilities: UFS Controller Capabilities * @mcq_capabilities: UFS Multi Circular Queue capabilities * @nutrs: Transfer Request Queue depth supported by controller - * @nortt - Max outstanding RTTs supported by controller + * @nortt: Max outstanding RTTs supported by controller * @nutmrs: Task Management Queue depth supported by controller * @ufs_version: UFS Version to which controller complies * @vops: pointer to variant specific operations @@ -1035,7 +1043,6 @@ enum ufshcd_mcq_opr { * device is known or not. * @wb_mutex: used to serialize devfreq and sysfs write booster toggling * @clk_scaling_lock: used to serialize device commands and clock scaling - * @desc_size: descriptor sizes reported by device * @bsg_dev: struct device associated with the BSG queue * @bsg_queue: BSG queue associated with the UFS controller * @rpm_dev_flush_recheck_work: used to suspend from RPM (runtime power @@ -1050,16 +1057,19 @@ enum ufshcd_mcq_opr { * @debugfs_ee_work: used to restore ee_ctrl_mask after a delay * @debugfs_ee_rate_limit_ms: user configurable delay after which to restore * ee_ctrl_mask + * @trigger_eh_attr: fault attributes for the fault injection trigger EH + * @timeout_attr: fault attributes for the timeout fault injection trigger * @luns_avail: number of regular and well known LUNs supported by the UFS * device * @nr_hw_queues: number of hardware queues configured * @nr_queues: number of Queues of different queue types * @complete_put: whether or not to call ufshcd_rpm_put() from inside * ufshcd_resume_complete() + * @scsi_host_added: indicates that scsi_add_host() has been called * @mcq_sup: is mcq supported by UFSHC + * @lsdb_sup: LSDBS capability * @mcq_enabled: is mcq ready to accept requests * @mcq_esi_enabled: is mcq ESI configured - * @res: array of resource info of MCQ registers * @mcq_base: Multi circular queue registers base address * @uhq: array of supported hardware queues * @mcq_opr: MCQ operation and runtime registers @@ -1075,6 +1085,7 @@ enum ufshcd_mcq_opr { * field within the Host Controller's UECDME register. Bit[0] is a flag * indicating that the DME QoS Monitor has been reset by the host. * @dme_qos_sysfs_handle: handle for 'dme_qos_notification' sysfs entry + * @vcc_off_delay_us: length of delay after VCC is powered off * @rpmbs: list of OP-TEE RPMB devices (one per RPMB region) * @host_preshoot_cap: a bitfield to indicate supported PreShoot dBs of host's TX lanes, cache of * host M-PHY TX_HS_PreShoot_Setting_Capability Attribute (ID 0x15) @@ -1093,11 +1104,13 @@ struct ufs_hba { struct utp_transfer_cmd_desc *ucdl_base_addr; struct utp_transfer_req_desc *utrdl_base_addr; struct utp_task_req_desc *utmrdl_base_addr; + struct utp_devman_cmd_desc *devman_ucd_base_addr; /* DMA memory reference */ dma_addr_t ucdl_dma_addr; dma_addr_t utrdl_dma_addr; dma_addr_t utmrdl_dma_addr; + dma_addr_t devman_ucd_dma_addr; struct Scsi_Host *host; struct device *dev; @@ -1281,7 +1294,7 @@ struct ufs_hba { * @cqe_dma_addr: completion queue dma address * @max_entries: max number of slots in this hardware queue * @id: hardware queue ID - * @sq_tp_slot: current slot to which SQ tail pointer is pointing + * @sq_tail_slot: current slot to which SQ tail pointer is pointing * @sq_lock: serialize submission queue access * @cq_tail_slot: current slot to which CQ tail pointer is pointing * @cq_head_slot: current slot to which CQ head pointer is pointing @@ -1356,6 +1369,18 @@ static inline size_t ufshcd_get_ucd_size(const struct ufs_hba *hba) return sizeof(struct utp_transfer_cmd_desc) + SG_ALL * ufshcd_sg_entry_size(hba); } +/* + * Two entries should be enough for the largest devman PRDT transfer (4 KiB), + * like advanced RPMB. + */ +#define UFSHCD_DEVMAN_SG_ENTRIES 2 + +static inline size_t ufshcd_get_devman_ucd_size(const struct ufs_hba *hba) +{ + return sizeof(struct utp_devman_cmd_desc) + + UFSHCD_DEVMAN_SG_ENTRIES * ufshcd_sg_entry_size(hba); +} + /* Returns true if clocks can be gated. Otherwise false */ static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba) { @@ -1505,6 +1530,8 @@ static inline void ufshcd_set_variant(struct ufs_hba *hba, void *variant) /** * ufshcd_get_variant - get variant specific data from the hba * @hba: per adapter instance + * + * Returns: pointer to the hba's private data area */ static inline void *ufshcd_get_variant(struct ufs_hba *hba) { diff --git a/include/ufs/ufshci.h b/include/ufs/ufshci.h index 9f0fdd850e54..c3537bb544ae 100644 --- a/include/ufs/ufshci.h +++ b/include/ufs/ufshci.h @@ -18,6 +18,8 @@ enum { TASK_REQ_UPIU_SIZE_DWORDS = 8, TASK_RSP_UPIU_SIZE_DWORDS = 8, ALIGNED_UPIU_SIZE = 512, + /* Larger response area, only for the devman UCD */ + ALIGNED_DEVMAN_RSP_SIZE = 4096, }; /* UFSHCI Registers */ @@ -501,6 +503,16 @@ struct utp_transfer_cmd_desc { u8 prd_table[]; }; +/* Dedicated UCD for the devman/reserved slot */ +struct utp_devman_cmd_desc { + u8 command_upiu[ALIGNED_UPIU_SIZE]; + u8 response_upiu[ALIGNED_DEVMAN_RSP_SIZE]; + u8 prd_table[]; +}; + +static_assert(sizeof(struct utp_upiu_req) + QUERY_AGGREGATED_MAX_SIZE <= + ALIGNED_DEVMAN_RSP_SIZE); + /** * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD */ diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index 4fe04669e5e6..7e576e5c6b8b 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -979,6 +979,9 @@ size_t dma_max_mapping_size(struct device *dev) const struct dma_map_ops *ops = get_dma_ops(dev); size_t size = SIZE_MAX; + if (!dev->dma_mask) + return 0; + if (dma_map_direct(dev, ops)) size = dma_direct_max_mapping_size(dev); else if (use_dma_iommu(dev))