From patchwork Mon Oct 12 22:51:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269830 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78491C433E7 for ; Mon, 12 Oct 2020 22:52:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 094F720878 for ; Mon, 12 Oct 2020 22:51:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="STvUjZdR" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390498AbgJLWv7 (ORCPT ); Mon, 12 Oct 2020 18:51:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60506 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390420AbgJLWv5 (ORCPT ); Mon, 12 Oct 2020 18:51:57 -0400 Received: from mail-pf1-x442.google.com (mail-pf1-x442.google.com [IPv6:2607:f8b0:4864:20::442]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AEFBBC0613D0 for ; Mon, 12 Oct 2020 15:51:57 -0700 (PDT) Received: by mail-pf1-x442.google.com with SMTP id 144so15109554pfb.4 for ; Mon, 12 Oct 2020 15:51:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=sSpkA1ZyBzZzNwthpD4+UgpUeXniky4AXKytWtlowCc=; b=STvUjZdR/nwcJcD2OQNrg23BjdwZcGTn7CYTHjGgT9nFzQDAWFtduQpCNK5UXjyBk4 qZiFqpwBGTdAjClMSUT89W4d6JA5YgkXV6GU1KqWpPx2Og6AsVu26p3KGxhYvSMtZQuJ iRuTuK+u8qJtLoUmQZcDAcsI3wtgTzdKSkULs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=sSpkA1ZyBzZzNwthpD4+UgpUeXniky4AXKytWtlowCc=; b=PPc3FS0ilr7JETI7Sv+Nv2PkoKIJcVFn21c5YymojrwNd3T/2Ir0UZeNmdO4ov2Bic nB9jLjOzLqwdw0gAzVKDt9Fic5E8MEdgpKIEHK8Vq8aZTzliYPWhiE0tJWNpS89lXi67 CBElK0YvnbuGbkydZGTMKdPzEtleka/y76CBJbsYjv8ffJ5DCh2WO55in4aGBdWGtjTX tczZChVHe8oP9z/1OMx+Ae31nCcz1BnpemrtumsZWEABR4vPmj7BO8fIWUFGbSVpMA/5 +gVC6JXMrl2njLrLxwpv6DbpWbsZuVzW+XS2tTHTI9aTCVmM7Dc4Vw8D2JTsO6/hnPA4 AWqw== X-Gm-Message-State: AOAM532Mu/VicF/M6vFntBVzd1jEKm/RnXFgePiBQmG0vsMq9IK5HnyV AC40+qg1A19fRxoMx515Tbl3AeehTSsA5NbyvdaXrGF1Cp/GkW34HdK/SuwoU0nZ4VItaBlZypp knlxzhssAX/qL6xq8VwK/dZCoQztTwaQdIg+wF0tCkoJdQK9Ir7RR00hiAXllURINSADdrHYPMj shFfM= X-Google-Smtp-Source: ABdhPJyg5S5i/sQd2v6OAVkPxy7ifEZL9FxxS2XrqgBnqoFsD6XUcbVcNhqRs0mxZxbdO3Eq5lNi1w== X-Received: by 2002:a17:90a:7c0c:: with SMTP id v12mr21904233pjf.71.1602543116506; Mon, 12 Oct 2020 15:51:56 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.51.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:51:55 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 01/31] elx: libefc_sli: SLI-4 register offsets and field definitions Date: Mon, 12 Oct 2020 15:51:17 -0700 Message-Id: <20201012225147.54404-2-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This is the initial patch for the new Emulex target mode SCSI driver sources. This patch: - Creates the new Emulex source level directory drivers/scsi/elx and adds the directory to the MAINTAINERS file. - Creates the first library subdirectory drivers/scsi/elx/libefc_sli. This library is a SLI-4 interface library. - Starts the population of the libefc_sli library with definitions of SLI-4 hardware register offsets and definitions. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Reviewed-by: Daniel Wagner --- v4: Add target-devel@vger.kernel.org in MAINTAINERS Removed unnessary brackets for enum defines Changed doorbell defines to inline functions --- MAINTAINERS | 9 + drivers/scsi/elx/libefc_sli/sli4.c | 21 +++ drivers/scsi/elx/libefc_sli/sli4.h | 294 +++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/scsi/elx/libefc_sli/sli4.c create mode 100644 drivers/scsi/elx/libefc_sli/sli4.h diff --git a/MAINTAINERS b/MAINTAINERS index 1ef851db33f5..7d3aa7dc2049 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6440,6 +6440,15 @@ S: Supported W: http://www.broadcom.com F: drivers/scsi/lpfc/ +EMULEX/BROADCOM EFCT FC/FCOE SCSI TARGET DRIVER +M: James Smart +M: Ram Vegesna +L: linux-scsi@vger.kernel.org +L: target-devel@vger.kernel.org +S: Supported +W: http://www.broadcom.com +F: drivers/scsi/elx/ + ENE CB710 FLASH CARD READER DRIVER M: Michał Mirosław S: Maintained diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c new file mode 100644 index 000000000000..cd503860c959 --- /dev/null +++ b/drivers/scsi/elx/libefc_sli/sli4.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/** + * All common (i.e. transport-independent) SLI-4 functions are implemented + * in this file. + */ +#include "sli4.h" + +static struct sli4_asic_entry_t sli4_asic_table[] = { + { SLI4_ASIC_REV_B0, SLI4_ASIC_GEN_5}, + { SLI4_ASIC_REV_D0, SLI4_ASIC_GEN_5}, + { SLI4_ASIC_REV_A3, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A0, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A1, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A3, SLI4_ASIC_GEN_6}, + { SLI4_ASIC_REV_A1, SLI4_ASIC_GEN_7}, +}; diff --git a/drivers/scsi/elx/libefc_sli/sli4.h b/drivers/scsi/elx/libefc_sli/sli4.h new file mode 100644 index 000000000000..859fea0fb1da --- /dev/null +++ b/drivers/scsi/elx/libefc_sli/sli4.h @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + * + */ + +/* + * All common SLI-4 structures and function prototypes. + */ + +#ifndef _SLI4_H +#define _SLI4_H + +#include +#include +#include "scsi/fc/fc_els.h" +#include "scsi/fc/fc_fs.h" +#include "../include/efc_common.h" + +/************************************************************************* + * Common SLI-4 register offsets and field definitions + */ + +/* SLI_INTF - SLI Interface Definition Register */ +#define SLI4_INTF_REG 0x0058 +enum sli4_intf { + SLI4_INTF_REV_SHIFT = 4, + SLI4_INTF_REV_MASK = 0xf0, + + SLI4_INTF_REV_S3 = 0x30, + SLI4_INTF_REV_S4 = 0x40, + + SLI4_INTF_FAMILY_SHIFT = 8, + SLI4_INTF_FAMILY_MASK = 0x0f00, + + SLI4_FAMILY_CHECK_ASIC_TYPE = 0x0f00, + + SLI4_INTF_IF_TYPE_SHIFT = 12, + SLI4_INTF_IF_TYPE_MASK = 0xf000, + + SLI4_INTF_IF_TYPE_2 = 0x2000, + SLI4_INTF_IF_TYPE_6 = 0x6000, + + SLI4_INTF_VALID_SHIFT = 29, + SLI4_INTF_VALID_MASK = 0xe0000000, + + SLI4_INTF_VALID_VALUE = 0xc0000000, +}; + +/* ASIC_ID - SLI ASIC Type and Revision Register */ +#define SLI4_ASIC_ID_REG 0x009c +enum sli4_asic { + SLI4_ASIC_GEN_SHIFT = 8, + SLI4_ASIC_GEN_MASK = 0xff00, + SLI4_ASIC_GEN_5 = 0x0b00, + SLI4_ASIC_GEN_6 = 0x0c00, + SLI4_ASIC_GEN_7 = 0x0d00, +}; + +enum sli4_acic_revisions { + SLI4_ASIC_REV_A0 = 0x00, + SLI4_ASIC_REV_A1 = 0x01, + SLI4_ASIC_REV_A2 = 0x02, + SLI4_ASIC_REV_A3 = 0x03, + SLI4_ASIC_REV_B0 = 0x10, + SLI4_ASIC_REV_B1 = 0x11, + SLI4_ASIC_REV_B2 = 0x12, + SLI4_ASIC_REV_C0 = 0x20, + SLI4_ASIC_REV_C1 = 0x21, + SLI4_ASIC_REV_C2 = 0x22, + SLI4_ASIC_REV_D0 = 0x30, +}; + +struct sli4_asic_entry_t { + u32 rev_id; + u32 family; +}; + +/* BMBX - Bootstrap Mailbox Register */ +#define SLI4_BMBX_REG 0x0160 +enum sli4_bmbx { + SLI4_BMBX_MASK_HI = 0x3, + SLI4_BMBX_MASK_LO = 0xf, + SLI4_BMBX_RDY = 1 << 0, + SLI4_BMBX_HI = 1 << 1, + SLI4_BMBX_SIZE = 256, +}; + +#define SLI4_BMBX_WRITE_HI(r) \ + ((upper_32_bits(r) & ~SLI4_BMBX_MASK_HI) | SLI4_BMBX_HI) +#define SLI4_BMBX_WRITE_LO(r) \ + (((upper_32_bits(r) & SLI4_BMBX_MASK_HI) << 30) | \ + (((r) & ~SLI4_BMBX_MASK_LO) >> 2)) + +/* SLIPORT_CONTROL - SLI Port Control Register */ +#define SLI4_PORT_CTRL_REG 0x0408 +enum sli4_port_ctrl { + SLI4_PORT_CTRL_IP = 1u << 27, + SLI4_PORT_CTRL_IDIS = 1u << 22, + SLI4_PORT_CTRL_FDD = 1u << 31, +}; + +/* SLI4_SLIPORT_ERROR - SLI Port Error Register */ +#define SLI4_PORT_ERROR1 0x040c +#define SLI4_PORT_ERROR2 0x0410 + +/* EQCQ_DOORBELL - EQ and CQ Doorbell Register */ +#define SLI4_EQCQ_DB_REG 0x120 +enum sli4_eqcq_e { + SLI4_EQ_ID_LO_MASK = 0x01ff, + + SLI4_CQ_ID_LO_MASK = 0x03ff, + + SLI4_EQCQ_CI_EQ = 0x0200, + + SLI4_EQCQ_QT_EQ = 0x00000400, + SLI4_EQCQ_QT_CQ = 0x00000000, + + SLI4_EQCQ_ID_HI_SHIFT = 11, + SLI4_EQCQ_ID_HI_MASK = 0xf800, + + SLI4_EQCQ_NUM_SHIFT = 16, + SLI4_EQCQ_NUM_MASK = 0x1fff0000, + + SLI4_EQCQ_ARM = 0x20000000, + SLI4_EQCQ_UNARM = 0x00000000, +}; + +static inline u32 +sli_format_eq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = (id & SLI4_EQ_ID_LO_MASK) | SLI4_EQCQ_QT_EQ; + reg |= (((id) >> 9) << SLI4_EQCQ_ID_HI_SHIFT) & SLI4_EQCQ_ID_HI_MASK; + reg |= ((num_popped) << SLI4_EQCQ_NUM_SHIFT) & SLI4_EQCQ_NUM_MASK; + reg |= arm | SLI4_EQCQ_CI_EQ; + + return reg; +} + +static inline u32 +sli_format_cq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = ((id) & SLI4_CQ_ID_LO_MASK) | SLI4_EQCQ_QT_CQ; + reg |= (((id) >> 10) << SLI4_EQCQ_ID_HI_SHIFT) & SLI4_EQCQ_ID_HI_MASK; + reg |= ((num_popped) << SLI4_EQCQ_NUM_SHIFT) & SLI4_EQCQ_NUM_MASK; + reg |= arm; + + return reg; +} + +/* EQ_DOORBELL - EQ Doorbell Register for IF_TYPE = 6*/ +#define SLI4_IF6_EQ_DB_REG 0x120 +enum sli4_eq_e { + SLI4_IF6_EQ_ID_MASK = 0x0fff, + + SLI4_IF6_EQ_NUM_SHIFT = 16, + SLI4_IF6_EQ_NUM_MASK = 0x1fff0000, +}; + +static inline u32 +sli_format_if6_eq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = id & SLI4_IF6_EQ_ID_MASK; + reg |= (num_popped << SLI4_IF6_EQ_NUM_SHIFT) & SLI4_IF6_EQ_NUM_MASK; + reg |= arm; + + return reg; +} + +/* CQ_DOORBELL - CQ Doorbell Register for IF_TYPE = 6 */ +#define SLI4_IF6_CQ_DB_REG 0xc0 +enum sli4_cq_e { + SLI4_IF6_CQ_ID_MASK = 0xffff, + + SLI4_IF6_CQ_NUM_SHIFT = 16, + SLI4_IF6_CQ_NUM_MASK = 0x1fff0000, +}; + +static inline u32 +sli_format_if6_cq_db_data(u16 num_popped, u16 id, u32 arm) { + u32 reg; + + reg = id & SLI4_IF6_CQ_ID_MASK; + reg |= ((num_popped) << SLI4_IF6_CQ_NUM_SHIFT) & SLI4_IF6_CQ_NUM_MASK; + reg |= arm; + + return reg; +} + +/* MQ_DOORBELL - MQ Doorbell Register */ +#define SLI4_MQ_DB_REG 0x0140 +#define SLI4_IF6_MQ_DB_REG 0x0160 +enum sli4_mq_e { + SLI4_MQ_ID_MASK = 0xffff, + + SLI4_MQ_NUM_SHIFT = 16, + SLI4_MQ_NUM_MASK = 0x3fff0000, +}; + +static inline u32 +sli_format_mq_db_data(u16 id) { + u32 reg; + + reg = id & SLI4_MQ_ID_MASK; + reg |= (1 << SLI4_MQ_NUM_SHIFT) & SLI4_MQ_NUM_MASK; + + return reg; +} + +/* RQ_DOORBELL - RQ Doorbell Register */ +#define SLI4_RQ_DB_REG 0x0a0 +#define SLI4_IF6_RQ_DB_REG 0x0080 +enum sli4_rq_e { + SLI4_RQ_DB_ID_MASK = 0xffff, + + SLI4_RQ_DB_NUM_SHIFT = 16, + SLI4_RQ_DB_NUM_MASK = 0x3fff0000, +}; + +static inline u32 +sli_format_rq_db_data(u16 id) { + u32 reg; + + reg = id & SLI4_RQ_DB_ID_MASK; + reg |= (1 << SLI4_RQ_DB_NUM_SHIFT) & SLI4_RQ_DB_NUM_MASK; + + return reg; +} + +/* WQ_DOORBELL - WQ Doorbell Register */ +#define SLI4_IO_WQ_DB_REG 0x040 +#define SLI4_IF6_WQ_DB_REG 0x040 +enum sli4_wq_e { + SLI4_WQ_ID_MASK = 0xffff, + + SLI4_WQ_IDX_SHIFT = 16, + SLI4_WQ_IDX_MASK = 0xff0000, + + SLI4_WQ_NUM_SHIFT = 24, + SLI4_WQ_NUM_MASK = 0x0ff00000, +}; + +static inline u32 +sli_format_wq_db_data(u16 id) { + u32 reg; + + reg = id & SLI4_WQ_ID_MASK; + reg |= (1 << SLI4_WQ_NUM_SHIFT) & SLI4_WQ_NUM_MASK; + + return reg; +} + +/* SLIPORT_STATUS - SLI Port Status Register */ +#define SLI4_PORT_STATUS_REGOFF 0x0404 +enum sli4_port_status { + SLI4_PORT_STATUS_FDP = 1u << 21, + SLI4_PORT_STATUS_RDY = 1u << 23, + SLI4_PORT_STATUS_RN = 1u << 24, + SLI4_PORT_STATUS_DIP = 1u << 25, + SLI4_PORT_STATUS_OTI = 1u << 29, + SLI4_PORT_STATUS_ERR = 1u << 31, +}; + +#define SLI4_PHYDEV_CTRL_REG 0x0414 +#define SLI4_PHYDEV_CTRL_FRST (1 << 1) +#define SLI4_PHYDEV_CTRL_DD (1 << 2) + +/* Register name enums */ +enum sli4_regname_en { + SLI4_REG_BMBX, + SLI4_REG_EQ_DOORBELL, + SLI4_REG_CQ_DOORBELL, + SLI4_REG_RQ_DOORBELL, + SLI4_REG_IO_WQ_DOORBELL, + SLI4_REG_MQ_DOORBELL, + SLI4_REG_PHYSDEV_CONTROL, + SLI4_REG_PORT_CONTROL, + SLI4_REG_PORT_ERROR1, + SLI4_REG_PORT_ERROR2, + SLI4_REG_PORT_SEMAPHORE, + SLI4_REG_PORT_STATUS, + SLI4_REG_UNKWOWN /* must be last */ +}; + +struct sli4_reg { + u32 rset; + u32 off; +}; + +#endif /* !_SLI4_H */ From patchwork Mon Oct 12 22:51:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269829 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 363E2C433DF for ; Mon, 12 Oct 2020 22:52:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C522E20838 for ; Mon, 12 Oct 2020 22:52:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="GIncQUuy" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390521AbgJLWwD (ORCPT ); Mon, 12 Oct 2020 18:52:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60520 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390499AbgJLWwC (ORCPT ); Mon, 12 Oct 2020 18:52:02 -0400 Received: from mail-pf1-x431.google.com (mail-pf1-x431.google.com [IPv6:2607:f8b0:4864:20::431]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0F50CC0613D1 for ; Mon, 12 Oct 2020 15:52:02 -0700 (PDT) Received: by mail-pf1-x431.google.com with SMTP id y14so15094999pfp.13 for ; Mon, 12 Oct 2020 15:52:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=lmOcfMAgnZgr2xCx9q/idXKo2b2aKfBcaBjytFVEEFc=; b=GIncQUuyAa2gX5xF44l6kFw+bMlgiRTpKZV3rQuQNVfSS9kF5qwCsJ/R7Y5KY1uH9i ntZqKszIkfJY/y22jIDZJSegV+Fb4jycCsx0ADO5j6HBBpph8y53nA8ePCNQk12PIRIE KfHcDJV0IAiR8YZ2SVTwBNeQz+1QTXRuRUuT4= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=lmOcfMAgnZgr2xCx9q/idXKo2b2aKfBcaBjytFVEEFc=; b=qRI49GacNQU7KZR/N/2Q0AYXaPnXxq7UxMlKsxd4l3tsKkDQAULqhVRPbtGzM9mUDy 3c1pvSHmR0prK7O1VP0YhCNKmh/pse3voccig6vWyckkyDcCEbcJ1J4Wzct4Q+/0fLfz NsAfC/0UCiqR8PXawvFnrQpiPWaxbAOckHx3ZMuUaEmfNBmz+EhGSwZogVaiqoRFdRUB G5izaPTQiph4t5nZ5ttwJ7ifYUMytvZRTQf0NEojJQi7rJTWOn9bYOISNr0SwwRTbqAi in7tecKWIuEqQ/i6KTEJXLKl2FXvVRwRA1E2HZI1e13qSrqDHNyTqKiv0wVDzvHWVoYj 3HRg== X-Gm-Message-State: AOAM533l0xIcWn+Mx+OEiTV74VHEX3EeddPevrRZ5h6VjQkYCoSgsazp Gommb4qj4hjSZTfuuRIYZrWgEKrTa5eiDZlYasUzuPLmMvNpwe0ybZGQKmatoEPqWuZr/EN5HPY N6UOBQP36ojwo8XvQhu1IkQuGttSSWARdFK7bD14gcXpXtjfRQ7apy/a8yD1Yn+XqYL6bgTcAaT pbGqg= X-Google-Smtp-Source: ABdhPJw9xxnIrs/EeyAGUgWYRRbDUpyWxFR2Ve90e9wL52F2+hUnLm+fR1aEQYrWDVSXLmJp65yiVw== X-Received: by 2002:a17:90a:8c84:: with SMTP id b4mr19211694pjo.16.1602543119677; Mon, 12 Oct 2020 15:51:59 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.51.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:51:58 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 03/31] elx: libefc_sli: Data structures and defines for mbox commands Date: Mon, 12 Oct 2020 15:51:19 -0700 Message-Id: <20201012225147.54404-4-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc_sli SLI-4 library population. This patch adds definitions for SLI-4 mailbox commands and responses. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- v4: Indentation changes Page size calculation changes Added SLI4_ prefix to missing defines --- drivers/scsi/elx/libefc_sli/sli4.h | 1624 ++++++++++++++++++++++++++++ 1 file changed, 1624 insertions(+) diff --git a/drivers/scsi/elx/libefc_sli/sli4.h b/drivers/scsi/elx/libefc_sli/sli4.h index b63fca0c2dfe..2960a6cb082f 100644 --- a/drivers/scsi/elx/libefc_sli/sli4.h +++ b/drivers/scsi/elx/libefc_sli/sli4.h @@ -1983,6 +1983,1518 @@ enum sli4_els_cmd_type { SLI4_ELS_REQUEST64_CMD_FABRIC = 0x0d, }; +#define SLI_PAGE_SIZE SZ_4K + +#define SLI4_BMBX_TIMEOUT_MSEC 30000 +#define SLI4_FW_READY_TIMEOUT_MSEC 30000 + +#define SLI4_BMBX_DELAY_US 1000 /* 1 ms */ +#define SLI4_INIT_PORT_DELAY_US 10000 /* 10 ms */ + +static inline u32 +sli_page_count(size_t bytes, u32 page_size) +{ + if (!page_size) + return 0; + + return (bytes + (page_size - 1)) >> __ffs(page_size); +} + +/************************************************************************* + * SLI-4 mailbox command formats and definitions + */ + +struct sli4_mbox_command_header { + u8 resvd0; + u8 command; + __le16 status; /* Port writes to indicate success/fail */ +}; + +enum sli4_mbx_cmd_value { + SLI4_MBX_CMD_CONFIG_LINK = 0x07, + SLI4_MBX_CMD_DUMP = 0x17, + SLI4_MBX_CMD_DOWN_LINK = 0x06, + SLI4_MBX_CMD_INIT_LINK = 0x05, + SLI4_MBX_CMD_INIT_VFI = 0xa3, + SLI4_MBX_CMD_INIT_VPI = 0xa4, + SLI4_MBX_CMD_POST_XRI = 0xa7, + SLI4_MBX_CMD_RELEASE_XRI = 0xac, + SLI4_MBX_CMD_READ_CONFIG = 0x0b, + SLI4_MBX_CMD_READ_STATUS = 0x0e, + SLI4_MBX_CMD_READ_NVPARMS = 0x02, + SLI4_MBX_CMD_READ_REV = 0x11, + SLI4_MBX_CMD_READ_LNK_STAT = 0x12, + SLI4_MBX_CMD_READ_SPARM64 = 0x8d, + SLI4_MBX_CMD_READ_TOPOLOGY = 0x95, + SLI4_MBX_CMD_REG_FCFI = 0xa0, + SLI4_MBX_CMD_REG_FCFI_MRQ = 0xaf, + SLI4_MBX_CMD_REG_RPI = 0x93, + SLI4_MBX_CMD_REG_RX_RQ = 0xa6, + SLI4_MBX_CMD_REG_VFI = 0x9f, + SLI4_MBX_CMD_REG_VPI = 0x96, + SLI4_MBX_CMD_RQST_FEATURES = 0x9d, + SLI4_MBX_CMD_SLI_CONFIG = 0x9b, + SLI4_MBX_CMD_UNREG_FCFI = 0xa2, + SLI4_MBX_CMD_UNREG_RPI = 0x14, + SLI4_MBX_CMD_UNREG_VFI = 0xa1, + SLI4_MBX_CMD_UNREG_VPI = 0x97, + SLI4_MBX_CMD_WRITE_NVPARMS = 0x03, + SLI4_MBX_CMD_CFG_AUTO_XFER_RDY = 0xad, +}; + +enum sli4_mbx_status { + SLI4_MBX_STATUS_SUCCESS = 0x0000, + SLI4_MBX_STATUS_FAILURE = 0x0001, + SLI4_MBX_STATUS_RPI_NOT_REG = 0x1400, +}; + +/* CONFIG_LINK - configure link-oriented parameters, + * such as default N_Port_ID address and various timers + */ +enum sli4_cmd_config_link_flags { + SLI4_CFG_LINK_BBSCN = 0xf00, + SLI4_CFG_LINK_CSCN = 0x1000, +}; + +struct sli4_cmd_config_link { + struct sli4_mbox_command_header hdr; + u8 maxbbc; + u8 rsvd5; + u8 rsvd6; + u8 rsvd7; + u8 alpa; + __le16 n_port_id; + u8 rsvd11; + __le32 rsvd12; + __le32 e_d_tov; + __le32 lp_tov; + __le32 r_a_tov; + __le32 r_t_tov; + __le32 al_tov; + __le32 rsvd36; + __le32 bbscn_dword; +}; + +#define SLI4_DUMP4_TYPE 0xf + +#define SLI4_WKI_TAG_SAT_TEM 0x1040 + +struct sli4_cmd_dump4 { + struct sli4_mbox_command_header hdr; + __le32 type_dword; + __le16 wki_selection; + __le16 rsvd10; + __le32 rsvd12; + __le32 returned_byte_cnt; + __le32 resp_data[59]; +}; + +/* INIT_LINK - initialize the link for a FC port */ +enum sli4_init_link_flags { + SLI4_INIT_LINK_F_LOOPBACK = 1 << 0, + + SLI4_INIT_LINK_F_P2P_ONLY = 1 << 1, + SLI4_INIT_LINK_F_FCAL_ONLY = 2 << 1, + SLI4_INIT_LINK_F_FCAL_FAIL_OVER = 0 << 1, + SLI4_INIT_LINK_F_P2P_FAIL_OVER = 1 << 1, + + SLI4_INIT_LINK_F_UNFAIR = 1 << 6, + SLI4_INIT_LINK_F_NO_LIRP = 1 << 7, + SLI4_INIT_LINK_F_LOOP_VALID_CHK = 1 << 8, + SLI4_INIT_LINK_F_NO_LISA = 1 << 9, + SLI4_INIT_LINK_F_FAIL_OVER = 1 << 10, + SLI4_INIT_LINK_F_FIXED_SPEED = 1 << 11, + SLI4_INIT_LINK_F_PICK_HI_ALPA = 1 << 15, + +}; + +enum sli4_fc_link_speed { + SLI4_LINK_SPEED_1G = 1, + SLI4_LINK_SPEED_2G, + SLI4_LINK_SPEED_AUTO_1_2, + SLI4_LINK_SPEED_4G, + SLI4_LINK_SPEED_AUTO_4_1, + SLI4_LINK_SPEED_AUTO_4_2, + SLI4_LINK_SPEED_AUTO_4_2_1, + SLI4_LINK_SPEED_8G, + SLI4_LINK_SPEED_AUTO_8_1, + SLI4_LINK_SPEED_AUTO_8_2, + SLI4_LINK_SPEED_AUTO_8_2_1, + SLI4_LINK_SPEED_AUTO_8_4, + SLI4_LINK_SPEED_AUTO_8_4_1, + SLI4_LINK_SPEED_AUTO_8_4_2, + SLI4_LINK_SPEED_10G, + SLI4_LINK_SPEED_16G, + SLI4_LINK_SPEED_AUTO_16_8_4, + SLI4_LINK_SPEED_AUTO_16_8, + SLI4_LINK_SPEED_32G, + SLI4_LINK_SPEED_AUTO_32_16_8, + SLI4_LINK_SPEED_AUTO_32_16, + SLI4_LINK_SPEED_64G, + SLI4_LINK_SPEED_AUTO_64_32_16, + SLI4_LINK_SPEED_AUTO_64_32, + SLI4_LINK_SPEED_128G, + SLI4_LINK_SPEED_AUTO_128_64_32, + SLI4_LINK_SPEED_AUTO_128_64, +}; + +struct sli4_cmd_init_link { + struct sli4_mbox_command_header hdr; + __le32 sel_reset_al_pa_dword; + __le32 flags0; + __le32 link_speed_sel_code; +}; + +/* INIT_VFI - initialize the VFI resource */ +enum sli4_init_vfi_flags { + SLI4_INIT_VFI_FLAG_VP = 0x1000, + SLI4_INIT_VFI_FLAG_VF = 0x2000, + SLI4_INIT_VFI_FLAG_VT = 0x4000, + SLI4_INIT_VFI_FLAG_VR = 0x8000, + + SLI4_INIT_VFI_VFID = 0x1fff, + SLI4_INIT_VFI_PRI = 0xe000, + + SLI4_INIT_VFI_HOP_COUNT = 0xff000000, +}; + +struct sli4_cmd_init_vfi { + struct sli4_mbox_command_header hdr; + __le16 vfi; + __le16 flags0_word; + __le16 fcfi; + __le16 vpi; + __le32 vf_id_pri_dword; + __le32 hop_cnt_dword; +}; + +/* INIT_VPI - initialize the VPI resource */ +struct sli4_cmd_init_vpi { + struct sli4_mbox_command_header hdr; + __le16 vpi; + __le16 vfi; +}; + +/* POST_XRI - post XRI resources to the SLI Port */ +enum sli4_post_xri_flags { + SLI4_POST_XRI_COUNT = 0xfff, + SLI4_POST_XRI_FLAG_ENX = 0x1000, + SLI4_POST_XRI_FLAG_DL = 0x2000, + SLI4_POST_XRI_FLAG_DI = 0x4000, + SLI4_POST_XRI_FLAG_VAL = 0x8000, +}; + +struct sli4_cmd_post_xri { + struct sli4_mbox_command_header hdr; + __le16 xri_base; + __le16 xri_count_flags; +}; + +/* RELEASE_XRI - Release XRI resources from the SLI Port */ +enum sli4_release_xri_flags { + SLI4_RELEASE_XRI_REL_XRI_CNT = 0x1f, + SLI4_RELEASE_XRI_COUNT = 0x1f, +}; + +struct sli4_cmd_release_xri { + struct sli4_mbox_command_header hdr; + __le16 rel_xri_count_word; + __le16 xri_count_word; + + struct { + __le16 xri_tag0; + __le16 xri_tag1; + } xri_tbl[62]; +}; + +/* READ_CONFIG - read SLI port configuration parameters */ +struct sli4_cmd_read_config { + struct sli4_mbox_command_header hdr; +}; + +enum sli4_read_cfg_resp_flags { + SLI4_READ_CFG_RESP_RESOURCE_EXT = 0x80000000, /* DW1 */ + SLI4_READ_CFG_RESP_TOPOLOGY = 0xff000000, /* DW2 */ +}; + +enum sli4_read_cfg_topo { + SLI4_READ_CFG_TOPO_FC = 0x1, /* FC topology unknown */ + SLI4_READ_CFG_TOPO_FC_DA = 0x2, /* FC Direct Attach */ + SLI4_READ_CFG_TOPO_FC_AL = 0x3, /* FC-AL topology */ +}; + +struct sli4_rsp_read_config { + struct sli4_mbox_command_header hdr; + __le32 ext_dword; + __le32 topology_dword; + __le32 resvd8; + __le16 e_d_tov; + __le16 resvd14; + __le32 resvd16; + __le16 r_a_tov; + __le16 resvd22; + __le32 resvd24; + __le32 resvd28; + __le16 lmt; + __le16 resvd34; + __le32 resvd36; + __le32 resvd40; + __le16 xri_base; + __le16 xri_count; + __le16 rpi_base; + __le16 rpi_count; + __le16 vpi_base; + __le16 vpi_count; + __le16 vfi_base; + __le16 vfi_count; + __le16 resvd60; + __le16 fcfi_count; + __le16 rq_count; + __le16 eq_count; + __le16 wq_count; + __le16 cq_count; + __le32 pad[45]; +}; + +/* READ_NVPARMS - read SLI port configuration parameters */ +enum sli4_read_nvparms_flags { + SLI4_READ_NVPARAMS_HARD_ALPA = 0xff, + SLI4_READ_NVPARAMS_PREFERRED_D_ID = 0xffffff00, +}; + +struct sli4_cmd_read_nvparms { + struct sli4_mbox_command_header hdr; + __le32 resvd0; + __le32 resvd4; + __le32 resvd8; + __le32 resvd12; + u8 wwpn[8]; + u8 wwnn[8]; + __le32 hard_alpa_d_id; +}; + +/* WRITE_NVPARMS - write SLI port configuration parameters */ +struct sli4_cmd_write_nvparms { + struct sli4_mbox_command_header hdr; + __le32 resvd0; + __le32 resvd4; + __le32 resvd8; + __le32 resvd12; + u8 wwpn[8]; + u8 wwnn[8]; + __le32 hard_alpa_d_id; +}; + +/* READ_REV - read the Port revision levels */ +enum { + SLI4_READ_REV_FLAG_SLI_LEVEL = 0xf, + SLI4_READ_REV_FLAG_FCOEM = 0x10, + SLI4_READ_REV_FLAG_CEEV = 0x60, + SLI4_READ_REV_FLAG_VPD = 0x2000, + + SLI4_READ_REV_AVAILABLE_LENGTH = 0xffffff, +}; + +struct sli4_cmd_read_rev { + struct sli4_mbox_command_header hdr; + __le16 resvd0; + __le16 flags0_word; + __le32 first_hw_rev; + __le32 second_hw_rev; + __le32 resvd12; + __le32 third_hw_rev; + u8 fc_ph_low; + u8 fc_ph_high; + u8 feature_level_low; + u8 feature_level_high; + __le32 resvd24; + __le32 first_fw_id; + u8 first_fw_name[16]; + __le32 second_fw_id; + u8 second_fw_name[16]; + __le32 rsvd18[30]; + __le32 available_length_dword; + struct sli4_dmaaddr hostbuf; + __le32 returned_vpd_length; + __le32 actual_vpd_length; +}; + +/* READ_SPARM64 - read the Port service parameters */ +#define SLI4_READ_SPARM64_WWPN_OFFSET (4 * sizeof(u32)) +#define SLI4_READ_SPARM64_WWNN_OFFSET (6 * sizeof(u32)) + +struct sli4_cmd_read_sparm64 { + struct sli4_mbox_command_header hdr; + __le32 resvd0; + __le32 resvd4; + struct sli4_bde bde_64; + __le16 vpi; + __le16 resvd22; + __le16 port_name_start; + __le16 port_name_len; + __le16 node_name_start; + __le16 node_name_len; +}; + +/* READ_TOPOLOGY - read the link event information */ +enum sli4_read_topo_e { + SLI4_READTOPO_ATTEN_TYPE = 0xff, + SLI4_READTOPO_FLAG_IL = 0x100, + SLI4_READTOPO_FLAG_PB_RECVD = 0x200, + + SLI4_READTOPO_LINKSTATE_RECV = 0x3, + SLI4_READTOPO_LINKSTATE_TRANS = 0xc, + SLI4_READTOPO_LINKSTATE_MACHINE = 0xf0, + SLI4_READTOPO_LINKSTATE_SPEED = 0xff00, + SLI4_READTOPO_LINKSTATE_TF = 0x40000000, + SLI4_READTOPO_LINKSTATE_LU = 0x80000000, + + SLI4_READTOPO_SCN_BBSCN = 0xf, + SLI4_READTOPO_SCN_CBBSCN = 0xf0, + + SLI4_READTOPO_R_T_TOV = 0x1ff, + SLI4_READTOPO_AL_TOV = 0xf000, + + SLI4_READTOPO_PB_FLAG = 0x80, + + SLI4_READTOPO_INIT_N_PORTID = 0xffffff, +}; + +#define SLI4_MIN_LOOP_MAP_BYTES 128 + +struct sli4_cmd_read_topology { + struct sli4_mbox_command_header hdr; + __le32 event_tag; + __le32 dw2_attentype; + u8 topology; + u8 lip_type; + u8 lip_al_ps; + u8 al_pa_granted; + struct sli4_bde bde_loop_map; + __le32 linkdown_state; + __le32 currlink_state; + u8 max_bbc; + u8 init_bbc; + u8 scn_flags; + u8 rsvd39; + __le16 dw10w0_al_rt_tov; + __le16 lp_tov; + u8 acquired_al_pa; + u8 pb_flags; + __le16 specified_al_pa; + __le32 dw12_init_n_port_id; +}; + +enum sli4_read_topo_link { + SLI4_READ_TOPOLOGY_LINK_UP = 0x1, + SLI4_READ_TOPOLOGY_LINK_DOWN, + SLI4_READ_TOPOLOGY_LINK_NO_ALPA, +}; + +enum sli4_read_topo { + SLI4_READ_TOPOLOGY_UNKNOWN = 0x0, + SLI4_READ_TOPOLOGY_NPORT, + SLI4_READ_TOPOLOGY_FC_AL, +}; + +enum sli4_read_topo_speed { + SLI4_READ_TOPOLOGY_SPEED_NONE = 0x00, + SLI4_READ_TOPOLOGY_SPEED_1G = 0x04, + SLI4_READ_TOPOLOGY_SPEED_2G = 0x08, + SLI4_READ_TOPOLOGY_SPEED_4G = 0x10, + SLI4_READ_TOPOLOGY_SPEED_8G = 0x20, + SLI4_READ_TOPOLOGY_SPEED_10G = 0x40, + SLI4_READ_TOPOLOGY_SPEED_16G = 0x80, + SLI4_READ_TOPOLOGY_SPEED_32G = 0x90, +}; + +/* REG_FCFI - activate a FC Forwarder */ +struct sli4_cmd_reg_fcfi_rq_cfg { + u8 r_ctl_mask; + u8 r_ctl_match; + u8 type_mask; + u8 type_match; +}; + +enum sli4_regfcfi_tag { + SLI4_REGFCFI_VLAN_TAG = 0xfff, + SLI4_REGFCFI_VLANTAG_VALID = 0x1000, +}; + +#define SLI4_CMD_REG_FCFI_NUM_RQ_CFG 4 +struct sli4_cmd_reg_fcfi { + struct sli4_mbox_command_header hdr; + __le16 fcf_index; + __le16 fcfi; + __le16 rqid1; + __le16 rqid0; + __le16 rqid3; + __le16 rqid2; + struct sli4_cmd_reg_fcfi_rq_cfg + rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG]; + __le32 dw8_vlan; +}; + +#define SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG 4 +#define SLI4_CMD_REG_FCFI_MRQ_MAX_NUM_RQ 32 +#define SLI4_CMD_REG_FCFI_SET_FCFI_MODE 0 +#define SLI4_CMD_REG_FCFI_SET_MRQ_MODE 1 + +enum sli4_reg_fcfi_mrq { + SLI4_REGFCFI_MRQ_VLAN_TAG = 0xfff, + SLI4_REGFCFI_MRQ_VLANTAG_VALID = 0x1000, + SLI4_REGFCFI_MRQ_MODE = 0x2000, + + SLI4_REGFCFI_MRQ_MASK_NUM_PAIRS = 0xff, + SLI4_REGFCFI_MRQ_FILTER_BITMASK = 0xf00, + SLI4_REGFCFI_MRQ_RQ_SEL_POLICY = 0xf000, +}; + +struct sli4_cmd_reg_fcfi_mrq { + struct sli4_mbox_command_header hdr; + __le16 fcf_index; + __le16 fcfi; + __le16 rqid1; + __le16 rqid0; + __le16 rqid3; + __le16 rqid2; + struct sli4_cmd_reg_fcfi_rq_cfg + rq_cfg[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG]; + __le32 dw8_vlan; + __le32 dw9_mrqflags; +}; + +struct sli4_cmd_rq_cfg { + __le16 rq_id; + u8 r_ctl_mask; + u8 r_ctl_match; + u8 type_mask; + u8 type_match; +}; + +/* REG_RPI - register a Remote Port Indicator */ +enum sli4_reg_rpi { + SLI4_REGRPI_REMOTE_N_PORTID = 0xffffff, /* DW2 */ + SLI4_REGRPI_UPD = 0x1000000, + SLI4_REGRPI_ETOW = 0x8000000, + SLI4_REGRPI_TERP = 0x20000000, + SLI4_REGRPI_CI = 0x80000000, +}; + +struct sli4_cmd_reg_rpi { + struct sli4_mbox_command_header hdr; + __le16 rpi; + __le16 rsvd2; + __le32 dw2_rportid_flags; + struct sli4_bde bde_64; + __le16 vpi; + __le16 rsvd26; +}; + +#define SLI4_REG_RPI_BUF_LEN 0x70 + +/* REG_VFI - register a Virtual Fabric Indicator */ +enum sli_reg_vfi { + SLI4_REGVFI_VP = 0x1000, /* DW1 */ + SLI4_REGVFI_UPD = 0x2000, + + SLI4_REGVFI_LOCAL_N_PORTID = 0xffffff, /* DW10 */ +}; + +struct sli4_cmd_reg_vfi { + struct sli4_mbox_command_header hdr; + __le16 vfi; + __le16 dw0w1_flags; + __le16 fcfi; + __le16 vpi; + u8 wwpn[8]; + struct sli4_bde sparm; + __le32 e_d_tov; + __le32 r_a_tov; + __le32 dw10_lportid_flags; +}; + +/* REG_VPI - register a Virtual Port Indicator */ +enum sli4_reg_vpi { + SLI4_REGVPI_LOCAL_N_PORTID = 0xffffff, + SLI4_REGVPI_UPD = 0x1000000, +}; + +struct sli4_cmd_reg_vpi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le32 dw2_lportid_flags; + u8 wwpn[8]; + __le32 rsvd12; + __le16 vpi; + __le16 vfi; +}; + +/* REQUEST_FEATURES - request / query SLI features */ +enum sli4_req_features_flags { + SLI4_REQFEAT_QRY = 0x1, /* Dw1 */ + + SLI4_REQFEAT_IAAB = 1 << 0, /* DW2 & DW3 */ + SLI4_REQFEAT_NPIV = 1 << 1, + SLI4_REQFEAT_DIF = 1 << 2, + SLI4_REQFEAT_VF = 1 << 3, + SLI4_REQFEAT_FCPI = 1 << 4, + SLI4_REQFEAT_FCPT = 1 << 5, + SLI4_REQFEAT_FCPC = 1 << 6, + SLI4_REQFEAT_RSVD = 1 << 7, + SLI4_REQFEAT_RQD = 1 << 8, + SLI4_REQFEAT_IAAR = 1 << 9, + SLI4_REQFEAT_HLM = 1 << 10, + SLI4_REQFEAT_PERFH = 1 << 11, + SLI4_REQFEAT_RXSEQ = 1 << 12, + SLI4_REQFEAT_RXRI = 1 << 13, + SLI4_REQFEAT_DCL2 = 1 << 14, + SLI4_REQFEAT_RSCO = 1 << 15, + SLI4_REQFEAT_MRQP = 1 << 16, +}; + +struct sli4_cmd_request_features { + struct sli4_mbox_command_header hdr; + __le32 dw1_qry; + __le32 cmd; + __le32 resp; +}; + +/* + * SLI_CONFIG - submit a configuration command to Port + * + * Command is either embedded as part of the payload (embed) or located + * in a separate memory buffer (mem) + */ +enum sli4_sli_config { + SLI4_SLICONF_EMB = 0x1, /* DW1 */ + SLI4_SLICONF_PMDCMD_SHIFT = 3, + SLI4_SLICONF_PMDCMD_MASK = 0xf8, + SLI4_SLICONF_PMDCMD_VAL_1 = 8, + SLI4_SLICONF_PMDCNT = 0xf8, + + SLI4_SLICONF_PMD_LEN = 0x00ffffff, +}; + +struct sli4_cmd_sli_config { + struct sli4_mbox_command_header hdr; + __le32 dw1_flags; + __le32 payload_len; + __le32 rsvd12[3]; + union { + u8 embed[58 * sizeof(u32)]; + struct sli4_bufptr mem; + } payload; +}; + +/* READ_STATUS - read tx/rx status of a particular port */ +#define SLI4_READSTATUS_CLEAR_COUNTERS 0x1 + +struct sli4_cmd_read_status { + struct sli4_mbox_command_header hdr; + __le32 dw1_flags; + __le32 rsvd4; + __le32 trans_kbyte_cnt; + __le32 recv_kbyte_cnt; + __le32 trans_frame_cnt; + __le32 recv_frame_cnt; + __le32 trans_seq_cnt; + __le32 recv_seq_cnt; + __le32 tot_exchanges_orig; + __le32 tot_exchanges_resp; + __le32 recv_p_bsy_cnt; + __le32 recv_f_bsy_cnt; + __le32 no_rq_buf_dropped_frames_cnt; + __le32 empty_rq_timeout_cnt; + __le32 no_xri_dropped_frames_cnt; + __le32 empty_xri_pool_cnt; +}; + +/* READ_LNK_STAT - read link status of a particular port */ +enum sli4_read_link_stats_flags { + SLI4_READ_LNKSTAT_REC = 1u << 0, + SLI4_READ_LNKSTAT_GEC = 1u << 1, + SLI4_READ_LNKSTAT_W02OF = 1u << 2, + SLI4_READ_LNKSTAT_W03OF = 1u << 3, + SLI4_READ_LNKSTAT_W04OF = 1u << 4, + SLI4_READ_LNKSTAT_W05OF = 1u << 5, + SLI4_READ_LNKSTAT_W06OF = 1u << 6, + SLI4_READ_LNKSTAT_W07OF = 1u << 7, + SLI4_READ_LNKSTAT_W08OF = 1u << 8, + SLI4_READ_LNKSTAT_W09OF = 1u << 9, + SLI4_READ_LNKSTAT_W10OF = 1u << 10, + SLI4_READ_LNKSTAT_W11OF = 1u << 11, + SLI4_READ_LNKSTAT_W12OF = 1u << 12, + SLI4_READ_LNKSTAT_W13OF = 1u << 13, + SLI4_READ_LNKSTAT_W14OF = 1u << 14, + SLI4_READ_LNKSTAT_W15OF = 1u << 15, + SLI4_READ_LNKSTAT_W16OF = 1u << 16, + SLI4_READ_LNKSTAT_W17OF = 1u << 17, + SLI4_READ_LNKSTAT_W18OF = 1u << 18, + SLI4_READ_LNKSTAT_W19OF = 1u << 19, + SLI4_READ_LNKSTAT_W20OF = 1u << 20, + SLI4_READ_LNKSTAT_W21OF = 1u << 21, + SLI4_READ_LNKSTAT_CLRC = 1u << 30, + SLI4_READ_LNKSTAT_CLOF = 1u << 31, +}; + +struct sli4_cmd_read_link_stats { + struct sli4_mbox_command_header hdr; + __le32 dw1_flags; + __le32 linkfail_errcnt; + __le32 losssync_errcnt; + __le32 losssignal_errcnt; + __le32 primseq_errcnt; + __le32 inval_txword_errcnt; + __le32 crc_errcnt; + __le32 primseq_eventtimeout_cnt; + __le32 elastic_bufoverrun_errcnt; + __le32 arbit_fc_al_timeout_cnt; + __le32 adv_rx_buftor_to_buf_credit; + __le32 curr_rx_buf_to_buf_credit; + __le32 adv_tx_buf_to_buf_credit; + __le32 curr_tx_buf_to_buf_credit; + __le32 rx_eofa_cnt; + __le32 rx_eofdti_cnt; + __le32 rx_eofni_cnt; + __le32 rx_soff_cnt; + __le32 rx_dropped_no_aer_cnt; + __le32 rx_dropped_no_avail_rpi_rescnt; + __le32 rx_dropped_no_avail_xri_rescnt; +}; + +/* Format a WQE with WQ_ID Association performance hint */ +static inline void +sli_set_wq_id_association(void *entry, u16 q_id) +{ + u32 *wqe = entry; + + /* + * Set Word 10, bit 0 to zero + * Set Word 10, bits 15:1 to the WQ ID + */ + wqe[10] &= ~0xffff; + wqe[10] |= q_id << 1; +} + +/* UNREG_FCFI - unregister a FCFI */ +struct sli4_cmd_unreg_fcfi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le16 fcfi; + __le16 rsvd6; +}; + +/* UNREG_RPI - unregister one or more RPI */ +enum sli4_unreg_rpi { + SLI4_UNREG_RPI_DP = 0x2000, + SLI4_UNREG_RPI_II_SHIFT = 14, + SLI4_UNREG_RPI_II_MASK = 0xc000, + SLI4_UNREG_RPI_II_RPI = 0x0000, + SLI4_UNREG_RPI_II_VPI = 0x4000, + SLI4_UNREG_RPI_II_VFI = 0x8000, + SLI4_UNREG_RPI_II_FCFI = 0xc000, + + SLI4_UNREG_RPI_DEST_N_PORTID_MASK = 0x00ffffff, +}; + +struct sli4_cmd_unreg_rpi { + struct sli4_mbox_command_header hdr; + __le16 index; + __le16 dw1w1_flags; + __le32 dw2_dest_n_portid; +}; + +/* UNREG_VFI - unregister one or more VFI */ +enum sli4_unreg_vfi { + SLI4_UNREG_VFI_II_SHIFT = 14, + SLI4_UNREG_VFI_II_MASK = 0xc000, + SLI4_UNREG_VFI_II_VFI = 0x0000, + SLI4_UNREG_VFI_II_FCFI = 0xc000, +}; + +struct sli4_cmd_unreg_vfi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le16 index; + __le16 dw2_flags; +}; + +enum sli4_unreg_type { + SLI4_UNREG_TYPE_PORT, + SLI4_UNREG_TYPE_DOMAIN, + SLI4_UNREG_TYPE_FCF, + SLI4_UNREG_TYPE_ALL +}; + +/* UNREG_VPI - unregister one or more VPI */ +enum sli4_unreg_vpi { + SLI4_UNREG_VPI_II_SHIFT = 14, + SLI4_UNREG_VPI_II_MASK = 0xc000, + SLI4_UNREG_VPI_II_VPI = 0x0000, + SLI4_UNREG_VPI_II_VFI = 0x8000, + SLI4_UNREG_VPI_II_FCFI = 0xc000, +}; + +struct sli4_cmd_unreg_vpi { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le16 index; + __le16 dw2w0_flags; +}; + +/* AUTO_XFER_RDY - Configure the auto-generate XFER-RDY feature */ +struct sli4_cmd_config_auto_xfer_rdy { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le32 max_burst_len; +}; + +#define SLI4_CONFIG_AUTO_XFERRDY_BLKSIZE 0xffff + +struct sli4_cmd_config_auto_xfer_rdy_hp { + struct sli4_mbox_command_header hdr; + __le32 rsvd0; + __le32 max_burst_len; + __le32 dw3_esoc_flags; + __le16 block_size; + __le16 rsvd14; +}; + +/************************************************************************* + * SLI-4 common configuration command formats and definitions + */ + +/* + * Subsystem values. + */ +enum sli4_subsystem { + SLI4_SUBSYSTEM_COMMON = 0x01, + SLI4_SUBSYSTEM_LOWLEVEL = 0x0b, + SLI4_SUBSYSTEM_FC = 0x0c, + SLI4_SUBSYSTEM_DMTF = 0x11, +}; + +#define SLI4_OPC_LOWLEVEL_SET_WATCHDOG 0X36 + +/* + * Common opcode (OPC) values. + */ +enum sli4_cmn_opcode { + SLI4_CMN_FUNCTION_RESET = 0x3d, + SLI4_CMN_CREATE_CQ = 0x0c, + SLI4_CMN_CREATE_CQ_SET = 0x1d, + SLI4_CMN_DESTROY_CQ = 0x36, + SLI4_CMN_MODIFY_EQ_DELAY = 0x29, + SLI4_CMN_CREATE_EQ = 0x0d, + SLI4_CMN_DESTROY_EQ = 0x37, + SLI4_CMN_CREATE_MQ_EXT = 0x5a, + SLI4_CMN_DESTROY_MQ = 0x35, + SLI4_CMN_GET_CNTL_ATTRIBUTES = 0x20, + SLI4_CMN_NOP = 0x21, + SLI4_CMN_GET_RSC_EXTENT_INFO = 0x9a, + SLI4_CMN_GET_SLI4_PARAMS = 0xb5, + SLI4_CMN_QUERY_FW_CONFIG = 0x3a, + SLI4_CMN_GET_PORT_NAME = 0x4d, + + SLI4_CMN_WRITE_FLASHROM = 0x07, + /* TRANSCEIVER Data */ + SLI4_CMN_READ_TRANS_DATA = 0x49, + SLI4_CMN_GET_CNTL_ADDL_ATTRS = 0x79, + SLI4_CMN_GET_FUNCTION_CFG = 0xa0, + SLI4_CMN_GET_PROFILE_CFG = 0xa4, + SLI4_CMN_SET_PROFILE_CFG = 0xa5, + SLI4_CMN_GET_PROFILE_LIST = 0xa6, + SLI4_CMN_GET_ACTIVE_PROFILE = 0xa7, + SLI4_CMN_SET_ACTIVE_PROFILE = 0xa8, + SLI4_CMN_READ_OBJECT = 0xab, + SLI4_CMN_WRITE_OBJECT = 0xac, + SLI4_CMN_DELETE_OBJECT = 0xae, + SLI4_CMN_READ_OBJECT_LIST = 0xad, + SLI4_CMN_SET_DUMP_LOCATION = 0xb8, + SLI4_CMN_SET_FEATURES = 0xbf, + SLI4_CMN_GET_RECFG_LINK_INFO = 0xc9, + SLI4_CMN_SET_RECNG_LINK_ID = 0xca, +}; + +/* DMTF opcode (OPC) values */ +#define DMTF_EXEC_CLP_CMD 0x01 + +/* + * COMMON_FUNCTION_RESET + * + * Resets the Port, returning it to a power-on state. This configuration + * command does not have a payload and should set/expect the lengths to + * be zero. + */ +struct sli4_rqst_cmn_function_reset { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_function_reset { + struct sli4_rsp_hdr hdr; +}; + + +/* + * COMMON_GET_CNTL_ATTRIBUTES + * + * Query for information about the SLI Port + */ +enum sli4_cntrl_attr_flags { + SLI4_CNTL_ATTR_PORTNUM = 0x3f, + SLI4_CNTL_ATTR_PORTTYPE = 0xc0, +}; + +struct sli4_rsp_cmn_get_cntl_attributes { + struct sli4_rsp_hdr hdr; + u8 version_str[32]; + u8 manufacturer_name[32]; + __le32 supported_modes; + u8 eprom_version_lo; + u8 eprom_version_hi; + __le16 rsvd17; + __le32 mbx_ds_version; + __le32 ep_fw_ds_version; + u8 ncsi_version_str[12]; + __le32 def_extended_timeout; + u8 model_number[32]; + u8 description[64]; + u8 serial_number[32]; + u8 ip_version_str[32]; + u8 fw_version_str[32]; + u8 bios_version_str[32]; + u8 redboot_version_str[32]; + u8 driver_version_str[32]; + u8 fw_on_flash_version_str[32]; + __le32 functionalities_supported; + __le16 max_cdb_length; + u8 asic_revision; + u8 generational_guid0; + __le32 generational_guid1_12[3]; + __le16 generational_guid13_14; + u8 generational_guid15; + u8 hba_port_count; + __le16 default_link_down_timeout; + u8 iscsi_version_min_max; + u8 multifunctional_device; + u8 cache_valid; + u8 hba_status; + u8 max_domains_supported; + u8 port_num_type_flags; + __le32 firmware_post_status; + __le32 hba_mtu; + u8 iscsi_features; + u8 rsvd121[3]; + __le16 pci_vendor_id; + __le16 pci_device_id; + __le16 pci_sub_vendor_id; + __le16 pci_sub_system_id; + u8 pci_bus_number; + u8 pci_device_number; + u8 pci_function_number; + u8 interface_type; + __le64 unique_identifier; + u8 number_of_netfilters; + u8 rsvd122[3]; +}; + +/* + * COMMON_GET_CNTL_ATTRIBUTES + * + * This command queries the controller information from the Flash ROM. + */ +struct sli4_rqst_cmn_get_cntl_addl_attributes { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_cntl_addl_attributes { + struct sli4_rsp_hdr hdr; + __le16 ipl_file_number; + u8 ipl_file_version; + u8 rsvd4; + u8 on_die_temperature; + u8 rsvd5[3]; + __le32 driver_advanced_features_supported; + __le32 rsvd7[4]; + char universal_bios_version[32]; + char x86_bios_version[32]; + char efi_bios_version[32]; + char fcode_version[32]; + char uefi_bios_version[32]; + char uefi_nic_version[32]; + char uefi_fcode_version[32]; + char uefi_iscsi_version[32]; + char iscsi_x86_bios_version[32]; + char pxe_x86_bios_version[32]; + u8 default_wwpn[8]; + u8 ext_phy_version[32]; + u8 fc_universal_bios_version[32]; + u8 fc_x86_bios_version[32]; + u8 fc_efi_bios_version[32]; + u8 fc_fcode_version[32]; + u8 ext_phy_crc_label[8]; + u8 ipl_file_name[16]; + u8 rsvd139[72]; +}; + +/* + * COMMON_NOP + * + * This command does not do anything; it only returns + * the payload in the completion. + */ +struct sli4_rqst_cmn_nop { + struct sli4_rqst_hdr hdr; + __le32 context[2]; +}; + +struct sli4_rsp_cmn_nop { + struct sli4_rsp_hdr hdr; + __le32 context[2]; +}; + +struct sli4_rqst_cmn_get_resource_extent_info { + struct sli4_rqst_hdr hdr; + __le16 resource_type; + __le16 rsvd16; +}; + +enum sli4_rsc_type { + SLI4_RSC_TYPE_VFI = 0x20, + SLI4_RSC_TYPE_VPI = 0x21, + SLI4_RSC_TYPE_RPI = 0x22, + SLI4_RSC_TYPE_XRI = 0x23, +}; + +struct sli4_rsp_cmn_get_resource_extent_info { + struct sli4_rsp_hdr hdr; + __le16 resource_extent_count; + __le16 resource_extent_size; +}; + +#define SLI4_128BYTE_WQE_SUPPORT 0x02 + +#define GET_Q_CNT_METHOD(m) \ + (((m) & SLI4_PARAM_Q_CNT_MTHD_MASK) >> SLI4_PARAM_Q_CNT_MTHD_SHFT) +#define GET_Q_CREATE_VERSION(v) \ + (((v) & SLI4_PARAM_QV_MASK) >> SLI4_PARAM_QV_SHIFT) + +enum sli4_rsp_get_params_e { + /*GENERIC*/ + SLI4_PARAM_Q_CNT_MTHD_SHFT = 24, + SLI4_PARAM_Q_CNT_MTHD_MASK = 0xf << 24, + SLI4_PARAM_QV_SHIFT = 14, + SLI4_PARAM_QV_MASK = 3 << 14, + + /* DW4 */ + SLI4_PARAM_PROTO_TYPE_MASK = 0xff, + /* DW5 */ + SLI4_PARAM_FT = 1 << 0, + SLI4_PARAM_SLI_REV_MASK = 0xf << 4, + SLI4_PARAM_SLI_FAM_MASK = 0xf << 8, + SLI4_PARAM_IF_TYPE_MASK = 0xf << 12, + SLI4_PARAM_SLI_HINT1_MASK = 0xff << 16, + SLI4_PARAM_SLI_HINT2_MASK = 0x1f << 24, + /* DW6 */ + SLI4_PARAM_EQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_EQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_EQ_PAGE_SZS_MASK = 0xff << 16, + /* DW8 */ + SLI4_PARAM_CQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_CQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_CQ_PAGE_SZS_MASK = 0xff << 16, + /* DW10 */ + SLI4_PARAM_MQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_MQ_PAGE_SZS_MASK = 0xff << 16, + /* DW12 */ + SLI4_PARAM_WQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_WQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_WQ_PAGE_SZS_MASK = 0xff << 16, + /* DW14 */ + SLI4_PARAM_RQ_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_RQE_SZS_MASK = 0xf << 8, + SLI4_PARAM_RQ_PAGE_SZS_MASK = 0xff << 16, + /* DW15W1*/ + SLI4_PARAM_RQ_DB_WINDOW_MASK = 0xf000, + /* DW16 */ + SLI4_PARAM_FC = 1 << 0, + SLI4_PARAM_EXT = 1 << 1, + SLI4_PARAM_HDRR = 1 << 2, + SLI4_PARAM_SGLR = 1 << 3, + SLI4_PARAM_FBRR = 1 << 4, + SLI4_PARAM_AREG = 1 << 5, + SLI4_PARAM_TGT = 1 << 6, + SLI4_PARAM_TERP = 1 << 7, + SLI4_PARAM_ASSI = 1 << 8, + SLI4_PARAM_WCHN = 1 << 9, + SLI4_PARAM_TCCA = 1 << 10, + SLI4_PARAM_TRTY = 1 << 11, + SLI4_PARAM_TRIR = 1 << 12, + SLI4_PARAM_PHOFF = 1 << 13, + SLI4_PARAM_PHON = 1 << 14, + SLI4_PARAM_PHWQ = 1 << 15, + SLI4_PARAM_BOUND_4GA = 1 << 16, + SLI4_PARAM_RXC = 1 << 17, + SLI4_PARAM_HLM = 1 << 18, + SLI4_PARAM_IPR = 1 << 19, + SLI4_PARAM_RXRI = 1 << 20, + SLI4_PARAM_SGLC = 1 << 21, + SLI4_PARAM_TIMM = 1 << 22, + SLI4_PARAM_TSMM = 1 << 23, + SLI4_PARAM_OAS = 1 << 25, + SLI4_PARAM_LC = 1 << 26, + SLI4_PARAM_AGXF = 1 << 27, + SLI4_PARAM_LOOPBACK_MASK = 0xf << 28, + /* DW18 */ + SLI4_PARAM_SGL_PAGE_CNT_MASK = 0xf << 0, + SLI4_PARAM_SGL_PAGE_SZS_MASK = 0xff << 8, + SLI4_PARAM_SGL_PP_ALIGN_MASK = 0xff << 16, +}; + +struct sli4_rqst_cmn_get_sli4_params { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_sli4_params { + struct sli4_rsp_hdr hdr; + __le32 dw4_protocol_type; + __le32 dw5_sli; + __le32 dw6_eq_page_cnt; + __le16 eqe_count_mask; + __le16 rsvd26; + __le32 dw8_cq_page_cnt; + __le16 cqe_count_mask; + __le16 rsvd34; + __le32 dw10_mq_page_cnt; + __le16 mqe_count_mask; + __le16 rsvd42; + __le32 dw12_wq_page_cnt; + __le16 wqe_count_mask; + __le16 rsvd50; + __le32 dw14_rq_page_cnt; + __le16 rqe_count_mask; + __le16 dw15w1_rq_db_window; + __le32 dw16_loopback_scope; + __le32 sge_supported_length; + __le32 dw18_sgl_page_cnt; + __le16 min_rq_buffer_size; + __le16 rsvd75; + __le32 max_rq_buffer_size; + __le16 physical_xri_max; + __le16 physical_rpi_max; + __le16 physical_vpi_max; + __le16 physical_vfi_max; + __le32 rsvd88; + __le16 frag_num_field_offset; + __le16 frag_num_field_size; + __le16 sgl_index_field_offset; + __le16 sgl_index_field_size; + __le32 chain_sge_initial_value_lo; + __le32 chain_sge_initial_value_hi; +}; + +/*Port Types*/ +enum sli4_port_types { + SLI4_PORT_TYPE_ETH = 0, + SLI4_PORT_TYPE_FC = 1, +}; + +struct sli4_rqst_cmn_get_port_name { + struct sli4_rqst_hdr hdr; + u8 port_type; + u8 rsvd4[3]; +}; + +struct sli4_rsp_cmn_get_port_name { + struct sli4_rsp_hdr hdr; + char port_name[4]; +}; + +struct sli4_rqst_cmn_write_flashrom { + struct sli4_rqst_hdr hdr; + __le32 flash_rom_access_opcode; + __le32 flash_rom_access_operation_type; + __le32 data_buffer_size; + __le32 offset; + u8 data_buffer[4]; +}; + +/* + * COMMON_READ_TRANSCEIVER_DATA + * + * This command reads SFF transceiver data(Format is defined + * by the SFF-8472 specification). + */ +struct sli4_rqst_cmn_read_transceiver_data { + struct sli4_rqst_hdr hdr; + __le32 page_number; + __le32 port; +}; + +struct sli4_rsp_cmn_read_transceiver_data { + struct sli4_rsp_hdr hdr; + __le32 page_number; + __le32 port; + u8 page_data[128]; + u8 page_data_2[128]; +}; + +#define SLI4_REQ_DESIRE_READLEN 0xffffff + +struct sli4_rqst_cmn_read_object { + struct sli4_rqst_hdr hdr; + __le32 desired_read_length_dword; + __le32 read_offset; + u8 object_name[104]; + __le32 host_buffer_descriptor_count; + struct sli4_bde host_buffer_descriptor[0]; +}; + +#define RSP_COM_READ_OBJ_EOF 0x80000000 + +struct sli4_rsp_cmn_read_object { + struct sli4_rsp_hdr hdr; + __le32 actual_read_length; + __le32 eof_dword; +}; + +enum sli4_rqst_write_object_flags { + SLI4_RQ_DES_WRITE_LEN = 0xffffff, + SLI4_RQ_DES_WRITE_LEN_NOC = 0x40000000, + SLI4_RQ_DES_WRITE_LEN_EOF = 0x80000000, +}; + +struct sli4_rqst_cmn_write_object { + struct sli4_rqst_hdr hdr; + __le32 desired_write_len_dword; + __le32 write_offset; + u8 object_name[104]; + __le32 host_buffer_descriptor_count; + struct sli4_bde host_buffer_descriptor[0]; +}; + +#define RSP_CHANGE_STATUS 0xff + +struct sli4_rsp_cmn_write_object { + struct sli4_rsp_hdr hdr; + __le32 actual_write_length; + __le32 change_status_dword; +}; + +struct sli4_rqst_cmn_delete_object { + struct sli4_rqst_hdr hdr; + __le32 rsvd4; + __le32 rsvd5; + u8 object_name[104]; +}; + +#define SLI4_RQ_OBJ_LIST_READ_LEN 0xffffff + +struct sli4_rqst_cmn_read_object_list { + struct sli4_rqst_hdr hdr; + __le32 desired_read_length_dword; + __le32 read_offset; + u8 object_name[104]; + __le32 host_buffer_descriptor_count; + struct sli4_bde host_buffer_descriptor[0]; +}; + +enum sli4_rqst_set_dump_flags { + SLI4_CMN_SET_DUMP_BUFFER_LEN = 0xffffff, + SLI4_CMN_SET_DUMP_FDB = 0x20000000, + SLI4_CMN_SET_DUMP_BLP = 0x40000000, + SLI4_CMN_SET_DUMP_QRY = 0x80000000, +}; + +struct sli4_rqst_cmn_set_dump_location { + struct sli4_rqst_hdr hdr; + __le32 buffer_length_dword; + __le32 buf_addr_low; + __le32 buf_addr_high; +}; + +struct sli4_rsp_cmn_set_dump_location { + struct sli4_rsp_hdr hdr; + __le32 buffer_length_dword; +}; + +enum sli4_dump_level { + SLI4_DUMP_LEVEL_NONE, + SLI4_CHIP_LEVEL_DUMP, + SLI4_FUNC_DESC_DUMP, +}; + +enum sli4_dump_state { + SLI4_DUMP_STATE_NONE, + SLI4_CHIP_DUMP_STATE_VALID, + SLI4_FUNC_DUMP_STATE_VALID, +}; + +enum sli4_dump_status { + SLI4_DUMP_READY_STATUS_NOT_READY, + SLI4_DUMP_READY_STATUS_DD_PRESENT, + SLI4_DUMP_READY_STATUS_FDB_PRESENT, + SLI4_DUMP_READY_STATUS_SKIP_DUMP, + SLI4_DUMP_READY_STATUS_FAILED = -1, +}; + +enum sli4_set_features { + SLI4_SET_FEATURES_DIF_SEED = 0x01, + SLI4_SET_FEATURES_XRI_TIMER = 0x03, + SLI4_SET_FEATURES_MAX_PCIE_SPEED = 0x04, + SLI4_SET_FEATURES_FCTL_CHECK = 0x05, + SLI4_SET_FEATURES_FEC = 0x06, + SLI4_SET_FEATURES_PCIE_RECV_DETECT = 0x07, + SLI4_SET_FEATURES_DIF_MEMORY_MODE = 0x08, + SLI4_SET_FEATURES_DISABLE_SLI_PORT_PAUSE_STATE = 0x09, + SLI4_SET_FEATURES_ENABLE_PCIE_OPTIONS = 0x0a, + SLI4_SET_FEAT_CFG_AUTO_XFER_RDY_T10PI = 0x0c, + SLI4_SET_FEATURES_ENABLE_MULTI_RECEIVE_QUEUE = 0x0d, + SLI4_SET_FEATURES_SET_FTD_XFER_HINT = 0x0f, + SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK = 0x11, +}; + +struct sli4_rqst_cmn_set_features { + struct sli4_rqst_hdr hdr; + __le32 feature; + __le32 param_len; + __le32 params[8]; +}; + +struct sli4_rqst_cmn_set_features_dif_seed { + __le16 seed; + __le16 rsvd16; +}; + +enum sli4_rqst_set_mrq_features { + SLI4_RQ_MULTIRQ_ISR = 0x1, + SLI4_RQ_MULTIRQ_AUTOGEN_XFER_RDY = 0x2, + + SLI4_RQ_MULTIRQ_NUM_RQS = 0xff, + SLI4_RQ_MULTIRQ_RQ_SELECT = 0xf00, +}; + +struct sli4_rqst_cmn_set_features_multirq { + __le32 auto_gen_xfer_dword; + __le32 num_rqs_dword; +}; + +enum sli4_rqst_health_check_flags { + SLI4_RQ_HEALTH_CHECK_ENABLE = 0x1, + SLI4_RQ_HEALTH_CHECK_QUERY = 0x2, +}; + +struct sli4_rqst_cmn_set_features_health_check { + __le32 health_check_dword; +}; + +struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint { + __le32 fdt_xfer_hint; +}; + +struct sli4_rqst_dmtf_exec_clp_cmd { + struct sli4_rqst_hdr hdr; + __le32 cmd_buf_length; + __le32 resp_buf_length; + __le32 cmd_buf_addr_low; + __le32 cmd_buf_addr_high; + __le32 resp_buf_addr_low; + __le32 resp_buf_addr_high; +}; + +struct sli4_rsp_dmtf_exec_clp_cmd { + struct sli4_rsp_hdr hdr; + __le32 rsvd4; + __le32 resp_length; + __le32 rsvd6; + __le32 rsvd7; + __le32 rsvd8; + __le32 rsvd9; + __le32 clp_status; + __le32 clp_detailed_status; +}; + +#define SLI4_PROTOCOL_FC 0x10 +#define SLI4_PROTOCOL_DEFAULT 0xff + +struct sli4_rspource_descriptor_v1 { + u8 descriptor_type; + u8 descriptor_length; + __le16 rsvd16; + __le32 type_specific[0]; +}; + +enum sli4_pcie_desc_flags { + SLI4_PCIE_DESC_IMM = 0x4000, + SLI4_PCIE_DESC_NOSV = 0x8000, + + SLI4_PCIE_DESC_PF_NO = 0x3ff0000, + + SLI4_PCIE_DESC_MISSN_ROLE = 0xff, + SLI4_PCIE_DESC_PCHG = 0x8000000, + SLI4_PCIE_DESC_SCHG = 0x10000000, + SLI4_PCIE_DESC_XCHG = 0x20000000, + SLI4_PCIE_DESC_XROM = 0xc0000000 +}; + +struct sli4_pcie_resource_descriptor_v1 { + u8 descriptor_type; + u8 descriptor_length; + __le16 imm_nosv_dword; + __le32 pf_number_dword; + __le32 rsvd3; + u8 sriov_state; + u8 pf_state; + u8 pf_type; + u8 rsvd4; + __le16 number_of_vfs; + __le16 rsvd5; + __le32 mission_roles_dword; + __le32 rsvd7[16]; +}; + +struct sli4_rqst_cmn_get_function_config { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_function_config { + struct sli4_rsp_hdr hdr; + __le32 desc_count; + __le32 desc[54]; +}; + +/* Link Config Descriptor for link config functions */ +struct sli4_link_config_descriptor { + u8 link_config_id; + u8 rsvd1[3]; + __le32 config_description[8]; +}; + +#define MAX_LINK_DES 10 + +struct sli4_rqst_cmn_get_reconfig_link_info { + struct sli4_rqst_hdr hdr; +}; + +struct sli4_rsp_cmn_get_reconfig_link_info { + struct sli4_rsp_hdr hdr; + u8 active_link_config_id; + u8 rsvd17; + u8 next_link_config_id; + u8 rsvd19; + __le32 link_configuration_descriptor_count; + struct sli4_link_config_descriptor + desc[MAX_LINK_DES]; +}; + +enum sli4_set_reconfig_link_flags { + SLI4_SET_RECONFIG_LINKID_NEXT = 0xff, + SLI4_SET_RECONFIG_LINKID_FD = 1u << 31, +}; + +struct sli4_rqst_cmn_set_reconfig_link_id { + struct sli4_rqst_hdr hdr; + __le32 dw4_flags; +}; + +struct sli4_rsp_cmn_set_reconfig_link_id { + struct sli4_rsp_hdr hdr; +}; + +struct sli4_rqst_lowlevel_set_watchdog { + struct sli4_rqst_hdr hdr; + __le16 watchdog_timeout; + __le16 rsvd18; +}; + +struct sli4_rsp_lowlevel_set_watchdog { + struct sli4_rsp_hdr hdr; + __le32 rsvd; +}; + +/* FC opcode (OPC) values */ +enum sli4_fc_opcodes { + SLI4_OPC_WQ_CREATE = 0x1, + SLI4_OPC_WQ_DESTROY = 0x2, + SLI4_OPC_POST_SGL_PAGES = 0x3, + SLI4_OPC_RQ_CREATE = 0x5, + SLI4_OPC_RQ_DESTROY = 0x6, + SLI4_OPC_READ_FCF_TABLE = 0x8, + SLI4_OPC_POST_HDR_TEMPLATES = 0xb, + SLI4_OPC_REDISCOVER_FCF = 0x10, +}; + +/* Use the default CQ associated with the WQ */ +#define SLI4_CQ_DEFAULT 0xffff + +/* + * POST_SGL_PAGES + * + * Register the scatter gather list (SGL) memory and + * associate it with an XRI. + */ +struct sli4_rqst_post_sgl_pages { + struct sli4_rqst_hdr hdr; + __le16 xri_start; + __le16 xri_count; + struct { + __le32 page0_low; + __le32 page0_high; + __le32 page1_low; + __le32 page1_high; + } page_set[10]; +}; + +struct sli4_rsp_post_sgl_pages { + struct sli4_rsp_hdr hdr; +}; + +struct sli4_rqst_post_hdr_templates { + struct sli4_rqst_hdr hdr; + __le16 rpi_offset; + __le16 page_count; + struct sli4_dmaaddr page_descriptor[0]; +}; + +#define SLI4_HDR_TEMPLATE_SIZE 64 + +enum sli4_io_flags { +/* The XRI associated with this IO is already active */ + SLI4_IO_CONTINUATION = 1 << 0, +/* Automatically generate a good RSP frame */ + SLI4_IO_AUTO_GOOD_RESPONSE = 1 << 1, + SLI4_IO_NO_ABORT = 1 << 2, +/* Set the DNRX bit because no auto xref rdy buffer is posted */ + SLI4_IO_DNRX = 1 << 3, +}; + +enum sli4_callback { + SLI4_CB_LINK, + SLI4_CB_MAX, +}; + +enum sli4_link_status { + SLI4_LINK_STATUS_UP, + SLI4_LINK_STATUS_DOWN, + SLI4_LINK_STATUS_NO_ALPA, + SLI4_LINK_STATUS_MAX, +}; + +enum sli4_link_topology { + SLI4_LINK_TOPO_NPORT = 1, + SLI4_LINK_TOPO_LOOP, + SLI4_LINK_TOPO_LOOPBACK_INTERNAL, + SLI4_LINK_TOPO_LOOPBACK_EXTERNAL, + SLI4_LINK_TOPO_NONE, + SLI4_LINK_TOPO_MAX, +}; + +enum sli4_link_medium { + SLI4_LINK_MEDIUM_ETHERNET, + SLI4_LINK_MEDIUM_FC, + SLI4_LINK_MEDIUM_MAX, +}; /******Driver specific structures******/ struct sli4_queue { @@ -2074,4 +3586,116 @@ struct sli_fcp_tgt_params { u16 tag; }; +struct sli4_link_event { + enum sli4_link_status status; + enum sli4_link_topology topology; + enum sli4_link_medium medium; + u32 speed; + u8 *loop_map; + u32 fc_id; +}; + +enum sli4_resource { + SLI4_RSRC_VFI, + SLI4_RSRC_VPI, + SLI4_RSRC_RPI, + SLI4_RSRC_XRI, + SLI4_RSRC_FCFI, + SLI4_RSRC_MAX, +}; + +struct sli4_extent { + u32 number; + u32 size; + u32 n_alloc; + u32 *base; + unsigned long *use_map; + u32 map_size; +}; + +struct sli4_queue_info { + u16 max_qcount[SLI4_QTYPE_MAX]; + u32 max_qentries[SLI4_QTYPE_MAX]; + u16 count_mask[SLI4_QTYPE_MAX]; + u16 count_method[SLI4_QTYPE_MAX]; + u32 qpage_count[SLI4_QTYPE_MAX]; +}; + +struct sli4_params { + u8 has_extents; + u8 auto_reg; + u8 auto_xfer_rdy; + u8 hdr_template_req; + u8 perf_hint; + u8 perf_wq_id_association; + u8 cq_create_version; + u8 mq_create_version; + u8 high_login_mode; + u8 sgl_pre_registered; + u8 sgl_pre_reg_required; + u8 t10_dif_inline_capable; + u8 t10_dif_separate_capable; +}; + +struct sli4 { + void *os; + struct pci_dev *pci; + void __iomem *reg[PCI_STD_NUM_BARS]; + + u32 sli_rev; + u32 sli_family; + u32 if_type; + + u16 asic_type; + u16 asic_rev; + + u16 e_d_tov; + u16 r_a_tov; + struct sli4_queue_info qinfo; + u16 link_module_type; + u8 rq_batch; + u8 port_number; + char port_name[2]; + u16 rq_min_buf_size; + u32 rq_max_buf_size; + u8 topology; + u8 wwpn[8]; + u8 wwnn[8]; + u32 fw_rev[2]; + u8 fw_name[2][16]; + char ipl_name[16]; + u32 hw_rev[3]; + char modeldesc[64]; + char bios_version_string[32]; + u32 wqe_size; + u32 vpd_length; + /* + * Tracks the port resources using extents metaphor. For + * devices that don't implement extents (i.e. + * has_extents == FALSE), the code models each resource as + * a single large extent. + */ + struct sli4_extent ext[SLI4_RSRC_MAX]; + u32 features; + struct sli4_params params; + u32 sge_supported_length; + u32 sgl_page_sizes; + u32 max_sgl_pages; + + /* + * Callback functions + */ + int (*link)(void *ctx, void *event); + void *link_arg; + + struct efc_dma bmbx; + + /* Save pointer to physical memory descriptor for non-embedded + * SLI_CONFIG commands for BMBX dumping purposes + */ + struct efc_dma *bmbx_non_emb_pmd; + + struct efc_dma vpd_data; +}; + #endif /* !_SLI4_H */ From patchwork Mon Oct 12 22:51:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269828 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B2EB5C433DF for ; Mon, 12 Oct 2020 22:52:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5687E20878 for ; Mon, 12 Oct 2020 22:52:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="TxSeGWen" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390571AbgJLWwI (ORCPT ); Mon, 12 Oct 2020 18:52:08 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60534 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390499AbgJLWwE (ORCPT ); Mon, 12 Oct 2020 18:52:04 -0400 Received: from mail-pf1-x443.google.com (mail-pf1-x443.google.com [IPv6:2607:f8b0:4864:20::443]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7671AC0613D1 for ; Mon, 12 Oct 2020 15:52:04 -0700 (PDT) Received: by mail-pf1-x443.google.com with SMTP id 144so15109906pfb.4 for ; Mon, 12 Oct 2020 15:52:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=ggb6JiJA9w+2jt/TucZoq9lV8my+GkG+LnYOENOZS5I=; b=TxSeGWenfTQd4DmgXsUrTRJQ50sXYsEvYnzMm6hbp4rNAAg16/HSFmQmcjuuz6uZOH KnmY0ppnb19IeOK3t1BGWe3LEynliR+YJahxTG1AiSNRNT72XNPvMvsdMzy8EFHu703B PrGSBHnKJBS1dzPOegYy6BgEjKUjkXiK/WZ5w= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=ggb6JiJA9w+2jt/TucZoq9lV8my+GkG+LnYOENOZS5I=; b=Ek8WO6Ls+ImYiqeIYoZDXGai2qf4S2cDEW+GFirOno2CosYW2jGeLf4Nh7QVRowEn3 aE2f1HNdmSOUVoGk/1cTtwosXGS/GFnMnBIbC3N2/IJ1l5bfAXkhn98gQlvKTTa0jGqB GZ+1MXi79clqbWe/J8/aDIY+K1qXBoepzibDjpimU9iaPfS+8W7lqqCqLPfI/Sv8Pv2X IJ1z2HHpJ2/5rjWh+oAcydh8dnoqwxyi6JSogW5IlTwf0XCtNkYobehKUeufqagdILPJ 4NqfHPDCQCa/Nmw3lKUq0Ch+V2n5gTPPEUFwSLt3ZmOeY7jYN0BEQX2+NA99YM3O8GQ7 pLKw== X-Gm-Message-State: AOAM531XEDJUiD4edbTH/sZZalMzTG7+gmzkAad1LHXPBVlIY9tE4Ntj nRilKt3sQSEMwk3ydps69Ha5/owjH+DK9aNqs2APwWJLBjfp7CHmHRHyz+gHLB2DJQBdn5dkGBA ZQVIS11h4hMmlNrbBl5JxToTvH6Sz+MBR65EhIPH9YdJKBSagOcjlGOpS1utC2YP8SCWXm1XHm3 jyWms= X-Google-Smtp-Source: ABdhPJzcBA0pjk+6471fCh1JfdeYTTTCby0NtlqTGLlrW8NGDl+Do/weV27gRVY0SVzmfLMN9QaClw== X-Received: by 2002:a17:90a:6582:: with SMTP id k2mr22343450pjj.139.1602543122735; Mon, 12 Oct 2020 15:52:02 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:01 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 05/31] elx: libefc_sli: Populate and post different WQEs Date: Mon, 12 Oct 2020 15:51:21 -0700 Message-Id: <20201012225147.54404-6-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc_sli SLI-4 library population. This patch adds service routines to create different WQEs and adds APIs to issue iread, iwrite, treceive, tsend and other work queue entries. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Reviewed-by: Daniel Wagner --- v4: Reduce function parameters for WQE filling functions --- drivers/scsi/elx/libefc_sli/sli4.c | 1499 ++++++++++++++++++++++++++++ 1 file changed, 1499 insertions(+) diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c index 3678cf69e1dd..a9126f3a2343 100644 --- a/drivers/scsi/elx/libefc_sli/sli4.c +++ b/drivers/scsi/elx/libefc_sli/sli4.c @@ -1382,3 +1382,1502 @@ sli_cq_parse(struct sli4 *sli4, struct sli4_queue *cq, u8 *cqe, return rc; } + +int +sli_abort_wqe(struct sli4 *sli, void *buf, enum sli4_abort_type type, + bool send_abts, u32 ids, u32 mask, u16 tag, u16 cq_id) +{ + struct sli4_abort_wqe *abort = buf; + + memset(buf, 0, sli->wqe_size); + + switch (type) { + case SLI4_ABORT_XRI: + abort->criteria = SLI4_ABORT_CRITERIA_XRI_TAG; + if (mask) { + efc_log_warn(sli, "%#x aborting XRI %#x warning non-zero mask", + mask, ids); + mask = 0; + } + break; + case SLI4_ABORT_ABORT_ID: + abort->criteria = SLI4_ABORT_CRITERIA_ABORT_TAG; + break; + case SLI4_ABORT_REQUEST_ID: + abort->criteria = SLI4_ABORT_CRITERIA_REQUEST_TAG; + break; + default: + efc_log_info(sli, "unsupported type %#x\n", type); + return EFC_FAIL; + } + + abort->ia_ir_byte |= send_abts ? 0 : 1; + + /* Suppress ABTS retries */ + abort->ia_ir_byte |= SLI4_ABRT_WQE_IR; + + abort->t_mask = cpu_to_le32(mask); + abort->t_tag = cpu_to_le32(ids); + abort->command = SLI4_WQE_ABORT; + abort->request_tag = cpu_to_le16(tag); + + abort->dw10w0_flags = cpu_to_le16(SLI4_ABRT_WQE_QOSD); + + abort->cq_id = cpu_to_le16(cq_id); + abort->cmdtype_wqec_byte |= SLI4_CMD_ABORT_WQE; + + return EFC_SUCCESS; +} + +int +sli_els_request64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + struct sli_els_params *params) +{ + struct sli4_els_request64_wqe *els = buf; + struct sli4_sge *sge = sgl->virt; + bool is_fabric = false; + struct sli4_bde *bptr; + + memset(buf, 0, sli->wqe_size); + + bptr = &els->els_request_payload; + if (sli->params.sgl_pre_registered) { + els->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_REQ_WQE_XBL; + + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->xmit_len & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + ((2 * sizeof(struct sli4_sge)) & + SLI4_BDE_LEN_MASK)); + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys)); + } + + els->els_request_payload_length = cpu_to_le32(params->xmit_len); + els->max_response_payload_length = cpu_to_le32(params->rsp_len); + + els->xri_tag = cpu_to_le16(params->xri); + els->timer = params->timeout; + els->class_byte |= SLI4_GENERIC_CLASS_CLASS_3; + + els->command = SLI4_WQE_ELS_REQUEST64; + + els->request_tag = cpu_to_le16(params->tag); + + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_IOD; + + els->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_REQ_WQE_QOSD; + + /* figure out the ELS_ID value from the request buffer */ + + switch (params->cmd) { + case ELS_LOGO: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_LOGO << SLI4_REQ_WQE_ELSID_SHFT; + if (params->rpi_registered) { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->rpi); + } else { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + } + if (params->d_id == FC_FID_FLOGI) + is_fabric = true; + break; + case ELS_FDISC: + if (params->d_id == FC_FID_FLOGI) + is_fabric = true; + if (params->s_id == 0) { + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_FDISC << SLI4_REQ_WQE_ELSID_SHFT; + is_fabric = true; + } else { + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT; + } + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + els->sid_sp_dword |= cpu_to_le32(1 << SLI4_REQ_WQE_SP_SHFT); + break; + case ELS_FLOGI: + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + /* + * Set SP here ... we haven't done a REG_VPI yet + * need to maybe not set this when we have + * completed VFI/VPI registrations ... + * + * Use the FC_ID of the SPORT if it has been allocated, + * otherwise use an S_ID of zero. + */ + els->sid_sp_dword |= cpu_to_le32(1 << SLI4_REQ_WQE_SP_SHFT); + if (params->s_id != U32_MAX) + els->sid_sp_dword |= cpu_to_le32(params->s_id); + break; + case ELS_PLOGI: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_PLOGI << SLI4_REQ_WQE_ELSID_SHFT; + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + break; + case ELS_SCR: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT; + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + break; + default: + els->cmdtype_elsid_byte |= + SLI4_ELS_REQUEST64_OTHER << SLI4_REQ_WQE_ELSID_SHFT; + if (params->rpi_registered) { + els->ct_byte |= (SLI4_GENERIC_CONTEXT_RPI << + SLI4_REQ_WQE_CT_SHFT); + els->context_tag = cpu_to_le16(params->vpi); + } else { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_REQ_WQE_CT_SHFT; + els->context_tag = cpu_to_le16(params->vpi); + } + break; + } + + if (is_fabric) + els->cmdtype_elsid_byte |= SLI4_ELS_REQUEST64_CMD_FABRIC; + else + els->cmdtype_elsid_byte |= SLI4_ELS_REQUEST64_CMD_NON_FABRIC; + + els->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + if (((els->ct_byte & SLI4_REQ_WQE_CT) >> SLI4_REQ_WQE_CT_SHFT) != + SLI4_GENERIC_CONTEXT_RPI) + els->remote_id_dword = cpu_to_le32(params->d_id); + + if (((els->ct_byte & SLI4_REQ_WQE_CT) >> SLI4_REQ_WQE_CT_SHFT) == + SLI4_GENERIC_CONTEXT_VPI) + els->temporary_rpi = cpu_to_le16(params->rpi); + + return EFC_SUCCESS; +} + +int +sli_fcp_icmnd64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, u16 xri, + u16 tag, u16 cq_id, u32 rpi, u32 rnode_fcid, u8 timeout) +{ + struct sli4_fcp_icmnd64_wqe *icmnd = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + u32 len; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return EFC_FAIL; + } + sge = sgl->virt; + bptr = &icmnd->bde; + if (sli->params.sgl_pre_registered) { + icmnd->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_ICMD_WQE_XBL; + + icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & + SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys)); + } + + len = le32_to_cpu(sge[0].buffer_length) + + le32_to_cpu(sge[1].buffer_length); + icmnd->payload_offset_length = cpu_to_le16(len); + icmnd->xri_tag = cpu_to_le16(xri); + icmnd->context_tag = cpu_to_le16(rpi); + icmnd->timer = timeout; + + /* WQE word 4 contains read transfer length */ + icmnd->class_pu_byte |= 2 << SLI4_ICMD_WQE_PU_SHFT; + icmnd->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + icmnd->command = SLI4_WQE_FCP_ICMND64; + icmnd->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_ICMD_WQE_CT_SHFT; + + icmnd->abort_tag = cpu_to_le32(xri); + + icmnd->request_tag = cpu_to_le16(tag); + icmnd->len_loc1_byte |= SLI4_ICMD_WQE_LEN_LOC_BIT1; + icmnd->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_ICMD_WQE_LEN_LOC_BIT2; + icmnd->cmd_type_byte |= SLI4_CMD_FCP_ICMND64_WQE; + icmnd->cq_id = cpu_to_le16(cq_id); + + return EFC_SUCCESS; +} + +int +sli_fcp_iread64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u32 xfer_len, u16 xri, u16 tag, + u16 cq_id, u32 rpi, u32 rnode_fcid, + u8 dif, u8 bs, u8 timeout) +{ + struct sli4_fcp_iread64_wqe *iread = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + u32 sge_flags, len; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return EFC_FAIL; + } + + sge = sgl->virt; + bptr = &iread->bde; + if (sli->params.sgl_pre_registered) { + iread->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_IR_WQE_XBL; + + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & + SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = sge[0].buffer_address_low; + bptr->u.blp.high = sge[0].buffer_address_high; + } else { + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = + cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + + /* + * fill out fcp_cmnd buffer len and change resp buffer to be of + * type "skip" (note: response will still be written to sge[1] + * if necessary) + */ + len = le32_to_cpu(sge[0].buffer_length); + iread->fcp_cmd_buffer_length = cpu_to_le16(len); + + sge_flags = le32_to_cpu(sge[1].dw2_flags); + sge_flags &= (~SLI4_SGE_TYPE_MASK); + sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT); + sge[1].dw2_flags = cpu_to_le32(sge_flags); + } + + len = le32_to_cpu(sge[0].buffer_length) + + le32_to_cpu(sge[1].buffer_length); + iread->payload_offset_length = cpu_to_le16(len); + iread->total_transfer_length = cpu_to_le32(xfer_len); + + iread->xri_tag = cpu_to_le16(xri); + iread->context_tag = cpu_to_le16(rpi); + + iread->timer = timeout; + + /* WQE word 4 contains read transfer length */ + iread->class_pu_byte |= 2 << SLI4_IR_WQE_PU_SHFT; + iread->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + iread->command = SLI4_WQE_FCP_IREAD64; + iread->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_IR_WQE_CT_SHFT; + iread->dif_ct_bs_byte |= dif; + iread->dif_ct_bs_byte |= bs << SLI4_IR_WQE_BS_SHFT; + + iread->abort_tag = cpu_to_le32(xri); + + iread->request_tag = cpu_to_le16(tag); + iread->len_loc1_byte |= SLI4_IR_WQE_LEN_LOC_BIT1; + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_LEN_LOC_BIT2; + iread->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IR_WQE_IOD; + iread->cmd_type_byte |= SLI4_CMD_FCP_IREAD64_WQE; + iread->cq_id = cpu_to_le16(cq_id); + + if (sli->params.perf_hint) { + bptr = &iread->first_data_bde; + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = + sge[first_data_sge].buffer_address_low; + bptr->u.data.high = + sge[first_data_sge].buffer_address_high; + } + + return EFC_SUCCESS; +} + +int +sli_fcp_iwrite64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u32 xfer_len, + u32 first_burst, u16 xri, u16 tag, + u16 cq_id, u32 rpi, + u32 rnode_fcid, + u8 dif, u8 bs, u8 timeout) +{ + struct sli4_fcp_iwrite64_wqe *iwrite = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + u32 sge_flags, min, len; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return EFC_FAIL; + } + sge = sgl->virt; + bptr = &iwrite->bde; + if (sli->params.sgl_pre_registered) { + iwrite->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_IWR_WQE_XBL; + + iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_DBDE; + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & SLI4_BDE_LEN_MASK)); + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_XBL; + + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = cpu_to_le32(upper_32_bits(sgl->phys)); + + /* + * fill out fcp_cmnd buffer len and change resp buffer to be of + * type "skip" (note: response will still be written to sge[1] + * if necessary) + */ + len = le32_to_cpu(sge[0].buffer_length); + iwrite->fcp_cmd_buffer_length = cpu_to_le16(len); + sge_flags = le32_to_cpu(sge[1].dw2_flags); + sge_flags &= ~SLI4_SGE_TYPE_MASK; + sge_flags |= (SLI4_SGE_TYPE_SKIP << SLI4_SGE_TYPE_SHIFT); + sge[1].dw2_flags = cpu_to_le32(sge_flags); + } + + len = le32_to_cpu(sge[0].buffer_length) + + le32_to_cpu(sge[1].buffer_length); + iwrite->payload_offset_length = cpu_to_le16(len); + iwrite->total_transfer_length = cpu_to_le16(xfer_len); + min = (xfer_len < first_burst) ? xfer_len : first_burst; + iwrite->initial_transfer_length = cpu_to_le16(min); + + iwrite->xri_tag = cpu_to_le16(xri); + iwrite->context_tag = cpu_to_le16(rpi); + + iwrite->timer = timeout; + /* WQE word 4 contains read transfer length */ + iwrite->class_pu_byte |= 2 << SLI4_IWR_WQE_PU_SHFT; + iwrite->class_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + iwrite->command = SLI4_WQE_FCP_IWRITE64; + iwrite->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_IWR_WQE_CT_SHFT; + iwrite->dif_ct_bs_byte |= dif; + iwrite->dif_ct_bs_byte |= bs << SLI4_IWR_WQE_BS_SHFT; + + iwrite->abort_tag = cpu_to_le32(xri); + + iwrite->request_tag = cpu_to_le16(tag); + iwrite->len_loc1_byte |= SLI4_IWR_WQE_LEN_LOC_BIT1; + iwrite->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_IWR_WQE_LEN_LOC_BIT2; + iwrite->cmd_type_byte |= SLI4_CMD_FCP_IWRITE64_WQE; + iwrite->cq_id = cpu_to_le16(cq_id); + + if (sli->params.perf_hint) { + bptr = &iwrite->first_data_bde; + + bptr->bde_type_buflen = cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[first_data_sge].buffer_address_low; + bptr->u.data.high = sge[first_data_sge].buffer_address_high; + } + + return EFC_SUCCESS; +} + +int +sli_fcp_treceive64_wqe(struct sli4 *sli, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params) +{ + struct sli4_fcp_treceive64_wqe *trecv = buf; + struct sli4_fcp_128byte_wqe *trecv_128 = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + + memset(buf, 0, sli->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return EFC_FAIL; + } + sge = sgl->virt; + bptr = &trecv->bde; + if (sli->params.sgl_pre_registered) { + trecv->qosd_xbl_hlm_iod_dbde_wqes &= ~SLI4_TRCV_WQE_XBL; + + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) + & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + + trecv->payload_offset_length = sge[0].buffer_length; + } else { + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_XBL; + + /* if data is a single physical address, use a BDE */ + if (!dif && + params->xmit_len <= le32_to_cpu(sge[2].buffer_length)) { + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[2].buffer_length) + & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[2].buffer_address_low; + bptr->u.data.high = sge[2].buffer_address_high; + } else { + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & SLI4_BDE_LEN_MASK)); + bptr->u.blp.low = cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + } + } + + trecv->relative_offset = cpu_to_le32(params->offset); + + if (params->flags & SLI4_IO_CONTINUATION) + trecv->eat_xc_ccpe |= SLI4_TRCV_WQE_XC; + + trecv->xri_tag = cpu_to_le16(params->xri); + + trecv->context_tag = cpu_to_le16(params->rpi); + + /* WQE uses relative offset */ + trecv->class_ar_pu_byte |= 1 << SLI4_TRCV_WQE_PU_SHFT; + + if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) + trecv->class_ar_pu_byte |= SLI4_TRCV_WQE_AR; + + trecv->command = SLI4_WQE_FCP_TRECEIVE64; + trecv->class_ar_pu_byte |= SLI4_GENERIC_CLASS_CLASS_3; + trecv->dif_ct_bs_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_TRCV_WQE_CT_SHFT; + trecv->dif_ct_bs_byte |= bs << SLI4_TRCV_WQE_BS_SHFT; + + trecv->remote_xid = cpu_to_le16(params->ox_id); + + trecv->request_tag = cpu_to_le16(params->tag); + + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_IOD; + + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_LEN_LOC_BIT2; + + trecv->cmd_type_byte |= SLI4_CMD_FCP_TRECEIVE64_WQE; + + trecv->cq_id = cpu_to_le16(cq_id); + + trecv->fcp_data_receive_length = cpu_to_le32(params->xmit_len); + + if (sli->params.perf_hint) { + bptr = &trecv->first_data_bde; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = sge[first_data_sge].buffer_address_low; + bptr->u.data.high = sge[first_data_sge].buffer_address_high; + } + + /* The upper 7 bits of csctl is the priority */ + if (params->cs_ctl & SLI4_MASK_CCP) { + trecv->eat_xc_ccpe |= SLI4_TRCV_WQE_CCPE; + trecv->ccp = (params->cs_ctl & SLI4_MASK_CCP); + } + + if (params->app_id && sli->wqe_size == SLI4_WQE_EXT_BYTES && + !(trecv->eat_xc_ccpe & SLI4_TRSP_WQE_EAT)) { + trecv->lloc1_appid |= SLI4_TRCV_WQE_APPID; + trecv->qosd_xbl_hlm_iod_dbde_wqes |= SLI4_TRCV_WQE_WQES; + trecv_128->dw[31] = params->app_id; + } + return EFC_SUCCESS; +} + +int +sli_fcp_cont_treceive64_wqe(struct sli4 *sli, void *buf, + struct efc_dma *sgl, u32 first_data_sge, + u16 sec_xri, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params) +{ + int rc; + + rc = sli_fcp_treceive64_wqe(sli, buf, sgl, first_data_sge, + cq_id, dif, bs, params); + if (!rc) { + struct sli4_fcp_treceive64_wqe *trecv = buf; + + trecv->command = SLI4_WQE_FCP_CONT_TRECEIVE64; + trecv->dword5.sec_xri_tag = cpu_to_le16(sec_xri); + } + return rc; +} + +int +sli_fcp_trsp64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u16 cq_id, u8 port_owned, struct sli_fcp_tgt_params *params) +{ + struct sli4_fcp_trsp64_wqe *trsp = buf; + struct sli4_fcp_128byte_wqe *trsp_128 = buf; + + memset(buf, 0, sli4->wqe_size); + + if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) { + trsp->class_ag_byte |= SLI4_TRSP_WQE_AG; + } else { + struct sli4_sge *sge = sgl->virt; + struct sli4_bde *bptr; + + if (sli4->params.sgl_pre_registered || port_owned) + trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_DBDE; + else + trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_XBL; + bptr = &trsp->bde; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[0].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + + trsp->fcp_response_length = cpu_to_le32(params->xmit_len); + } + + if (params->flags & SLI4_IO_CONTINUATION) + trsp->eat_xc_ccpe |= SLI4_TRSP_WQE_XC; + + trsp->xri_tag = cpu_to_le16(params->xri); + trsp->rpi = cpu_to_le16(params->rpi); + + trsp->command = SLI4_WQE_FCP_TRSP64; + trsp->class_ag_byte |= SLI4_GENERIC_CLASS_CLASS_3; + + trsp->remote_xid = cpu_to_le16(params->ox_id); + trsp->request_tag = cpu_to_le16(params->tag); + if (params->flags & SLI4_IO_DNRX) + trsp->ct_dnrx_byte |= SLI4_TRSP_WQE_DNRX; + else + trsp->ct_dnrx_byte &= ~SLI4_TRSP_WQE_DNRX; + + trsp->lloc1_appid |= 0x1; + trsp->cq_id = cpu_to_le16(cq_id); + trsp->cmd_type_byte = SLI4_CMD_FCP_TRSP64_WQE; + + /* The upper 7 bits of csctl is the priority */ + if (params->cs_ctl & SLI4_MASK_CCP) { + trsp->eat_xc_ccpe |= SLI4_TRSP_WQE_CCPE; + trsp->ccp = (params->cs_ctl & SLI4_MASK_CCP); + } + + if (params->app_id && sli4->wqe_size == SLI4_WQE_EXT_BYTES && + !(trsp->eat_xc_ccpe & SLI4_TRSP_WQE_EAT)) { + trsp->lloc1_appid |= SLI4_TRSP_WQE_APPID; + trsp->qosd_xbl_hlm_dbde_wqes |= SLI4_TRSP_WQE_WQES; + trsp_128->dw[31] = params->app_id; + } + return EFC_SUCCESS; +} + +int +sli_fcp_tsend64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + u32 first_data_sge, u16 cq_id, u8 dif, u8 bs, + struct sli_fcp_tgt_params *params) +{ + struct sli4_fcp_tsend64_wqe *tsend = buf; + struct sli4_fcp_128byte_wqe *tsend_128 = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + + memset(buf, 0, sli4->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return EFC_FAIL; + } + sge = sgl->virt; + + bptr = &tsend->bde; + if (sli4->params.sgl_pre_registered) { + tsend->ll_qd_xbl_hlm_iod_dbde &= ~SLI4_TSEND_WQE_XBL; + + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[2].buffer_length) & + SLI4_BDE_LEN_MASK)); + + /* TSEND64_WQE specifies first two SGE are skipped (3rd is + * valid) + */ + bptr->u.data.low = sge[2].buffer_address_low; + bptr->u.data.high = sge[2].buffer_address_high; + } else { + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_XBL; + + /* if data is a single physical address, use a BDE */ + if (!dif && + params->xmit_len <= le32_to_cpu(sge[2].buffer_length)) { + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQE_DBDE; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[2].buffer_length) & + SLI4_BDE_LEN_MASK)); + /* + * TSEND64_WQE specifies first two SGE are skipped + * (i.e. 3rd is valid) + */ + bptr->u.data.low = + sge[2].buffer_address_low; + bptr->u.data.high = + sge[2].buffer_address_high; + } else { + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + (sgl->size & + SLI4_BDE_LEN_MASK)); + bptr->u.blp.low = + cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + } + } + + tsend->relative_offset = cpu_to_le32(params->offset); + + if (params->flags & SLI4_IO_CONTINUATION) + tsend->dw10byte2 |= SLI4_TSEND_XC; + + tsend->xri_tag = cpu_to_le16(params->xri); + + tsend->rpi = cpu_to_le16(params->rpi); + /* WQE uses relative offset */ + tsend->class_pu_ar_byte |= 1 << SLI4_TSEND_WQE_PU_SHFT; + + if (params->flags & SLI4_IO_AUTO_GOOD_RESPONSE) + tsend->class_pu_ar_byte |= SLI4_TSEND_WQE_AR; + + tsend->command = SLI4_WQE_FCP_TSEND64; + tsend->class_pu_ar_byte |= SLI4_GENERIC_CLASS_CLASS_3; + tsend->ct_byte |= SLI4_GENERIC_CONTEXT_RPI << SLI4_TSEND_CT_SHFT; + tsend->ct_byte |= dif; + tsend->ct_byte |= bs << SLI4_TSEND_BS_SHFT; + + tsend->remote_xid = cpu_to_le16(params->ox_id); + + tsend->request_tag = cpu_to_le16(params->tag); + + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_LEN_LOC_BIT2; + + tsend->cq_id = cpu_to_le16(cq_id); + + tsend->cmd_type_byte |= SLI4_CMD_FCP_TSEND64_WQE; + + tsend->fcp_data_transmit_length = cpu_to_le32(params->xmit_len); + + if (sli4->params.perf_hint) { + bptr = &tsend->first_data_bde; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (le32_to_cpu(sge[first_data_sge].buffer_length) & + SLI4_BDE_LEN_MASK)); + bptr->u.data.low = + sge[first_data_sge].buffer_address_low; + bptr->u.data.high = + sge[first_data_sge].buffer_address_high; + } + + /* The upper 7 bits of csctl is the priority */ + if (params->cs_ctl & SLI4_MASK_CCP) { + tsend->dw10byte2 |= SLI4_TSEND_CCPE; + tsend->ccp = (params->cs_ctl & SLI4_MASK_CCP); + } + + if (params->app_id && sli4->wqe_size == SLI4_WQE_EXT_BYTES && + !(tsend->dw10byte2 & SLI4_TSEND_EAT)) { + tsend->dw10byte0 |= SLI4_TSEND_APPID_VALID; + tsend->ll_qd_xbl_hlm_iod_dbde |= SLI4_TSEND_WQES; + tsend_128->dw[31] = params->app_id; + } + return EFC_SUCCESS; +} + +int +sli_gen_request64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *sgl, + struct sli_ct_params *params) +{ + struct sli4_gen_request64_wqe *gen = buf; + struct sli4_sge *sge = NULL; + struct sli4_bde *bptr; + + memset(buf, 0, sli4->wqe_size); + + if (!sgl || !sgl->virt) { + efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n", + sgl, sgl ? sgl->virt : NULL); + return EFC_FAIL; + } + sge = sgl->virt; + bptr = &gen->bde; + + if (sli4->params.sgl_pre_registered) { + gen->dw10flags1 &= ~SLI4_GEN_REQ64_WQE_XBL; + + gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_DBDE; + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->xmit_len & SLI4_BDE_LEN_MASK)); + + bptr->u.data.low = sge[0].buffer_address_low; + bptr->u.data.high = sge[0].buffer_address_high; + } else { + gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_XBL; + + bptr->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(BLP)) | + ((2 * sizeof(struct sli4_sge)) & + SLI4_BDE_LEN_MASK)); + + bptr->u.blp.low = + cpu_to_le32(lower_32_bits(sgl->phys)); + bptr->u.blp.high = + cpu_to_le32(upper_32_bits(sgl->phys)); + } + + gen->request_payload_length = cpu_to_le32(params->xmit_len); + gen->max_response_payload_length = cpu_to_le32(params->rsp_len); + + gen->df_ctl = params->df_ctl; + gen->type = params->type; + gen->r_ctl = params->r_ctl; + + gen->xri_tag = cpu_to_le16(params->xri); + + gen->ct_byte = SLI4_GENERIC_CONTEXT_RPI << SLI4_GEN_REQ64_CT_SHFT; + gen->context_tag = cpu_to_le16(params->rpi); + + gen->class_byte = SLI4_GENERIC_CLASS_CLASS_3; + + gen->command = SLI4_WQE_GEN_REQUEST64; + + gen->timer = params->timeout; + + gen->request_tag = cpu_to_le16(params->tag); + + gen->dw10flags1 |= SLI4_GEN_REQ64_WQE_IOD; + + gen->dw10flags0 |= SLI4_GEN_REQ64_WQE_QOSD; + + gen->cmd_type_byte = SLI4_CMD_GEN_REQUEST64_WQE; + + gen->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + return EFC_SUCCESS; +} + +int +sli_send_frame_wqe(struct sli4 *sli, void *buf, u8 sof, u8 eof, u32 *hdr, + struct efc_dma *payload, u32 req_len, + u8 timeout, u16 xri, u16 req_tag) +{ + struct sli4_send_frame_wqe *sf = buf; + + memset(buf, 0, sli->wqe_size); + + sf->dw10flags1 |= SLI4_SF_WQE_DBDE; + sf->bde.bde_type_buflen = cpu_to_le32(req_len & + SLI4_BDE_LEN_MASK); + sf->bde.u.data.low = cpu_to_le32(lower_32_bits(payload->phys)); + sf->bde.u.data.high = cpu_to_le32(upper_32_bits(payload->phys)); + + /* Copy FC header */ + sf->fc_header_0_1[0] = cpu_to_le32(hdr[0]); + sf->fc_header_0_1[1] = cpu_to_le32(hdr[1]); + sf->fc_header_2_5[0] = cpu_to_le32(hdr[2]); + sf->fc_header_2_5[1] = cpu_to_le32(hdr[3]); + sf->fc_header_2_5[2] = cpu_to_le32(hdr[4]); + sf->fc_header_2_5[3] = cpu_to_le32(hdr[5]); + + sf->frame_length = cpu_to_le32(req_len); + + sf->xri_tag = cpu_to_le16(xri); + sf->dw7flags0 &= ~SLI4_SF_PU; + sf->context_tag = 0; + + sf->ct_byte &= ~SLI4_SF_CT; + sf->command = SLI4_WQE_SEND_FRAME; + sf->dw7flags0 |= SLI4_GENERIC_CLASS_CLASS_3; + sf->timer = timeout; + + sf->request_tag = cpu_to_le16(req_tag); + sf->eof = eof; + sf->sof = sof; + + sf->dw10flags1 &= ~SLI4_SF_QOSD; + sf->dw10flags0 |= SLI4_SF_LEN_LOC_BIT1; + sf->dw10flags2 &= ~SLI4_SF_XC; + + sf->dw10flags1 |= SLI4_SF_XBL; + + sf->cmd_type_byte |= SLI4_CMD_SEND_FRAME_WQE; + sf->cq_id = cpu_to_le16(0xffff); + + return EFC_SUCCESS; +} + +int +sli_xmit_bls_rsp64_wqe(struct sli4 *sli, void *buf, + struct sli_bls_payload *payload, struct sli_bls_params *params) +{ + struct sli4_xmit_bls_rsp_wqe *bls = buf; + u32 dw_ridflags = 0; + + /* + * Callers can either specify RPI or S_ID, but not both + */ + if (params->rpi_registered && params->s_id != U32_MAX) { + efc_log_info(sli, "S_ID specified for attached remote node %d\n", + params->rpi); + return EFC_FAIL; + } + + memset(buf, 0, sli->wqe_size); + + if (payload->type == SLI4_SLI_BLS_ACC) { + bls->payload_word0 = + cpu_to_le32((payload->u.acc.seq_id_last << 16) | + (payload->u.acc.seq_id_validity << 24)); + bls->high_seq_cnt = payload->u.acc.high_seq_cnt; + bls->low_seq_cnt = payload->u.acc.low_seq_cnt; + } else if (payload->type == SLI4_SLI_BLS_RJT) { + bls->payload_word0 = + cpu_to_le32(*((u32 *)&payload->u.rjt)); + dw_ridflags |= SLI4_BLS_RSP_WQE_AR; + } else { + efc_log_info(sli, "bad BLS type %#x\n", payload->type); + return EFC_FAIL; + } + + bls->ox_id = payload->ox_id; + bls->rx_id = payload->rx_id; + + if (params->rpi_registered) { + bls->dw8flags0 |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_BLS_RSP_WQE_CT_SHFT; + bls->context_tag = cpu_to_le16(params->rpi); + } else { + bls->dw8flags0 |= + SLI4_GENERIC_CONTEXT_VPI << SLI4_BLS_RSP_WQE_CT_SHFT; + bls->context_tag = cpu_to_le16(params->vpi); + + if (params->s_id != U32_MAX) + bls->local_n_port_id_dword |= + cpu_to_le32(params->s_id & 0x00ffffff); + else + bls->local_n_port_id_dword |= + cpu_to_le32(params->s_id & 0x00ffffff); + + dw_ridflags = (dw_ridflags & ~SLI4_BLS_RSP_RID) | + (params->d_id & SLI4_BLS_RSP_RID); + + bls->temporary_rpi = cpu_to_le16(params->rpi); + } + + bls->xri_tag = cpu_to_le16(params->xri); + + bls->dw8flags1 |= SLI4_GENERIC_CLASS_CLASS_3; + + bls->command = SLI4_WQE_XMIT_BLS_RSP; + + bls->request_tag = cpu_to_le16(params->tag); + + bls->dw11flags1 |= SLI4_BLS_RSP_WQE_QOSD; + + bls->remote_id_dword = cpu_to_le32(dw_ridflags); + bls->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + bls->dw12flags0 |= SLI4_CMD_XMIT_BLS_RSP64_WQE; + + return EFC_SUCCESS; +} + +int +sli_xmit_els_rsp64_wqe(struct sli4 *sli, void *buf, struct efc_dma *rsp, + struct sli_els_params *params) +{ + struct sli4_xmit_els_rsp64_wqe *els = buf; + + memset(buf, 0, sli->wqe_size); + + if (sli->params.sgl_pre_registered) + els->flags2 |= SLI4_ELS_DBDE; + else + els->flags2 |= SLI4_ELS_XBL; + + els->els_response_payload.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->rsp_len & SLI4_BDE_LEN_MASK)); + els->els_response_payload.u.data.low = + cpu_to_le32(lower_32_bits(rsp->phys)); + els->els_response_payload.u.data.high = + cpu_to_le32(upper_32_bits(rsp->phys)); + + els->els_response_payload_length = cpu_to_le32(params->rsp_len); + + els->xri_tag = cpu_to_le16(params->xri); + + els->class_byte |= SLI4_GENERIC_CLASS_CLASS_3; + + els->command = SLI4_WQE_ELS_RSP64; + + els->request_tag = cpu_to_le16(params->tag); + + els->ox_id = cpu_to_le16(params->ox_id); + + els->flags2 |= SLI4_ELS_IOD & SLI4_ELS_REQUEST64_DIR_WRITE; + + els->flags2 |= SLI4_ELS_QOSD; + + els->cmd_type_wqec = SLI4_ELS_REQUEST64_CMD_GEN; + + els->cq_id = cpu_to_le16(SLI4_CQ_DEFAULT); + + if (params->rpi_registered) { + els->ct_byte |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_ELS_CT_OFFSET; + els->context_tag = cpu_to_le16(params->rpi); + return EFC_SUCCESS; + } + + els->ct_byte |= SLI4_GENERIC_CONTEXT_VPI << SLI4_ELS_CT_OFFSET; + els->context_tag = cpu_to_le16(params->vpi); + els->rid_dw = cpu_to_le32(params->d_id & SLI4_ELS_RID); + els->temporary_rpi = cpu_to_le16(params->rpi); + if (params->s_id != U32_MAX) { + els->sid_dw |= + cpu_to_le32(SLI4_ELS_SP | (params->s_id & SLI4_ELS_SID)); + } + + return EFC_SUCCESS; +} + +int +sli_xmit_sequence64_wqe(struct sli4 *sli4, void *buf, struct efc_dma *payload, + struct sli_ct_params *params) +{ + struct sli4_xmit_sequence64_wqe *xmit = buf; + + memset(buf, 0, sli4->wqe_size); + + if (!payload || !payload->virt) { + efc_log_err(sli4, "bad parameter sgl=%p virt=%p\n", + payload, payload ? payload->virt : NULL); + return EFC_FAIL; + } + + if (sli4->params.sgl_pre_registered) + xmit->dw10w0 |= cpu_to_le16(SLI4_SEQ_WQE_DBDE); + else + xmit->dw10w0 |= cpu_to_le16(SLI4_SEQ_WQE_XBL); + + xmit->bde.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (params->rsp_len & SLI4_BDE_LEN_MASK)); + xmit->bde.u.data.low = + cpu_to_le32(lower_32_bits(payload->phys)); + xmit->bde.u.data.high = + cpu_to_le32(upper_32_bits(payload->phys)); + xmit->sequence_payload_len = cpu_to_le32(params->rsp_len); + + xmit->remote_n_port_id_dword |= cpu_to_le32(params->d_id & 0x00ffffff); + + xmit->relative_offset = 0; + + /* sequence initiative - this matches what is seen from + * FC switches in response to FCGS commands + */ + xmit->dw5flags0 &= (~SLI4_SEQ_WQE_SI); + xmit->dw5flags0 &= (~SLI4_SEQ_WQE_FT);/* force transmit */ + xmit->dw5flags0 &= (~SLI4_SEQ_WQE_XO);/* exchange responder */ + xmit->dw5flags0 |= SLI4_SEQ_WQE_LS;/* last in seqence */ + xmit->df_ctl = params->df_ctl; + xmit->type = params->type; + xmit->r_ctl = params->r_ctl; + + xmit->xri_tag = cpu_to_le16(params->xri); + xmit->context_tag = cpu_to_le16(params->rpi); + + xmit->dw7flags0 &= ~SLI4_SEQ_WQE_DIF; + xmit->dw7flags0 |= + SLI4_GENERIC_CONTEXT_RPI << SLI4_SEQ_WQE_CT_SHIFT; + xmit->dw7flags0 &= ~SLI4_SEQ_WQE_BS; + + xmit->command = SLI4_WQE_XMIT_SEQUENCE64; + xmit->dw7flags1 |= SLI4_GENERIC_CLASS_CLASS_3; + xmit->dw7flags1 &= ~SLI4_SEQ_WQE_PU; + xmit->timer = params->timeout; + + xmit->abort_tag = 0; + xmit->request_tag = cpu_to_le16(params->tag); + xmit->remote_xid = cpu_to_le16(params->ox_id); + + xmit->dw10w0 |= + cpu_to_le16(SLI4_ELS_REQUEST64_DIR_READ << SLI4_SEQ_WQE_IOD_SHIFT); + + xmit->cmd_type_wqec_byte |= SLI4_CMD_XMIT_SEQUENCE64_WQE; + + xmit->dw10w0 |= cpu_to_le16(2 << SLI4_SEQ_WQE_LEN_LOC_SHIFT); + + xmit->cq_id = cpu_to_le16(0xFFFF); + + return EFC_SUCCESS; +} + +int +sli_requeue_xri_wqe(struct sli4 *sli4, void *buf, u16 xri, u16 tag, u16 cq_id) +{ + struct sli4_requeue_xri_wqe *requeue = buf; + + memset(buf, 0, sli4->wqe_size); + + requeue->command = SLI4_WQE_REQUEUE_XRI; + requeue->xri_tag = cpu_to_le16(xri); + requeue->request_tag = cpu_to_le16(tag); + requeue->flags2 |= cpu_to_le16(SLI4_REQU_XRI_WQE_XC); + requeue->flags1 |= cpu_to_le16(SLI4_REQU_XRI_WQE_QOSD); + requeue->cq_id = cpu_to_le16(cq_id); + requeue->cmd_type_wqec_byte = SLI4_CMD_REQUEUE_XRI_WQE; + return EFC_SUCCESS; +} + +int +sli_fc_process_link_attention(struct sli4 *sli4, void *acqe) +{ + struct sli4_link_attention *link_attn = acqe; + struct sli4_link_event event = { 0 }; + + efc_log_info(sli4, "link=%d attn_type=%#x top=%#x speed=%#x pfault=%#x\n", + link_attn->link_number, link_attn->attn_type, + link_attn->topology, link_attn->port_speed, + link_attn->port_fault); + efc_log_info(sli4, "shared_lnk_status=%#x logl_lnk_speed=%#x evttag=%#x\n", + link_attn->shared_link_status, + le16_to_cpu(link_attn->logical_link_speed), + le32_to_cpu(link_attn->event_tag)); + + if (!sli4->link) + return EFC_FAIL; + + event.medium = SLI4_LINK_MEDIUM_FC; + + switch (link_attn->attn_type) { + case SLI4_LNK_ATTN_TYPE_LINK_UP: + event.status = SLI4_LINK_STATUS_UP; + break; + case SLI4_LNK_ATTN_TYPE_LINK_DOWN: + event.status = SLI4_LINK_STATUS_DOWN; + break; + case SLI4_LNK_ATTN_TYPE_NO_HARD_ALPA: + efc_log_info(sli4, "attn_type: no hard alpa\n"); + event.status = SLI4_LINK_STATUS_NO_ALPA; + break; + default: + efc_log_info(sli4, "attn_type: unknown\n"); + break; + } + + switch (link_attn->event_type) { + case SLI4_EVENT_LINK_ATTENTION: + break; + case SLI4_EVENT_SHARED_LINK_ATTENTION: + efc_log_info(sli4, "event_type: FC shared link event\n"); + break; + default: + efc_log_info(sli4, "event_type: unknown\n"); + break; + } + + switch (link_attn->topology) { + case SLI4_LNK_ATTN_P2P: + event.topology = SLI4_LINK_TOPO_NPORT; + break; + case SLI4_LNK_ATTN_FC_AL: + event.topology = SLI4_LINK_TOPO_LOOP; + break; + case SLI4_LNK_ATTN_INTERNAL_LOOPBACK: + efc_log_info(sli4, "topology Internal loopback\n"); + event.topology = SLI4_LINK_TOPO_LOOPBACK_INTERNAL; + break; + case SLI4_LNK_ATTN_SERDES_LOOPBACK: + efc_log_info(sli4, "topology serdes loopback\n"); + event.topology = SLI4_LINK_TOPO_LOOPBACK_EXTERNAL; + break; + default: + efc_log_info(sli4, "topology: unknown\n"); + break; + } + + event.speed = link_attn->port_speed * 1000; + + sli4->link(sli4->link_arg, (void *)&event); + + return EFC_SUCCESS; +} + +int +sli_fc_cqe_parse(struct sli4 *sli4, struct sli4_queue *cq, + u8 *cqe, enum sli4_qentry *etype, u16 *r_id) +{ + u8 code = cqe[SLI4_CQE_CODE_OFFSET]; + int rc; + + switch (code) { + case SLI4_CQE_CODE_WORK_REQUEST_COMPLETION: + { + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + *etype = SLI4_QENTRY_WQ; + *r_id = le16_to_cpu(wcqe->request_tag); + rc = wcqe->status; + + /* Flag errors except for FCP_RSP_FAILURE */ + if (rc && rc != SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE) { + efc_log_info(sli4, "WCQE: status=%#x hw_status=%#x tag=%#x\n", + wcqe->status, wcqe->hw_status, + le16_to_cpu(wcqe->request_tag)); + efc_log_info(sli4, "w1=%#x w2=%#x xb=%d\n", + le32_to_cpu(wcqe->wqe_specific_1), + le32_to_cpu(wcqe->wqe_specific_2), + (wcqe->flags & SLI4_WCQE_XB)); + efc_log_info(sli4, " %08X %08X %08X %08X\n", + ((u32 *)cqe)[0], + ((u32 *)cqe)[1], + ((u32 *)cqe)[2], + ((u32 *)cqe)[3]); + } + + break; + } + case SLI4_CQE_CODE_RQ_ASYNC: + { + struct sli4_fc_async_rcqe *rcqe = (void *)cqe; + + *etype = SLI4_QENTRY_RQ; + *r_id = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID; + rc = rcqe->status; + break; + } + case SLI4_CQE_CODE_RQ_ASYNC_V1: + { + struct sli4_fc_async_rcqe_v1 *rcqe = (void *)cqe; + + *etype = SLI4_QENTRY_RQ; + *r_id = le16_to_cpu(rcqe->rq_id); + rc = rcqe->status; + break; + } + case SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD: + { + struct sli4_fc_optimized_write_cmd_cqe *optcqe = (void *)cqe; + + *etype = SLI4_QENTRY_OPT_WRITE_CMD; + *r_id = le16_to_cpu(optcqe->rq_id); + rc = optcqe->status; + break; + } + case SLI4_CQE_CODE_OPTIMIZED_WRITE_DATA: + { + struct sli4_fc_optimized_write_data_cqe *dcqe = (void *)cqe; + + *etype = SLI4_QENTRY_OPT_WRITE_DATA; + *r_id = le16_to_cpu(dcqe->xri); + rc = dcqe->status; + + /* Flag errors */ + if (rc != SLI4_FC_WCQE_STATUS_SUCCESS) { + efc_log_info(sli4, "Optimized DATA CQE: status=%#x\n", + dcqe->status); + efc_log_info(sli4, "hstat=%#x xri=%#x dpl=%#x w3=%#x xb=%d\n", + dcqe->hw_status, le16_to_cpu(dcqe->xri), + le32_to_cpu(dcqe->total_data_placed), + ((u32 *)cqe)[3], + (dcqe->flags & SLI4_OCQE_XB)); + } + break; + } + case SLI4_CQE_CODE_RQ_COALESCING: + { + struct sli4_fc_coalescing_rcqe *rcqe = (void *)cqe; + + *etype = SLI4_QENTRY_RQ; + *r_id = le16_to_cpu(rcqe->rq_id); + rc = rcqe->status; + break; + } + case SLI4_CQE_CODE_XRI_ABORTED: + { + struct sli4_fc_xri_aborted_cqe *xa = (void *)cqe; + + *etype = SLI4_QENTRY_XABT; + *r_id = le16_to_cpu(xa->xri); + rc = EFC_SUCCESS; + break; + } + case SLI4_CQE_CODE_RELEASE_WQE: { + struct sli4_fc_wqec *wqec = (void *)cqe; + + *etype = SLI4_QENTRY_WQ_RELEASE; + *r_id = le16_to_cpu(wqec->wq_id); + rc = EFC_SUCCESS; + break; + } + default: + efc_log_info(sli4, "CQE completion code %d not handled\n", + code); + *etype = SLI4_QENTRY_MAX; + *r_id = U16_MAX; + rc = -EINVAL; + } + + return rc; +} + +u32 +sli_fc_response_length(struct sli4 *sli4, u8 *cqe) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + return le32_to_cpu(wcqe->wqe_specific_1); +} + +u32 +sli_fc_io_length(struct sli4 *sli4, u8 *cqe) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + return le32_to_cpu(wcqe->wqe_specific_1); +} + +int +sli_fc_els_did(struct sli4 *sli4, u8 *cqe, u32 *d_id) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + + *d_id = 0; + + if (wcqe->status) + return EFC_FAIL; + *d_id = le32_to_cpu(wcqe->wqe_specific_2) & 0x00ffffff; + return EFC_SUCCESS; +} + +u32 +sli_fc_ext_status(struct sli4 *sli4, u8 *cqe) +{ + struct sli4_fc_wcqe *wcqe = (void *)cqe; + u32 mask; + + switch (wcqe->status) { + case SLI4_FC_WCQE_STATUS_FCP_RSP_FAILURE: + mask = U32_MAX; + break; + case SLI4_FC_WCQE_STATUS_LOCAL_REJECT: + case SLI4_FC_WCQE_STATUS_CMD_REJECT: + mask = 0xff; + break; + case SLI4_FC_WCQE_STATUS_NPORT_RJT: + case SLI4_FC_WCQE_STATUS_FABRIC_RJT: + case SLI4_FC_WCQE_STATUS_NPORT_BSY: + case SLI4_FC_WCQE_STATUS_FABRIC_BSY: + case SLI4_FC_WCQE_STATUS_LS_RJT: + mask = U32_MAX; + break; + case SLI4_FC_WCQE_STATUS_DI_ERROR: + mask = U32_MAX; + break; + default: + mask = 0; + } + + return le32_to_cpu(wcqe->wqe_specific_2) & mask; +} + +int +sli_fc_rqe_rqid_and_index(struct sli4 *sli4, u8 *cqe, u16 *rq_id, u32 *index) +{ + int rc = EFC_FAIL; + u8 code = 0; + u16 rq_element_index; + + *rq_id = 0; + *index = U32_MAX; + + code = cqe[SLI4_CQE_CODE_OFFSET]; + + /* Retrieve the RQ index from the completion */ + if (code == SLI4_CQE_CODE_RQ_ASYNC) { + struct sli4_fc_async_rcqe *rcqe = (void *)cqe; + + *rq_id = le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID; + rq_element_index = + le16_to_cpu(rcqe->rq_elmt_indx_word) & SLI4_RACQE_RQ_EL_INDX; + *index = rq_element_index; + if (rcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + rc = EFC_SUCCESS; + } else { + rc = rcqe->status; + efc_log_info(sli4, "status=%02x (%s) rq_id=%d\n", + rcqe->status, + sli_fc_get_status_string(rcqe->status), + le16_to_cpu(rcqe->fcfi_rq_id_word) & + SLI4_RACQE_RQ_ID); + + efc_log_info(sli4, "pdpl=%x sof=%02x eof=%02x hdpl=%x\n", + le16_to_cpu(rcqe->data_placement_length), + rcqe->sof_byte, rcqe->eof_byte, + rcqe->hdpl_byte & SLI4_RACQE_HDPL); + } + } else if (code == SLI4_CQE_CODE_RQ_ASYNC_V1) { + struct sli4_fc_async_rcqe_v1 *rcqe_v1 = (void *)cqe; + + *rq_id = le16_to_cpu(rcqe_v1->rq_id); + rq_element_index = + (le16_to_cpu(rcqe_v1->rq_elmt_indx_word) & + SLI4_RACQE_RQ_EL_INDX); + *index = rq_element_index; + if (rcqe_v1->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + rc = EFC_SUCCESS; + } else { + rc = rcqe_v1->status; + efc_log_info(sli4, "status=%02x (%s) rq_id=%d, index=%x\n", + rcqe_v1->status, + sli_fc_get_status_string(rcqe_v1->status), + le16_to_cpu(rcqe_v1->rq_id), rq_element_index); + + efc_log_info(sli4, "pdpl=%x sof=%02x eof=%02x hdpl=%x\n", + le16_to_cpu(rcqe_v1->data_placement_length), + rcqe_v1->sof_byte, rcqe_v1->eof_byte, + rcqe_v1->hdpl_byte & SLI4_RACQE_HDPL); + } + } else if (code == SLI4_CQE_CODE_OPTIMIZED_WRITE_CMD) { + struct sli4_fc_optimized_write_cmd_cqe *optcqe = (void *)cqe; + + *rq_id = le16_to_cpu(optcqe->rq_id); + *index = le16_to_cpu(optcqe->w1) & SLI4_OCQE_RQ_EL_INDX; + if (optcqe->status == SLI4_FC_ASYNC_RQ_SUCCESS) { + rc = EFC_SUCCESS; + } else { + rc = optcqe->status; + efc_log_info(sli4, "stat=%02x (%s) rqid=%d, idx=%x pdpl=%x\n", + optcqe->status, + sli_fc_get_status_string(optcqe->status), + le16_to_cpu(optcqe->rq_id), *index, + le16_to_cpu(optcqe->data_placement_length)); + + efc_log_info(sli4, "hdpl=%x oox=%d agxr=%d xri=0x%x rpi=%x\n", + (optcqe->hdpl_vld & SLI4_OCQE_HDPL), + (optcqe->flags1 & SLI4_OCQE_OOX), + (optcqe->flags1 & SLI4_OCQE_AGXR), optcqe->xri, + le16_to_cpu(optcqe->rpi)); + } + } else if (code == SLI4_CQE_CODE_RQ_COALESCING) { + struct sli4_fc_coalescing_rcqe *rcqe = (void *)cqe; + + rq_element_index = (le16_to_cpu(rcqe->rq_elmt_indx_word) & + SLI4_RCQE_RQ_EL_INDX); + + *rq_id = le16_to_cpu(rcqe->rq_id); + if (rcqe->status == SLI4_FC_COALESCE_RQ_SUCCESS) { + *index = rq_element_index; + rc = EFC_SUCCESS; + } else { + *index = U32_MAX; + rc = rcqe->status; + + efc_log_info(sli4, "stat=%02x (%s) rq_id=%d, idx=%x\n", + rcqe->status, + sli_fc_get_status_string(rcqe->status), + le16_to_cpu(rcqe->rq_id), rq_element_index); + efc_log_info(sli4, "rq_id=%#x sdpl=%x\n", + le16_to_cpu(rcqe->rq_id), + le16_to_cpu(rcqe->sequence_reporting_placement_length)); + } + } else { + struct sli4_fc_async_rcqe *rcqe = (void *)cqe; + + *index = U32_MAX; + rc = rcqe->status; + + efc_log_info(sli4, "status=%02x rq_id=%d, index=%x pdpl=%x\n", + rcqe->status, + le16_to_cpu(rcqe->fcfi_rq_id_word) & SLI4_RACQE_RQ_ID, + (le16_to_cpu(rcqe->rq_elmt_indx_word) & SLI4_RACQE_RQ_EL_INDX), + le16_to_cpu(rcqe->data_placement_length)); + efc_log_info(sli4, "sof=%02x eof=%02x hdpl=%x\n", + rcqe->sof_byte, rcqe->eof_byte, + rcqe->hdpl_byte & SLI4_RACQE_HDPL); + } + + return rc; +} From patchwork Mon Oct 12 22:51:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269827 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2E68AC43457 for ; Mon, 12 Oct 2020 22:52:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D63672087D for ; Mon, 12 Oct 2020 22:52:10 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="dQPvCPX/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390578AbgJLWwJ (ORCPT ); Mon, 12 Oct 2020 18:52:09 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60536 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390541AbgJLWwG (ORCPT ); Mon, 12 Oct 2020 18:52:06 -0400 Received: from mail-pf1-x436.google.com (mail-pf1-x436.google.com [IPv6:2607:f8b0:4864:20::436]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F042DC0613D2 for ; Mon, 12 Oct 2020 15:52:05 -0700 (PDT) Received: by mail-pf1-x436.google.com with SMTP id y14so15095268pfp.13 for ; Mon, 12 Oct 2020 15:52:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=/KOhNM2Quz/WyKQV8MJfRStn9E5L7/m2NtQXogjQYdA=; b=dQPvCPX/xDX0KduSj+4H0JlW2SAoBEfxlddFZMPzL9iSHaJwNqwCVbWCTgRL/Hozvr 4AKkK3t5HThHMYJoFCkMvs/oj5Kzx4+W7p+ls7/SMAwaEgUGv8K1V7E+iyhWt7WBIhXl 8+hDzDDymO2im5CkezHDq+pPgP3OjsXlDDGsE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=/KOhNM2Quz/WyKQV8MJfRStn9E5L7/m2NtQXogjQYdA=; b=Aq3HXTh/LQQxtI3wdpfsmF19jJNQWjVVitQ+Lx3CKPpe7xV2kSdj7C/Jw/exCjFUeM LgcOJBnjWm4Yap/0DsZRtb2Cc7/N1M18i6YSxI2p6TByJ69MLQHIExIi31NBAzo+cW37 j9/fQcm4jI1QN6RgUdCNlvkijbT9bQIOoYuJ489Ixt6PEXfNnZNRWphsqWLMe8APScdb br+dYjmsJwaTXqD9RBG9g8+CpvQKIMH43vBRDcytitGu5BET/q+azywnZYMXfV7ZYXyD L3qqu4a63/zKtHeQH8+ba+7OnLzlHV54H2klS1w7B+vE1tTkipm1TRJw+rP3/B8da2JE FqlA== X-Gm-Message-State: AOAM533oeVM2j4mo9nO/J5F+BT65mMblDEdx/MjfcNo7hsInXzg8To2d pHp15ATLgdq9bSLo0kEoQU4S3vulD8wQE4pjHvw4LwcZc2B4mEiCtsXcrONKZQ6BrwbDekZos+B wxbsjaCkC6j5mWwwkRU1Kx+RpoYsPBDzswRNCEZipLgLwPi1Ai9lYz4GKHdOMwaqBbQYmd3kGlG CaEV0= X-Google-Smtp-Source: ABdhPJzRBMksl7VB6sX7Dxq19jY27e+D5FqPc5yIbZmBnpAP07GcfX7GkLE+oRW/RaQptvDh+8hMfg== X-Received: by 2002:a17:90b:19cc:: with SMTP id nm12mr11750271pjb.203.1602543124377; Mon, 12 Oct 2020 15:52:04 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:03 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 06/31] elx: libefc_sli: bmbx routines and SLI config commands Date: Mon, 12 Oct 2020 15:51:22 -0700 Message-Id: <20201012225147.54404-7-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc_sli SLI-4 library population. This patch adds routines to create mailbox commands used during adapter initialization and adds APIs to issue mailbox commands to the adapter through the bootstrap mailbox register. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Reviewed-by: Daniel Wagner --- v4: Changed bmbx_wait and fw_ready routines to use usleep_range and time_before apis Removed size input for all the cmds as its fixed to SLI4_BMBX_SIZE. --- drivers/scsi/elx/libefc_sli/sli4.c | 1167 ++++++++++++++++++++++++++++ 1 file changed, 1167 insertions(+) diff --git a/drivers/scsi/elx/libefc_sli/sli4.c b/drivers/scsi/elx/libefc_sli/sli4.c index a9126f3a2343..f907434d9117 100644 --- a/drivers/scsi/elx/libefc_sli/sli4.c +++ b/drivers/scsi/elx/libefc_sli/sli4.c @@ -2881,3 +2881,1170 @@ sli_fc_rqe_rqid_and_index(struct sli4 *sli4, u8 *cqe, u16 *rq_id, u32 *index) return rc; } + +static int +sli_bmbx_wait(struct sli4 *sli4, u32 msec) +{ + u32 val; + unsigned long end; + + /* Wait for the bootstrap mailbox to report "ready" */ + end = jiffies + msecs_to_jiffies(msec); + do { + val = readl(sli4->reg[0] + SLI4_BMBX_REG); + if (val & SLI4_BMBX_RDY) + return EFC_SUCCESS; + + usleep_range(1000, 2000); + } while (time_before(jiffies, end)); + + return EFC_FAIL; +} + +static int +sli_bmbx_write(struct sli4 *sli4) +{ + u32 val; + + /* write buffer location to bootstrap mailbox register */ + val = SLI4_BMBX_WRITE_HI(sli4->bmbx.phys); + writel(val, (sli4->reg[0] + SLI4_BMBX_REG)); + + if (sli_bmbx_wait(sli4, SLI4_BMBX_DELAY_US)) { + efc_log_crit(sli4, "BMBX WRITE_HI failed\n"); + return EFC_FAIL; + } + val = SLI4_BMBX_WRITE_LO(sli4->bmbx.phys); + writel(val, (sli4->reg[0] + SLI4_BMBX_REG)); + + /* wait for SLI Port to set ready bit */ + return sli_bmbx_wait(sli4, SLI4_BMBX_TIMEOUT_MSEC); +} + +int +sli_bmbx_command(struct sli4 *sli4) +{ + void *cqe = (u8 *)sli4->bmbx.virt + SLI4_BMBX_SIZE; + + if (sli_fw_error_status(sli4) > 0) { + efc_log_crit(sli4, "Chip is in an error state -Mailbox command rejected"); + efc_log_crit(sli4, " status=%#x error1=%#x error2=%#x\n", + sli_reg_read_status(sli4), + sli_reg_read_err1(sli4), + sli_reg_read_err2(sli4)); + return EFC_FAIL; + } + + /* Submit a command to the bootstrap mailbox and check the status */ + if (sli_bmbx_write(sli4)) { + efc_log_crit(sli4, "bootstrap mailbox write fail phys=%p reg=%#x\n", + (void *)sli4->bmbx.phys, + readl(sli4->reg[0] + SLI4_BMBX_REG)); + return EFC_FAIL; + } + + /* check completion queue entry status */ + if (le32_to_cpu(((struct sli4_mcqe *)cqe)->dw3_flags) & + SLI4_MCQE_VALID) { + return sli_cqe_mq(sli4, cqe); + } + efc_log_crit(sli4, "invalid or wrong type\n"); + return EFC_FAIL; +} + +int +sli_cmd_config_link(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_config_link *config_link = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + config_link->hdr.command = SLI4_MBX_CMD_CONFIG_LINK; + + /* Port interprets zero in a field as "use default value" */ + + return EFC_SUCCESS; +} + +int +sli_cmd_down_link(struct sli4 *sli4, void *buf) +{ + struct sli4_mbox_command_header *hdr = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + hdr->command = SLI4_MBX_CMD_DOWN_LINK; + + /* Port interprets zero in a field as "use default value" */ + + return EFC_SUCCESS; +} + +int +sli_cmd_dump_type4(struct sli4 *sli4, void *buf, u16 wki) +{ + struct sli4_cmd_dump4 *cmd = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + cmd->hdr.command = SLI4_MBX_CMD_DUMP; + cmd->type_dword = cpu_to_le32(0x4); + cmd->wki_selection = cpu_to_le16(wki); + return EFC_SUCCESS; +} + +int +sli_cmd_common_read_transceiver_data(struct sli4 *sli4, void *buf, u32 page_num, + struct efc_dma *dma) +{ + struct sli4_rqst_cmn_read_transceiver_data *req = NULL; + u32 psize; + + if (!dma) + psize = SLI4_CFG_PYLD_LENGTH(cmn_read_transceiver_data); + else + psize = dma->size; + + req = sli_config_cmd_init(sli4, buf, psize, dma); + if (!req) + return EFC_FAIL; + + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_READ_TRANS_DATA, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_read_transceiver_data)); + + req->page_number = cpu_to_le32(page_num); + req->port = cpu_to_le32(sli4->port_number); + + return EFC_SUCCESS; +} + +int +sli_cmd_read_link_stats(struct sli4 *sli4, void *buf, u8 req_ext_counters, + u8 clear_overflow_flags, + u8 clear_all_counters) +{ + struct sli4_cmd_read_link_stats *cmd = buf; + u32 flags; + + memset(buf, 0, SLI4_BMBX_SIZE); + + cmd->hdr.command = SLI4_MBX_CMD_READ_LNK_STAT; + + flags = 0; + if (req_ext_counters) + flags |= SLI4_READ_LNKSTAT_REC; + if (clear_all_counters) + flags |= SLI4_READ_LNKSTAT_CLRC; + if (clear_overflow_flags) + flags |= SLI4_READ_LNKSTAT_CLOF; + + cmd->dw1_flags = cpu_to_le32(flags); + return EFC_SUCCESS; +} + +int +sli_cmd_read_status(struct sli4 *sli4, void *buf, u8 clear_counters) +{ + struct sli4_cmd_read_status *cmd = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + cmd->hdr.command = SLI4_MBX_CMD_READ_STATUS; + if (clear_counters) + flags |= SLI4_READSTATUS_CLEAR_COUNTERS; + else + flags &= ~SLI4_READSTATUS_CLEAR_COUNTERS; + + cmd->dw1_flags = cpu_to_le32(flags); + return EFC_SUCCESS; +} + +int +sli_cmd_init_link(struct sli4 *sli4, void *buf, u32 speed, u8 reset_alpa) +{ + struct sli4_cmd_init_link *init_link = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + init_link->hdr.command = SLI4_MBX_CMD_INIT_LINK; + + init_link->sel_reset_al_pa_dword = + cpu_to_le32(reset_alpa); + flags &= ~SLI4_INIT_LINK_F_LOOPBACK; + + init_link->link_speed_sel_code = cpu_to_le32(speed); + switch (speed) { + case SLI4_LINK_SPEED_1G: + case SLI4_LINK_SPEED_2G: + case SLI4_LINK_SPEED_4G: + case SLI4_LINK_SPEED_8G: + case SLI4_LINK_SPEED_16G: + case SLI4_LINK_SPEED_32G: + case SLI4_LINK_SPEED_64G: + flags |= SLI4_INIT_LINK_F_FIXED_SPEED; + break; + case SLI4_LINK_SPEED_10G: + efc_log_info(sli4, "unsupported FC speed %d\n", speed); + init_link->flags0 = cpu_to_le32(flags); + return EFC_FAIL; + } + + switch (sli4->topology) { + case SLI4_READ_CFG_TOPO_FC: + /* Attempt P2P but failover to FC-AL */ + flags |= SLI4_INIT_LINK_F_FAIL_OVER; + flags |= SLI4_INIT_LINK_F_P2P_FAIL_OVER; + break; + case SLI4_READ_CFG_TOPO_FC_AL: + flags |= SLI4_INIT_LINK_F_FCAL_ONLY; + if (speed == SLI4_LINK_SPEED_16G || + speed == SLI4_LINK_SPEED_32G) { + efc_log_info(sli4, "unsupported FC-AL speed %d\n", + speed); + init_link->flags0 = cpu_to_le32(flags); + return EFC_FAIL; + } + break; + case SLI4_READ_CFG_TOPO_FC_DA: + flags |= SLI4_INIT_LINK_F_P2P_ONLY; + break; + default: + + efc_log_info(sli4, "unsupported topology %#x\n", + sli4->topology); + + init_link->flags0 = cpu_to_le32(flags); + return EFC_FAIL; + } + + flags &= ~SLI4_INIT_LINK_F_UNFAIR; + flags &= ~SLI4_INIT_LINK_F_NO_LIRP; + flags &= ~SLI4_INIT_LINK_F_LOOP_VALID_CHK; + flags &= ~SLI4_INIT_LINK_F_NO_LISA; + flags &= ~SLI4_INIT_LINK_F_PICK_HI_ALPA; + init_link->flags0 = cpu_to_le32(flags); + + return EFC_SUCCESS; +} + +int +sli_cmd_init_vfi(struct sli4 *sli4, void *buf, u16 vfi, u16 fcfi, u16 vpi) +{ + struct sli4_cmd_init_vfi *init_vfi = buf; + u16 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + init_vfi->hdr.command = SLI4_MBX_CMD_INIT_VFI; + init_vfi->vfi = cpu_to_le16(vfi); + init_vfi->fcfi = cpu_to_le16(fcfi); + + /* + * If the VPI is valid, initialize it at the same time as + * the VFI + */ + if (vpi != U16_MAX) { + flags |= SLI4_INIT_VFI_FLAG_VP; + init_vfi->flags0_word = cpu_to_le16(flags); + init_vfi->vpi = cpu_to_le16(vpi); + } + + return EFC_SUCCESS; +} + +int +sli_cmd_init_vpi(struct sli4 *sli4, void *buf, u16 vpi, u16 vfi) +{ + struct sli4_cmd_init_vpi *init_vpi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + init_vpi->hdr.command = SLI4_MBX_CMD_INIT_VPI; + init_vpi->vpi = cpu_to_le16(vpi); + init_vpi->vfi = cpu_to_le16(vfi); + + return EFC_SUCCESS; +} + +int +sli_cmd_post_xri(struct sli4 *sli4, void *buf, u16 xri_base, u16 xri_count) +{ + struct sli4_cmd_post_xri *post_xri = buf; + u16 xri_count_flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + post_xri->hdr.command = SLI4_MBX_CMD_POST_XRI; + post_xri->xri_base = cpu_to_le16(xri_base); + xri_count_flags = xri_count & SLI4_POST_XRI_COUNT; + xri_count_flags |= SLI4_POST_XRI_FLAG_ENX; + xri_count_flags |= SLI4_POST_XRI_FLAG_VAL; + post_xri->xri_count_flags = cpu_to_le16(xri_count_flags); + + return EFC_SUCCESS; +} + +int +sli_cmd_release_xri(struct sli4 *sli4, void *buf, u8 num_xri) +{ + struct sli4_cmd_release_xri *release_xri = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + release_xri->hdr.command = SLI4_MBX_CMD_RELEASE_XRI; + release_xri->xri_count_word = cpu_to_le16(num_xri & + SLI4_RELEASE_XRI_COUNT); + + return EFC_SUCCESS; +} + +static int +sli_cmd_read_config(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_read_config *read_config = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_config->hdr.command = SLI4_MBX_CMD_READ_CONFIG; + + return EFC_SUCCESS; +} + +int +sli_cmd_read_nvparms(struct sli4 *sli4, void *buf) +{ + struct sli4_cmd_read_nvparms *read_nvparms = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_nvparms->hdr.command = SLI4_MBX_CMD_READ_NVPARMS; + + return EFC_SUCCESS; +} + +int +sli_cmd_write_nvparms(struct sli4 *sli4, void *buf, u8 *wwpn, u8 *wwnn, + u8 hard_alpa, u32 preferred_d_id) +{ + struct sli4_cmd_write_nvparms *write_nvparms = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + write_nvparms->hdr.command = SLI4_MBX_CMD_WRITE_NVPARMS; + memcpy(write_nvparms->wwpn, wwpn, 8); + memcpy(write_nvparms->wwnn, wwnn, 8); + + write_nvparms->hard_alpa_d_id = + cpu_to_le32((preferred_d_id << 8) | hard_alpa); + return EFC_SUCCESS; +} + +static int +sli_cmd_read_rev(struct sli4 *sli4, void *buf, struct efc_dma *vpd) +{ + struct sli4_cmd_read_rev *read_rev = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_rev->hdr.command = SLI4_MBX_CMD_READ_REV; + + if (vpd && vpd->size) { + read_rev->flags0_word |= cpu_to_le16(SLI4_READ_REV_FLAG_VPD); + + read_rev->available_length_dword = + cpu_to_le32(vpd->size & + SLI4_READ_REV_AVAILABLE_LENGTH); + + read_rev->hostbuf.low = + cpu_to_le32(lower_32_bits(vpd->phys)); + read_rev->hostbuf.high = + cpu_to_le32(upper_32_bits(vpd->phys)); + } + + return EFC_SUCCESS; +} + +int +sli_cmd_read_sparm64(struct sli4 *sli4, void *buf, struct efc_dma *dma, u16 vpi) +{ + struct sli4_cmd_read_sparm64 *read_sparm64 = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + if (vpi == U16_MAX) { + efc_log_err(sli4, "special VPI not supported!!!\n"); + return EFC_FAIL; + } + + if (!dma || !dma->phys) { + efc_log_err(sli4, "bad DMA buffer\n"); + return EFC_FAIL; + } + + read_sparm64->hdr.command = SLI4_MBX_CMD_READ_SPARM64; + + read_sparm64->bde_64.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (dma->size & SLI4_BDE_LEN_MASK)); + read_sparm64->bde_64.u.data.low = + cpu_to_le32(lower_32_bits(dma->phys)); + read_sparm64->bde_64.u.data.high = + cpu_to_le32(upper_32_bits(dma->phys)); + + read_sparm64->vpi = cpu_to_le16(vpi); + + return EFC_SUCCESS; +} + +int +sli_cmd_read_topology(struct sli4 *sli4, void *buf, struct efc_dma *dma) +{ + struct sli4_cmd_read_topology *read_topo = buf; + + if (!dma || !dma->size) + return EFC_FAIL; + + if (dma->size < SLI4_MIN_LOOP_MAP_BYTES) { + efc_log_err(sli4, "loop map buffer too small %zx\n", dma->size); + return EFC_FAIL; + } + + memset(buf, 0, SLI4_BMBX_SIZE); + + read_topo->hdr.command = SLI4_MBX_CMD_READ_TOPOLOGY; + + memset(dma->virt, 0, dma->size); + + read_topo->bde_loop_map.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (dma->size & SLI4_BDE_LEN_MASK)); + read_topo->bde_loop_map.u.data.low = + cpu_to_le32(lower_32_bits(dma->phys)); + read_topo->bde_loop_map.u.data.high = + cpu_to_le32(upper_32_bits(dma->phys)); + + return EFC_SUCCESS; +} + +int +sli_cmd_reg_fcfi(struct sli4 *sli4, void *buf, u16 index, + struct sli4_cmd_rq_cfg rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG]) +{ + struct sli4_cmd_reg_fcfi *reg_fcfi = buf; + u32 i; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_fcfi->hdr.command = SLI4_MBX_CMD_REG_FCFI; + + reg_fcfi->fcf_index = cpu_to_le16(index); + + for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { + switch (i) { + case 0: + reg_fcfi->rqid0 = rq_cfg[0].rq_id; + break; + case 1: + reg_fcfi->rqid1 = rq_cfg[1].rq_id; + break; + case 2: + reg_fcfi->rqid2 = rq_cfg[2].rq_id; + break; + case 3: + reg_fcfi->rqid3 = rq_cfg[3].rq_id; + break; + } + reg_fcfi->rq_cfg[i].r_ctl_mask = rq_cfg[i].r_ctl_mask; + reg_fcfi->rq_cfg[i].r_ctl_match = rq_cfg[i].r_ctl_match; + reg_fcfi->rq_cfg[i].type_mask = rq_cfg[i].type_mask; + reg_fcfi->rq_cfg[i].type_match = rq_cfg[i].type_match; + } + + return EFC_SUCCESS; +} + +int +sli_cmd_reg_fcfi_mrq(struct sli4 *sli4, void *buf, u8 mode, u16 fcf_index, + u8 rq_selection_policy, u8 mrq_bit_mask, u16 num_mrqs, + struct sli4_cmd_rq_cfg rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG]) +{ + struct sli4_cmd_reg_fcfi_mrq *reg_fcfi_mrq = buf; + u32 i; + u32 mrq_flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_fcfi_mrq->hdr.command = SLI4_MBX_CMD_REG_FCFI_MRQ; + if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) { + reg_fcfi_mrq->fcf_index = cpu_to_le16(fcf_index); + goto done; + } + + reg_fcfi_mrq->dw8_vlan = cpu_to_le32(SLI4_REGFCFI_MRQ_MODE); + + for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { + reg_fcfi_mrq->rq_cfg[i].r_ctl_mask = rq_cfg[i].r_ctl_mask; + reg_fcfi_mrq->rq_cfg[i].r_ctl_match = rq_cfg[i].r_ctl_match; + reg_fcfi_mrq->rq_cfg[i].type_mask = rq_cfg[i].type_mask; + reg_fcfi_mrq->rq_cfg[i].type_match = rq_cfg[i].type_match; + + switch (i) { + case 3: + reg_fcfi_mrq->rqid3 = rq_cfg[i].rq_id; + break; + case 2: + reg_fcfi_mrq->rqid2 = rq_cfg[i].rq_id; + break; + case 1: + reg_fcfi_mrq->rqid1 = rq_cfg[i].rq_id; + break; + case 0: + reg_fcfi_mrq->rqid0 = rq_cfg[i].rq_id; + break; + } + } + + mrq_flags = num_mrqs & SLI4_REGFCFI_MRQ_MASK_NUM_PAIRS; + mrq_flags |= (mrq_bit_mask << 8); + mrq_flags |= (rq_selection_policy << 12); + reg_fcfi_mrq->dw9_mrqflags = cpu_to_le32(mrq_flags); +done: + return EFC_SUCCESS; +} + +int +sli_cmd_reg_rpi(struct sli4 *sli4, void *buf, u32 rpi, u32 vpi, u32 fc_id, + struct efc_dma *dma, u8 update, u8 enable_t10_pi) +{ + struct sli4_cmd_reg_rpi *reg_rpi = buf; + u32 rportid_flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_rpi->hdr.command = SLI4_MBX_CMD_REG_RPI; + + reg_rpi->rpi = cpu_to_le16(rpi); + + rportid_flags = fc_id & SLI4_REGRPI_REMOTE_N_PORTID; + + if (update) + rportid_flags |= SLI4_REGRPI_UPD; + else + rportid_flags &= ~SLI4_REGRPI_UPD; + + if (enable_t10_pi) + rportid_flags |= SLI4_REGRPI_ETOW; + else + rportid_flags &= ~SLI4_REGRPI_ETOW; + + reg_rpi->dw2_rportid_flags = cpu_to_le32(rportid_flags); + + reg_rpi->bde_64.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (SLI4_REG_RPI_BUF_LEN & SLI4_BDE_LEN_MASK)); + reg_rpi->bde_64.u.data.low = + cpu_to_le32(lower_32_bits(dma->phys)); + reg_rpi->bde_64.u.data.high = + cpu_to_le32(upper_32_bits(dma->phys)); + + reg_rpi->vpi = cpu_to_le16(vpi); + + return EFC_SUCCESS; +} + +int +sli_cmd_reg_vfi(struct sli4 *sli4, void *buf, size_t size, + u16 vfi, u16 fcfi, struct efc_dma dma, + u16 vpi, __be64 sli_wwpn, u32 fc_id) +{ + struct sli4_cmd_reg_vfi *reg_vfi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_vfi->hdr.command = SLI4_MBX_CMD_REG_VFI; + + reg_vfi->vfi = cpu_to_le16(vfi); + + reg_vfi->fcfi = cpu_to_le16(fcfi); + + reg_vfi->sparm.bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (SLI4_REG_RPI_BUF_LEN & SLI4_BDE_LEN_MASK)); + reg_vfi->sparm.u.data.low = + cpu_to_le32(lower_32_bits(dma.phys)); + reg_vfi->sparm.u.data.high = + cpu_to_le32(upper_32_bits(dma.phys)); + + reg_vfi->e_d_tov = cpu_to_le32(sli4->e_d_tov); + reg_vfi->r_a_tov = cpu_to_le32(sli4->r_a_tov); + + reg_vfi->dw0w1_flags |= cpu_to_le16(SLI4_REGVFI_VP); + reg_vfi->vpi = cpu_to_le16(vpi); + memcpy(reg_vfi->wwpn, &sli_wwpn, sizeof(reg_vfi->wwpn)); + reg_vfi->dw10_lportid_flags = cpu_to_le32(fc_id); + + return EFC_SUCCESS; +} + +int +sli_cmd_reg_vpi(struct sli4 *sli4, void *buf, u32 fc_id, __be64 sli_wwpn, + u16 vpi, u16 vfi, bool update) +{ + struct sli4_cmd_reg_vpi *reg_vpi = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + reg_vpi->hdr.command = SLI4_MBX_CMD_REG_VPI; + + flags = (fc_id & SLI4_REGVPI_LOCAL_N_PORTID); + if (update) + flags |= SLI4_REGVPI_UPD; + else + flags &= ~SLI4_REGVPI_UPD; + + reg_vpi->dw2_lportid_flags = cpu_to_le32(flags); + memcpy(reg_vpi->wwpn, &sli_wwpn, sizeof(reg_vpi->wwpn)); + reg_vpi->vpi = cpu_to_le16(vpi); + reg_vpi->vfi = cpu_to_le16(vfi); + + return EFC_SUCCESS; +} + +static int +sli_cmd_request_features(struct sli4 *sli4, void *buf, u32 features_mask, + bool query) +{ + struct sli4_cmd_request_features *req_features = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + req_features->hdr.command = SLI4_MBX_CMD_RQST_FEATURES; + + if (query) + req_features->dw1_qry = cpu_to_le32(SLI4_REQFEAT_QRY); + + req_features->cmd = cpu_to_le32(features_mask); + + return EFC_SUCCESS; +} + +int +sli_cmd_unreg_fcfi(struct sli4 *sli4, void *buf, u16 indicator) +{ + struct sli4_cmd_unreg_fcfi *unreg_fcfi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_fcfi->hdr.command = SLI4_MBX_CMD_UNREG_FCFI; + unreg_fcfi->fcfi = cpu_to_le16(indicator); + + return EFC_SUCCESS; +} + +int +sli_cmd_unreg_rpi(struct sli4 *sli4, void *buf, u16 indicator, + enum sli4_resource which, u32 fc_id) +{ + struct sli4_cmd_unreg_rpi *unreg_rpi = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_rpi->hdr.command = SLI4_MBX_CMD_UNREG_RPI; + switch (which) { + case SLI4_RSRC_RPI: + flags |= SLI4_UNREG_RPI_II_RPI; + if (fc_id == U32_MAX) + break; + + flags |= SLI4_UNREG_RPI_DP; + unreg_rpi->dw2_dest_n_portid = + cpu_to_le32(fc_id & SLI4_UNREG_RPI_DEST_N_PORTID_MASK); + break; + case SLI4_RSRC_VPI: + flags |= SLI4_UNREG_RPI_II_VPI; + break; + case SLI4_RSRC_VFI: + flags |= SLI4_UNREG_RPI_II_VFI; + break; + case SLI4_RSRC_FCFI: + flags |= SLI4_UNREG_RPI_II_FCFI; + break; + default: + efc_log_info(sli4, "unknown type %#x\n", which); + return EFC_FAIL; + } + + unreg_rpi->dw1w1_flags = cpu_to_le16(flags); + unreg_rpi->index = cpu_to_le16(indicator); + + return EFC_SUCCESS; +} + +int +sli_cmd_unreg_vfi(struct sli4 *sli4, void *buf, u16 index, u32 which) +{ + struct sli4_cmd_unreg_vfi *unreg_vfi = buf; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_vfi->hdr.command = SLI4_MBX_CMD_UNREG_VFI; + switch (which) { + case SLI4_UNREG_TYPE_DOMAIN: + unreg_vfi->index = cpu_to_le16(index); + break; + case SLI4_UNREG_TYPE_FCF: + unreg_vfi->index = cpu_to_le16(index); + break; + case SLI4_UNREG_TYPE_ALL: + unreg_vfi->index = cpu_to_le16(U32_MAX); + break; + default: + return EFC_FAIL; + } + + if (which != SLI4_UNREG_TYPE_DOMAIN) + unreg_vfi->dw2_flags = cpu_to_le16(SLI4_UNREG_VFI_II_FCFI); + + return EFC_SUCCESS; +} + +int +sli_cmd_unreg_vpi(struct sli4 *sli4, void *buf, u16 indicator, u32 which) +{ + struct sli4_cmd_unreg_vpi *unreg_vpi = buf; + u32 flags = 0; + + memset(buf, 0, SLI4_BMBX_SIZE); + + unreg_vpi->hdr.command = SLI4_MBX_CMD_UNREG_VPI; + unreg_vpi->index = cpu_to_le16(indicator); + switch (which) { + case SLI4_UNREG_TYPE_PORT: + flags |= SLI4_UNREG_VPI_II_VPI; + break; + case SLI4_UNREG_TYPE_DOMAIN: + flags |= SLI4_UNREG_VPI_II_VFI; + break; + case SLI4_UNREG_TYPE_FCF: + flags |= SLI4_UNREG_VPI_II_FCFI; + break; + case SLI4_UNREG_TYPE_ALL: + /* override indicator */ + unreg_vpi->index = cpu_to_le16(U32_MAX); + flags |= SLI4_UNREG_VPI_II_FCFI; + break; + default: + return EFC_FAIL; + } + + unreg_vpi->dw2w0_flags = cpu_to_le16(flags); + return EFC_SUCCESS; +} + +static int +sli_cmd_common_modify_eq_delay(struct sli4 *sli4, void *buf, + struct sli4_queue *q, int num_q, u32 shift, + u32 delay_mult) +{ + struct sli4_rqst_cmn_modify_eq_delay *req = NULL; + int i; + + req = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_modify_eq_delay), NULL); + if (!req) + return EFC_FAIL; + + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_MODIFY_EQ_DELAY, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_modify_eq_delay)); + req->num_eq = cpu_to_le32(num_q); + + for (i = 0; i < num_q; i++) { + req->eq_delay_record[i].eq_id = cpu_to_le32(q[i].id); + req->eq_delay_record[i].phase = cpu_to_le32(shift); + req->eq_delay_record[i].delay_multiplier = + cpu_to_le32(delay_mult); + } + + return EFC_SUCCESS; +} + +void +sli4_cmd_lowlevel_set_watchdog(struct sli4 *sli4, void *buf, + size_t size, u16 timeout) +{ + struct sli4_rqst_lowlevel_set_watchdog *req = NULL; + + req = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(lowlevel_set_watchdog), NULL); + if (!req) + return; + + sli_cmd_fill_hdr(&req->hdr, SLI4_OPC_LOWLEVEL_SET_WATCHDOG, + SLI4_SUBSYSTEM_LOWLEVEL, CMD_V0, + SLI4_RQST_PYLD_LEN(lowlevel_set_watchdog)); + req->watchdog_timeout = cpu_to_le16(timeout); +} + +static int +sli_cmd_common_get_cntl_attributes(struct sli4 *sli4, void *buf, + struct efc_dma *dma) +{ + struct sli4_rqst_hdr *hdr = NULL; + + hdr = sli_config_cmd_init(sli4, buf, SLI4_RQST_CMDSZ(hdr), dma); + if (!hdr) + return EFC_FAIL; + + hdr->opcode = SLI4_CMN_GET_CNTL_ATTRIBUTES; + hdr->subsystem = SLI4_SUBSYSTEM_COMMON; + hdr->request_length = cpu_to_le32(dma->size); + + return EFC_SUCCESS; +} + +static int +sli_cmd_common_get_cntl_addl_attributes(struct sli4 *sli4, void *buf, + struct efc_dma *dma) +{ + struct sli4_rqst_hdr *hdr = NULL; + + hdr = sli_config_cmd_init(sli4, buf, SLI4_RQST_CMDSZ(hdr), dma); + if (!hdr) + return EFC_FAIL; + + hdr->opcode = SLI4_CMN_GET_CNTL_ADDL_ATTRS; + hdr->subsystem = SLI4_SUBSYSTEM_COMMON; + hdr->request_length = cpu_to_le32(dma->size); + + return EFC_SUCCESS; +} + +int +sli_cmd_common_nop(struct sli4 *sli4, void *buf, uint64_t context) +{ + struct sli4_rqst_cmn_nop *nop = NULL; + + nop = sli_config_cmd_init(sli4, buf, SLI4_CFG_PYLD_LENGTH(cmn_nop), + NULL); + if (!nop) + return EFC_FAIL; + + sli_cmd_fill_hdr(&nop->hdr, SLI4_CMN_NOP, SLI4_SUBSYSTEM_COMMON, + CMD_V0, SLI4_RQST_PYLD_LEN(cmn_nop)); + + memcpy(&nop->context, &context, sizeof(context)); + + return EFC_SUCCESS; +} + +int +sli_cmd_common_get_resource_extent_info(struct sli4 *sli4, void *buf, u16 rtype) +{ + struct sli4_rqst_cmn_get_resource_extent_info *ext = NULL; + + ext = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_get_resource_extent_info), NULL); + if (!ext) + return EFC_FAIL; + + sli_cmd_fill_hdr(&ext->hdr, SLI4_CMN_GET_RSC_EXTENT_INFO, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_get_resource_extent_info)); + + ext->resource_type = cpu_to_le16(rtype); + + return EFC_SUCCESS; +} + +int +sli_cmd_common_get_sli4_parameters(struct sli4 *sli4, void *buf) +{ + struct sli4_rqst_hdr *hdr = NULL; + + hdr = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_get_sli4_params), NULL); + if (!hdr) + return EFC_FAIL; + + hdr->opcode = SLI4_CMN_GET_SLI4_PARAMS; + hdr->subsystem = SLI4_SUBSYSTEM_COMMON; + hdr->request_length = SLI4_RQST_PYLD_LEN(cmn_get_sli4_params); + + return EFC_SUCCESS; +} + +static int +sli_cmd_common_get_port_name(struct sli4 *sli4, void *buf) +{ + struct sli4_rqst_cmn_get_port_name *pname; + + pname = sli_config_cmd_init(sli4, buf, + SLI4_CFG_PYLD_LENGTH(cmn_get_port_name), NULL); + if (!pname) + return EFC_FAIL; + + sli_cmd_fill_hdr(&pname->hdr, SLI4_CMN_GET_PORT_NAME, + SLI4_SUBSYSTEM_COMMON, CMD_V1, + SLI4_RQST_PYLD_LEN(cmn_get_port_name)); + + /* Set the port type value (ethernet=0, FC=1) for V1 commands */ + pname->port_type = SLI4_PORT_TYPE_FC; + + return EFC_SUCCESS; +} + +int +sli_cmd_common_write_object(struct sli4 *sli4, void *buf, u16 noc, + u16 eof, u32 desired_write_length, + u32 offset, char *object_name, + struct efc_dma *dma) +{ + struct sli4_rqst_cmn_write_object *wr_obj = NULL; + struct sli4_bde *bde; + u32 dwflags = 0; + + wr_obj = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_write_object) + sizeof(*bde), NULL); + if (!wr_obj) + return EFC_FAIL; + + sli_cmd_fill_hdr(&wr_obj->hdr, SLI4_CMN_WRITE_OBJECT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN_VAR(cmn_write_object, sizeof(*bde))); + + if (noc) + dwflags |= SLI4_RQ_DES_WRITE_LEN_NOC; + if (eof) + dwflags |= SLI4_RQ_DES_WRITE_LEN_EOF; + dwflags |= (desired_write_length & SLI4_RQ_DES_WRITE_LEN); + + wr_obj->desired_write_len_dword = cpu_to_le32(dwflags); + + wr_obj->write_offset = cpu_to_le32(offset); + strncpy(wr_obj->object_name, object_name, sizeof(wr_obj->object_name)); + wr_obj->host_buffer_descriptor_count = cpu_to_le32(1); + + bde = (struct sli4_bde *)wr_obj->host_buffer_descriptor; + + /* Setup to transfer xfer_size bytes to device */ + bde->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (desired_write_length & SLI4_BDE_LEN_MASK)); + bde->u.data.low = cpu_to_le32(lower_32_bits(dma->phys)); + bde->u.data.high = cpu_to_le32(upper_32_bits(dma->phys)); + + return EFC_SUCCESS; +} + +int +sli_cmd_common_delete_object(struct sli4 *sli4, void *buf, char *object_name) +{ + struct sli4_rqst_cmn_delete_object *req = NULL; + + req = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_delete_object), NULL); + if (!req) + return EFC_FAIL; + + sli_cmd_fill_hdr(&req->hdr, SLI4_CMN_DELETE_OBJECT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_delete_object)); + + strncpy(req->object_name, object_name, sizeof(req->object_name)); + return EFC_SUCCESS; +} + +int +sli_cmd_common_read_object(struct sli4 *sli4, void *buf, u32 desired_read_len, + u32 offset, char *object_name, struct efc_dma *dma) +{ + struct sli4_rqst_cmn_read_object *rd_obj = NULL; + struct sli4_bde *bde; + + rd_obj = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_read_object) + sizeof(*bde), NULL); + if (!rd_obj) + return EFC_FAIL; + + sli_cmd_fill_hdr(&rd_obj->hdr, SLI4_CMN_READ_OBJECT, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN_VAR(cmn_read_object, sizeof(*bde))); + rd_obj->desired_read_length_dword = + cpu_to_le32(desired_read_len & SLI4_REQ_DESIRE_READLEN); + + rd_obj->read_offset = cpu_to_le32(offset); + strncpy(rd_obj->object_name, object_name, sizeof(rd_obj->object_name)); + rd_obj->host_buffer_descriptor_count = cpu_to_le32(1); + + bde = (struct sli4_bde *)rd_obj->host_buffer_descriptor; + + /* Setup to transfer xfer_size bytes to device */ + bde->bde_type_buflen = + cpu_to_le32((SLI4_BDE_TYPE_VAL(64)) | + (desired_read_len & SLI4_BDE_LEN_MASK)); + if (dma) { + bde->u.data.low = cpu_to_le32(lower_32_bits(dma->phys)); + bde->u.data.high = cpu_to_le32(upper_32_bits(dma->phys)); + } else { + bde->u.data.low = 0; + bde->u.data.high = 0; + } + + return EFC_SUCCESS; +} + +int +sli_cmd_dmtf_exec_clp_cmd(struct sli4 *sli4, void *buf, struct efc_dma *cmd, + struct efc_dma *resp) +{ + struct sli4_rqst_dmtf_exec_clp_cmd *clp_cmd = NULL; + + clp_cmd = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(dmtf_exec_clp_cmd), NULL); + if (!clp_cmd) + return EFC_FAIL; + + sli_cmd_fill_hdr(&clp_cmd->hdr, DMTF_EXEC_CLP_CMD, SLI4_SUBSYSTEM_DMTF, + CMD_V0, SLI4_RQST_PYLD_LEN(dmtf_exec_clp_cmd)); + + clp_cmd->cmd_buf_length = cpu_to_le32(cmd->size); + clp_cmd->cmd_buf_addr_low = cpu_to_le32(lower_32_bits(cmd->phys)); + clp_cmd->cmd_buf_addr_high = cpu_to_le32(upper_32_bits(cmd->phys)); + clp_cmd->resp_buf_length = cpu_to_le32(resp->size); + clp_cmd->resp_buf_addr_low = cpu_to_le32(lower_32_bits(resp->phys)); + clp_cmd->resp_buf_addr_high = cpu_to_le32(upper_32_bits(resp->phys)); + return EFC_SUCCESS; +} + +int +sli_cmd_common_set_dump_location(struct sli4 *sli4, void *buf, bool query, + bool is_buffer_list, + struct efc_dma *buffer, u8 fdb) +{ + struct sli4_rqst_cmn_set_dump_location *set_dump_loc = NULL; + u32 buffer_length_flag = 0; + + set_dump_loc = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_set_dump_location), NULL); + if (!set_dump_loc) + return EFC_FAIL; + + sli_cmd_fill_hdr(&set_dump_loc->hdr, SLI4_CMN_SET_DUMP_LOCATION, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_set_dump_location)); + + if (is_buffer_list) + buffer_length_flag |= SLI4_CMN_SET_DUMP_BLP; + + if (query) + buffer_length_flag |= SLI4_CMN_SET_DUMP_QRY; + + if (fdb) + buffer_length_flag |= SLI4_CMN_SET_DUMP_FDB; + + if (buffer) { + set_dump_loc->buf_addr_low = + cpu_to_le32(lower_32_bits(buffer->phys)); + set_dump_loc->buf_addr_high = + cpu_to_le32(upper_32_bits(buffer->phys)); + + buffer_length_flag |= + buffer->len & SLI4_CMN_SET_DUMP_BUFFER_LEN; + } else { + set_dump_loc->buf_addr_low = 0; + set_dump_loc->buf_addr_high = 0; + set_dump_loc->buffer_length_dword = 0; + } + set_dump_loc->buffer_length_dword = cpu_to_le32(buffer_length_flag); + return EFC_SUCCESS; +} + +int +sli_cmd_common_set_features(struct sli4 *sli4, void *buf, u32 feature, + u32 param_len, void *parameter) +{ + struct sli4_rqst_cmn_set_features *cmd = NULL; + + cmd = sli_config_cmd_init(sli4, buf, + SLI4_RQST_CMDSZ(cmn_set_features), NULL); + if (!cmd) + return EFC_FAIL; + + sli_cmd_fill_hdr(&cmd->hdr, SLI4_CMN_SET_FEATURES, + SLI4_SUBSYSTEM_COMMON, CMD_V0, + SLI4_RQST_PYLD_LEN(cmn_set_features)); + + cmd->feature = cpu_to_le32(feature); + cmd->param_len = cpu_to_le32(param_len); + memcpy(cmd->params, parameter, param_len); + + return EFC_SUCCESS; +} + +int +sli_cqe_mq(struct sli4 *sli4, void *buf) +{ + struct sli4_mcqe *mcqe = buf; + u32 dwflags = le32_to_cpu(mcqe->dw3_flags); + /* + * Firmware can split mbx completions into two MCQEs: first with only + * the "consumed" bit set and a second with the "complete" bit set. + * Thus, ignore MCQE unless "complete" is set. + */ + if (!(dwflags & SLI4_MCQE_COMPLETED)) + return SLI4_MCQE_STATUS_NOT_COMPLETED; + + if (le16_to_cpu(mcqe->completion_status)) { + efc_log_info(sli4, "status(st=%#x ext=%#x con=%d cmp=%d ae=%d val=%d)\n", + le16_to_cpu(mcqe->completion_status), + le16_to_cpu(mcqe->extended_status), + (dwflags & SLI4_MCQE_CONSUMED), + (dwflags & SLI4_MCQE_COMPLETED), + (dwflags & SLI4_MCQE_AE), + (dwflags & SLI4_MCQE_VALID)); + } + + return le16_to_cpu(mcqe->completion_status); +} + +int +sli_cqe_async(struct sli4 *sli4, void *buf) +{ + struct sli4_acqe *acqe = buf; + int rc = EFC_FAIL; + + if (!buf) { + efc_log_err(sli4, "bad parameter sli4=%p buf=%p\n", sli4, buf); + return EFC_FAIL; + } + + switch (acqe->event_code) { + case SLI4_ACQE_EVENT_CODE_LINK_STATE: + efc_log_info(sli4, "Unsupported by FC link, evt code:%#x\n", + acqe->event_code); + break; + case SLI4_ACQE_EVENT_CODE_GRP_5: + efc_log_info(sli4, "ACQE GRP5\n"); + break; + case SLI4_ACQE_EVENT_CODE_SLI_PORT_EVENT: + efc_log_info(sli4, "ACQE SLI Port, type=0x%x, data1,2=0x%08x,0x%08x\n", + acqe->event_type, + le32_to_cpu(acqe->event_data[0]), + le32_to_cpu(acqe->event_data[1])); + break; + case SLI4_ACQE_EVENT_CODE_FC_LINK_EVENT: + rc = sli_fc_process_link_attention(sli4, buf); + break; + default: + efc_log_info(sli4, "ACQE unknown=%#x\n", + acqe->event_code); + } + + return rc; +} From patchwork Mon Oct 12 22:51:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269826 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5DF61C433DF for ; Mon, 12 Oct 2020 22:52:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EFC0D20709 for ; Mon, 12 Oct 2020 22:52:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="IDu9Okhn" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390612AbgJLWwP (ORCPT ); Mon, 12 Oct 2020 18:52:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60552 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390548AbgJLWwK (ORCPT ); Mon, 12 Oct 2020 18:52:10 -0400 Received: from mail-pl1-x643.google.com (mail-pl1-x643.google.com [IPv6:2607:f8b0:4864:20::643]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D1934C0613D0 for ; Mon, 12 Oct 2020 15:52:09 -0700 (PDT) Received: by mail-pl1-x643.google.com with SMTP id w21so6766302plq.3 for ; Mon, 12 Oct 2020 15:52:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=oJENUULrrwHl1ZFGGxTGu+g0VRwl/IC9otgu/PrnqxI=; b=IDu9OkhnF0qVv3T6SdtcDraNIlWjhEorH5jGRgl9uZIgr/iTaq+PZPr9EWVfGeHqI4 ZnSImXCAMV2DtqdYMX2fEDgsiWoCBfkbW96NTKcO0pSjIN1x9yFJmniUY6D175tNA+Qo pJIbtNXvc/7+EFcwnHc4JMN1zjNgPuACelNCQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=oJENUULrrwHl1ZFGGxTGu+g0VRwl/IC9otgu/PrnqxI=; b=I5RsPmmtbmSddMwZamkX8WgQbP8LAxonVn18bq9l+HgKi91Keq9eL0caDruKIaCNaU zLo0gUcDYqo2NsTMfgRJIo4G5MepJ3wXibM5cUvjbMY0aNZ6QILu6fLF8ID18fBK5riZ mkxJ2Qc/BIDBzcih/70VAF4sgG+e2zjf+S7163PCzHw8RuwwdnIbsBn3eQuaPsxUhqSr npogIifFT6/D0829QunEM1we1GsLNw4aPutm+4rtzxx5TmDREQOmGumsgo54Y0icmTKN w3VhycXnkQtqgmTZNjKVnPIW8RafeD5uHQDDRC9QmknCJXa/OKUVmgjbcz8ODoXkyI+d 40ZQ== X-Gm-Message-State: AOAM531ipBYIXmfNlM8DsfzVvfODBl/HyKvvlaeN16ODbXcL+eV0z5/a IRwja2xJSPka2gw6+TFKU0uIBVS/b8n0w2QEoPGQhqmoc5uYWvitk+TF6IVFTE6T7ZrBImQEWUS jJKjBkifLmrKbBtEwQjFBu0XBvrLuPSLnQrHRjdk5JvWtHHTzjY27THCAbuRewwX1ioLsHghiGq Y1+/8= X-Google-Smtp-Source: ABdhPJx8F+Iu8j+kmBO8tASrHtz0a2+rG8aVvNvOnrEuE/QEJR3hSWHXLd6twpXcHhivrvXj5Klehg== X-Received: by 2002:a17:902:bf4b:b029:d1:e5e7:bddd with SMTP id u11-20020a170902bf4bb02900d1e5e7bdddmr24949375pls.61.1602543128482; Mon, 12 Oct 2020 15:52:08 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:07 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 09/31] elx: libefc: Emulex FC discovery library APIs and definitions Date: Mon, 12 Oct 2020 15:51:25 -0700 Message-Id: <20201012225147.54404-10-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc library population. This patch adds library interface definitions for: - SLI/Local FC port objects - efc_domain_s: FC domain (aka fabric) objects - efc_node_s: FC node (aka remote ports) objects Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- v4: Moved hold frames and pending frames handling to library. Added efc locking notes. Added kref to domain, nport and node objects. Renamed efc_sli_port object to efc_nport Reworked on APIs and libefc_function_template , Moved ELS handling to Library, Moved registering discovery objects to library. Added issue_mbox_rqst, send_els, send_bls template functions. --- drivers/scsi/elx/libefc/efc.h | 69 ++++ drivers/scsi/elx/libefc/efc_lib.c | 73 ++++ drivers/scsi/elx/libefc/efclib.h | 629 ++++++++++++++++++++++++++++++ 3 files changed, 771 insertions(+) create mode 100644 drivers/scsi/elx/libefc/efc.h create mode 100644 drivers/scsi/elx/libefc/efc_lib.c create mode 100644 drivers/scsi/elx/libefc/efclib.h diff --git a/drivers/scsi/elx/libefc/efc.h b/drivers/scsi/elx/libefc/efc.h new file mode 100644 index 000000000000..5234b15f7119 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFC_H__ +#define __EFC_H__ + +#include "../include/efc_common.h" +#include "efclib.h" +#include "efc_sm.h" +#include "efc_cmds.h" +#include "efc_domain.h" +#include "efc_nport.h" +#include "efc_node.h" +#include "efc_fabric.h" +#include "efc_device.h" +#include "efc_els.h" + +#define EFC_MAX_REMOTE_NODES 2048 +#define NODE_SPARAMS_SIZE 256 + +enum efc_hw_rtn { + EFC_HW_RTN_SUCCESS = 0, + EFC_HW_RTN_SUCCESS_SYNC = 1, + EFC_HW_RTN_ERROR = -1, + EFC_HW_RTN_NO_RESOURCES = -2, + EFC_HW_RTN_NO_MEMORY = -3, + EFC_HW_RTN_IO_NOT_ACTIVE = -4, + EFC_HW_RTN_IO_ABORT_IN_PROGRESS = -5, + EFC_HW_RTN_IO_PORT_OWNED_ALREADY_ABORTED = -6, + EFC_HW_RTN_INVALID_ARG = -7, +}; + +#define EFC_HW_RTN_IS_ERROR(e) ((e) < 0) + +enum efc_scsi_del_initiator_reason { + EFC_SCSI_INITIATOR_DELETED, + EFC_SCSI_INITIATOR_MISSING, +}; + +enum efc_scsi_del_target_reason { + EFC_SCSI_TARGET_DELETED, + EFC_SCSI_TARGET_MISSING, +}; + +#define EFC_SCSI_CALL_COMPLETE 0 +#define EFC_SCSI_CALL_ASYNC 1 + +#define EFC_FC_ELS_DEFAULT_RETRIES 3 + +#define domain_sm_trace(domain) \ + efc_log_debug(domain->efc, "[domain:%s] %-20s %-20s\n", \ + domain->display_name, __func__, efc_sm_event_name(evt)) \ + +#define domain_trace(domain, fmt, ...) \ + efc_log_debug(domain->efc, \ + "[%s]" fmt, domain->display_name, ##__VA_ARGS__) \ + +#define node_sm_trace() \ + efc_log_debug(node->efc, \ + "[%s] %-20s\n", node->display_name, efc_sm_event_name(evt)) \ + +#define nport_sm_trace(nport) \ + efc_log_debug(nport->efc, \ + "[%s] %-20s\n", nport->display_name, efc_sm_event_name(evt)) \ + +#endif /* __EFC_H__ */ diff --git a/drivers/scsi/elx/libefc/efc_lib.c b/drivers/scsi/elx/libefc/efc_lib.c new file mode 100644 index 000000000000..c70d2a90b4e8 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_lib.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * LIBEFC LOCKING + * + * The critical sections protected by the efc's spinlock are quite broad and + * may be improved upon in the future. The libefc code and its locking doesn't + * influence the I/O path, so excessive locking doesn't impact I/O performance. + * + * The strategy is to lock whenever processing a request from user driver. This + * means that the entry points into the libefc library are protected by efc + * lock. So all the state machine transitions are protected. + */ + +#include +#include +#include "efc.h" + +int efcport_init(struct efc *efc) +{ + u32 rc = EFC_SUCCESS; + + spin_lock_init(&efc->lock); + INIT_LIST_HEAD(&efc->vport_list); + efc->hold_frames = false; + spin_lock_init(&efc->pend_frames_lock); + INIT_LIST_HEAD(&efc->pend_frames); + + /* Create Node pool */ + efc->node_pool = mempool_create_kmalloc_pool(EFC_MAX_REMOTE_NODES, + sizeof(struct efc_node)); + if (!efc->node_pool) { + efc_log_err(efc, "Can't allocate node pool\n"); + return EFC_FAIL; + } + + efc->node_dma_pool = dma_pool_create("node_dma_pool", &efc->pci->dev, + NODE_SPARAMS_SIZE, 0, 0); + if (!efc->node_dma_pool) { + efc_log_err(efc, "Can't allocate node dma pool\n"); + mempool_destroy(efc->node_pool); + return EFC_FAIL; + } + + return rc; +} + +static void +efc_purge_pending(struct efc *efc) +{ + struct efc_hw_sequence *frame, *next; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->pend_frames_lock, flags); + + list_for_each_entry_safe(frame, next, &efc->pend_frames, list_entry) { + list_del(&frame->list_entry); + efc->tt.hw_seq_free(efc, frame); + } + + spin_unlock_irqrestore(&efc->pend_frames_lock, flags); +} + +void efcport_destroy(struct efc *efc) +{ + efc_purge_pending(efc); + mempool_destroy(efc->node_pool); + dma_pool_destroy(efc->node_dma_pool); +} diff --git a/drivers/scsi/elx/libefc/efclib.h b/drivers/scsi/elx/libefc/efclib.h new file mode 100644 index 000000000000..58d9049e499a --- /dev/null +++ b/drivers/scsi/elx/libefc/efclib.h @@ -0,0 +1,629 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFCLIB_H__ +#define __EFCLIB_H__ + +#include "scsi/fc/fc_els.h" +#include "scsi/fc/fc_fs.h" +#include "scsi/fc/fc_ns.h" +#include "scsi/fc/fc_gs.h" +#include "scsi/fc_frame.h" +#include "../include/efc_common.h" +#include "../libefc_sli/sli4.h" + +#define EFC_SERVICE_PARMS_LENGTH 0x74 +#define EFC_NAME_LENGTH 32 +#define EFC_SM_NAME_LENGTH 64 +#define EFC_DISPLAY_BUS_INFO_LENGTH 16 + +#define EFC_WWN_LENGTH 32 + +#define EFC_FC_ELS_DEFAULT_RETRIES 3 + +/* Timeouts */ +#define EFC_FC_ELS_SEND_DEFAULT_TIMEOUT 0 +#define EFC_FC_FLOGI_TIMEOUT_SEC 5 +#define EFC_SHUTDOWN_TIMEOUT_USEC 30000000 + +/* Local port topology */ +enum efc_nport_topology { + EFC_SPORT_TOPOLOGY_UNKNOWN = 0, + EFC_SPORT_TOPOLOGY_FABRIC, + EFC_SPORT_TOPOLOGY_P2P, + EFC_SPORT_TOPOLOGY_LOOP, +}; + +#define enable_target_rscn(efc) 1 + +enum efc_node_shutd_rsn { + EFC_NODE_SHUTDOWN_DEFAULT = 0, + EFC_NODE_SHUTDOWN_EXPLICIT_LOGO, + EFC_NODE_SHUTDOWN_IMPLICIT_LOGO, +}; + +enum efc_node_send_ls_acc { + EFC_NODE_SEND_LS_ACC_NONE = 0, + EFC_NODE_SEND_LS_ACC_PLOGI, + EFC_NODE_SEND_LS_ACC_PRLI, +}; + +#define EFC_LINK_STATUS_UP 0 +#define EFC_LINK_STATUS_DOWN 1 + +/* State machine context header */ +struct efc_sm_ctx { + void (*current_state)(struct efc_sm_ctx *ctx, + u32 evt, void *arg); + + const char *description; + void *app; +}; + +/* Description of discovered Fabric Domain */ +struct efc_domain_record { + u32 index; + u32 priority; + u8 address[6]; + u8 wwn[8]; + union { + u8 vlan[512]; + u8 loop[128]; + } map; + u32 speed; + u32 fc_id; + bool is_loop; + bool is_nport; +}; + +/* Fabric/Domain events */ +enum efc_hw_domain_event { + EFC_HW_DOMAIN_ALLOC_OK, + EFC_HW_DOMAIN_ALLOC_FAIL, + EFC_HW_DOMAIN_ATTACH_OK, + EFC_HW_DOMAIN_ATTACH_FAIL, + EFC_HW_DOMAIN_FREE_OK, + EFC_HW_DOMAIN_FREE_FAIL, + EFC_HW_DOMAIN_LOST, + EFC_HW_DOMAIN_FOUND, + EFC_HW_DOMAIN_CHANGED, +}; + +enum efc_hw_port_event { + EFC_HW_PORT_ALLOC_OK, + EFC_HW_PORT_ALLOC_FAIL, + EFC_HW_PORT_ATTACH_OK, + EFC_HW_PORT_ATTACH_FAIL, + EFC_HW_PORT_FREE_OK, + EFC_HW_PORT_FREE_FAIL, +}; + +enum efc_hw_remote_node_event { + EFC_HW_NODE_ATTACH_OK, + EFC_HW_NODE_ATTACH_FAIL, + EFC_HW_NODE_FREE_OK, + EFC_HW_NODE_FREE_FAIL, + EFC_HW_NODE_FREE_ALL_OK, + EFC_HW_NODE_FREE_ALL_FAIL, +}; + +enum efc_hw_node_els_event { + EFC_HW_SRRS_ELS_REQ_OK, + EFC_HW_SRRS_ELS_CMPL_OK, + EFC_HW_SRRS_ELS_REQ_FAIL, + EFC_HW_SRRS_ELS_CMPL_FAIL, + EFC_HW_SRRS_ELS_REQ_RJT, + EFC_HW_ELS_REQ_ABORTED, +}; + +struct efc_nport { + struct list_head list_entry; + struct kref ref; + void (*release)(struct kref *arg); + struct efc *efc; + u32 tgt_id; + u32 index; + u32 instance_index; + char display_name[EFC_NAME_LENGTH]; + bool is_vport; + bool free_req_pending; + bool attached; + bool p2p_winner; + struct efc_domain *domain; + u64 wwpn; + u64 wwnn; + struct list_head node_list; + void *ini_nport; + void *tgt_nport; + void *tgt_data; + void *ini_data; + + /* Members private to HW/SLI */ + void *hw; + u32 indicator; + u32 fc_id; + struct efc_dma dma; + + u8 wwnn_str[EFC_WWN_LENGTH]; + __be64 sli_wwpn; + __be64 sli_wwnn; + + struct efc_sm_ctx sm; + struct xarray lookup; + bool enable_ini; + bool enable_tgt; + bool enable_rscn; + bool shutting_down; + u32 p2p_port_id; + enum efc_nport_topology topology; + u8 service_params[EFC_SERVICE_PARMS_LENGTH]; + u32 p2p_remote_port_id; +}; + +/** + * Fibre Channel domain object + * + * This object is a container for the various SLI components needed + * to connect to the domain of a FC or FCoE switch + * @efc: pointer back to efc + * @instance_index: unique instance index value + * @display_name: Node display name + * @nport_list: linked list of nports associated with this domain + * @ref: Reference count, each nport takes a reference + * @release: Function to free domain object + * @ini_domain: initiator backend private domain data + * @tgt_domain: target backend private domain data + * @hw: pointer to HW + * @sm: state machine context + * @fcf: FC Forwarder table index + * @fcf_indicator: FCFI + * @indicator: VFI + * @nport_count: Number of nports allocated + * @dma: memory for Service Parameters + * @fcf_wwn: WWN for FCF/switch + * @drvsm: driver domain sm context + * @attached: set true after attach completes + * @is_fc: is FC + * @is_loop: is loop topology + * @is_nlport: is public loop + * @domain_found_pending:A domain found is pending, drec is updated + * @req_domain_free: True if domain object should be free'd + * @req_accept_frames: set in domain state machine to enable frames + * @domain_notify_pend: Set in domain SM to avoid duplicate node event post + * @pending_drec: Pending drec if a domain found is pending + * @service_params: any nports service parameters + * @flogi_service_params:Fabric/P2p service parameters from FLOGI + * @lookup: d_id to node lookup object + * @nport: Pointer to first (physical) SLI port + */ +struct efc_domain { + struct efc *efc; + char display_name[EFC_NAME_LENGTH]; + struct list_head nport_list; + struct kref ref; + void (*release)(struct kref *arg); + void *ini_domain; + void *tgt_domain; + + /* Declarations private to HW/SLI */ + void *hw; + u32 fcf; + u32 fcf_indicator; + u32 indicator; + u32 nport_count; + struct efc_dma dma; + + /* Declarations private to FC trannport */ + u64 fcf_wwn; + struct efc_sm_ctx drvsm; + bool attached; + bool is_fc; + bool is_loop; + bool is_nlport; + bool domain_found_pending; + bool req_domain_free; + bool req_accept_frames; + bool domain_notify_pend; + + struct efc_domain_record pending_drec; + u8 service_params[EFC_SERVICE_PARMS_LENGTH]; + u8 flogi_service_params[EFC_SERVICE_PARMS_LENGTH]; + + struct xarray lookup; + + struct efc_nport *nport; +}; + +/** + * Remote Node object + * + * This object represents a connection between the SLI port and another + * Nx_Port on the fabric. Note this can be either a well known port such + * as a F_Port (i.e. ff:ff:fe) or another N_Port. + * @indicator: RPI + * @fc_id: FC address + * @attached: true if attached + * @nport: associated SLI port + * @node: associated node + */ +struct efc_remote_node { + u32 indicator; + u32 index; + u32 fc_id; + + bool attached; + + struct efc_nport *nport; + void *node; +}; + +/** + * FC Node object + * @efc: pointer back to efc structure + * @display_name: Node display name + * @nort: Assosiated nport pointer. + * @hold_frames: hold incoming frames if true + * @els_io_enabled: Enable allocating els ios for this node + * @els_ios_lock: lock to protect the els ios list + * @els_ios_list: ELS I/O's for this node + * @ini_node: backend initiator private node data + * @tgt_node: backend target private node data + * @rnode: Remote node + * @sm: state machine context + * @evtdepth: current event posting nesting depth + * @req_free: this node is to be free'd + * @attached: node is attached (REGLOGIN complete) + * @fcp_enabled: node is enabled to handle FCP + * @rscn_pending: for name server node RSCN is pending + * @send_plogi: send PLOGI accept, upon completion of node attach + * @send_plogi_acc: TRUE if io_alloc() is enabled. + * @send_ls_acc: type of LS acc to send + * @ls_acc_io: SCSI IO for LS acc + * @ls_acc_oxid: OX_ID for pending accept + * @ls_acc_did: D_ID for pending accept + * @shutdown_reason: reason for node shutdown + * @sparm_dma_buf: service parameters buffer + * @service_params: plogi/acc frame from remote device + * @pend_frames_lock: lock for inbound pending frames list + * @pend_frames: inbound pending frames list + * @pend_frames_processed:count of frames processed in hold frames interval + * @ox_id_in_use: used to verify one at a time us of ox_id + * @els_retries_remaining:for ELS, number of retries remaining + * @els_req_cnt: number of outstanding ELS requests + * @els_cmpl_cnt: number of outstanding ELS completions + * @abort_cnt: Abort counter for debugging purpos + * @current_state_name: current node state + * @prev_state_name: previous node state + * @current_evt: current event + * @prev_evt: previous event + * @targ: node is target capable + * @init: node is init capable + * @refound: Handle node refound case when node is being deleted + * @els_io_pend_list: list of pending (not yet processed) ELS IOs + * @els_io_active_list: list of active (processed) ELS IOs + * @nodedb_state: Node debugging, saved state + * @gidpt_delay_timer: GIDPT delay timer + * @time_last_gidpt_msec:Start time of last target RSCN GIDPT + * @wwnn: remote port WWNN + * @wwpn: remote port WWPN + */ +struct efc_node { + struct efc *efc; + char display_name[EFC_NAME_LENGTH]; + struct efc_nport *nport; + struct list_head list_entry; + struct kref ref; + void (*release)(struct kref *arg); + bool hold_frames; + bool els_io_enabled; + bool send_plogi_acc; + bool send_plogi; + bool rscn_pending; + bool fcp_enabled; + bool attached; + bool req_free; + + spinlock_t els_ios_lock; + struct list_head els_ios_list; + void *ini_node; + void *tgt_node; + + struct efc_remote_node rnode; + /* Declarations private to FC trannport */ + struct efc_sm_ctx sm; + u32 evtdepth; + + enum efc_node_send_ls_acc send_ls_acc; + void *ls_acc_io; + u32 ls_acc_oxid; + u32 ls_acc_did; + enum efc_node_shutd_rsn shutdown_reason; + bool targ; + bool init; + bool refound; + struct efc_dma sparm_dma_buf; + u8 service_params[EFC_SERVICE_PARMS_LENGTH]; + spinlock_t pend_frames_lock; + struct list_head pend_frames; + u32 pend_frames_processed; + u32 ox_id_in_use; + u32 els_retries_remaining; + u32 els_req_cnt; + u32 els_cmpl_cnt; + u32 abort_cnt; + + char current_state_name[EFC_SM_NAME_LENGTH]; + char prev_state_name[EFC_SM_NAME_LENGTH]; + int current_evt; + int prev_evt; + + void (*nodedb_state)(struct efc_sm_ctx *ctx, + u32 evt, void *arg); + struct timer_list gidpt_delay_timer; + u64 time_last_gidpt_msec; + + char wwnn[EFC_WWN_LENGTH]; + char wwpn[EFC_WWN_LENGTH]; +}; + +/** + * NPIV port + * + * Collection of the information required to restore a virtual port across + * link events + * @wwnn: node name + * @wwpn: port name + * @fc_id: port id + * @tgt_data: target backend pointer + * @ini_data: initiator backend pointe + * @nport: Used to match record after attaching for update + * + */ + +struct efc_vport_spec { + struct list_head list_entry; + u64 wwnn; + u64 wwpn; + u32 fc_id; + bool enable_tgt; + bool enable_ini; + void *tgt_data; + void *ini_data; + struct efc_nport *nport; +}; + +#define node_printf(node, fmt, args...) \ + pr_info("[%s] " fmt, node->display_name, ##args) + +/* Node SM IO Context Callback structure */ +struct efc_node_cb { + int status; + int ext_status; + struct efc_hw_rq_buffer *header; + struct efc_hw_rq_buffer *payload; + struct efc_dma els_rsp; + + /* Actual length of data received */ + int rsp_len; +}; + +/* HW unsolicited callback status */ +enum efc_hw_unsol_status { + EFC_HW_UNSOL_SUCCESS, + EFC_HW_UNSOL_ERROR, + EFC_HW_UNSOL_ABTS_RCVD, + EFC_HW_UNSOL_MAX, /**< must be last */ +}; + +enum efc_hw_rq_buffer_type { + EFC_HW_RQ_BUFFER_TYPE_HDR, + EFC_HW_RQ_BUFFER_TYPE_PAYLOAD, + EFC_HW_RQ_BUFFER_TYPE_MAX, +}; + +struct efc_hw_rq_buffer { + u16 rqindex; + struct efc_dma dma; +}; + +/* + * Defines a general FC sequence object, + * consisting of a header, payload buffers + * and a HW IO in the case of port owned XRI + */ +struct efc_hw_sequence { + struct list_head list_entry; + void *hw; + u8 fcfi; + u8 auto_xrdy; + u8 out_of_xris; + enum efc_hw_unsol_status status; + + struct efc_hw_rq_buffer *header; + struct efc_hw_rq_buffer *payload; + + void *hw_priv; +}; + +enum efc_disc_io_type { + EFC_DISC_IO_ELS_REQ, + EFC_DISC_IO_ELS_RESP, + EFC_DISC_IO_CT_REQ, + EFC_DISC_IO_CT_RESP +}; + +struct efc_io_els_params { + u32 s_id; + u16 ox_id; + u8 timeout; +}; + +struct efc_io_ct_params { + u8 r_ctl; + u8 type; + u8 df_ctl; + u8 timeout; + u16 ox_id; +}; + +union efc_disc_io_param { + struct efc_io_els_params els; + struct efc_io_ct_params ct; +}; + +struct efc_disc_io { + struct efc_dma req; /* send buffer */ + struct efc_dma rsp; /* receive buffer */ + enum efc_disc_io_type io_type; /* EFC_DISC_IO_TYPE enum*/ + u16 xmit_len; /* Length of els request*/ + u16 rsp_len; /* Max length of rsps to be rcvd */ + u32 rpi; /* Registered RPI */ + u32 vpi; /* VPI for this nport */ + u32 s_id; + u32 d_id; + bool rpi_registered; /* if false, use tmp RPI */ + union efc_disc_io_param iparam; +}; + +/* Return value indiacating the sequence can not be freed */ +#define EFC_HW_SEQ_HOLD 0 +/* Return value indiacating the sequence can be freed */ +#define EFC_HW_SEQ_FREE 1 + +struct libefc_function_template { + /*Sport*/ + int (*new_nport)(struct efc *efc, struct efc_nport *sp); + void (*del_nport)(struct efc *efc, struct efc_nport *sp); + + /*Scsi Node*/ + int (*scsi_new_node)(struct efc *efc, struct efc_node *n); + int (*scsi_del_node)(struct efc *efc, struct efc_node *n, int reason); + + int (*issue_mbox_rqst)(void *efct, void *buf, void *cb, void *arg); + /*Send ELS IO*/ + int (*send_els)(struct efc *efc, struct efc_disc_io *io); + /*Send BLS IO*/ + int (*send_bls)(struct efc *efc, u32 type, struct sli_bls_params *bls); + /*Free HW frame*/ + int (*hw_seq_free)(struct efc *efc, struct efc_hw_sequence *seq); +}; + +#define EFC_LOG_LIB 0x01 +#define EFC_LOG_NODE 0x02 +#define EFC_LOG_PORT 0x04 +#define EFC_LOG_DOMAIN 0x08 +#define EFC_LOG_ELS 0x10 +#define EFC_LOG_DOMAIN_SM 0x20 +#define EFC_LOG_SM 0x40 + +/* efc library port structure */ +struct efc { + void *base; + struct pci_dev *pci; + struct sli4 *sli; + u32 fcfi; + u64 req_wwpn; + u64 req_wwnn; + + u64 def_wwpn; + u64 def_wwnn; + u64 max_xfer_size; + mempool_t *node_pool; + struct dma_pool *node_dma_pool; + u32 nodes_count; + + u32 link_status; + + struct list_head vport_list; + /* lock to protect the vport list */ + spinlock_t vport_lock; + + struct libefc_function_template tt; + /* lock to protect the discovery library. + * Refer to efc_lib.c for more details. + */ + spinlock_t lock; + + bool enable_ini; + bool enable_tgt; + + u32 log_level; + + struct efc_domain *domain; + void (*domain_free_cb)(struct efc *efc, void *arg); + void *domain_free_cb_arg; + + u64 tgt_rscn_delay_msec; + u64 tgt_rscn_period_msec; + + bool external_loopback; + u32 nodedb_mask; + u32 logmask; + atomic_t els_io_alloc_failed_count; + + /* hold pending frames */ + bool hold_frames; + /* lock to protect pending frames list access */ + spinlock_t pend_frames_lock; + struct list_head pend_frames; + /* count of pending frames that were processed */ + u32 pend_frames_processed; + +}; + +/* + * EFC library registration + * **********************************/ +int efcport_init(struct efc *efc); +void efcport_destroy(struct efc *efc); +/* + * EFC Domain + * **********************************/ +int efc_domain_cb(void *arg, int event, void *data); +void +efc_register_domain_free_cb(struct efc *efc, + void (*callback)(struct efc *efc, void *arg), + void *arg); + +/* + * EFC nport + * **********************************/ +int efc_nport_cb(void *arg, int event, void *data); +struct efc_vport_spec *efc_vport_create_spec(struct efc *efc, u64 wwnn, + u64 wwpn, u32 fc_id, bool enable_ini, bool enable_tgt, + void *tgt_data, void *ini_data); +int efc_nport_vport_new(struct efc_domain *domain, u64 wwpn, + u64 wwnn, u32 fc_id, bool ini, bool tgt, + void *tgt_data, void *ini_data); +int efc_nport_vport_del(struct efc *efc, struct efc_domain *domain, + u64 wwpn, u64 wwnn); + +void efc_vport_del_all(struct efc *efc); + +/* + * EFC Node + * **********************************/ +int efc_remote_node_cb(void *arg, int event, void *data); +void efc_node_fcid_display(u32 fc_id, char *buffer, u32 buf_len); +void efc_node_post_shutdown(struct efc_node *node, u32 evt, void *arg); +u64 efc_node_get_wwpn(struct efc_node *node); + +/* + * EFC FCP/ELS/CT interface + * **********************************/ +void efc_dispatch_frame(struct efc *efc, struct efc_hw_sequence *seq); +void efc_disc_io_complete(struct efc_disc_io *io, u32 len, u32 status, + u32 ext_status); + +/* + * EFC SCSI INTERACTION LAYER + * **********************************/ +void efc_scsi_sess_reg_complete(struct efc_node *node, u32 status); +void efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node); +void efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node); +void efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node); + +#endif /* __EFCLIB_H__ */ From patchwork Mon Oct 12 22:51:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269825 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5C186C43457 for ; Mon, 12 Oct 2020 22:52:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0B35B20709 for ; Mon, 12 Oct 2020 22:52:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="hXoyWQhN" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390622AbgJLWwQ (ORCPT ); Mon, 12 Oct 2020 18:52:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60566 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390597AbgJLWwM (ORCPT ); Mon, 12 Oct 2020 18:52:12 -0400 Received: from mail-pg1-x529.google.com (mail-pg1-x529.google.com [IPv6:2607:f8b0:4864:20::529]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A1B09C0613D2 for ; Mon, 12 Oct 2020 15:52:12 -0700 (PDT) Received: by mail-pg1-x529.google.com with SMTP id r10so15880151pgb.10 for ; Mon, 12 Oct 2020 15:52:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=nMm+ZHMR1cinJKMec49Z5SsmxARvIJipUYe0tYYjcBQ=; b=hXoyWQhNAeCZrb+8SAoVhEW1XMBYvUVE8wbGFAI94/O148p189ij4L9McKY429iK/S 9EZFC0eNi2JKOM1TSuYuvbOZqK+3CIwX159ph9aKw+n8sx2GLpbwxhEVyakYYOTAysv1 SnxcXmi9tqU8rxayBe8q4yZQ6rUHSe4fvCfjU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=nMm+ZHMR1cinJKMec49Z5SsmxARvIJipUYe0tYYjcBQ=; b=LrKCWKhlWuAveR6FNi9uu3GDZ2t3D7YNa63cYhzpc3Yjs7Qa2crzEMLDCi9vChHH8T ckWkFxCfmcJafNq2wFTm5N3LR/b2fUPwfva59ssZ3XWQUVPC3rw+783MH5yccegv/hHZ RaXqZ7loD7yLUpfASi/4011qgttVVUmVDFkSYWjCjOIUxULZbH4x4wzbFJ8bdKELJQn0 icj76gvk5yhJk1hvXwSXO4hrU6CcMTNMX2OJf+SrnuMGuQ0Qc/3ARpTVsxS0tSnt4V/t oJX9Uyz3lttQbT+bVriqNGmwqS7PNosp9tKYX/g0XnKVBw7Hx4rvpm+QDTR+PkEH5Zld btqQ== X-Gm-Message-State: AOAM533oGISBzNSc3qUIjDCPdbjdS6y5Uk4Ea9dPfL5i5vFLUKw2uXOb zf48Zy0lR6DkKze8KLxqC349JG9MfbTyZblNwDKJN+TcJOJGNfJA32DvXyjmFFiyExhFm00T0O7 sRz6qi3CuXr/lCRcV1EcgeQF3pjzmaIbzyryFomMryyMai5WCohajs1PfdOE44YOUK8yt4LI8r2 q7B1Q= X-Google-Smtp-Source: ABdhPJy3F8G3x4Wn/UCh2Yb4Sppphfa9e4WMS3dhc/bNbpV8hrhFyN16N9mZRfu6pGXm84poK8m/0w== X-Received: by 2002:a17:90a:2dcd:: with SMTP id q13mr21779648pjm.202.1602543131255; Mon, 12 Oct 2020 15:52:11 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:10 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 11/31] elx: libefc: SLI and FC PORT state machine interfaces Date: Mon, 12 Oct 2020 15:51:27 -0700 Message-Id: <20201012225147.54404-12-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc library population. This patch adds library interface definitions for: - SLI and FC port (aka n_port_id) registration, allocation and deallocation. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- v4: Renamed efc_sport.[ch] to efc_nport.[ch] Kref for Nport structure. Get kref while lookup for nport during unsolicited frame processing. Release after processing the frame. Get Kref while Allocating nport for vport creation. Release while deleting the vport. --- drivers/scsi/elx/libefc/efc_nport.c | 825 ++++++++++++++++++++++++++++ drivers/scsi/elx/libefc/efc_nport.h | 50 ++ 2 files changed, 875 insertions(+) create mode 100644 drivers/scsi/elx/libefc/efc_nport.c create mode 100644 drivers/scsi/elx/libefc/efc_nport.h diff --git a/drivers/scsi/elx/libefc/efc_nport.c b/drivers/scsi/elx/libefc/efc_nport.c new file mode 100644 index 000000000000..077fc6b1369f --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_nport.c @@ -0,0 +1,825 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * NPORT + * + * Port object for physical port and NPIV ports. + */ + +/* + * NPORT REFERENCE COUNTING + * + * A nport reference should be taken when: + * - an nport is allocated + * - a vport populates associated nport + * - a remote node is allocated + * - a unsolicited frame is processed + * The reference should be dropped when: + * - the unsolicited frame processesing is done + * - the remote node is removed + * - the vport is removed + * - the nport is removed + */ + +#include "efc.h" + +int +efc_nport_cb(void *arg, int event, void *data) +{ + struct efc *efc = arg; + struct efc_nport *nport = data; + enum efc_sm_event sm_event = EFC_EVT_LAST; + unsigned long flags = 0; + + /* HW nport callback events from the user driver */ + switch (event) { + case EFC_HW_PORT_ALLOC_OK: + sm_event = EFC_EVT_SPORT_ALLOC_OK; + break; + case EFC_HW_PORT_ALLOC_FAIL: + sm_event = EFC_EVT_SPORT_ALLOC_FAIL; + break; + case EFC_HW_PORT_ATTACH_OK: + sm_event = EFC_EVT_SPORT_ATTACH_OK; + break; + case EFC_HW_PORT_ATTACH_FAIL: + sm_event = EFC_EVT_SPORT_ATTACH_FAIL; + break; + case EFC_HW_PORT_FREE_OK: + sm_event = EFC_EVT_SPORT_FREE_OK; + break; + case EFC_HW_PORT_FREE_FAIL: + sm_event = EFC_EVT_SPORT_FREE_FAIL; + break; + default: + efc_log_err(efc, "unknown event %#x\n", event); + return EFC_FAIL; + } + + efc_log_debug(efc, "nport event: %s\n", efc_sm_event_name(sm_event)); + + spin_lock_irqsave(&efc->lock, flags); + efc_sm_post_event(&nport->sm, sm_event, NULL); + spin_unlock_irqrestore(&efc->lock, flags); + + return EFC_SUCCESS; +} + +static struct efc_nport * +efc_nport_find_wwn(struct efc_domain *domain, uint64_t wwnn, uint64_t wwpn) +{ + struct efc_nport *nport = NULL; + + /* Find a nport, given the WWNN and WWPN */ + list_for_each_entry(nport, &domain->nport_list, list_entry) { + if (nport->wwnn == wwnn && nport->wwpn == wwpn) + return nport; + } + return NULL; +} + +static void +_efc_nport_free(struct kref *arg) +{ + struct efc_nport *nport = container_of(arg, struct efc_nport, ref); + + kfree(nport); +} + +struct efc_nport * +efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn, + u32 fc_id, bool enable_ini, bool enable_tgt) +{ + struct efc_nport *nport; + + if (domain->efc->enable_ini) + enable_ini = 0; + + /* Return a failure if this nport has already been allocated */ + if (wwpn != 0) { + nport = efc_nport_find_wwn(domain, wwnn, wwpn); + if (nport) { + efc_log_err(domain->efc, + "Err: NPORT %016llX %016llX already allocated\n", + wwnn, wwpn); + return NULL; + } + } + + nport = kzalloc(sizeof(*nport), GFP_ATOMIC); + if (!nport) + return nport; + + /* initialize refcount */ + kref_init(&nport->ref); + nport->release = _efc_nport_free; + + nport->efc = domain->efc; + snprintf(nport->display_name, sizeof(nport->display_name), "------"); + nport->domain = domain; + xa_init(&nport->lookup); + nport->instance_index = domain->nport_count++; + INIT_LIST_HEAD(&nport->node_list); + nport->sm.app = nport; + nport->enable_ini = enable_ini; + nport->enable_tgt = enable_tgt; + nport->enable_rscn = (nport->enable_ini || + (nport->enable_tgt && enable_target_rscn(nport->efc))); + + /* Copy service parameters from domain */ + memcpy(nport->service_params, domain->service_params, + sizeof(struct fc_els_flogi)); + + /* Update requested fc_id */ + nport->fc_id = fc_id; + + /* Update the nport's service parameters for the new wwn's */ + nport->wwpn = wwpn; + nport->wwnn = wwnn; + snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), "%016llX", + (unsigned long long)wwnn); + + /* + * if this is the "first" nport of the domain, + * then make it the "phys" nport + */ + if (list_empty(&domain->nport_list)) + domain->nport = nport; + + INIT_LIST_HEAD(&nport->list_entry); + list_add_tail(&nport->list_entry, &domain->nport_list); + + kref_get(&domain->ref); + + efc_log_debug(domain->efc, "New Nport [%s]\n", nport->display_name); + + return nport; +} + +void +efc_nport_free(struct efc_nport *nport) +{ + struct efc_domain *domain; + + if (!nport) + return; + + domain = nport->domain; + efc_log_debug(domain->efc, "[%s] free nport\n", nport->display_name); + list_del(&nport->list_entry); + /* + * if this is the physical nport, + * then clear it out of the domain + */ + if (nport == domain->nport) + domain->nport = NULL; + + xa_destroy(&nport->lookup); + xa_erase(&domain->lookup, nport->fc_id); + + if (list_empty(&domain->nport_list)) + efc_domain_post_event(domain, EFC_EVT_ALL_CHILD_NODES_FREE, + NULL); + + kref_put(&domain->ref, domain->release); + kref_put(&nport->ref, nport->release); + +} + +struct efc_nport * +efc_nport_find(struct efc_domain *domain, u32 d_id) +{ + struct efc_nport *nport; + + /* Find a nport object, given an FC_ID */ + nport = xa_load(&domain->lookup, d_id); + if (!nport || !kref_get_unless_zero(&nport->ref)) + return NULL; + + return nport; +} + +int +efc_nport_attach(struct efc_nport *nport, u32 fc_id) +{ + int rc; + struct efc_node *node; + struct efc *efc = nport->efc; + + /* Set our lookup */ + rc = xa_err(xa_store(&nport->domain->lookup, fc_id, nport, GFP_ATOMIC)); + if (rc) { + efc_log_err(efc, "Sport lookup store failed: %d\n", rc); + return rc; + } + + /* Update our display_name */ + efc_node_fcid_display(fc_id, nport->display_name, + sizeof(nport->display_name)); + + list_for_each_entry(node, &nport->node_list, list_entry) { + efc_node_update_display_name(node); + } + + efc_log_debug(nport->efc, "[%s] attach nport: fc_id x%06x\n", + nport->display_name, fc_id); + + /* Register a nport, given an FC_ID */ + rc = efc_cmd_nport_attach(efc, nport, fc_id); + if (rc != EFC_HW_RTN_SUCCESS) { + efc_log_err(nport->efc, + "efc_hw_port_attach failed: %d\n", rc); + return EFC_FAIL; + } + return EFC_SUCCESS; +} + +static void +efc_nport_shutdown(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + struct efc_node *node; + struct efc_node *node_next; + + list_for_each_entry_safe(node, node_next, + &nport->node_list, list_entry) { + if (!(node->rnode.fc_id == FC_FID_FLOGI && nport->is_vport)) { + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + continue; + } + + /* + * If this is a vport, logout of the fabric + * controller so that it deletes the vport + * on the switch. + */ + /* if link is down, don't send logo */ + if (efc->link_status == EFC_LINK_STATUS_DOWN) { + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + continue; + } + + efc_log_debug(efc, "[%s] nport shutdown vport, send logo\n", + node->display_name); + + if (!efc_send_logo(node)) { + /* sent LOGO, wait for response */ + efc_node_transition(node, __efc_d_wait_logo_rsp, NULL); + continue; + } + + /* + * failed to send LOGO, + * go ahead and cleanup node anyways + */ + node_printf(node, "Failed to send LOGO\n"); + efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); + } +} + +static void +efc_vport_link_down(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + struct efc_vport_spec *vport; + + /* Clear the nport reference in the vport specification */ + list_for_each_entry(vport, &efc->vport_list, list_entry) { + if (vport->nport == nport) { + kref_put(&nport->ref, nport->release); + vport->nport = NULL; + break; + } + } +} + +static void +__efc_nport_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc_domain *domain = nport->domain; + struct efc *efc = nport->efc; + + switch (evt) { + case EFC_EVT_ENTER: + case EFC_EVT_REENTER: + case EFC_EVT_EXIT: + case EFC_EVT_ALL_CHILD_NODES_FREE: + break; + case EFC_EVT_SPORT_ATTACH_OK: + efc_sm_transition(ctx, __efc_nport_attached, NULL); + break; + case EFC_EVT_SHUTDOWN: { + int node_list_empty; + + /* Flag this nport as shutting down */ + nport->shutting_down = true; + + if (nport->is_vport) + efc_vport_link_down(nport); + + node_list_empty = list_empty(&nport->node_list); + + if (node_list_empty) { + /* Remove the nport from the domain's lookup table */ + xa_erase(&domain->lookup, nport->fc_id); + efc_sm_transition(ctx, __efc_nport_wait_port_free, + NULL); + if (efc_cmd_nport_free(efc, nport)) { + efc_log_test(nport->efc, + "efc_hw_port_free failed\n"); + /* Not much we can do, free the nport anyways */ + efc_nport_free(nport); + } + } else { + /* sm: node list is not empty / shutdown nodes */ + efc_sm_transition(ctx, + __efc_nport_wait_shutdown, NULL); + efc_nport_shutdown(nport); + } + break; + } + default: + efc_log_test(nport->efc, "[%s] %-20s %-20s not handled\n", + nport->display_name, funcname, + efc_sm_event_name(evt)); + } +} + +void +__efc_nport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc_domain *domain = nport->domain; + + nport_sm_trace(nport); + + switch (evt) { + /* the physical nport is attached */ + case EFC_EVT_SPORT_ATTACH_OK: + WARN_ON(nport != domain->nport); + efc_sm_transition(ctx, __efc_nport_attached, NULL); + break; + + case EFC_EVT_SPORT_ALLOC_OK: + /* ignore */ + break; + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_vport_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_ENTER: { + __be64 be_wwpn = cpu_to_be64(nport->wwpn); + + if (nport->wwpn == 0) + efc_log_debug(efc, "vport: letting f/w select WWN\n"); + + if (nport->fc_id != U32_MAX) { + efc_log_debug(efc, "vport: hard coding port id: %x\n", + nport->fc_id); + } + + efc_sm_transition(ctx, __efc_nport_vport_wait_alloc, NULL); + /* If wwpn is zero, then we'll let the f/w */ + if (efc_cmd_nport_alloc(efc, nport, nport->domain, + nport->wwpn == 0 ? NULL : + (uint8_t *)&be_wwpn)) { + efc_log_err(efc, "Can't allocate port\n"); + break; + } + + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_SPORT_ALLOC_OK: { + struct fc_els_flogi *sp; + + sp = (struct fc_els_flogi *)nport->service_params; + /* + * If we let f/w assign wwn's, + * then nport wwn's with those returned by hw + */ + if (nport->wwnn == 0) { + nport->wwnn = be64_to_cpu(nport->sli_wwnn); + nport->wwpn = be64_to_cpu(nport->sli_wwpn); + snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), + "%016llX", nport->wwpn); + } + + /* Update the nport's service parameters */ + sp->fl_wwpn = cpu_to_be64(nport->wwpn); + sp->fl_wwnn = cpu_to_be64(nport->wwnn); + + /* + * if nport->fc_id is uninitialized, + * then request that the fabric node use FDISC + * to find an fc_id. + * Otherwise we're restoring vports, or we're in + * fabric emulation mode, so attach the fc_id + */ + if (nport->fc_id == U32_MAX) { + struct efc_node *fabric; + + fabric = efc_node_alloc(nport, FC_FID_FLOGI, false, + false); + if (!fabric) { + efc_log_err(efc, "efc_node_alloc() failed\n"); + return; + } + efc_node_transition(fabric, __efc_vport_fabric_init, + NULL); + } else { + snprintf(nport->wwnn_str, sizeof(nport->wwnn_str), + "%016llX", nport->wwpn); + efc_nport_attach(nport, nport->fc_id); + } + efc_sm_transition(ctx, __efc_nport_vport_allocated, NULL); + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_vport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + /* + * This state is entered after the nport is allocated; + * it then waits for a fabric node + * FDISC to complete, which requests a nport attach. + * The nport attach complete is handled in this state. + */ + switch (evt) { + case EFC_EVT_SPORT_ATTACH_OK: { + struct efc_node *node; + + /* Find our fabric node, and forward this event */ + node = efc_node_find(nport, FC_FID_FLOGI); + if (!node) { + efc_log_test(efc, "can't find node %06x\n", + FC_FID_FLOGI); + break; + } + /* sm: / forward nport attach to fabric node */ + efc_node_post_event(node, evt, NULL); + efc_sm_transition(ctx, __efc_nport_attached, NULL); + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +static void +efc_vport_update_spec(struct efc_nport *nport) +{ + struct efc *efc = nport->efc; + struct efc_vport_spec *vport; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry(vport, &efc->vport_list, list_entry) { + if (vport->nport == nport) { + vport->wwnn = nport->wwnn; + vport->wwpn = nport->wwpn; + vport->tgt_data = nport->tgt_data; + vport->ini_data = nport->ini_data; + break; + } + } + spin_unlock_irqrestore(&efc->vport_lock, flags); +} + +void +__efc_nport_attached(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_ENTER: { + struct efc_node *node; + + efc_log_debug(efc, + "[%s] SPORT attached WWPN %016llX WWNN %016llX\n", + nport->display_name, + nport->wwpn, nport->wwnn); + + list_for_each_entry(node, &nport->node_list, list_entry) { + efc_node_update_display_name(node); + } + + nport->tgt_id = nport->fc_id; + + efc->tt.new_nport(efc, nport); + + /* + * Update the vport (if its not the physical nport) + * parameters + */ + if (nport->is_vport) + efc_vport_update_spec(nport); + break; + } + + case EFC_EVT_EXIT: + efc_log_debug(efc, + "[%s] SPORT deattached WWPN %016llX WWNN %016llX\n", + nport->display_name, + nport->wwpn, nport->wwnn); + + efc->tt.del_nport(efc, nport); + break; + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + + +void +__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + struct efc_domain *domain = nport->domain; + struct efc *efc = nport->efc; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_SPORT_ALLOC_OK: + case EFC_EVT_SPORT_ALLOC_FAIL: + case EFC_EVT_SPORT_ATTACH_OK: + case EFC_EVT_SPORT_ATTACH_FAIL: + /* ignore these events - just wait for the all free event */ + break; + + case EFC_EVT_ALL_CHILD_NODES_FREE: { + /* + * Remove the nport from the domain's + * sparse vector lookup table + */ + xa_erase(&domain->lookup, nport->fc_id); + efc_sm_transition(ctx, __efc_nport_wait_port_free, NULL); + if (efc_cmd_nport_free(efc, nport)) { + efc_log_err(nport->efc, "efc_hw_port_free failed\n"); + /* Not much we can do, free the nport anyways */ + efc_nport_free(nport); + } + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +void +__efc_nport_wait_port_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_nport *nport = ctx->app; + + nport_sm_trace(nport); + + switch (evt) { + case EFC_EVT_SPORT_ATTACH_OK: + /* Ignore as we are waiting for the free CB */ + break; + case EFC_EVT_SPORT_FREE_OK: { + /* All done, free myself */ + efc_nport_free(nport); + break; + } + default: + __efc_nport_common(__func__, ctx, evt, arg); + } +} + +static int +efc_vport_nport_alloc(struct efc_domain *domain, struct efc_vport_spec *vport) +{ + struct efc_nport *nport; + + lockdep_assert_held(&domain->efc->lock); + + nport = efc_nport_alloc(domain, vport->wwpn, vport->wwnn, vport->fc_id, + vport->enable_ini, vport->enable_tgt); + vport->nport = nport; + if (!nport) + return EFC_FAIL; + + kref_get(&nport->ref); + nport->is_vport = true; + nport->tgt_data = vport->tgt_data; + nport->ini_data = vport->ini_data; + + efc_sm_transition(&nport->sm, __efc_nport_vport_init, NULL); + + return EFC_SUCCESS; +} + +int +efc_vport_start(struct efc_domain *domain) +{ + struct efc *efc = domain->efc; + struct efc_vport_spec *vport; + struct efc_vport_spec *next; + int rc = EFC_SUCCESS; + unsigned long flags = 0; + + /* Use the vport spec to find the associated vports and start them */ + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) { + if (!vport->nport) { + if (efc_vport_nport_alloc(domain, vport)) + rc = EFC_FAIL; + } + } + spin_unlock_irqrestore(&efc->vport_lock, flags); + + return rc; +} + +int +efc_nport_vport_new(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn, + u32 fc_id, bool ini, bool tgt, void *tgt_data, + void *ini_data) +{ + struct efc *efc = domain->efc; + struct efc_vport_spec *vport; + int rc = EFC_SUCCESS; + unsigned long flags = 0; + + if (ini && domain->efc->enable_ini == 0) { + efc_log_debug(efc, + "driver initiator functionality not enabled\n"); + return EFC_FAIL; + } + + if (tgt && domain->efc->enable_tgt == 0) { + efc_log_debug(efc, + "driver target functionality not enabled\n"); + return EFC_FAIL; + } + + /* + * Create a vport spec if we need to recreate + * this vport after a link up event + */ + vport = efc_vport_create_spec(domain->efc, wwnn, wwpn, fc_id, ini, tgt, + tgt_data, ini_data); + if (!vport) { + efc_log_err(efc, "failed to create vport object entry\n"); + return EFC_FAIL; + } + + spin_lock_irqsave(&efc->lock, flags); + rc = efc_vport_nport_alloc(domain, vport); + spin_unlock_irqrestore(&efc->lock, flags); + + return rc; +} + +int +efc_nport_vport_del(struct efc *efc, struct efc_domain *domain, + u64 wwpn, uint64_t wwnn) +{ + struct efc_nport *nport; + int found = 0; + struct efc_vport_spec *vport; + struct efc_vport_spec *next; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->vport_lock, flags); + /* walk the efc_vport_list and remove from there */ + list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) { + if (vport->wwpn == wwpn && vport->wwnn == wwnn) { + list_del(&vport->list_entry); + kfree(vport); + break; + } + } + spin_unlock_irqrestore(&efc->vport_lock, flags); + + if (!domain) { + /* No domain means no nport to look for */ + return EFC_SUCCESS; + } + + spin_lock_irqsave(&efc->lock, flags); + list_for_each_entry(nport, &domain->nport_list, list_entry) { + if (nport->wwpn == wwpn && nport->wwnn == wwnn) { + found = 1; + break; + } + } + + if (found) { + kref_put(&nport->ref, nport->release); + /* Shutdown this SPORT */ + efc_sm_post_event(&nport->sm, EFC_EVT_SHUTDOWN, NULL); + } + spin_unlock_irqrestore(&efc->lock, flags); + return EFC_SUCCESS; +} + +void +efc_vport_del_all(struct efc *efc) +{ + struct efc_vport_spec *vport; + struct efc_vport_spec *next; + unsigned long flags = 0; + + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry_safe(vport, next, &efc->vport_list, list_entry) { + list_del(&vport->list_entry); + kfree(vport); + } + spin_unlock_irqrestore(&efc->vport_lock, flags); +} + +struct efc_vport_spec * +efc_vport_create_spec(struct efc *efc, uint64_t wwnn, uint64_t wwpn, + u32 fc_id, bool enable_ini, + bool enable_tgt, void *tgt_data, void *ini_data) +{ + struct efc_vport_spec *vport; + unsigned long flags = 0; + + /* + * walk the efc_vport_list and return failure + * if a valid(vport with non zero WWPN and WWNN) vport entry + * is already created + */ + spin_lock_irqsave(&efc->vport_lock, flags); + list_for_each_entry(vport, &efc->vport_list, list_entry) { + if ((wwpn && vport->wwpn == wwpn) && + (wwnn && vport->wwnn == wwnn)) { + efc_log_err(efc, + "Failed: VPORT %016llX %016llX already allocated\n", + wwnn, wwpn); + spin_unlock_irqrestore(&efc->vport_lock, flags); + return NULL; + } + } + + vport = kzalloc(sizeof(*vport), GFP_ATOMIC); + if (!vport) { + spin_unlock_irqrestore(&efc->vport_lock, flags); + return NULL; + } + + vport->wwnn = wwnn; + vport->wwpn = wwpn; + vport->fc_id = fc_id; + vport->enable_tgt = enable_tgt; + vport->enable_ini = enable_ini; + vport->tgt_data = tgt_data; + vport->ini_data = ini_data; + + INIT_LIST_HEAD(&vport->list_entry); + list_add_tail(&vport->list_entry, &efc->vport_list); + spin_unlock_irqrestore(&efc->vport_lock, flags); + return vport; +} diff --git a/drivers/scsi/elx/libefc/efc_nport.h b/drivers/scsi/elx/libefc/efc_nport.h new file mode 100644 index 000000000000..7201e2778855 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_nport.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/** + * EFC FC port (NPORT) exported declarations + * + */ + +#ifndef __EFC_NPORT_H__ +#define __EFC_NPORT_H__ + +struct efc_nport * +efc_nport_find(struct efc_domain *domain, u32 d_id); +struct efc_nport * +efc_nport_alloc(struct efc_domain *domain, uint64_t wwpn, uint64_t wwnn, + u32 fc_id, bool enable_ini, bool enable_tgt); +void +efc_nport_free(struct efc_nport *nport); +int +efc_nport_attach(struct efc_nport *nport, u32 fc_id); + +void +__efc_nport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_wait_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_wait_port_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_vport_init(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_vport_wait_alloc(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_vport_allocated(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_nport_attached(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +int +efc_vport_start(struct efc_domain *domain); + +#endif /* __EFC_NPORT_H__ */ From patchwork Mon Oct 12 22:51:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269824 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1041CC4363A for ; Mon, 12 Oct 2020 22:52:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A8E9920838 for ; Mon, 12 Oct 2020 22:52:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="MqqY1DmF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390632AbgJLWwQ (ORCPT ); Mon, 12 Oct 2020 18:52:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60568 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390607AbgJLWwO (ORCPT ); Mon, 12 Oct 2020 18:52:14 -0400 Received: from mail-pf1-x42e.google.com (mail-pf1-x42e.google.com [IPv6:2607:f8b0:4864:20::42e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3E160C0613D5 for ; Mon, 12 Oct 2020 15:52:14 -0700 (PDT) Received: by mail-pf1-x42e.google.com with SMTP id b26so15128299pff.3 for ; Mon, 12 Oct 2020 15:52:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=aIBYL/29Hg0znowBekH8V2SoqpR3J3ZFsoDXBM5xXQw=; b=MqqY1DmFPLvRCC25fJRgksNQA/rtPqJtwHRkLik56F9ycnCRVJeSA/ps4lsvTsM0RD Oyec4OiZtpjufFCDRJGSvKw9jSVr5Uyb9e0L8u79jxTARyh0fzfhZfHcETervZSugUr2 7Cn5d3BI2LJY1PjMJNaLxQq2zqldLK5Zw3oDQ= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=aIBYL/29Hg0znowBekH8V2SoqpR3J3ZFsoDXBM5xXQw=; b=OKN60+vDesykkaGHwZnX2bNZy3PlILeSKuJZJAE86Au4rAAt0TiUmbGlHPueFusly6 TghgcHYSb+Ea0yvJZ3twosaRCnLc199A8bQNpN9CGMxGY9QIxAa58bxs/ly5t6lC39au u0KOe3tUF5eKncAnhVNfOm9YceaYbeaPvtC4sh7A9p5t8ee830mOQ8gATSAwSdBHEAXn 83lI9ye8lVVjpEh6iDbpiSO1t9JVrjeRuAybdeFKUcNMb3sxCELzBgH8hFVVJsdNPV3W SBkNo+4ba5j04LrIBs4fMSJdE2+ElT0nQs/gYRVn7PfwAXxJxckgvnG2p/n5titvqvSq OCVA== X-Gm-Message-State: AOAM531c5MIGwq4G20qsuC08IdgVsXjda1IcahlQDKl4NUu8bsfewTEI bDbJIP8hUX/si9fty9W1HTz5AtP9jkAZ2zECepbu1zC0lVLFss/YGTHWFPUJ63fW+lvjstc2d4z uxyT1nMyFMmWVOmI71Gurxq2halNYacOfDjJrDj8T+uohN9Z2BQ5J8++XJ/2bG7rIsN4hYE4UDD hqTG4= X-Google-Smtp-Source: ABdhPJwxlfw3JC/Sackq8nnsPweU8C20NKzqKS+hw4xdMZNrrUDjg+ERoUEF5i8lHQTy/YiDY7f6WQ== X-Received: by 2002:a17:90a:fa86:: with SMTP id cu6mr12967194pjb.167.1602543132702; Mon, 12 Oct 2020 15:52:12 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:11 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 12/31] elx: libefc: Remote node state machine interfaces Date: Mon, 12 Oct 2020 15:51:28 -0700 Message-Id: <20201012225147.54404-13-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc library population. This patch adds library interface definitions for: - Remote node (aka remote port) allocation, initializaion and destroy routines. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- v4: Upon received PRLI, Wait for LIO session registation before sending the PRLI response. Changes to call new els functions. --- drivers/scsi/elx/libefc/efc_node.c | 1157 ++++++++++++++++++++++++++++ drivers/scsi/elx/libefc/efc_node.h | 191 +++++ 2 files changed, 1348 insertions(+) create mode 100644 drivers/scsi/elx/libefc/efc_node.c create mode 100644 drivers/scsi/elx/libefc/efc_node.h diff --git a/drivers/scsi/elx/libefc/efc_node.c b/drivers/scsi/elx/libefc/efc_node.c new file mode 100644 index 000000000000..9d9404eee216 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_node.c @@ -0,0 +1,1157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efc.h" + +int +efc_remote_node_cb(void *arg, int event, + void *data) +{ + struct efc *efc = arg; + enum efc_sm_event sm_event = EFC_EVT_LAST; + struct efc_remote_node *rnode = data; + struct efc_node *node = rnode->node; + unsigned long flags = 0; + + /* HW node callback events from the user driver */ + switch (event) { + case EFC_HW_NODE_ATTACH_OK: + sm_event = EFC_EVT_NODE_ATTACH_OK; + break; + + case EFC_HW_NODE_ATTACH_FAIL: + sm_event = EFC_EVT_NODE_ATTACH_FAIL; + break; + + case EFC_HW_NODE_FREE_OK: + sm_event = EFC_EVT_NODE_FREE_OK; + break; + + case EFC_HW_NODE_FREE_FAIL: + sm_event = EFC_EVT_NODE_FREE_FAIL; + break; + + default: + efc_log_test(efc, "unhandled event %#x\n", event); + return EFC_FAIL; + } + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, sm_event, NULL); + spin_unlock_irqrestore(&efc->lock, flags); + + return EFC_SUCCESS; +} + +struct efc_node * +efc_node_find(struct efc_nport *nport, u32 port_id) +{ + /* Find an FC node structure given the FC port ID */ + return xa_load(&nport->lookup, port_id); +} + +static void +_efc_node_free(struct kref *arg) +{ + struct efc_node *node = container_of(arg, struct efc_node, ref); + struct efc *efc = node->efc; + struct efc_dma *dma; + + dma = &node->sparm_dma_buf; + dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); + memset(dma, 0, sizeof(struct efc_dma)); + mempool_free(node, efc->node_pool); +} + +struct efc_node *efc_node_alloc(struct efc_nport *nport, + u32 port_id, bool init, bool targ) +{ + int rc; + struct efc_node *node = NULL; + struct efc *efc = nport->efc; + struct efc_dma *dma; + + if (nport->shutting_down) { + efc_log_debug(efc, "node allocation when shutting down %06x", + port_id); + return NULL; + } + + node = mempool_alloc(efc->node_pool, GFP_ATOMIC); + if (!node) { + efc_log_err(efc, "node allocation failed %06x", port_id); + return NULL; + } + memset(node, 0, sizeof(*node)); + + dma = &node->sparm_dma_buf; + dma->size = NODE_SPARAMS_SIZE; + dma->virt = dma_pool_zalloc(efc->node_dma_pool, GFP_ATOMIC, &dma->phys); + if (!dma->virt) { + efc_log_err(efc, "node dma alloc failed\n"); + goto dma_fail; + } + node->rnode.indicator = U32_MAX; + node->nport = nport; + INIT_LIST_HEAD(&node->list_entry); + list_add_tail(&node->list_entry, &nport->node_list); + + node->efc = efc; + node->init = init; + node->targ = targ; + + spin_lock_init(&node->pend_frames_lock); + INIT_LIST_HEAD(&node->pend_frames); + spin_lock_init(&node->els_ios_lock); + INIT_LIST_HEAD(&node->els_ios_list); + efc_io_alloc_enable(node); + + rc = efc_cmd_node_alloc(efc, &node->rnode, port_id, nport); + if (rc) { + efc_log_err(efc, "efc_hw_node_alloc failed: %d\n", rc); + goto hw_alloc_fail; + } + + node->rnode.node = node; + node->sm.app = node; + node->evtdepth = 0; + + efc_node_update_display_name(node); + + rc = xa_err(xa_store(&nport->lookup, port_id, node, GFP_ATOMIC)); + if (rc) { + efc_log_err(efc, "Node lookup store failed: %d\n", rc); + goto xa_fail; + } + + /* initialize refcount */ + kref_init(&node->ref); + node->release = _efc_node_free; + kref_get(&nport->ref); + + return node; + +xa_fail: + efc_node_free_resources(efc, &node->rnode); +hw_alloc_fail: + list_del(&node->list_entry); + dma_pool_free(efc->node_dma_pool, dma->virt, dma->phys); +dma_fail: + mempool_free(node, efc->node_pool); + return NULL; +} + +void +efc_node_free(struct efc_node *node) +{ + struct efc_nport *nport; + struct efc *efc; + int rc = 0; + struct efc_node *ns = NULL; + + nport = node->nport; + efc = node->efc; + + node_printf(node, "Free'd\n"); + + if (node->refound) { + /* + * Save the name server node. We will send fake RSCN event at + * the end to handle ignored RSCN event during node deletion + */ + ns = efc_node_find(node->nport, FC_FID_DIR_SERV); + } + + if (!node->nport) { + efc_log_err(efc, "Node already Freed\n"); + return; + } + + list_del(&node->list_entry); + + /* Free HW resources */ + rc = efc_node_free_resources(efc, &node->rnode); + if (EFC_HW_RTN_IS_ERROR(rc)) + efc_log_err(efc, "efc_hw_node_free failed: %d\n", rc); + + /* if the gidpt_delay_timer is still running, then delete it */ + if (timer_pending(&node->gidpt_delay_timer)) + del_timer(&node->gidpt_delay_timer); + + xa_erase(&nport->lookup, node->rnode.fc_id); + + /* + * If the node_list is empty, + * then post a ALL_CHILD_NODES_FREE event to the nport, + * after the lock is released. + * The nport may be free'd as a result of the event. + */ + if (list_empty(&nport->node_list)) + efc_sm_post_event(&nport->sm, EFC_EVT_ALL_CHILD_NODES_FREE, + NULL); + + node->nport = NULL; + node->sm.current_state = NULL; + + kref_put(&nport->ref, nport->release); + kref_put(&node->ref, node->release); + + if (ns) { + /* sending fake RSCN event to name server node */ + efc_node_post_event(ns, EFC_EVT_RSCN_RCVD, NULL); + } +} + +static void +efc_dma_copy_in(struct efc_dma *dma, void *buffer, u32 buffer_length) +{ + if (!dma || !buffer || !buffer_length) + return; + + if (buffer_length > dma->size) + buffer_length = dma->size; + + memcpy(dma->virt, buffer, buffer_length); + dma->len = buffer_length; +} + +int +efc_node_attach(struct efc_node *node) +{ + int rc = 0; + struct efc_nport *nport = node->nport; + struct efc_domain *domain = nport->domain; + struct efc *efc = node->efc; + + if (!domain->attached) { + efc_log_err(efc, "Warning: unattached domain\n"); + return EFC_FAIL; + } + /* Update node->wwpn/wwnn */ + + efc_node_build_eui_name(node->wwpn, sizeof(node->wwpn), + efc_node_get_wwpn(node)); + efc_node_build_eui_name(node->wwnn, sizeof(node->wwnn), + efc_node_get_wwnn(node)); + + efc_dma_copy_in(&node->sparm_dma_buf, node->service_params + 4, + sizeof(node->service_params) - 4); + + /* take lock to protect node->rnode.attached */ + rc = efc_cmd_node_attach(efc, &node->rnode, &node->sparm_dma_buf); + if (EFC_HW_RTN_IS_ERROR(rc)) + efc_log_test(efc, "efc_hw_node_attach failed: %d\n", rc); + + return rc; +} + +void +efc_node_fcid_display(u32 fc_id, char *buffer, u32 buffer_length) +{ + switch (fc_id) { + case FC_FID_FLOGI: + snprintf(buffer, buffer_length, "fabric"); + break; + case FC_FID_FCTRL: + snprintf(buffer, buffer_length, "fabctl"); + break; + case FC_FID_DIR_SERV: + snprintf(buffer, buffer_length, "nserve"); + break; + default: + if (fc_id == FC_FID_DOM_MGR) { + snprintf(buffer, buffer_length, "dctl%02x", + (fc_id & 0x0000ff)); + } else { + snprintf(buffer, buffer_length, "%06x", fc_id); + } + break; + } +} + +void +efc_node_update_display_name(struct efc_node *node) +{ + u32 port_id = node->rnode.fc_id; + struct efc_nport *nport = node->nport; + char portid_display[16]; + + efc_node_fcid_display(port_id, portid_display, sizeof(portid_display)); + + snprintf(node->display_name, sizeof(node->display_name), "%s.%s", + nport->display_name, portid_display); +} + +void +efc_node_send_ls_io_cleanup(struct efc_node *node) +{ + if (node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE) { + efc_log_debug(node->efc, "[%s] cleaning up LS_ACC oxid=0x%x\n", + node->display_name, node->ls_acc_oxid); + + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + } +} + +static void efc_node_handle_implicit_logo(struct efc_node *node) +{ + int rc; + + /* + * currently, only case for implicit logo is PLOGI + * recvd. Thus, node's ELS IO pending list won't be + * empty (PLOGI will be on it) + */ + WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); + node_printf(node, "Reason: implicit logout, re-authenticate\n"); + + /* Re-attach node with the same HW node resources */ + node->req_free = false; + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, NULL); + +} + +static void efc_node_handle_explicit_logo(struct efc_node *node) +{ + s8 pend_frames_empty; + unsigned long flags = 0; + + /* cleanup any pending LS_ACC ELSs */ + efc_node_send_ls_io_cleanup(node); + + spin_lock_irqsave(&node->pend_frames_lock, flags); + pend_frames_empty = list_empty(&node->pend_frames); + spin_unlock_irqrestore(&node->pend_frames_lock, flags); + + /* + * there are two scenarios where we want to keep + * this node alive: + * 1. there are pending frames that need to be + * processed or + * 2. we're an initiator and the remote node is + * a target and we need to re-authenticate + */ + node_printf(node, "Shutdown: explicit logo pend=%d ", + !pend_frames_empty); + node_printf(node, "nport.ini=%d node.tgt=%d\n", + node->nport->enable_ini, node->targ); + if (!pend_frames_empty || (node->nport->enable_ini && node->targ)) { + u8 send_plogi = false; + + if (node->nport->enable_ini && node->targ) { + /* + * we're an initiator and + * node shutting down is a target; + * we'll need to re-authenticate in + * initial state + */ + send_plogi = true; + } + + /* + * transition to __efc_d_init + * (will retain HW node resources) + */ + node->req_free = false; + + /* + * either pending frames exist, + * or we're re-authenticating with PLOGI + * (or both); in either case, + * return to initial state + */ + efc_node_init_device(node, send_plogi); + } + /* else: let node shutdown occur */ +} + +static void +efc_node_purge_pending(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_hw_sequence *frame, *next; + unsigned long flags = 0; + + spin_lock_irqsave(&node->pend_frames_lock, flags); + + list_for_each_entry_safe(frame, next, &node->pend_frames, list_entry) { + list_del(&frame->list_entry); + efc->tt.hw_seq_free(efc, frame); + } + + spin_unlock_irqrestore(&node->pend_frames_lock, flags); +} + +void +__efc_node_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + efc_node_hold_frames(node); + WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); + /* by default, we will be freeing node after we unwind */ + node->req_free = true; + + switch (node->shutdown_reason) { + case EFC_NODE_SHUTDOWN_IMPLICIT_LOGO: + /* Node shutdown b/c of PLOGI received when node + * already logged in. We have PLOGI service + * parameters, so submit node attach; we won't be + * freeing this node + */ + + efc_node_handle_implicit_logo(node); + break; + + case EFC_NODE_SHUTDOWN_EXPLICIT_LOGO: + efc_node_handle_explicit_logo(node); + break; + + case EFC_NODE_SHUTDOWN_DEFAULT: + default: { + /* + * shutdown due to link down, + * node going away (xport event) or + * nport shutdown, purge pending and + * proceed to cleanup node + */ + + /* cleanup any pending LS_ACC ELSs */ + efc_node_send_ls_io_cleanup(node); + + node_printf(node, + "Shutdown reason: default, purge pending\n"); + efc_node_purge_pending(node); + break; + } + } + + break; + } + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +static bool +efc_node_check_els_quiesced(struct efc_node *node) +{ + /* check to see if ELS requests, completions are quiesced */ + if (node->els_req_cnt == 0 && node->els_cmpl_cnt == 0 && + efc_els_io_list_empty(node, &node->els_ios_list)) { + if (!node->attached) { + /* hw node detach already completed, proceed */ + node_printf(node, "HW node not attached\n"); + efc_node_transition(node, + __efc_node_wait_ios_shutdown, + NULL); + } else { + /* + * hw node detach hasn't completed, + * transition and wait + */ + node_printf(node, "HW node still attached\n"); + efc_node_transition(node, __efc_node_wait_node_free, + NULL); + } + return true; + } + return false; +} + +void +efc_node_initiate_cleanup(struct efc_node *node) +{ + /* + * if ELS's have already been quiesced, will move to next state + * if ELS's have not been quiesced, abort them + */ + if (!efc_node_check_els_quiesced(node)) { + efc_node_hold_frames(node); + efc_node_transition(node, __efc_node_wait_els_shutdown, NULL); + } +} + +void +__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + bool check_quiesce = false; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + /* Node state machine: Wait for all ELSs to complete */ + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + if (efc_els_io_list_empty(node, &node->els_ios_list)) { + node_printf(node, "All ELS IOs complete\n"); + check_quiesce = true; + } + break; + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: + case EFC_EVT_SRRS_ELS_REQ_FAIL: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_ELS_REQ_ABORTED: + if (WARN_ON(!node->els_req_cnt)) + break; + node->els_req_cnt--; + check_quiesce = true; + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + if (WARN_ON(!node->els_cmpl_cnt)) + break; + node->els_cmpl_cnt--; + check_quiesce = true; + break; + + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* all ELS IO's complete */ + node_printf(node, "All ELS IOs complete\n"); + WARN_ON(!efc_els_io_list_empty(node, &node->els_ios_list)); + check_quiesce = true; + break; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + check_quiesce = true; + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + + default: + __efc_node_common(__func__, ctx, evt, arg); + } + + if (check_quiesce) + efc_node_check_els_quiesced(node); +} + +void +__efc_node_wait_node_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NODE_FREE_OK: + /* node is officially no longer attached */ + node->attached = false; + efc_node_transition(node, __efc_node_wait_ios_shutdown, NULL); + break; + + case EFC_EVT_ALL_CHILD_NODES_FREE: + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + /* As IOs and ELS IO's complete we expect to get these events */ + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +void +__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + + /* first check to see if no ELS IOs are outstanding */ + if (efc_els_io_list_empty(node, &node->els_ios_list)) + /* If there are any active IOS, Free them. */ + efc_node_transition(node, __efc_node_shutdown, NULL); + break; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + case EFC_EVT_ALL_CHILD_NODES_FREE: + if (efc_els_io_list_empty(node, &node->els_ios_list)) + efc_node_transition(node, __efc_node_shutdown, NULL); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* Can happen as ELS IO IO's complete */ + if (WARN_ON(!node->els_req_cnt)) + break; + node->els_req_cnt--; + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + efc_log_debug(efc, "[%s] %-20s\n", node->display_name, + efc_sm_event_name(evt)); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +void +__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = NULL; + struct efc *efc = NULL; + struct efc_node_cb *cbdata = arg; + + node = ctx->app; + efc = node->efc; + + switch (evt) { + case EFC_EVT_ENTER: + case EFC_EVT_REENTER: + case EFC_EVT_EXIT: + case EFC_EVT_SPORT_TOPOLOGY_NOTIFY: + case EFC_EVT_NODE_MISSING: + case EFC_EVT_FCP_CMD_RCVD: + break; + + case EFC_EVT_NODE_REFOUND: + node->refound = true; + break; + + /* + * node->attached must be set appropriately + * for all node attach/detach events + */ + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + break; + + case EFC_EVT_NODE_FREE_OK: + case EFC_EVT_NODE_ATTACH_FAIL: + node->attached = false; + break; + + /* + * handle any ELS completions that + * other states either didn't care about + * or forgot about + */ + case EFC_EVT_SRRS_ELS_CMPL_OK: + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + if (WARN_ON(!node->els_cmpl_cnt)) + break; + node->els_cmpl_cnt--; + break; + + /* + * handle any ELS request completions that + * other states either didn't care about + * or forgot about + */ + case EFC_EVT_SRRS_ELS_REQ_OK: + case EFC_EVT_SRRS_ELS_REQ_FAIL: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_ELS_REQ_ABORTED: + if (WARN_ON(!node->els_req_cnt)) + break; + node->els_req_cnt--; + break; + + case EFC_EVT_ELS_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + /* + * Unsupported ELS was received, + * send LS_RJT, command not supported + */ + efc_log_debug(efc, + "[%s] (%s) ELS x%02x, LS_RJT not supported\n", + node->display_name, funcname, + ((uint8_t *)cbdata->payload->dma.virt)[0]); + + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNSUP, ELS_EXPL_NONE, 0); + break; + } + + case EFC_EVT_PLOGI_RCVD: + case EFC_EVT_FLOGI_RCVD: + case EFC_EVT_LOGO_RCVD: + case EFC_EVT_PRLI_RCVD: + case EFC_EVT_PRLO_RCVD: + case EFC_EVT_PDISC_RCVD: + case EFC_EVT_FDISC_RCVD: + case EFC_EVT_ADISC_RCVD: + case EFC_EVT_RSCN_RCVD: + case EFC_EVT_SCR_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + /* sm: / send ELS_RJT */ + efc_log_debug(efc, "[%s] (%s) %s sending ELS_RJT\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + /* if we didn't catch this in a state, send generic LS_RJT */ + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_NONE, 0); + break; + } + case EFC_EVT_ABTS_RCVD: { + efc_log_debug(efc, "[%s] (%s) %s sending BA_ACC\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + + /* sm: / send BA_ACC */ + efc_send_bls_acc(node, cbdata->header->dma.virt); + break; + } + + default: + efc_log_test(node->efc, "[%s] %-20s %-20s not handled\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + } +} + +void +efc_node_save_sparms(struct efc_node *node, void *payload) +{ + memcpy(node->service_params, payload, sizeof(node->service_params)); +} + +void +efc_node_post_event(struct efc_node *node, + enum efc_sm_event evt, void *arg) +{ + bool free_node = false; + + node->evtdepth++; + + efc_sm_post_event(&node->sm, evt, arg); + + /* If our event call depth is one and + * we're not holding frames + * then we can dispatch any pending frames. + * We don't want to allow the efc_process_node_pending() + * call to recurse. + */ + if (!node->hold_frames && node->evtdepth == 1) + efc_process_node_pending(node); + + node->evtdepth--; + + /* + * Free the node object if so requested, + * and we're at an event call depth of zero + */ + if (node->evtdepth == 0 && node->req_free) + free_node = true; + + if (free_node) + efc_node_free(node); +} + +void +efc_node_transition(struct efc_node *node, + void (*state)(struct efc_sm_ctx *, + enum efc_sm_event, void *), void *data) +{ + struct efc_sm_ctx *ctx = &node->sm; + + if (ctx->current_state == state) { + efc_node_post_event(node, EFC_EVT_REENTER, data); + } else { + efc_node_post_event(node, EFC_EVT_EXIT, data); + ctx->current_state = state; + efc_node_post_event(node, EFC_EVT_ENTER, data); + } +} + +void +efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name) +{ + memset(buf, 0, buf_len); + + snprintf(buf, buf_len, "eui.%016llX", (unsigned long long)eui_name); +} + +u64 +efc_node_get_wwpn(struct efc_node *node) +{ + struct fc_els_flogi *sp = + (struct fc_els_flogi *)node->service_params; + + return be64_to_cpu(sp->fl_wwpn); +} + +u64 +efc_node_get_wwnn(struct efc_node *node) +{ + struct fc_els_flogi *sp = + (struct fc_els_flogi *)node->service_params; + + return be64_to_cpu(sp->fl_wwnn); +} + +int +efc_node_check_els_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg, + uint8_t cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname) +{ + return 0; +} + +int +efc_node_check_ns_req(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg, + uint16_t cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname) +{ + return 0; +} + +int +efc_els_io_list_empty(struct efc_node *node, struct list_head *list) +{ + int empty; + unsigned long flags = 0; + + spin_lock_irqsave(&node->els_ios_lock, flags); + empty = list_empty(list); + spin_unlock_irqrestore(&node->els_ios_lock, flags); + return empty; +} + +void +efc_node_pause(struct efc_node *node, + void (*state)(struct efc_sm_ctx *, + enum efc_sm_event, void *)) + +{ + node->nodedb_state = state; + efc_node_transition(node, __efc_node_paused, NULL); +} + +void +__efc_node_paused(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * This state is entered when a state is "paused". When resumed, the + * node is transitioned to a previously saved state (node->ndoedb_state) + */ + switch (evt) { + case EFC_EVT_ENTER: + node_printf(node, "Paused\n"); + break; + + case EFC_EVT_RESUME: { + void (*pf)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + + pf = node->nodedb_state; + + node->nodedb_state = NULL; + efc_node_transition(node, pf, NULL); + break; + } + + case EFC_EVT_DOMAIN_ATTACH_OK: + break; + + case EFC_EVT_SHUTDOWN: + node->req_free = true; + break; + + default: + __efc_node_common(__func__, ctx, evt, arg); + } +} + +void +efc_node_recv_els_frame(struct efc_node *node, + struct efc_hw_sequence *seq) +{ + u32 prli_size = sizeof(struct fc_els_prli) + sizeof(struct fc_els_spp); + struct { + u32 cmd; + enum efc_sm_event evt; + u32 payload_size; + } els_cmd_list[] = { + {ELS_PLOGI, EFC_EVT_PLOGI_RCVD, sizeof(struct fc_els_flogi)}, + {ELS_FLOGI, EFC_EVT_FLOGI_RCVD, sizeof(struct fc_els_flogi)}, + {ELS_LOGO, EFC_EVT_LOGO_RCVD, sizeof(struct fc_els_ls_acc)}, + {ELS_PRLI, EFC_EVT_PRLI_RCVD, prli_size}, + {ELS_PRLO, EFC_EVT_PRLO_RCVD, prli_size}, + {ELS_PDISC, EFC_EVT_PDISC_RCVD, MAX_ACC_REJECT_PAYLOAD}, + {ELS_FDISC, EFC_EVT_FDISC_RCVD, MAX_ACC_REJECT_PAYLOAD}, + {ELS_ADISC, EFC_EVT_ADISC_RCVD, sizeof(struct fc_els_adisc)}, + {ELS_RSCN, EFC_EVT_RSCN_RCVD, MAX_ACC_REJECT_PAYLOAD}, + {ELS_SCR, EFC_EVT_SCR_RCVD, MAX_ACC_REJECT_PAYLOAD}, + }; + struct efc_node_cb cbdata; + u8 *buf = seq->payload->dma.virt; + enum efc_sm_event evt = EFC_EVT_ELS_RCVD; + u32 i; + + memset(&cbdata, 0, sizeof(cbdata)); + cbdata.header = seq->header; + cbdata.payload = seq->payload; + + /* find a matching event for the ELS command */ + for (i = 0; i < ARRAY_SIZE(els_cmd_list); i++) { + if (els_cmd_list[i].cmd == buf[0]) { + evt = els_cmd_list[i].evt; + break; + } + } + + efc_node_post_event(node, evt, &cbdata); +} + +void +efc_node_recv_ct_frame(struct efc_node *node, + struct efc_hw_sequence *seq) +{ + struct fc_ct_hdr *iu = seq->payload->dma.virt; + struct fc_frame_header *hdr = seq->header->dma.virt; + struct efc *efc = node->efc; + u16 gscmd = be16_to_cpu(iu->ct_cmd); + + efc_log_err(efc, "[%s] Received cmd :%x sending CT_REJECT\n", + node->display_name, gscmd); + efc_send_ct_rsp(efc, node, be16_to_cpu(hdr->fh_ox_id), iu, + FC_FS_RJT, FC_FS_RJT_UNSUP, 0); +} + +void +efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq) +{ + struct efc_node_cb cbdata; + + memset(&cbdata, 0, sizeof(cbdata)); + cbdata.header = seq->header; + cbdata.payload = seq->payload; + + efc_node_post_event(node, EFC_EVT_FCP_CMD_RCVD, &cbdata); +} + +void +efc_process_node_pending(struct efc_node *node) +{ + struct efc *efc = node->efc; + struct efc_hw_sequence *seq = NULL; + u32 pend_frames_processed = 0; + unsigned long flags = 0; + + for (;;) { + /* need to check for hold frames condition after each frame + * processed because any given frame could cause a transition + * to a state that holds frames + */ + if (node->hold_frames) + break; + + /* Get next frame/sequence */ + spin_lock_irqsave(&node->pend_frames_lock, flags); + + if (!list_empty(&node->pend_frames)) { + seq = list_first_entry(&node->pend_frames, + struct efc_hw_sequence, list_entry); + list_del(&seq->list_entry); + } + spin_unlock_irqrestore(&node->pend_frames_lock, flags); + + if (!seq) { + pend_frames_processed = node->pend_frames_processed; + node->pend_frames_processed = 0; + break; + } + node->pend_frames_processed++; + + /* now dispatch frame(s) to dispatch function */ + efc_node_dispatch_frame(node, seq); + efc->tt.hw_seq_free(efc, seq); + } + + if (pend_frames_processed != 0) + efc_log_debug(efc, "%u node frames held and processed\n", + pend_frames_processed); +} + +void +efc_scsi_sess_reg_complete(struct efc_node *node, u32 status) +{ + unsigned long flags = 0; + enum efc_sm_event evt = EFC_EVT_NODE_SESS_REG_OK; + + if (status) + evt = EFC_EVT_NODE_SESS_REG_FAIL; + + spin_lock_irqsave(&node->efc->lock, flags); + /* Notify the node to resume */ + efc_node_post_event(node, evt, NULL); + spin_unlock_irqrestore(&node->efc->lock, flags); +} + +void +efc_scsi_del_initiator_complete(struct efc *efc, struct efc_node *node) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + /* Notify the node to resume */ + efc_node_post_event(node, EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void +efc_scsi_del_target_complete(struct efc *efc, struct efc_node *node) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + /* Notify the node to resume */ + efc_node_post_event(node, EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void +efc_scsi_io_list_empty(struct efc *efc, struct efc_node *node) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY, NULL); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void efc_node_post_els_resp(struct efc_node *node, + enum efc_hw_node_els_event evt, void *arg) +{ + enum efc_sm_event sm_event = EFC_EVT_LAST; + struct efc *efc = node->efc; + unsigned long flags = 0; + + switch (evt) { + case EFC_HW_SRRS_ELS_REQ_OK: + sm_event = EFC_EVT_SRRS_ELS_REQ_OK; + break; + case EFC_HW_SRRS_ELS_CMPL_OK: + sm_event = EFC_EVT_SRRS_ELS_CMPL_OK; + break; + case EFC_HW_SRRS_ELS_REQ_FAIL: + sm_event = EFC_EVT_SRRS_ELS_REQ_FAIL; + break; + case EFC_HW_SRRS_ELS_CMPL_FAIL: + sm_event = EFC_EVT_SRRS_ELS_CMPL_FAIL; + break; + case EFC_HW_SRRS_ELS_REQ_RJT: + sm_event = EFC_EVT_SRRS_ELS_REQ_RJT; + break; + case EFC_HW_ELS_REQ_ABORTED: + sm_event = EFC_EVT_ELS_REQ_ABORTED; + break; + default: + efc_log_test(efc, "unhandled event %#x\n", evt); + return; + } + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, sm_event, arg); + spin_unlock_irqrestore(&efc->lock, flags); +} + +void efc_node_post_shutdown(struct efc_node *node, + u32 evt, void *arg) +{ + unsigned long flags = 0; + struct efc *efc = node->efc; + + spin_lock_irqsave(&efc->lock, flags); + efc_node_post_event(node, EFC_EVT_SHUTDOWN, arg); + spin_unlock_irqrestore(&efc->lock, flags); +} diff --git a/drivers/scsi/elx/libefc/efc_node.h b/drivers/scsi/elx/libefc/efc_node.h new file mode 100644 index 000000000000..866a6a3d9196 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_node.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#if !defined(__EFC_NODE_H__) +#define __EFC_NODE_H__ +#include "scsi/fc/fc_ns.h" + +#define EFC_NODEDB_PAUSE_FABRIC_LOGIN (1 << 0) +#define EFC_NODEDB_PAUSE_NAMESERVER (1 << 1) +#define EFC_NODEDB_PAUSE_NEW_NODES (1 << 2) + +#define MAX_ACC_REJECT_PAYLOAD sizeof(struct fc_els_ls_rjt) + +#define scsi_io_printf(io, fmt, ...) \ + efc_log_debug(io->efc, "[%s] [%04x][i:%04x t:%04x h:%04x]" fmt, \ + io->node->display_name, io->instance_index, io->init_task_tag, \ + io->tgt_task_tag, io->hw_tag, ##__VA_ARGS__) + +static inline void +efc_node_evt_set(struct efc_sm_ctx *ctx, enum efc_sm_event evt, + const char *handler) +{ + struct efc_node *node = ctx->app; + + if (evt == EFC_EVT_ENTER) { + strncpy(node->current_state_name, handler, + sizeof(node->current_state_name)); + } else if (evt == EFC_EVT_EXIT) { + strncpy(node->prev_state_name, node->current_state_name, + sizeof(node->prev_state_name)); + strncpy(node->current_state_name, "invalid", + sizeof(node->current_state_name)); + } + node->prev_evt = node->current_evt; + node->current_evt = evt; +} + +/** + * hold frames in pending frame list + * + * Unsolicited receive frames are held on the node pending frame list, + * rather than being processed. + */ + +static inline void +efc_node_hold_frames(struct efc_node *node) +{ + node->hold_frames = true; +} + +/** + * accept frames + * + * Unsolicited receive frames processed rather than being held on the node + * pending frame list. + */ + +static inline void +efc_node_accept_frames(struct efc_node *node) +{ + node->hold_frames = false; +} + +/* + * Node initiator/target enable defines + * All combinations of the SLI port (nport) initiator/target enable, + * and remote node initiator/target enable are enumerated. + * ex: EFC_NODE_ENABLE_T_TO_IT decodes to target mode is enabled on SLI port + * and I+T is enabled on remote node. + */ +enum efc_node_enable { + EFC_NODE_ENABLE_x_TO_x, + EFC_NODE_ENABLE_x_TO_T, + EFC_NODE_ENABLE_x_TO_I, + EFC_NODE_ENABLE_x_TO_IT, + EFC_NODE_ENABLE_T_TO_x, + EFC_NODE_ENABLE_T_TO_T, + EFC_NODE_ENABLE_T_TO_I, + EFC_NODE_ENABLE_T_TO_IT, + EFC_NODE_ENABLE_I_TO_x, + EFC_NODE_ENABLE_I_TO_T, + EFC_NODE_ENABLE_I_TO_I, + EFC_NODE_ENABLE_I_TO_IT, + EFC_NODE_ENABLE_IT_TO_x, + EFC_NODE_ENABLE_IT_TO_T, + EFC_NODE_ENABLE_IT_TO_I, + EFC_NODE_ENABLE_IT_TO_IT, +}; + +static inline enum efc_node_enable +efc_node_get_enable(struct efc_node *node) +{ + u32 retval = 0; + + if (node->nport->enable_ini) + retval |= (1U << 3); + if (node->nport->enable_tgt) + retval |= (1U << 2); + if (node->init) + retval |= (1U << 1); + if (node->targ) + retval |= (1U << 0); + return (enum efc_node_enable)retval; +} + +int +efc_node_check_els_req(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg, + u8 cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname); +int +efc_node_check_ns_req(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg, + u16 cmd, void (*efc_node_common_func)(const char *, + struct efc_sm_ctx *, enum efc_sm_event, void *), + const char *funcname); +int +efc_node_attach(struct efc_node *node); +struct efc_node * +efc_node_alloc(struct efc_nport *nport, u32 port_id, + bool init, bool targ); +void +efc_node_free(struct efc_node *efc); +void +efc_node_update_display_name(struct efc_node *node); +void efc_node_post_event(struct efc_node *node, enum efc_sm_event evt, + void *arg); + +void +__efc_node_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_node_wait_node_free(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_node_wait_els_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_node_wait_ios_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +efc_node_save_sparms(struct efc_node *node, void *payload); +void +efc_node_transition(struct efc_node *node, + void (*state)(struct efc_sm_ctx *, + enum efc_sm_event, void *), void *data); +void +__efc_node_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +void +efc_node_initiate_cleanup(struct efc_node *node); + +void +efc_node_build_eui_name(char *buf, u32 buf_len, uint64_t eui_name); + +void +efc_node_pause(struct efc_node *node, + void (*state)(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg)); +void +__efc_node_paused(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +int +efc_node_active_ios_empty(struct efc_node *node); +void +efc_node_send_ls_io_cleanup(struct efc_node *node); + +int +efc_els_io_list_empty(struct efc_node *node, struct list_head *list); + +void +efc_process_node_pending(struct efc_node *domain); + +u64 efc_node_get_wwnn(struct efc_node *node); +struct efc_node * +efc_node_find(struct efc_nport *nport, u32 id); +void +efc_node_post_els_resp(struct efc_node *node, u32 evt, void *arg); +void +efc_node_recv_els_frame(struct efc_node *node, struct efc_hw_sequence *s); +void +efc_node_recv_ct_frame(struct efc_node *node, struct efc_hw_sequence *seq); +void +efc_node_recv_fcp_cmd(struct efc_node *node, struct efc_hw_sequence *seq); + +#endif /* __EFC_NODE_H__ */ From patchwork Mon Oct 12 22:51:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269823 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 461D7C433E7 for ; Mon, 12 Oct 2020 22:52:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E36962073A for ; Mon, 12 Oct 2020 22:52:21 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="bVEc6Cjm" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390670AbgJLWwV (ORCPT ); Mon, 12 Oct 2020 18:52:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60582 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390640AbgJLWwR (ORCPT ); Mon, 12 Oct 2020 18:52:17 -0400 Received: from mail-pl1-x629.google.com (mail-pl1-x629.google.com [IPv6:2607:f8b0:4864:20::629]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9DE05C0613D0 for ; Mon, 12 Oct 2020 15:52:17 -0700 (PDT) Received: by mail-pl1-x629.google.com with SMTP id h2so9519299pll.11 for ; Mon, 12 Oct 2020 15:52:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=Zzd4cX7RNFTxJS/ZBheWpaudjDrTqVP0Vu4Xc16oXIg=; b=bVEc6Cjmt6SHamNK1BF/J/eWVGn/TxC8Db0NF8sVARM3CcazfchxLTOyQULjfA3CCm +DEgKuXPrFUUqnQAQhiLFh0JNDr6QTJtt/A7gcQkAspVee6laRvwSohSV34FWc3eN87s VcP2ymC4MTiINvw+/IR2rlQm0Q9s/DdviS0o0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=Zzd4cX7RNFTxJS/ZBheWpaudjDrTqVP0Vu4Xc16oXIg=; b=gW2XMiPQp/mJjTNHgSpDop+yYlFoLT/aH84Y3ogWAYc5j0leHiz8tMlpgeLtBBhhGM +vIi4UAbrTWmJlMmFp2xSHvlQ15pAe5KslGJ+lWrGTdGyMcSgdq512tywNvO2ibTosJ9 rQ2DXBB+i+0OOSlskJs3NZpPwD+wcYPHxeB54G+6SSmQGvKOGCgZdbyHHqceNqw7t08d 3XwT0e6Es/Y8Nofu9YafVxCqsMzVwF4uMd7+88y2EL3UCSCSDaxmiiJcJQ4fpYwTaz8b PKMnvP2tqHB/4aEjkYvdNf1zJWhVVlbD6Z+awDNawprbFKdT0m63NPMuv2voabUlDf0I nwPQ== X-Gm-Message-State: AOAM533j/rWv93A/PQtrrTidwHbYugCUdBdxZ25JHfwx++FnGijTOzhh Z2ieLcOEZaoNK7RlNMatveRehXxrxTOAbFogSPu1Im9bMROHLff7o7qaAJt0ouXBUaiy58jxnl4 5gcK5uwA3wyMYVqiaUTdpr0v4zcyoAtm5ByVNEebitWnbrfClfbPx0bHUT5ECUYCMXXckPeqGQw f9+mU= X-Google-Smtp-Source: ABdhPJx9ZCc6d02PxnPSUqJIkTfJ3TUxSnBUa2fgXfeS/QBaqs+jecM+PNOYrB/oZHkhRT1xseWWCQ== X-Received: by 2002:a17:902:fe86:b029:d4:d451:eb56 with SMTP id x6-20020a170902fe86b02900d4d451eb56mr9483846plm.79.1602543135512; Mon, 12 Oct 2020 15:52:15 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:14 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 14/31] elx: libefc: FC node ELS and state handling Date: Mon, 12 Oct 2020 15:51:30 -0700 Message-Id: <20201012225147.54404-15-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the libefc library population. This patch adds library interface definitions for: - FC node PRLI handling and state management Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke --- v4: Replace fall through comment with fallthrough statement. --- drivers/scsi/elx/libefc/efc_device.c | 1600 ++++++++++++++++++++++++++ drivers/scsi/elx/libefc/efc_device.h | 72 ++ 2 files changed, 1672 insertions(+) create mode 100644 drivers/scsi/elx/libefc/efc_device.c create mode 100644 drivers/scsi/elx/libefc/efc_device.h diff --git a/drivers/scsi/elx/libefc/efc_device.c b/drivers/scsi/elx/libefc/efc_device.c new file mode 100644 index 000000000000..6ec87587923c --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_device.c @@ -0,0 +1,1600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * device_sm Node State Machine: Remote Device States + */ + +#include "efc.h" +#include "efc_device.h" +#include "efc_fabric.h" + +void +efc_d_send_prli_rsp(struct efc_node *node, uint16_t ox_id) +{ + u32 rc = EFC_SCSI_CALL_COMPLETE; + struct efc *efc = node->efc; + + node->ls_acc_oxid = ox_id; + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_PRLI; + + /* + * Wait for backend session registration + * to complete before sending PRLI resp + */ + + if (node->init) { + efc_log_info(efc, "[%s] found(initiator) WWPN:%s WWNN:%s\n", + node->display_name, node->wwpn, node->wwnn); + if (node->nport->enable_tgt) + rc = efc->tt.scsi_new_node(efc, node); + } + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, EFC_EVT_NODE_SESS_REG_OK, NULL); +} + +static void +__efc_d_common(const char *funcname, struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = NULL; + struct efc *efc = NULL; + + node = ctx->app; + efc = node->efc; + + switch (evt) { + /* Handle shutdown events */ + case EFC_EVT_SHUTDOWN: + efc_log_debug(efc, "[%s] %-20s %-20s\n", node->display_name, + funcname, efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + efc_log_debug(efc, "[%s] %-20s %-20s\n", + node->display_name, funcname, + efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_EXPLICIT_LOGO; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + efc_log_debug(efc, "[%s] %-20s %-20s\n", node->display_name, + funcname, efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_IMPLICIT_LOGO; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + default: + /* call default event handler common to all nodes */ + __efc_node_common(funcname, ctx, evt, arg); + } +} + +static void +__efc_d_wait_del_node(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + /* + * State is entered when a node sends a delete initiator/target call + * to the target-server/initiator-client and needs to wait for that + * work to complete. + */ + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + fallthrough; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* These are expected events. */ + break; + + case EFC_EVT_NODE_DEL_INI_COMPLETE: + case EFC_EVT_NODE_DEL_TGT_COMPLETE: + /* + * node has either been detached or is in the process + * of being detached, + * call common node's initiate cleanup function + */ + efc_node_initiate_cleanup(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* Can happen as ELS IO IO's complete */ + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +static void +__efc_d_wait_del_ini_tgt(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + fallthrough; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* These are expected events. */ + break; + + case EFC_EVT_NODE_DEL_INI_COMPLETE: + case EFC_EVT_NODE_DEL_TGT_COMPLETE: + efc_node_transition(node, __efc_d_wait_del_node, NULL); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* Can happen as ELS IO IO's complete */ + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_initiate_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + /* assume no wait needed */ + int rc = EFC_SCSI_CALL_COMPLETE; + + efc_io_alloc_disable(node); + + /* make necessary delete upcall(s) */ + if (node->init && !node->targ) { + efc_log_info(node->efc, + "[%s] delete (initiator) WWPN %s WWNN %s\n", + node->display_name, + node->wwpn, node->wwnn); + efc_node_transition(node, + __efc_d_wait_del_node, + NULL); + if (node->nport->enable_tgt) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); + + } else if (node->targ && !node->init) { + efc_log_info(node->efc, + "[%s] delete (target) WWPN %s WWNN %s\n", + node->display_name, + node->wwpn, node->wwnn); + efc_node_transition(node, + __efc_d_wait_del_node, + NULL); + if (node->nport->enable_ini) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); + + } else if (node->init && node->targ) { + efc_log_info(node->efc, + "[%s] delete (I+T) WWPN %s WWNN %s\n", + node->display_name, node->wwpn, node->wwnn); + efc_node_transition(node, __efc_d_wait_del_ini_tgt, + NULL); + if (node->nport->enable_tgt) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_INI_COMPLETE, NULL); + /* assume no wait needed */ + rc = EFC_SCSI_CALL_COMPLETE; + if (node->nport->enable_ini) + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_DELETED); + + if (rc == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, + EFC_EVT_NODE_DEL_TGT_COMPLETE, NULL); + } + + /* we've initiated the upcalls as needed, now kick off the node + * detach to precipitate the aborting of outstanding exchanges + * associated with said node + * + * Beware: if we've made upcall(s), we've already transitioned + * to a new state by the time we execute this. + * consider doing this before the upcalls? + */ + if (node->attached) { + /* issue hw node free; don't care if succeeds right + * away or sometime later, will check node->attached + * later in shutdown process + */ + rc = efc_cmd_node_detach(efc, &node->rnode); + if (rc != EFC_HW_RTN_SUCCESS && + rc != EFC_HW_RTN_SUCCESS_SYNC) + node_printf(node, + "Failed freeing HW node, rc=%d\n", + rc); + } + + /* if neither initiator nor target, proceed to cleanup */ + if (!node->init && !node->targ) { + /* + * node has either been detached or is in + * the process of being detached, + * call common node's initiate cleanup function + */ + efc_node_initiate_cleanup(node); + } + break; + } + case EFC_EVT_ALL_CHILD_NODES_FREE: + /* Ignore, this can happen if an ELS is + * aborted while in a delay/retry state + */ + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_loop(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: { + /* send PLOGI automatically if initiator */ + efc_node_init_device(node, true); + break; + } + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +efc_send_ls_acc_after_attach(struct efc_node *node, + struct fc_frame_header *hdr, + enum efc_node_send_ls_acc ls) +{ + u16 ox_id = be16_to_cpu(hdr->fh_ox_id); + + /* Save the OX_ID for sending LS_ACC sometime later */ + WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_NONE); + + node->ls_acc_oxid = ox_id; + node->send_ls_acc = ls; + node->ls_acc_did = ntoh24(hdr->fh_d_id); +} + +void +efc_process_prli_payload(struct efc_node *node, void *prli) +{ + struct { + struct fc_els_prli prli; + struct fc_els_spp sp; + } *pp; + + pp = prli; + node->init = (pp->sp.spp_flags & FCP_SPPF_INIT_FCN) != 0; + node->targ = (pp->sp.spp_flags & FCP_SPPF_TARG_FCN) != 0; +} + +void +__efc_d_wait_plogi_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: /* PLOGI ACC completions */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + efc_node_transition(node, __efc_d_port_logged_in, NULL); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_logo_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: + case EFC_EVT_SRRS_ELS_REQ_RJT: + case EFC_EVT_SRRS_ELS_REQ_FAIL: + /* LOGO response received, sent shutdown */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_LOGO, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + node_printf(node, + "LOGO sent (evt=%s), shutdown node\n", + efc_sm_event_name(evt)); + /* sm: / post explicit logout */ + efc_node_post_event(node, EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +efc_node_init_device(struct efc_node *node, bool send_plogi) +{ + node->send_plogi = send_plogi; + if ((node->efc->nodedb_mask & EFC_NODEDB_PAUSE_NEW_NODES) && + (node->rnode.fc_id != FC_FID_DOM_MGR)) { + node->nodedb_state = __efc_d_init; + efc_node_transition(node, __efc_node_paused, NULL); + } else { + efc_node_transition(node, __efc_d_init, NULL); + } +} + +static void +efc_d_check_plogi_topology(struct efc_node *node, u32 d_id) +{ + switch (node->nport->topology) { + case EFC_SPORT_TOPOLOGY_P2P: + /* we're not attached and nport is p2p, + * need to attach + */ + efc_domain_attach(node->nport->domain, d_id); + efc_node_transition(node, __efc_d_wait_domain_attach, NULL); + break; + case EFC_SPORT_TOPOLOGY_FABRIC: + /* we're not attached and nport is fabric, domain + * attach should have already been requested as part + * of the fabric state machine, wait for it + */ + efc_node_transition(node, __efc_d_wait_domain_attach, NULL); + break; + case EFC_SPORT_TOPOLOGY_UNKNOWN: + /* Two possibilities: + * 1. received a PLOGI before our FLOGI has completed + * (possible since completion comes in on another + * CQ), thus we don't know what we're connected to + * yet; transition to a state to wait for the + * fabric node to tell us; + * 2. PLOGI received before link went down and we + * haven't performed domain attach yet. + * Note: we cannot distinguish between 1. and 2. + * so have to assume PLOGI + * was received after link back up. + */ + node_printf(node, "received PLOGI, unknown topology did=0x%x\n", + d_id); + efc_node_transition(node, __efc_d_wait_topology_notify, NULL); + break; + default: + node_printf(node, "received PLOGI, unexpected topology %d\n", + node->nport->topology); + } +} + +void +__efc_d_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + /* + * This state is entered when a node is instantiated, + * either having been discovered from a name services query, + * or having received a PLOGI/FLOGI. + */ + switch (evt) { + case EFC_EVT_ENTER: + if (!node->send_plogi) + break; + /* only send if we have initiator capability, + * and domain is attached + */ + if (node->nport->enable_ini && + node->nport->domain->attached) { + efc_send_plogi(node); + + efc_node_transition(node, __efc_d_wait_plogi_rsp, NULL); + } else { + node_printf(node, "not sending plogi nport.ini=%d,", + node->nport->enable_ini); + node_printf(node, "domain attached=%d\n", + node->nport->domain->attached); + } + break; + case EFC_EVT_PLOGI_RCVD: { + /* T, or I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + int rc; + + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* domain not attached; several possibilities: */ + if (!node->nport->domain->attached) { + efc_d_check_plogi_topology(node, ntoh24(hdr->fh_d_id)); + break; + } + + /* domain already attached */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, NULL); + + break; + } + + case EFC_EVT_FDISC_RCVD: { + __efc_d_common(__func__, ctx, evt, arg); + break; + } + + case EFC_EVT_FLOGI_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + u32 d_id = ntoh24(hdr->fh_d_id); + + /* sm: / save sparams, send FLOGI acc */ + memcpy(node->nport->domain->flogi_service_params, + cbdata->payload->dma.virt, + sizeof(struct fc_els_flogi)); + + /* send FC LS_ACC response, override s_id */ + efc_fabric_set_topology(node, EFC_SPORT_TOPOLOGY_P2P); + + efc_send_flogi_p2p_acc(node, be16_to_cpu(hdr->fh_ox_id), d_id); + + if (efc_p2p_setup(node->nport)) { + node_printf(node, "p2p failed, shutting down node\n"); + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + } + + efc_node_transition(node, __efc_p2p_wait_flogi_acc_cmpl, NULL); + break; + } + + case EFC_EVT_LOGO_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + if (!node->nport->domain->attached) { + /* most likely a frame left over from before a link + * down; drop and + * shut node down w/ "explicit logout" so pending + * frames are processed + */ + node_printf(node, "%s domain not attached, dropping\n", + efc_sm_event_name(evt)); + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); + break; + } + + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + + case EFC_EVT_PRLI_RCVD: + case EFC_EVT_PRLO_RCVD: + case EFC_EVT_PDISC_RCVD: + case EFC_EVT_ADISC_RCVD: + case EFC_EVT_RSCN_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + if (!node->nport->domain->attached) { + /* most likely a frame left over from before a link + * down; drop and shut node down w/ "explicit logout" + * so pending frames are processed + */ + node_printf(node, "%s domain not attached, dropping\n", + efc_sm_event_name(evt)); + + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + } + node_printf(node, "%s received, sending reject\n", + efc_sm_event_name(evt)); + + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_PLOGI_REQD, 0); + + break; + } + + case EFC_EVT_FCP_CMD_RCVD: { + /* note: problem, we're now expecting an ELS REQ completion + * from both the LOGO and PLOGI + */ + if (!node->nport->domain->attached) { + /* most likely a frame left over from before a + * link down; drop and + * shut node down w/ "explicit logout" so pending + * frames are processed + */ + node_printf(node, "%s domain not attached, dropping\n", + efc_sm_event_name(evt)); + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + } + + /* Send LOGO */ + node_printf(node, "FCP_CMND received, send LOGO\n"); + if (efc_send_logo(node)) { + /* + * failed to send LOGO, go ahead and cleanup node + * anyways + */ + node_printf(node, "Failed to send LOGO\n"); + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + } else { + /* sent LOGO, wait for response */ + efc_node_transition(node, + __efc_d_wait_logo_rsp, NULL); + } + break; + } + case EFC_EVT_DOMAIN_ATTACH_OK: + /* don't care about domain_attach_ok */ + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_plogi_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_PLOGI_RCVD: { + /* T, or I+T */ + /* received PLOGI with svc parms, go ahead and attach node + * when PLOGI that was sent ultimately completes, it'll be a + * no-op + * + * If there is an outstanding PLOGI sent, can we set a flag + * to indicate that we don't want to retry it if it times out? + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + /* sm: domain->attached / efc_node_attach */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, + EFC_EVT_NODE_ATTACH_OK, NULL); + + break; + } + + case EFC_EVT_PRLI_RCVD: + /* I, or I+T */ + /* sent PLOGI and before completion was seen, received the + * PRLI from the remote node (WCQEs and RCQEs come in on + * different queues and order of processing cannot be assumed) + * Save OXID so PRLI can be sent after the attach and continue + * to wait for PLOGI response + */ + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PRLI); + efc_node_transition(node, __efc_d_wait_plogi_rsp_recvd_prli, + NULL); + break; + + case EFC_EVT_LOGO_RCVD: /* why don't we do a shutdown here?? */ + case EFC_EVT_PRLO_RCVD: + case EFC_EVT_PDISC_RCVD: + case EFC_EVT_FDISC_RCVD: + case EFC_EVT_ADISC_RCVD: + case EFC_EVT_RSCN_RCVD: + case EFC_EVT_SCR_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received, sending reject\n", + efc_sm_event_name(evt)); + + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_PLOGI_REQD, 0); + + break; + } + + case EFC_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */ + /* Completion from PLOGI sent */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, + EFC_EVT_NODE_ATTACH_OK, NULL); + + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ + /* PLOGI failed, shutdown the node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* Our PLOGI was rejected, this is ok in some cases */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + + case EFC_EVT_FCP_CMD_RCVD: { + /* not logged in yet and outstanding PLOGI so don't send LOGO, + * just drop + */ + node_printf(node, "FCP_CMND received, drop\n"); + break; + } + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* + * Since we've received a PRLI, we have a port login and will + * just need to wait for the PLOGI response to do the node + * attach and then we can send the LS_ACC for the PRLI. If, + * during this time, we receive FCP_CMNDs (which is possible + * since we've already sent a PRLI and our peer may have + * accepted). At this time, we are not waiting on any other + * unsolicited frames to continue with the login process. Thus, + * it will not hurt to hold frames here. + */ + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: /* PLOGI response received */ + /* Completion from PLOGI sent */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / save sparams, efc_node_attach */ + efc_node_save_sparms(node, cbdata->els_rsp.virt); + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, + NULL); + + break; + + case EFC_EVT_SRRS_ELS_REQ_FAIL: /* PLOGI response received */ + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* PLOGI failed, shutdown the node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PLOGI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_DOMAIN_ATTACH_OK: + WARN_ON(!node->nport->domain->attached); + /* sm: / efc_node_attach */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, EFC_EVT_NODE_ATTACH_OK, + NULL); + + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_topology_notify(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + int rc; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SPORT_TOPOLOGY_NOTIFY: { + enum efc_nport_topology topology = + (enum efc_nport_topology)arg; + + WARN_ON(node->nport->domain->attached); + + WARN_ON(node->send_ls_acc != EFC_NODE_SEND_LS_ACC_PLOGI); + + node_printf(node, "topology notification, topology=%d\n", + topology); + + /* At the time the PLOGI was received, the topology was unknown, + * so we didn't know which node would perform the domain attach: + * 1. The node from which the PLOGI was sent (p2p) or + * 2. The node to which the FLOGI was sent (fabric). + */ + if (topology == EFC_SPORT_TOPOLOGY_P2P) { + /* if this is p2p, need to attach to the domain using + * the d_id from the PLOGI received + */ + efc_domain_attach(node->nport->domain, + node->ls_acc_did); + } + /* else, if this is fabric, the domain attach + * should be performed by the fabric node (node sending FLOGI); + * just wait for attach to complete + */ + + efc_node_transition(node, __efc_d_wait_domain_attach, NULL); + break; + } + case EFC_EVT_DOMAIN_ATTACH_OK: + WARN_ON(!node->nport->domain->attached); + node_printf(node, "domain attach ok\n"); + /* sm: / efc_node_attach */ + rc = efc_node_attach(node); + efc_node_transition(node, __efc_d_wait_node_attach, NULL); + if (rc == EFC_HW_RTN_SUCCESS_SYNC) + efc_node_post_event(node, + EFC_EVT_NODE_ATTACH_OK, NULL); + + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + switch (node->send_ls_acc) { + case EFC_NODE_SEND_LS_ACC_PLOGI: { + /* sm: send_plogi_acc is set / send PLOGI acc */ + /* Normal case for T, or I+T */ + efc_send_plogi_acc(node, node->ls_acc_oxid); + efc_node_transition(node, __efc_d_wait_plogi_acc_cmpl, + NULL); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + break; + } + case EFC_NODE_SEND_LS_ACC_PRLI: { + efc_d_send_prli_rsp(node, node->ls_acc_oxid); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + node->ls_acc_io = NULL; + break; + } + case EFC_NODE_SEND_LS_ACC_NONE: + default: + /* Normal case for I */ + /* sm: send_plogi_acc is not set / send PLOGI acc */ + efc_node_transition(node, + __efc_d_port_logged_in, NULL); + break; + } + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + /* node attach failed, shutdown the node */ + node->attached = false; + node_printf(node, "node attach failed\n"); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + /* Handle shutdown events */ + case EFC_EVT_SHUTDOWN: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + efc_node_transition(node, __efc_d_wait_attach_evt_shutdown, + NULL); + break; + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_EXPLICIT_LOGO; + efc_node_transition(node, __efc_d_wait_attach_evt_shutdown, + NULL); + break; + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + node->shutdown_reason = EFC_NODE_SHUTDOWN_IMPLICIT_LOGO; + efc_node_transition(node, + __efc_d_wait_attach_evt_shutdown, NULL); + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + /* wait for any of these attach events and then shutdown */ + case EFC_EVT_NODE_ATTACH_OK: + node->attached = true; + node_printf(node, "Attach evt=%s, proceed to shutdown\n", + efc_sm_event_name(evt)); + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + case EFC_EVT_NODE_ATTACH_FAIL: + /* node attach failed, shutdown the node */ + node->attached = false; + node_printf(node, "Attach evt=%s, proceed to shutdown\n", + efc_sm_event_name(evt)); + efc_node_transition(node, __efc_d_initiate_shutdown, NULL); + break; + + /* ignore shutdown events as we're already in shutdown path */ + case EFC_EVT_SHUTDOWN: + /* have default shutdown event take precedence */ + node->shutdown_reason = EFC_NODE_SHUTDOWN_DEFAULT; + fallthrough; + + case EFC_EVT_SHUTDOWN_EXPLICIT_LOGO: + case EFC_EVT_SHUTDOWN_IMPLICIT_LOGO: + node_printf(node, "%s received\n", efc_sm_event_name(evt)); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_port_logged_in(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + /* Normal case for I or I+T */ + if (node->nport->enable_ini && + !(node->rnode.fc_id != FC_FID_DOM_MGR)) { + /* sm: if enable_ini / send PRLI */ + efc_send_prli(node); + /* can now expect ELS_REQ_OK/FAIL/RJT */ + } + break; + + case EFC_EVT_FCP_CMD_RCVD: { + break; + } + + case EFC_EVT_PRLI_RCVD: { + /* Normal case for T or I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + struct { + struct fc_els_prli prli; + struct fc_els_spp sp; + } *pp; + + pp = cbdata->payload->dma.virt; + if (pp->sp.spp_type != FC_TYPE_FCP) { + /*Only FCP is supported*/ + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_UNSUPR, 0); + break; + } + + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_d_send_prli_rsp(node, be16_to_cpu(hdr->fh_ox_id)); + break; + } + + case EFC_EVT_NODE_SESS_REG_OK: + if (node->send_ls_acc == EFC_NODE_SEND_LS_ACC_PRLI) + efc_send_prli_acc(node, node->ls_acc_oxid); + + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + efc_node_transition(node, __efc_d_device_ready, NULL); + break; + + case EFC_EVT_NODE_SESS_REG_FAIL: + efc_send_ls_rjt(node, node->ls_acc_oxid, ELS_RJT_UNAB, + ELS_EXPL_UNSUPR, 0); + node->send_ls_acc = EFC_NODE_SEND_LS_ACC_NONE; + break; + + case EFC_EVT_SRRS_ELS_REQ_OK: { /* PRLI response */ + /* Normal case for I or I+T */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / process PRLI payload */ + efc_process_prli_payload(node, cbdata->els_rsp.virt); + efc_node_transition(node, __efc_d_device_ready, NULL); + break; + } + + case EFC_EVT_SRRS_ELS_REQ_FAIL: { /* PRLI response failed */ + /* I, I+T, assume some link failure, shutdown node */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + break; + } + + case EFC_EVT_SRRS_ELS_REQ_RJT: { + /* PRLI rejected by remote + * Normal for I, I+T (connected to an I) + * Node doesn't want to be a target, stay here and wait for a + * PRLI from the remote node + * if it really wants to connect to us as target + */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_PRLI, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + break; + } + + case EFC_EVT_SRRS_ELS_CMPL_OK: { + /* Normal T, I+T, target-server rejected the process login */ + /* This would be received only in the case where we sent + * LS_RJT for the PRLI, so + * do nothing. (note: as T only we could shutdown the node) + */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + break; + } + + case EFC_EVT_PLOGI_RCVD: { + /*sm: / save sparams, set send_plogi_acc, + *post implicit logout + * Save plogi parameters + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* Restart node attach with new service parameters, + * and send ACC + */ + efc_node_post_event(node, EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, + NULL); + break; + } + + case EFC_EVT_LOGO_RCVD: { + /* I, T, I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), + node->attached); + /* sm: / send LOGO acc */ + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_logo_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + efc_node_hold_frames(node); + break; + + case EFC_EVT_EXIT: + efc_node_accept_frames(node); + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + /* sm: / post explicit logout */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, NULL); + break; + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_device_ready(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + if (evt != EFC_EVT_FCP_CMD_RCVD) + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: + node->fcp_enabled = true; + if (node->targ) { + efc_log_info(efc, + "[%s] found (target) WWPN %s WWNN %s\n", + node->display_name, + node->wwpn, node->wwnn); + if (node->nport->enable_ini) + efc->tt.scsi_new_node(efc, node); + } + break; + + case EFC_EVT_EXIT: + node->fcp_enabled = false; + break; + + case EFC_EVT_PLOGI_RCVD: { + /* sm: / save sparams, set send_plogi_acc, post implicit + * logout + * Save plogi parameters + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* + * Restart node attach with new service parameters, + * and send ACC + */ + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, NULL); + break; + } + + case EFC_EVT_PRLI_RCVD: { + /* T, I+T: remote initiator is slow to get started */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + struct { + struct fc_els_prli prli; + struct fc_els_spp sp; + } *pp; + + pp = cbdata->payload->dma.virt; + if (pp->sp.spp_type != FC_TYPE_FCP) { + /*Only FCP is supported*/ + efc_send_ls_rjt(node, be16_to_cpu(hdr->fh_ox_id), + ELS_RJT_UNAB, ELS_EXPL_UNSUPR, 0); + break; + } + + efc_process_prli_payload(node, cbdata->payload->dma.virt); + efc_send_prli_acc(node, be16_to_cpu(hdr->fh_ox_id)); + break; + } + + case EFC_EVT_PRLO_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + /* sm: / send PRLO acc */ + efc_send_prlo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + /* need implicit logout? */ + break; + } + + case EFC_EVT_LOGO_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), node->attached); + /* sm: / send LOGO acc */ + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + + case EFC_EVT_ADISC_RCVD: { + struct fc_frame_header *hdr = cbdata->header->dma.virt; + /* sm: / send ADISC acc */ + efc_send_adisc_acc(node, be16_to_cpu(hdr->fh_ox_id)); + break; + } + + case EFC_EVT_ABTS_RCVD: + /* sm: / process ABTS */ + efc_log_err(efc, "Unexpected event:%s\n", + efc_sm_event_name(evt)); + break; + + case EFC_EVT_NODE_ACTIVE_IO_LIST_EMPTY: + break; + + case EFC_EVT_NODE_REFOUND: + break; + + case EFC_EVT_NODE_MISSING: + if (node->nport->enable_rscn) + efc_node_transition(node, __efc_d_device_gone, NULL); + + break; + + case EFC_EVT_SRRS_ELS_CMPL_OK: + /* T, or I+T, PRLI accept completed ok */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + break; + + case EFC_EVT_SRRS_ELS_CMPL_FAIL: + /* T, or I+T, PRLI accept failed to complete */ + WARN_ON(!node->els_cmpl_cnt); + node->els_cmpl_cnt--; + node_printf(node, "Failed to send PRLI LS_ACC\n"); + break; + + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_device_gone(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + struct efc *efc = node->efc; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_ENTER: { + int rc = EFC_SCSI_CALL_COMPLETE; + int rc_2 = EFC_SCSI_CALL_COMPLETE; + static char const *labels[] = {"none", "initiator", "target", + "initiator+target"}; + + efc_log_info(efc, "[%s] missing (%s) WWPN %s WWNN %s\n", + node->display_name, + labels[(node->targ << 1) | (node->init)], + node->wwpn, node->wwnn); + + switch (efc_node_get_enable(node)) { + case EFC_NODE_ENABLE_T_TO_T: + case EFC_NODE_ENABLE_I_TO_T: + case EFC_NODE_ENABLE_IT_TO_T: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_MISSING); + break; + + case EFC_NODE_ENABLE_T_TO_I: + case EFC_NODE_ENABLE_I_TO_I: + case EFC_NODE_ENABLE_IT_TO_I: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_MISSING); + break; + + case EFC_NODE_ENABLE_T_TO_IT: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_MISSING); + break; + + case EFC_NODE_ENABLE_I_TO_IT: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_MISSING); + break; + + case EFC_NODE_ENABLE_IT_TO_IT: + rc = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_INITIATOR_MISSING); + rc_2 = efc->tt.scsi_del_node(efc, node, + EFC_SCSI_TARGET_MISSING); + break; + + default: + rc = EFC_SCSI_CALL_COMPLETE; + break; + } + + if (rc == EFC_SCSI_CALL_COMPLETE && + rc_2 == EFC_SCSI_CALL_COMPLETE) + efc_node_post_event(node, EFC_EVT_SHUTDOWN, NULL); + + break; + } + case EFC_EVT_NODE_REFOUND: + /* two approaches, reauthenticate with PLOGI/PRLI, or ADISC */ + + /* reauthenticate with PLOGI/PRLI */ + /* efc_node_transition(node, __efc_d_discovered, NULL); */ + + /* reauthenticate with ADISC */ + /* sm: / send ADISC */ + efc_send_adisc(node); + efc_node_transition(node, __efc_d_wait_adisc_rsp, NULL); + break; + + case EFC_EVT_PLOGI_RCVD: { + /* sm: / save sparams, set send_plogi_acc, post implicit + * logout + * Save plogi parameters + */ + efc_node_save_sparms(node, cbdata->payload->dma.virt); + efc_send_ls_acc_after_attach(node, + cbdata->header->dma.virt, + EFC_NODE_SEND_LS_ACC_PLOGI); + + /* + * Restart node attach with new service parameters, and send + * ACC + */ + efc_node_post_event(node, EFC_EVT_SHUTDOWN_IMPLICIT_LOGO, + NULL); + break; + } + + case EFC_EVT_FCP_CMD_RCVD: { + /* most likely a stale frame (received prior to link down), + * if attempt to send LOGO, will probably timeout and eat + * up 20s; thus, drop FCP_CMND + */ + node_printf(node, "FCP_CMND received, drop\n"); + break; + } + case EFC_EVT_LOGO_RCVD: { + /* I, T, I+T */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), node->attached); + /* sm: / send LOGO acc */ + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} + +void +__efc_d_wait_adisc_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg) +{ + struct efc_node_cb *cbdata = arg; + struct efc_node *node = ctx->app; + + efc_node_evt_set(ctx, evt, __func__); + + node_sm_trace(); + + switch (evt) { + case EFC_EVT_SRRS_ELS_REQ_OK: + if (efc_node_check_els_req(ctx, evt, arg, ELS_ADISC, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + efc_node_transition(node, __efc_d_device_ready, NULL); + break; + + case EFC_EVT_SRRS_ELS_REQ_RJT: + /* received an LS_RJT, in this case, send shutdown + * (explicit logo) event which will unregister the node, + * and start over with PLOGI + */ + if (efc_node_check_els_req(ctx, evt, arg, ELS_ADISC, + __efc_d_common, __func__)) + return; + + WARN_ON(!node->els_req_cnt); + node->els_req_cnt--; + /* sm: / post explicit logout */ + efc_node_post_event(node, + EFC_EVT_SHUTDOWN_EXPLICIT_LOGO, + NULL); + break; + + case EFC_EVT_LOGO_RCVD: { + /* In this case, we have the equivalent of an LS_RJT for + * the ADISC, so we need to abort the ADISC, and re-login + * with PLOGI + */ + /* sm: / request abort, send LOGO acc */ + struct fc_frame_header *hdr = cbdata->header->dma.virt; + + node_printf(node, "%s received attached=%d\n", + efc_sm_event_name(evt), node->attached); + + efc_send_logo_acc(node, be16_to_cpu(hdr->fh_ox_id)); + efc_node_transition(node, __efc_d_wait_logo_acc_cmpl, NULL); + break; + } + default: + __efc_d_common(__func__, ctx, evt, arg); + } +} diff --git a/drivers/scsi/elx/libefc/efc_device.h b/drivers/scsi/elx/libefc/efc_device.h new file mode 100644 index 000000000000..4c03ee566504 --- /dev/null +++ b/drivers/scsi/elx/libefc/efc_device.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +/* + * Node state machine functions for remote device node sm + */ + +#ifndef __EFCT_DEVICE_H__ +#define __EFCT_DEVICE_H__ +void +efc_node_init_device(struct efc_node *node, bool send_plogi); +void +efc_process_prli_payload(struct efc_node *node, + void *prli); +void +efc_d_send_prli_rsp(struct efc_node *node, uint16_t ox_id); +void +efc_send_ls_acc_after_attach(struct efc_node *node, + struct fc_frame_header *hdr, + enum efc_node_send_ls_acc ls); +void +__efc_d_wait_loop(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_plogi_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_init(struct efc_sm_ctx *ctx, enum efc_sm_event evt, void *arg); +void +__efc_d_wait_plogi_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_plogi_rsp_recvd_prli(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_domain_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_topology_notify(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_node_attach(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_attach_evt_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_initiate_shutdown(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_port_logged_in(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_logo_acc_cmpl(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_device_ready(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_device_gone(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_adisc_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); +void +__efc_d_wait_logo_rsp(struct efc_sm_ctx *ctx, + enum efc_sm_event evt, void *arg); + +#endif /* __EFCT_DEVICE_H__ */ From patchwork Mon Oct 12 22:51:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269819 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B66E1C433DF for ; Mon, 12 Oct 2020 22:52:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 435772073A for ; Mon, 12 Oct 2020 22:52:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="E4xfC+xv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390836AbgJLWwg (ORCPT ); Mon, 12 Oct 2020 18:52:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60604 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390662AbgJLWwX (ORCPT ); Mon, 12 Oct 2020 18:52:23 -0400 Received: from mail-pg1-x544.google.com (mail-pg1-x544.google.com [IPv6:2607:f8b0:4864:20::544]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A3816C0613D1 for ; Mon, 12 Oct 2020 15:52:23 -0700 (PDT) Received: by mail-pg1-x544.google.com with SMTP id u24so15918078pgi.1 for ; Mon, 12 Oct 2020 15:52:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=JsYFDVpAnxrEr//BZKbeze82i49iR8DQFk9kTWlP7Dc=; b=E4xfC+xvrfK7QHa8CcZavcwla3966ZkHGvN22xr4IiftxEHVqjpAKl/GarWLI/k5Vr GovyJrfhzdjkajV5qyjJTn5gxo33B6Xk8rXwN+K8HbHlrNH+fvlwK8+5iXB7nuDjL8TY 3U8Mckphh8jwG3Z3Bt2qYRny2PxH5nKDS3RZM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=JsYFDVpAnxrEr//BZKbeze82i49iR8DQFk9kTWlP7Dc=; b=mRRbfjjTR6smGsToiLeem2+NOv976GpWqONyyvFZfVVPsNmNBysNHy5apwxwKQohaR Heetdbczu56BU5AdzIpXivGmevwxEU+XMLeqZIwUz4Jm2ksynnImSnpT7ToA1f/p2Eog XPd4sKw9cjwMkRWYxr+9+5vafD6lkNXXQY22w8jJaXKAFvcvsLV302ghmYgExEgOstoC Ig3GRdU1YOcY2a+Lnpe6Vr/utgqt77QAvrvpyrebJ6A/m5kJMymmC7YQJYYQV08prq0y 17tVghRrBw++gbEITta5mSokZly7Fvd+KMrLdcb2O86X03VuNQ165RYX4iz4hK3yzGDe SlAA== X-Gm-Message-State: AOAM532IPDpVTltJeLj9SsRhiZXLytt8SltMYvini7M9tD2zwufaYd9V 0pzXm0V+8KLUkV8xuusOVh24zgRuovGnXlfNO1HRTiSE8LMsPYg8NoeG3lt6hQkNZZUDXJDSxGk 7laFGgwQW5+uxFd7YFolD1wbb7pAzVtpiSYlWcv/VxaSq2yd5o5F38/4r+hIdv9g2p9ePWXCEWH R7zmE= X-Google-Smtp-Source: ABdhPJw75DVeFbweS0BYlZXbqb029t2IeH4GV6VTEAp/mhMkC1XaKlb0sN8DzG44tUjSqYy0NxV3mA== X-Received: by 2002:a17:90a:8c:: with SMTP id a12mr9968243pja.155.1602543141503; Mon, 12 Oct 2020 15:52:21 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:20 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 18/31] elx: efct: Driver initialization routines Date: Mon, 12 Oct 2020 15:51:34 -0700 Message-Id: <20201012225147.54404-19-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: Emulex FC Target driver init, attach and hardware setup routines. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- v4: New library Template registration and associated functions Muti-RQ support, configure mrq. Support for Multi RQ/EQ. Configure filter to send all ELS traffic to one RQ and remaining IOs to all RQs. CPU based WQ scaling. Allocate loop map during initialisation Remove redundant includes in efc_driver.h --- drivers/scsi/elx/efct/efct_driver.c | 810 +++++++++++++++++++ drivers/scsi/elx/efct/efct_driver.h | 109 +++ drivers/scsi/elx/efct/efct_hw.c | 1166 +++++++++++++++++++++++++++ drivers/scsi/elx/efct/efct_hw.h | 15 + drivers/scsi/elx/efct/efct_xport.c | 519 ++++++++++++ drivers/scsi/elx/efct/efct_xport.h | 186 +++++ 6 files changed, 2805 insertions(+) create mode 100644 drivers/scsi/elx/efct/efct_driver.c create mode 100644 drivers/scsi/elx/efct/efct_driver.h create mode 100644 drivers/scsi/elx/efct/efct_hw.c create mode 100644 drivers/scsi/elx/efct/efct_xport.c create mode 100644 drivers/scsi/elx/efct/efct_xport.h diff --git a/drivers/scsi/elx/efct/efct_driver.c b/drivers/scsi/elx/efct/efct_driver.c new file mode 100644 index 000000000000..d0077512df71 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_driver.c @@ -0,0 +1,810 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efct_driver.h" + +#include "efct_hw.h" +#include "efct_unsol.h" +#include "efct_scsi.h" + +struct efct *efct_devices[MAX_EFCT_DEVICES]; + +static int logmask; +module_param(logmask, int, 0444); +MODULE_PARM_DESC(logmask, "logging bitmask (default 0)"); + +static struct libefc_function_template efct_libefc_templ = { + .issue_mbox_rqst = efct_issue_mbox_rqst, + .send_els = efct_els_hw_srrs_send, + .send_bls = efct_efc_bls_send, + + .new_nport = efct_scsi_tgt_new_nport, + .del_nport = efct_scsi_tgt_del_nport, + .scsi_new_node = efct_scsi_new_initiator, + .scsi_del_node = efct_scsi_del_initiator, + .hw_seq_free = efct_efc_hw_sequence_free, +}; + +static int +efct_device_init(void) +{ + int rc; + + /* driver-wide init for target-server */ + rc = efct_scsi_tgt_driver_init(); + if (rc) { + pr_err("efct_scsi_tgt_init failed rc=%d\n", + rc); + return rc; + } + + rc = efct_scsi_reg_fc_transport(); + if (rc) { + pr_err("failed to register to FC host\n"); + return rc; + } + + return EFC_SUCCESS; +} + +static void +efct_device_shutdown(void) +{ + efct_scsi_release_fc_transport(); + + efct_scsi_tgt_driver_exit(); +} + +static void * +efct_device_alloc(u32 nid) +{ + struct efct *efct = NULL; + u32 i; + + efct = kzalloc_node(sizeof(*efct), GFP_KERNEL, nid); + if (!efct) + return efct; + + for (i = 0; i < ARRAY_SIZE(efct_devices); i++) { + if (!efct_devices[i]) { + efct->instance_index = i; + efct_devices[i] = efct; + break; + } + } + + if (i == ARRAY_SIZE(efct_devices)) { + pr_err("Exceeded max supported devices.\n"); + kfree(efct); + efct = NULL; + } + + return efct; +} + +static void +efct_teardown_msix(struct efct *efct) +{ + u32 i; + + for (i = 0; i < efct->n_msix_vec; i++) { + free_irq(pci_irq_vector(efct->pci, i), + &efct->intr_context[i]); + } + + pci_free_irq_vectors(efct->pci); +} + +static int +efct_efclib_config(struct efct *efct, struct libefc_function_template *tt) +{ + struct efc *efc; + struct sli4 *sli; + int rc = EFC_SUCCESS; + + efc = kzalloc(sizeof(*efc), GFP_KERNEL); + if (!efc) + return EFC_FAIL; + + efct->efcport = efc; + + memcpy(&efc->tt, tt, sizeof(*tt)); + efc->base = efct; + efc->pci = efct->pci; + + efc->def_wwnn = efct_get_wwnn(&efct->hw); + efc->def_wwpn = efct_get_wwpn(&efct->hw); + efc->enable_tgt = 1; + efc->log_level = EFC_LOG_LIB; + + sli = &efct->hw.sli; + efc->max_xfer_size = sli->sge_supported_length * + sli_get_max_sgl(&efct->hw.sli); + efc->sli = sli; + efc->fcfi = efct->hw.fcf_indicator; + + rc = efcport_init(efc); + if (rc) + efc_log_err(efc, "efcport_init failed\n"); + + return rc; +} + +static int efct_request_firmware_update(struct efct *efct); + +static const char* +efct_pci_model(u16 device) +{ + switch (device) { + case EFCT_DEVICE_LANCER_G6: return "LPE31004"; + case EFCT_DEVICE_LANCER_G7: return "LPE36000"; + default: return "unknown"; + } +} + +static int +efct_device_attach(struct efct *efct) +{ + u32 rc = 0, i = 0; + + if (efct->attached) { + efc_log_err(efct, "Device is already attached\n"); + return EFC_FAIL; + } + + snprintf(efct->name, sizeof(efct->name), "[%s%d] ", "fc", + efct->instance_index); + + efct->logmask = logmask; + efct->filter_def = EFCT_DEFAULT_FILTER; + efct->max_isr_time_msec = EFCT_OS_MAX_ISR_TIME_MSEC; + + efct->model = efct_pci_model(efct->pci->device); + + efct->efct_req_fw_upgrade = true; + + /* Allocate transport object and bring online */ + efct->xport = efct_xport_alloc(efct); + if (!efct->xport) { + efc_log_err(efct, "failed to allocate transport object\n"); + rc = -ENOMEM; + goto out; + } + + rc = efct_xport_attach(efct->xport); + if (rc) { + efc_log_err(efct, "failed to attach transport object\n"); + goto xport_out; + } + + rc = efct_xport_initialize(efct->xport); + if (rc) { + efc_log_err(efct, "failed to initialize transport object\n"); + goto xport_out; + } + + rc = efct_efclib_config(efct, &efct_libefc_templ); + if (rc) { + efc_log_err(efct, "failed to init efclib\n"); + goto efclib_out; + } + + for (i = 0; i < efct->n_msix_vec; i++) { + efc_log_debug(efct, "irq %d enabled\n", i); + enable_irq(pci_irq_vector(efct->pci, i)); + } + + efct->attached = true; + + if (efct->efct_req_fw_upgrade) + efct_request_firmware_update(efct); + + return rc; + +efclib_out: + efct_xport_detach(efct->xport); +xport_out: + if (efct->xport) { + efct_xport_free(efct->xport); + efct->xport = NULL; + } +out: + return rc; +} + +static int +efct_device_detach(struct efct *efct) +{ + int i; + + if (!efct || !efct->attached) { + efc_log_warn(efct, "Device is not attached\n"); + return EFC_FAIL; + } + + if (efct_xport_control(efct->xport, EFCT_XPORT_SHUTDOWN)) + efc_log_err(efct, "Transport Shutdown timed out\n"); + + for (i = 0; i < efct->n_msix_vec; i++) + disable_irq(pci_irq_vector(efct->pci, i)); + + if (efct_xport_detach(efct->xport) != 0) + efc_log_err(efct, "Transport detach failed\n"); + + efct_xport_free(efct->xport); + efct->xport = NULL; + + efcport_destroy(efct->efcport); + kfree(efct->efcport); + + efct->attached = false; + + return EFC_SUCCESS; +} + +static void +efct_fw_write_cb(int status, u32 actual_write_length, + u32 change_status, void *arg) +{ + struct efct_fw_write_result *result = arg; + + result->status = status; + result->actual_xfer = actual_write_length; + result->change_status = change_status; + + complete(&result->done); +} + +static int +efct_firmware_write(struct efct *efct, const u8 *buf, size_t buf_len, + u8 *change_status) +{ + int rc = 0; + u32 bytes_left; + u32 xfer_size; + u32 offset; + struct efc_dma dma; + int last = 0; + struct efct_fw_write_result result; + + init_completion(&result.done); + + bytes_left = buf_len; + offset = 0; + + dma.size = FW_WRITE_BUFSIZE; + dma.virt = dma_alloc_coherent(&efct->pci->dev, + dma.size, &dma.phys, GFP_DMA); + if (!dma.virt) + return -ENOMEM; + + while (bytes_left > 0) { + if (bytes_left > FW_WRITE_BUFSIZE) + xfer_size = FW_WRITE_BUFSIZE; + else + xfer_size = bytes_left; + + memcpy(dma.virt, buf + offset, xfer_size); + + if (bytes_left == xfer_size) + last = 1; + + efct_hw_firmware_write(&efct->hw, &dma, xfer_size, offset, + last, efct_fw_write_cb, &result); + + if (wait_for_completion_interruptible(&result.done) != 0) { + rc = -ENXIO; + break; + } + + if (result.actual_xfer == 0 || result.status != 0) { + rc = -EFAULT; + break; + } + + if (last) + *change_status = result.change_status; + + bytes_left -= result.actual_xfer; + offset += result.actual_xfer; + } + + dma_free_coherent(&efct->pci->dev, dma.size, dma.virt, dma.phys); + return rc; +} + +static int +efct_fw_reset(struct efct *efct) +{ + int rc = 0; + + /* + * Firmware reset to activate the new firmware. + * Function 0 will update and load the new firmware + * during attach. + */ + if (timer_pending(&efct->xport->stats_timer)) + del_timer(&efct->xport->stats_timer); + + if (efct_hw_reset(&efct->hw, EFCT_HW_RESET_FIRMWARE)) { + efc_log_info(efct, "failed to reset firmware\n"); + rc = -1; + } else { + efc_log_info(efct, + "successfully reset firmware.Now resetting port\n"); + + efct_device_detach(efct); + rc = efct_device_attach(efct); + } + return rc; +} + +static int +efct_request_firmware_update(struct efct *efct) +{ + int rc = 0; + u8 file_name[256], fw_change_status = 0; + const struct firmware *fw; + struct efct_hw_grp_hdr *fw_image; + + snprintf(file_name, 256, "%s.grp", efct->model); + + rc = request_firmware(&fw, file_name, &efct->pci->dev); + if (rc) { + efc_log_debug(efct, "Firmware file(%s) not found.\n", + file_name); + return rc; + } + + fw_image = (struct efct_hw_grp_hdr *)fw->data; + + if (!strncmp(efct->hw.sli.fw_name[0], fw_image->revision, + strnlen(fw_image->revision, 16))) { + efc_log_debug(efct, + "Skipped update. Firmware is already up to date.\n"); + goto exit; + } + + efc_log_info(efct, "Firmware update is initiated. %s -> %s\n", + efct->hw.sli.fw_name[0], fw_image->revision); + + rc = efct_firmware_write(efct, fw->data, fw->size, &fw_change_status); + if (rc) { + efc_log_err(efct, + "Firmware update failed. Return code = %d\n", rc); + goto exit; + } + + efc_log_info(efct, "Firmware updated successfully\n"); + switch (fw_change_status) { + case 0x00: + efc_log_info(efct, "New firmware is active.\n"); + break; + case 0x01: + efc_log_info(efct, + "System reboot needed to activate the new firmware\n"); + break; + case 0x02: + case 0x03: + efc_log_info(efct, + "firmware is resetting to activate the new firmware\n"); + efct_fw_reset(efct); + break; + default: + efc_log_info(efct, + "Unexected value change_status:%d\n", fw_change_status); + break; + } + +exit: + release_firmware(fw); + + return rc; +} + +static void +efct_device_free(struct efct *efct) +{ + if (efct) { + efct_devices[efct->instance_index] = NULL; + + kfree(efct); + } +} + +static int +efct_device_interrupts_required(struct efct *efct) +{ + if (efct_hw_setup(&efct->hw, efct, efct->pci) != EFCT_HW_RTN_SUCCESS) + return -1; + + return efct->hw.config.n_eq; +} + +static irqreturn_t +efct_intr_thread(int irq, void *handle) +{ + struct efct_intr_context *intr_ctx = handle; + struct efct *efct = intr_ctx->efct; + + efct_hw_process(&efct->hw, intr_ctx->index, efct->max_isr_time_msec); + return IRQ_HANDLED; +} + +static irqreturn_t +efct_intr_msix(int irq, void *handle) +{ + return IRQ_WAKE_THREAD; +} + +static int +efct_setup_msix(struct efct *efct, u32 num_intrs) +{ + int rc = 0, i; + + if (!pci_find_capability(efct->pci, PCI_CAP_ID_MSIX)) { + dev_err(&efct->pci->dev, + "%s : MSI-X not available\n", __func__); + return -EINVAL; + } + + efct->n_msix_vec = num_intrs; + + rc = pci_alloc_irq_vectors(efct->pci, num_intrs, num_intrs, + PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); + + if (rc < 0) { + dev_err(&efct->pci->dev, "Failed to alloc irq : %d\n", rc); + return rc; + } + + for (i = 0; i < num_intrs; i++) { + struct efct_intr_context *intr_ctx = NULL; + + intr_ctx = &efct->intr_context[i]; + intr_ctx->efct = efct; + intr_ctx->index = i; + + rc = request_threaded_irq(pci_irq_vector(efct->pci, i), + efct_intr_msix, efct_intr_thread, 0, + EFCT_DRIVER_NAME, intr_ctx); + if (rc) { + dev_err(&efct->pci->dev, + "Failed to register %d vector: %d\n", i, rc); + goto out; + } + } + + return rc; + +out: + while (--i >= 0) + free_irq(pci_irq_vector(efct->pci, i), + &efct->intr_context[i]); + + pci_free_irq_vectors(efct->pci); + return rc; +} + +static struct pci_device_id efct_pci_table[] = { + {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G6), 0}, + {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G7), 0}, + {} /* terminate list */ +}; + +static int +efct_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct efct *efct = NULL; + int rc; + u32 i, r; + int num_interrupts = 0; + int nid; + + dev_info(&pdev->dev, "%s\n", EFCT_DRIVER_NAME); + + rc = pci_enable_device_mem(pdev); + if (rc) + return rc; + + pci_set_master(pdev); + + rc = pci_set_mwi(pdev); + if (rc) { + dev_info(&pdev->dev, "pci_set_mwi returned %d\n", rc); + goto mwi_out; + } + + rc = pci_request_regions(pdev, EFCT_DRIVER_NAME); + if (rc) { + dev_err(&pdev->dev, "pci_request_regions failed %d\n", rc); + goto req_regions_out; + } + + /* Fetch the Numa node id for this device */ + nid = dev_to_node(&pdev->dev); + if (nid < 0) { + dev_err(&pdev->dev, "Warning Numa node ID is %d\n", nid); + nid = 0; + } + + /* Allocate efct */ + efct = efct_device_alloc(nid); + if (!efct) { + dev_err(&pdev->dev, "Failed to allocate efct\n"); + rc = -ENOMEM; + goto alloc_out; + } + + efct->pci = pdev; + efct->numa_node = nid; + + /* Map all memory BARs */ + for (i = 0, r = 0; i < EFCT_PCI_MAX_REGS; i++) { + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { + efct->reg[r] = ioremap(pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + r++; + } + + /* + * If the 64-bit attribute is set, both this BAR and the + * next form the complete address. Skip processing the + * next BAR. + */ + if (pci_resource_flags(pdev, i) & IORESOURCE_MEM_64) + i++; + } + + pci_set_drvdata(pdev, efct); + + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0 || + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) { + dev_warn(&pdev->dev, "trying DMA_BIT_MASK(32)\n"); + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0 || + pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) { + dev_err(&pdev->dev, "setting DMA_BIT_MASK failed\n"); + rc = -1; + goto dma_mask_out; + } + } + + num_interrupts = efct_device_interrupts_required(efct); + if (num_interrupts < 0) { + efc_log_err(efct, "efct_device_interrupts_required failed\n"); + rc = -1; + goto dma_mask_out; + } + + /* + * Initialize MSIX interrupts, note, + * efct_setup_msix() enables the interrupt + */ + rc = efct_setup_msix(efct, num_interrupts); + if (rc) { + dev_err(&pdev->dev, "Can't setup msix\n"); + goto dma_mask_out; + } + /* Disable interrupt for now */ + for (i = 0; i < efct->n_msix_vec; i++) { + efc_log_debug(efct, "irq %d disabled\n", i); + disable_irq(pci_irq_vector(efct->pci, i)); + } + + rc = efct_device_attach(efct); + if (rc) + goto attach_out; + + return EFC_SUCCESS; + +attach_out: + efct_teardown_msix(efct); +dma_mask_out: + pci_set_drvdata(pdev, NULL); + + for (i = 0; i < EFCT_PCI_MAX_REGS; i++) { + if (efct->reg[i]) + iounmap(efct->reg[i]); + } + efct_device_free(efct); +alloc_out: + pci_release_regions(pdev); +req_regions_out: + pci_clear_mwi(pdev); +mwi_out: + pci_disable_device(pdev); + return rc; +} + +static void +efct_pci_remove(struct pci_dev *pdev) +{ + struct efct *efct = pci_get_drvdata(pdev); + u32 i; + + if (!efct) + return; + + efct_device_detach(efct); + + efct_teardown_msix(efct); + + for (i = 0; i < EFCT_PCI_MAX_REGS; i++) { + if (efct->reg[i]) + iounmap(efct->reg[i]); + } + + pci_set_drvdata(pdev, NULL); + + efct_devices[efct->instance_index] = NULL; + + efct_device_free(efct); + + pci_release_regions(pdev); + + pci_disable_device(pdev); +} + +static void +efct_device_prep_for_reset(struct efct *efct, struct pci_dev *pdev) +{ + if (efct) { + efc_log_debug(efct, + "PCI channel disable preparing for reset\n"); + efct_device_detach(efct); + /* Disable interrupt and pci device */ + efct_teardown_msix(efct); + } + pci_disable_device(pdev); +} + +static void +efct_device_prep_for_recover(struct efct *efct) +{ + if (efct) { + efc_log_debug(efct, "PCI channel preparing for recovery\n"); + efct_hw_io_abort_all(&efct->hw); + } +} + +/** + * efct_pci_io_error_detected - method for handling PCI I/O error + * @pdev: pointer to PCI device. + * @state: the current PCI connection state. + * + * This routine is registered to the PCI subsystem for error handling. This + * function is called by the PCI subsystem after a PCI bus error affecting + * this device has been detected. When this routine is invoked, it dispatches + * device error detected handling routine, which will perform the proper + * error detected operation. + * + * Return codes + * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery + * PCI_ERS_RESULT_DISCONNECT - device could not be recovered + */ +static pci_ers_result_t +efct_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ + struct efct *efct = pci_get_drvdata(pdev); + pci_ers_result_t rc; + + switch (state) { + case pci_channel_io_normal: + efct_device_prep_for_recover(efct); + rc = PCI_ERS_RESULT_CAN_RECOVER; + break; + case pci_channel_io_frozen: + efct_device_prep_for_reset(efct, pdev); + rc = PCI_ERS_RESULT_NEED_RESET; + break; + case pci_channel_io_perm_failure: + efct_device_detach(efct); + rc = PCI_ERS_RESULT_DISCONNECT; + break; + default: + efc_log_debug(efct, "Unknown PCI error state:0x%x\n", + state); + efct_device_prep_for_reset(efct, pdev); + rc = PCI_ERS_RESULT_NEED_RESET; + break; + } + + return rc; +} + +static pci_ers_result_t +efct_pci_io_slot_reset(struct pci_dev *pdev) +{ + int rc; + struct efct *efct = pci_get_drvdata(pdev); + + rc = pci_enable_device_mem(pdev); + if (rc) { + efc_log_err(efct, + "failed to re-enable PCI device after reset.\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + /* + * As the new kernel behavior of pci_restore_state() API call clears + * device saved_state flag, need to save the restored state again. + */ + + pci_save_state(pdev); + + pci_set_master(pdev); + + rc = efct_setup_msix(efct, efct->n_msix_vec); + if (rc) + efc_log_err(efct, "rc %d returned, IRQ allocation failed\n", + rc); + + /* Perform device reset */ + efct_device_detach(efct); + /* Bring device to online*/ + efct_device_attach(efct); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void +efct_pci_io_resume(struct pci_dev *pdev) +{ + struct efct *efct = pci_get_drvdata(pdev); + + /* Perform device reset */ + efct_device_detach(efct); + /* Bring device to online*/ + efct_device_attach(efct); +} + +MODULE_DEVICE_TABLE(pci, efct_pci_table); + +static struct pci_error_handlers efct_pci_err_handler = { + .error_detected = efct_pci_io_error_detected, + .slot_reset = efct_pci_io_slot_reset, + .resume = efct_pci_io_resume, +}; + +static struct pci_driver efct_pci_driver = { + .name = EFCT_DRIVER_NAME, + .id_table = efct_pci_table, + .probe = efct_pci_probe, + .remove = efct_pci_remove, + .err_handler = &efct_pci_err_handler, +}; + +static +int __init efct_init(void) +{ + int rc; + + rc = efct_device_init(); + if (rc) { + pr_err("efct_device_init failed rc=%d\n", rc); + return rc; + } + + rc = pci_register_driver(&efct_pci_driver); + if (rc) { + pr_err("pci_register_driver failed rc=%d\n", rc); + efct_device_shutdown(); + } + + return rc; +} + +static void __exit efct_exit(void) +{ + pci_unregister_driver(&efct_pci_driver); + efct_device_shutdown(); +} + +module_init(efct_init); +module_exit(efct_exit); +MODULE_VERSION(EFCT_DRIVER_VERSION); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Broadcom"); diff --git a/drivers/scsi/elx/efct/efct_driver.h b/drivers/scsi/elx/efct/efct_driver.h new file mode 100644 index 000000000000..9c49745f5d77 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_driver.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#if !defined(__EFCT_DRIVER_H__) +#define __EFCT_DRIVER_H__ + +/*************************************************************************** + * OS specific includes + */ +#include +#include +#include +#include +#include "../include/efc_common.h" +#include "../libefc/efclib.h" +#include "efct_hw.h" +#include "efct_io.h" +#include "efct_xport.h" + +#define EFCT_DRIVER_NAME "efct" +#define EFCT_DRIVER_VERSION "1.0.0.0" + +/* EFCT_DEFAULT_FILTER- + * MRQ filter to segregate the IO flow. + */ +#define EFCT_DEFAULT_FILTER "0x01ff22ff,0,0,0" + +/* EFCT_OS_MAX_ISR_TIME_MSEC - + * maximum time driver code should spend in an interrupt + * or kernel thread context without yielding + */ +#define EFCT_OS_MAX_ISR_TIME_MSEC 1000 + +#define EFCT_FC_MAX_SGL 64 +#define EFCT_FC_DIF_SEED 0 + +/* Watermark */ +#define EFCT_WATERMARK_HIGH_PCT 90 +#define EFCT_WATERMARK_LOW_PCT 80 +#define EFCT_IO_WATERMARK_PER_INITIATOR 8 + +#define EFCT_PCI_MAX_REGS 6 +#define MAX_PCI_INTERRUPTS 16 + +struct efct_intr_context { + struct efct *efct; + u32 index; +}; + +struct efct { + struct pci_dev *pci; + void __iomem *reg[EFCT_PCI_MAX_REGS]; + + u32 n_msix_vec; + bool attached; + bool soft_wwn_enable; + u8 efct_req_fw_upgrade; + struct efct_intr_context intr_context[MAX_PCI_INTERRUPTS]; + u32 numa_node; + + char name[EFC_NAME_LENGTH]; + u32 instance_index; + struct efct_scsi_tgt tgt_efct; + struct efct_xport *xport; + struct efc *efcport; + struct Scsi_Host *shost; + int logmask; + u32 max_isr_time_msec; + + const char *desc; + + const char *model; + + struct efct_hw hw; + + u32 rq_selection_policy; + char *filter_def; + int topology; + + /* Look up for target node */ + struct xarray lookup; + + /* + * Target IO timer value: + * Zero: target command timeout disabled. + * Non-zero: Timeout value, in seconds, for target commands + */ + u32 target_io_timer_sec; + + int speed; + struct dentry *sess_debugfs_dir; +}; + +#define FW_WRITE_BUFSIZE (64 * 1024) + +struct efct_fw_write_result { + struct completion done; + int status; + u32 actual_xfer; + u32 change_status; +}; + +#define MAX_EFCT_DEVICES 64 +extern struct efct *efct_devices[MAX_EFCT_DEVICES]; + +#endif /* __EFCT_DRIVER_H__ */ diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c new file mode 100644 index 000000000000..0af13aa12e35 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -0,0 +1,1166 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efct_driver.h" +#include "efct_hw.h" +#include "efct_unsol.h" + +struct efct_mbox_rqst_ctx { + int (*callback)(struct efc *efc, int status, u8 *mqe, void *arg); + void *arg; +}; + +static enum efct_hw_rtn +efct_hw_link_event_init(struct efct_hw *hw) +{ + hw->link.status = SLI4_LINK_STATUS_MAX; + hw->link.topology = SLI4_LINK_TOPO_NONE; + hw->link.medium = SLI4_LINK_MEDIUM_MAX; + hw->link.speed = 0; + hw->link.loop_map = NULL; + hw->link.fc_id = U32_MAX; + + return EFCT_HW_RTN_SUCCESS; +} + +static enum efct_hw_rtn +efct_hw_read_max_dump_size(struct efct_hw *hw) +{ + u8 buf[SLI4_BMBX_SIZE]; + struct efct *efct = hw->os; + int rc = EFCT_HW_RTN_SUCCESS; + struct sli4_rsp_cmn_set_dump_location *rsp; + + /* attempt to detemine the dump size for function 0 only. */ + if (PCI_FUNC(efct->pci->devfn) != 0) + return rc; + + if (sli_cmd_common_set_dump_location(&hw->sli, buf, 1, 0, NULL, 0)) + return EFC_FAIL; + + rsp = (struct sli4_rsp_cmn_set_dump_location *) + (buf + offsetof(struct sli4_cmd_sli_config, payload.embed)); + + rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); + if (rc != EFCT_HW_RTN_SUCCESS) { + efc_log_test(hw->os, "set dump location cmd failed\n"); + return rc; + } + + hw->dump_size = + le32_to_cpu(rsp->buffer_length_dword) & SLI4_CMN_SET_DUMP_BUFFER_LEN; + + efc_log_debug(hw->os, "Dump size %x\n", hw->dump_size); + + return rc; +} + +static int +__efct_read_topology_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg) +{ + struct sli4_cmd_read_topology *read_topo = + (struct sli4_cmd_read_topology *)mqe; + u8 speed; + struct efc_domain_record drec = {0}; + struct efct *efct = hw->os; + + if (status || le16_to_cpu(read_topo->hdr.status)) { + efc_log_debug(hw->os, "bad status cqe=%#x mqe=%#x\n", + status, + le16_to_cpu(read_topo->hdr.status)); + return EFC_FAIL; + } + + switch (le32_to_cpu(read_topo->dw2_attentype) & + SLI4_READTOPO_ATTEN_TYPE) { + case SLI4_READ_TOPOLOGY_LINK_UP: + hw->link.status = SLI4_LINK_STATUS_UP; + break; + case SLI4_READ_TOPOLOGY_LINK_DOWN: + hw->link.status = SLI4_LINK_STATUS_DOWN; + break; + case SLI4_READ_TOPOLOGY_LINK_NO_ALPA: + hw->link.status = SLI4_LINK_STATUS_NO_ALPA; + break; + default: + hw->link.status = SLI4_LINK_STATUS_MAX; + break; + } + + switch (read_topo->topology) { + case SLI4_READ_TOPOLOGY_NPORT: + hw->link.topology = SLI4_LINK_TOPO_NPORT; + break; + case SLI4_READ_TOPOLOGY_FC_AL: + hw->link.topology = SLI4_LINK_TOPO_LOOP; + if (hw->link.status == SLI4_LINK_STATUS_UP) + hw->link.loop_map = hw->loop_map.virt; + hw->link.fc_id = read_topo->acquired_al_pa; + break; + default: + hw->link.topology = SLI4_LINK_TOPO_MAX; + break; + } + + hw->link.medium = SLI4_LINK_MEDIUM_FC; + + speed = (le32_to_cpu(read_topo->currlink_state) & + SLI4_READTOPO_LINKSTATE_SPEED) >> 8; + switch (speed) { + case SLI4_READ_TOPOLOGY_SPEED_1G: + hw->link.speed = 1 * 1000; + break; + case SLI4_READ_TOPOLOGY_SPEED_2G: + hw->link.speed = 2 * 1000; + break; + case SLI4_READ_TOPOLOGY_SPEED_4G: + hw->link.speed = 4 * 1000; + break; + case SLI4_READ_TOPOLOGY_SPEED_8G: + hw->link.speed = 8 * 1000; + break; + case SLI4_READ_TOPOLOGY_SPEED_16G: + hw->link.speed = 16 * 1000; + hw->link.loop_map = NULL; + break; + case SLI4_READ_TOPOLOGY_SPEED_32G: + hw->link.speed = 32 * 1000; + hw->link.loop_map = NULL; + break; + } + + drec.speed = hw->link.speed; + drec.fc_id = hw->link.fc_id; + drec.is_nport = true; + efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND, &drec); + + return EFC_SUCCESS; +} + +static int +efct_hw_cb_link(void *ctx, void *e) +{ + struct efct_hw *hw = ctx; + struct sli4_link_event *event = e; + struct efc_domain *d = NULL; + int rc = EFCT_HW_RTN_ERROR; + struct efct *efct = hw->os; + + efct_hw_link_event_init(hw); + + switch (event->status) { + case SLI4_LINK_STATUS_UP: + + hw->link = *event; + efct->efcport->link_status = EFC_LINK_STATUS_UP; + + if (event->topology == SLI4_LINK_TOPO_NPORT) { + struct efc_domain_record drec = {0}; + + efc_log_info(hw->os, "Link Up, NPORT, speed is %d\n", + event->speed); + drec.speed = event->speed; + drec.fc_id = event->fc_id; + drec.is_nport = true; + efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_FOUND, + &drec); + } else if (event->topology == SLI4_LINK_TOPO_LOOP) { + u8 buf[SLI4_BMBX_SIZE]; + + efc_log_info(hw->os, "Link Up, LOOP, speed is %d\n", + event->speed); + + if (!sli_cmd_read_topology(&hw->sli, buf, + &hw->loop_map)) { + rc = efct_hw_command(hw, buf, EFCT_CMD_NOWAIT, + __efct_read_topology_cb, NULL); + } + + if (rc) + efc_log_test(hw->os, "READ_TOPOLOGY failed\n"); + } else { + efc_log_info(hw->os, "%s(%#x), speed is %d\n", + "Link Up, unsupported topology ", + event->topology, event->speed); + } + break; + case SLI4_LINK_STATUS_DOWN: + efc_log_info(hw->os, "Link down\n"); + + hw->link.status = event->status; + efct->efcport->link_status = EFC_LINK_STATUS_DOWN; + + d = efct->efcport->domain; + if (d) + efc_domain_cb(efct->efcport, EFC_HW_DOMAIN_LOST, d); + break; + default: + efc_log_test(hw->os, "unhandled link status %#x\n", + event->status); + break; + } + + return EFC_SUCCESS; +} + +enum efct_hw_rtn +efct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev) +{ + u32 i, max_sgl, cpus; + + if (hw->hw_setup_called) + return EFCT_HW_RTN_SUCCESS; + + /* + * efct_hw_init() relies on NULL pointers indicating that a structure + * needs allocation. If a structure is non-NULL, efct_hw_init() won't + * free/realloc that memory + */ + memset(hw, 0, sizeof(struct efct_hw)); + + hw->hw_setup_called = true; + + hw->os = os; + + mutex_init(&hw->bmbx_lock); + spin_lock_init(&hw->cmd_lock); + INIT_LIST_HEAD(&hw->cmd_head); + INIT_LIST_HEAD(&hw->cmd_pending); + hw->cmd_head_count = 0; + + /* Create mailbox command ctx pool */ + hw->cmd_ctx_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ, + sizeof(struct efct_command_ctx)); + if (!hw->cmd_ctx_pool) { + efc_log_err(hw->os, "failed to allocate mailbox buffer pool\n"); + return EFCT_HW_RTN_ERROR; + } + + /* Create mailbox request ctx pool for library callback */ + hw->mbox_rqst_pool = mempool_create_kmalloc_pool(EFCT_CMD_CTX_POOL_SZ, + sizeof(struct efct_mbox_rqst_ctx)); + if (!hw->mbox_rqst_pool) { + efc_log_err(hw->os, "failed to allocate mbox request pool\n"); + return EFCT_HW_RTN_ERROR; + } + + spin_lock_init(&hw->io_lock); + spin_lock_init(&hw->io_abort_lock); + + atomic_set(&hw->io_alloc_failed_count, 0); + + hw->config.speed = SLI4_LINK_SPEED_AUTO_16_8_4; + if (sli_setup(&hw->sli, hw->os, pdev, ((struct efct *)os)->reg)) { + efc_log_err(hw->os, "SLI setup failed\n"); + return EFCT_HW_RTN_ERROR; + } + + efct_hw_link_event_init(hw); + + sli_callback(&hw->sli, SLI4_CB_LINK, efct_hw_cb_link, hw); + + /* + * Set all the queue sizes to the maximum allowed. + */ + for (i = 0; i < ARRAY_SIZE(hw->num_qentries); i++) + hw->num_qentries[i] = hw->sli.qinfo.max_qentries[i]; + /* + * Adjust the size of the WQs so that the CQ is twice as big as + * the WQ to allow for 2 completions per IO. This allows us to + * handle multi-phase as well as aborts. + */ + hw->num_qentries[SLI4_QTYPE_WQ] = hw->num_qentries[SLI4_QTYPE_CQ] / 2; + + /* + * The RQ assignment for RQ pair mode. + */ + + hw->config.rq_default_buffer_size = EFCT_HW_RQ_SIZE_PAYLOAD; + hw->config.n_io = hw->sli.ext[SLI4_RSRC_XRI].size; + + cpus = num_possible_cpus(); + hw->config.n_eq = cpus > EFCT_HW_MAX_NUM_EQ ? EFCT_HW_MAX_NUM_EQ : cpus; + + max_sgl = sli_get_max_sgl(&hw->sli) - SLI4_SGE_MAX_RESERVED; + max_sgl = (max_sgl > EFCT_FC_MAX_SGL) ? EFCT_FC_MAX_SGL : max_sgl; + hw->config.n_sgl = max_sgl; + + (void)efct_hw_read_max_dump_size(hw); + + return EFCT_HW_RTN_SUCCESS; +} + +static void +efct_logfcfi(struct efct_hw *hw, u32 j, u32 i, u32 id) +{ + efc_log_info(hw->os, + "REG_FCFI: filter[%d] %08X -> RQ[%d] id=%d\n", + j, hw->config.filter_def[j], i, id); +} + +static inline void +efct_hw_init_free_io(struct efct_hw_io *io) +{ + /* + * Set io->done to NULL, to avoid any callbacks, should + * a completion be received for one of these IOs + */ + io->done = NULL; + io->abort_done = NULL; + io->status_saved = false; + io->abort_in_progress = false; + io->type = 0xFFFF; + io->wq = NULL; +} + +static u8 efct_hw_iotype_is_originator(u16 io_type) +{ + switch (io_type) { + case EFCT_HW_FC_CT: + case EFCT_HW_ELS_REQ: + return 1; + default: + return EFC_SUCCESS; + } +} + +static void +efct_hw_io_restore_sgl(struct efct_hw *hw, struct efct_hw_io *io) +{ + /* Restore the default */ + io->sgl = &io->def_sgl; + io->sgl_count = io->def_sgl_count; +} + +static void +efct_hw_wq_process_io(void *arg, u8 *cqe, int status) +{ + struct efct_hw_io *io = arg; + struct efct_hw *hw = io->hw; + struct sli4_fc_wcqe *wcqe = (void *)cqe; + u32 len = 0; + u32 ext = 0; + + /* clear xbusy flag if WCQE[XB] is clear */ + if (io->xbusy && (wcqe->flags & SLI4_WCQE_XB) == 0) + io->xbusy = false; + + /* get extended CQE status */ + switch (io->type) { + case EFCT_HW_BLS_ACC: + case EFCT_HW_BLS_RJT: + break; + case EFCT_HW_ELS_REQ: + sli_fc_els_did(&hw->sli, cqe, &ext); + len = sli_fc_response_length(&hw->sli, cqe); + break; + case EFCT_HW_ELS_RSP: + case EFCT_HW_FC_CT_RSP: + break; + case EFCT_HW_FC_CT: + len = sli_fc_response_length(&hw->sli, cqe); + break; + case EFCT_HW_IO_TARGET_WRITE: + len = sli_fc_io_length(&hw->sli, cqe); + break; + case EFCT_HW_IO_TARGET_READ: + len = sli_fc_io_length(&hw->sli, cqe); + break; + case EFCT_HW_IO_TARGET_RSP: + break; + case EFCT_HW_IO_DNRX_REQUEUE: + /* release the count for re-posting the buffer */ + /* efct_hw_io_free(hw, io); */ + break; + default: + efc_log_err(hw->os, "unhandled io type %#x for XRI 0x%x\n", + io->type, io->indicator); + break; + } + if (status) { + ext = sli_fc_ext_status(&hw->sli, cqe); + /* + * If we're not an originator IO, and XB is set, then issue + * abort for the IO from within the HW + */ + if ((!efct_hw_iotype_is_originator(io->type)) && + wcqe->flags & SLI4_WCQE_XB) { + enum efct_hw_rtn rc; + + efc_log_debug(hw->os, "aborting xri=%#x tag=%#x\n", + io->indicator, io->reqtag); + + /* + * Because targets may send a response when the IO + * completes using the same XRI, we must wait for the + * XRI_ABORTED CQE to issue the IO callback + */ + rc = efct_hw_io_abort(hw, io, false, NULL, NULL); + if (rc == EFCT_HW_RTN_SUCCESS) { + /* + * latch status to return after abort is + * complete + */ + io->status_saved = true; + io->saved_status = status; + io->saved_ext = ext; + io->saved_len = len; + goto exit_efct_hw_wq_process_io; + } else if (rc == EFCT_HW_RTN_IO_ABORT_IN_PROGRESS) { + /* + * Already being aborted by someone else (ABTS + * perhaps). Just return original + * error. + */ + efc_log_debug(hw->os, "%s%#x tag=%#x\n", + "abort in progress xri=", + io->indicator, io->reqtag); + + } else { + /* Failed to abort for some other reason, log + * error + */ + efc_log_test(hw->os, "%s%#x tag=%#x rc=%d\n", + "Failed to abort xri=", + io->indicator, io->reqtag, rc); + } + } + } + + if (io->done) { + efct_hw_done_t done = io->done; + + io->done = NULL; + + if (io->status_saved) { + /* use latched status if exists */ + status = io->saved_status; + len = io->saved_len; + ext = io->saved_ext; + io->status_saved = false; + } + + /* Restore default SGL */ + efct_hw_io_restore_sgl(hw, io); + done(io, len, status, ext, io->arg); + } + +exit_efct_hw_wq_process_io: + return; +} + +static enum efct_hw_rtn +efct_hw_setup_io(struct efct_hw *hw) +{ + u32 i = 0; + struct efct_hw_io *io = NULL; + uintptr_t xfer_virt = 0; + uintptr_t xfer_phys = 0; + u32 index; + bool new_alloc = true; + struct efc_dma *dma; + struct efct *efct = hw->os; + + if (!hw->io) { + hw->io = kmalloc_array(hw->config.n_io, sizeof(io), + GFP_KERNEL); + + if (!hw->io) + return EFCT_HW_RTN_NO_MEMORY; + + memset(hw->io, 0, hw->config.n_io * sizeof(io)); + + for (i = 0; i < hw->config.n_io; i++) { + hw->io[i] = kzalloc(sizeof(*io), GFP_KERNEL); + if (!hw->io[i]) + goto error; + } + + /* Create WQE buffs for IO */ + hw->wqe_buffs = kzalloc((hw->config.n_io * hw->sli.wqe_size), + GFP_KERNEL); + if (!hw->wqe_buffs) { + kfree(hw->io); + return EFCT_HW_RTN_NO_MEMORY; + } + + } else { + /* re-use existing IOs, including SGLs */ + new_alloc = false; + } + + if (new_alloc) { + dma = &hw->xfer_rdy; + dma->size = sizeof(struct fcp_txrdy) * hw->config.n_io; + dma->virt = dma_alloc_coherent(&efct->pci->dev, + dma->size, &dma->phys, GFP_DMA); + if (!dma->virt) + return EFCT_HW_RTN_NO_MEMORY; + } + xfer_virt = (uintptr_t)hw->xfer_rdy.virt; + xfer_phys = hw->xfer_rdy.phys; + + /* Initialize the pool of HW IO objects */ + for (i = 0; i < hw->config.n_io; i++) { + struct hw_wq_callback *wqcb; + + io = hw->io[i]; + + /* initialize IO fields */ + io->hw = hw; + + /* Assign a WQE buff */ + io->wqe.wqebuf = &hw->wqe_buffs[i * hw->sli.wqe_size]; + + /* Allocate the request tag for this IO */ + wqcb = efct_hw_reqtag_alloc(hw, efct_hw_wq_process_io, io); + if (!wqcb) { + efc_log_err(hw->os, "can't allocate request tag\n"); + return EFCT_HW_RTN_NO_RESOURCES; + } + io->reqtag = wqcb->instance_index; + + /* Now for the fields that are initialized on each free */ + efct_hw_init_free_io(io); + + /* The XB flag isn't cleared on IO free, so init to zero */ + io->xbusy = 0; + + if (sli_resource_alloc(&hw->sli, SLI4_RSRC_XRI, + &io->indicator, &index)) { + efc_log_err(hw->os, + "sli_resource_alloc failed @ %d\n", i); + return EFCT_HW_RTN_NO_MEMORY; + } + if (new_alloc) { + dma = &io->def_sgl; + dma->size = hw->config.n_sgl * + sizeof(struct sli4_sge); + dma->virt = dma_alloc_coherent(&efct->pci->dev, + dma->size, &dma->phys, + GFP_DMA); + if (!dma->virt) { + efc_log_err(hw->os, "dma_alloc fail %d\n", i); + memset(&io->def_sgl, 0, + sizeof(struct efc_dma)); + return EFCT_HW_RTN_NO_MEMORY; + } + } + io->def_sgl_count = hw->config.n_sgl; + io->sgl = &io->def_sgl; + io->sgl_count = io->def_sgl_count; + + if (hw->xfer_rdy.size) { + io->xfer_rdy.virt = (void *)xfer_virt; + io->xfer_rdy.phys = xfer_phys; + io->xfer_rdy.size = sizeof(struct fcp_txrdy); + + xfer_virt += sizeof(struct fcp_txrdy); + xfer_phys += sizeof(struct fcp_txrdy); + } + } + + return EFCT_HW_RTN_SUCCESS; +error: + for (i = 0; i < hw->config.n_io && hw->io[i]; i++) { + kfree(hw->io[i]); + hw->io[i] = NULL; + } + + kfree(hw->io); + hw->io = NULL; + + return EFCT_HW_RTN_NO_MEMORY; +} + + +static enum efct_hw_rtn +efct_hw_init_prereg_io(struct efct_hw *hw) +{ + u32 i, idx = 0; + struct efct_hw_io *io = NULL; + u8 cmd[SLI4_BMBX_SIZE]; + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + u32 n_rem; + u32 n = 0; + u32 sgls_per_request = 256; + struct efc_dma **sgls = NULL; + struct efc_dma req; + struct efct *efct = hw->os; + + sgls = kmalloc_array(sgls_per_request, sizeof(*sgls), GFP_KERNEL); + if (!sgls) + return EFCT_HW_RTN_NO_MEMORY; + + memset(&req, 0, sizeof(struct efc_dma)); + req.size = 32 + sgls_per_request * 16; + req.virt = dma_alloc_coherent(&efct->pci->dev, req.size, &req.phys, + GFP_DMA); + if (!req.virt) { + kfree(sgls); + return EFCT_HW_RTN_NO_MEMORY; + } + + for (n_rem = hw->config.n_io; n_rem; n_rem -= n) { + /* Copy address of SGL's into local sgls[] array, break + * out if the xri is not contiguous. + */ + u32 min = (sgls_per_request < n_rem) ? sgls_per_request : n_rem; + + for (n = 0; n < min; n++) { + /* Check that we have contiguous xri values */ + if (n > 0) { + if (hw->io[idx + n]->indicator != + hw->io[idx + n - 1]->indicator + 1) + break; + } + + sgls[n] = hw->io[idx + n]->sgl; + } + + if (sli_cmd_post_sgl_pages(&hw->sli, cmd, + hw->io[idx]->indicator, n, sgls, NULL, &req)) { + rc = EFCT_HW_RTN_ERROR; + break; + } + + if (efct_hw_command(hw, cmd, EFCT_CMD_POLL, NULL, NULL)) { + rc = EFCT_HW_RTN_ERROR; + efc_log_err(hw->os, "SGL post failed\n"); + break; + } + + /* Add to tail if successful */ + for (i = 0; i < n; i++, idx++) { + io = hw->io[idx]; + io->state = EFCT_HW_IO_STATE_FREE; + INIT_LIST_HEAD(&io->list_entry); + list_add_tail(&io->list_entry, &hw->io_free); + } + } + + dma_free_coherent(&efct->pci->dev, req.size, req.virt, req.phys); + memset(&req, 0, sizeof(struct efc_dma)); + kfree(sgls); + + return rc; +} + +static enum efct_hw_rtn +efct_hw_init_io(struct efct_hw *hw) +{ + u32 i, idx = 0; + bool prereg = false; + struct efct_hw_io *io = NULL; + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + + prereg = hw->sli.params.sgl_pre_registered; + + if (prereg) + return efct_hw_init_prereg_io(hw); + + for (i = 0; i < hw->config.n_io; i++, idx++) { + io = hw->io[idx]; + io->state = EFCT_HW_IO_STATE_FREE; + INIT_LIST_HEAD(&io->list_entry); + list_add_tail(&io->list_entry, &hw->io_free); + } + + return rc; +} + +static enum efct_hw_rtn +efct_hw_config_set_fdt_xfer_hint(struct efct_hw *hw, u32 fdt_xfer_hint) +{ + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + u8 buf[SLI4_BMBX_SIZE]; + struct sli4_rqst_cmn_set_features_set_fdt_xfer_hint param; + + memset(¶m, 0, sizeof(param)); + param.fdt_xfer_hint = cpu_to_le32(fdt_xfer_hint); + /* build the set_features command */ + sli_cmd_common_set_features(&hw->sli, buf, + SLI4_SET_FEATURES_SET_FTD_XFER_HINT, sizeof(param), ¶m); + + rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); + if (rc) + efc_log_warn(hw->os, "set FDT hint %d failed: %d\n", + fdt_xfer_hint, rc); + else + efc_log_info(hw->os, "Set FTD transfer hint to %d\n", + le32_to_cpu(param.fdt_xfer_hint)); + + return rc; +} + +static int +efct_hw_config_rq(struct efct_hw *hw) +{ + u32 min_rq_count, i, rc; + struct sli4_cmd_rq_cfg rq_cfg[SLI4_CMD_REG_FCFI_NUM_RQ_CFG]; + u8 buf[SLI4_BMBX_SIZE]; + + efc_log_info(hw->os, "using REG_FCFI standard\n"); + + /* + * Set the filter match/mask values from hw's + * filter_def values + */ + for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { + rq_cfg[i].rq_id = cpu_to_le16(0xffff); + rq_cfg[i].r_ctl_mask = (u8)hw->config.filter_def[i]; + rq_cfg[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 8); + rq_cfg[i].type_mask = (u8)(hw->config.filter_def[i] >> 16); + rq_cfg[i].type_match = (u8)(hw->config.filter_def[i] >> 24); + } + + /* + * Update the rq_id's of the FCF configuration + * (don't update more than the number of rq_cfg + * elements) + */ + min_rq_count = (hw->hw_rq_count < SLI4_CMD_REG_FCFI_NUM_RQ_CFG) ? + hw->hw_rq_count : SLI4_CMD_REG_FCFI_NUM_RQ_CFG; + for (i = 0; i < min_rq_count; i++) { + struct hw_rq *rq = hw->hw_rq[i]; + u32 j; + + for (j = 0; j < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; j++) { + u32 mask = (rq->filter_mask != 0) ? + rq->filter_mask : 1; + + if (!(mask & (1U << j))) + continue; + + rq_cfg[i].rq_id = cpu_to_le16(rq->hdr->id); + efct_logfcfi(hw, j, i, rq->hdr->id); + } + } + + rc = EFCT_HW_RTN_ERROR; + if (!sli_cmd_reg_fcfi(&hw->sli, buf, 0, rq_cfg)) { + rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, + NULL, NULL); + } + + if (rc != EFCT_HW_RTN_SUCCESS) { + efc_log_err(hw->os, + "FCFI registration failed\n"); + return rc; + } + hw->fcf_indicator = + le16_to_cpu(((struct sli4_cmd_reg_fcfi *)buf)->fcfi); + + return rc; +} + +static int32_t +efct_hw_config_mrq(struct efct_hw *hw, uint8_t mode, uint16_t fcf_index) +{ + u8 buf[SLI4_BMBX_SIZE], mrq_bitmask = 0; + struct hw_rq *rq; + struct sli4_cmd_reg_fcfi_mrq *rsp = NULL; + struct sli4_cmd_rq_cfg rq_filter[SLI4_CMD_REG_FCFI_MRQ_NUM_RQ_CFG]; + u32 rc, i; + + if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) + goto issue_cmd; + + /* Set the filter match/mask values from hw's filter_def values */ + for (i = 0; i < SLI4_CMD_REG_FCFI_NUM_RQ_CFG; i++) { + rq_filter[i].rq_id = cpu_to_le16(0xffff); + rq_filter[i].type_mask = (u8)hw->config.filter_def[i]; + rq_filter[i].type_match = (u8)(hw->config.filter_def[i] >> 8); + rq_filter[i].r_ctl_mask = (u8)(hw->config.filter_def[i] >> 16); + rq_filter[i].r_ctl_match = (u8)(hw->config.filter_def[i] >> 24); + } + + rq = hw->hw_rq[0]; + rq_filter[0].rq_id = cpu_to_le16(rq->hdr->id); + rq_filter[1].rq_id = cpu_to_le16(rq->hdr->id); + + mrq_bitmask = 0x2; +issue_cmd: + efc_log_debug(hw->os, "Issue reg_fcfi_mrq count:%d policy:%d mode:%d\n", + hw->hw_rq_count, hw->config.rq_selection_policy, mode); + /* Invoke REG_FCFI_MRQ */ + rc = sli_cmd_reg_fcfi_mrq(&hw->sli, buf, mode, fcf_index, + hw->config.rq_selection_policy, mrq_bitmask, + hw->hw_mrq_count, rq_filter); + if (rc) { + efc_log_err(hw->os, "sli_cmd_reg_fcfi_mrq() failed\n"); + return EFC_FAIL; + } + + rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); + + rsp = (struct sli4_cmd_reg_fcfi_mrq *)buf; + + if ((rc) || (le16_to_cpu(rsp->hdr.status))) { + efc_log_err(hw->os, "FCFI MRQ reg failed. cmd=%x status=%x\n", + rsp->hdr.command, le16_to_cpu(rsp->hdr.status)); + return EFCT_HW_RTN_ERROR; + } + + if (mode == SLI4_CMD_REG_FCFI_SET_FCFI_MODE) + hw->fcf_indicator = le16_to_cpu(rsp->fcfi); + return 0; +} + +static void +efct_hw_queue_hash_add(struct efct_queue_hash *hash, + u16 id, u16 index) +{ + u32 hash_index = id & (EFCT_HW_Q_HASH_SIZE - 1); + + /* + * Since the hash is always bigger than the number of queues, then we + * never have to worry about an infinite loop. + */ + while (hash[hash_index].in_use) + hash_index = (hash_index + 1) & (EFCT_HW_Q_HASH_SIZE - 1); + + /* not used, claim the entry */ + hash[hash_index].id = id; + hash[hash_index].in_use = true; + hash[hash_index].index = index; +} + +static enum efct_hw_rtn +efct_hw_config_sli_port_health_check(struct efct_hw *hw, u8 query, u8 enable) +{ + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + u8 buf[SLI4_BMBX_SIZE]; + struct sli4_rqst_cmn_set_features_health_check param; + u32 health_check_flag = 0; + + memset(¶m, 0, sizeof(param)); + + if (enable) + health_check_flag |= SLI4_RQ_HEALTH_CHECK_ENABLE; + + if (query) + health_check_flag |= SLI4_RQ_HEALTH_CHECK_QUERY; + + param.health_check_dword = cpu_to_le32(health_check_flag); + + /* build the set_features command */ + sli_cmd_common_set_features(&hw->sli, buf, + SLI4_SET_FEATURES_SLI_PORT_HEALTH_CHECK, sizeof(param), ¶m); + + rc = efct_hw_command(hw, buf, EFCT_CMD_POLL, NULL, NULL); + if (rc) + efc_log_err(hw->os, "efct_hw_command returns %d\n", rc); + else + efc_log_test(hw->os, "SLI Port Health Check is enabled\n"); + + return rc; +} + +enum efct_hw_rtn +efct_hw_init(struct efct_hw *hw) +{ + enum efct_hw_rtn rc; + u32 i = 0; + int rem_count; + unsigned long flags = 0; + struct efct_hw_io *temp; + struct efc_dma *dma; + + /* + * Make sure the command lists are empty. If this is start-of-day, + * they'll be empty since they were just initialized in efct_hw_setup. + * If we've just gone through a reset, the command and command pending + * lists should have been cleaned up as part of the reset + * (efct_hw_reset()). + */ + spin_lock_irqsave(&hw->cmd_lock, flags); + if (!list_empty(&hw->cmd_head)) { + spin_unlock_irqrestore(&hw->cmd_lock, flags); + efc_log_err(hw->os, "command found on cmd list\n"); + return EFCT_HW_RTN_ERROR; + } + if (!list_empty(&hw->cmd_pending)) { + spin_unlock_irqrestore(&hw->cmd_lock, flags); + efc_log_err(hw->os, "command found on pending list\n"); + return EFCT_HW_RTN_ERROR; + } + spin_unlock_irqrestore(&hw->cmd_lock, flags); + + /* Free RQ buffers if prevously allocated */ + efct_hw_rx_free(hw); + + /* + * The IO queues must be initialized here for the reset case. The + * efct_hw_init_io() function will re-add the IOs to the free list. + * The cmd_head list should be OK since we free all entries in + * efct_hw_command_cancel() that is called in the efct_hw_reset(). + */ + + /* If we are in this function due to a reset, there may be stale items + * on lists that need to be removed. Clean them up. + */ + rem_count = 0; + if (hw->io_wait_free.next) { + while ((!list_empty(&hw->io_wait_free))) { + rem_count++; + temp = list_first_entry(&hw->io_wait_free, + struct efct_hw_io, + list_entry); + list_del(&temp->list_entry); + } + if (rem_count > 0) { + efc_log_debug(hw->os, + "rmvd %d items from io_wait_free list\n", + rem_count); + } + } + rem_count = 0; + if (hw->io_inuse.next) { + while ((!list_empty(&hw->io_inuse))) { + rem_count++; + temp = list_first_entry(&hw->io_inuse, + struct efct_hw_io, + list_entry); + list_del(&temp->list_entry); + } + if (rem_count > 0) + efc_log_debug(hw->os, + "rmvd %d items from io_inuse list\n", + rem_count); + } + rem_count = 0; + if (hw->io_free.next) { + while ((!list_empty(&hw->io_free))) { + rem_count++; + temp = list_first_entry(&hw->io_free, + struct efct_hw_io, + list_entry); + list_del(&temp->list_entry); + } + if (rem_count > 0) + efc_log_debug(hw->os, + "rmvd %d items from io_free list\n", + rem_count); + } + + INIT_LIST_HEAD(&hw->io_inuse); + INIT_LIST_HEAD(&hw->io_free); + INIT_LIST_HEAD(&hw->io_wait_free); + + /* If MRQ not required, Make sure we dont request feature. */ + if (hw->config.n_rq == 1) + hw->sli.features &= (~SLI4_REQFEAT_MRQP); + + if (sli_init(&hw->sli)) { + efc_log_err(hw->os, "SLI failed to initialize\n"); + return EFCT_HW_RTN_ERROR; + } + + if (hw->sliport_healthcheck) { + rc = efct_hw_config_sli_port_health_check(hw, 0, 1); + if (rc != EFCT_HW_RTN_SUCCESS) { + efc_log_err(hw->os, "Enable port Health check fail\n"); + return rc; + } + } + + /* + * Set FDT transfer hint, only works on Lancer + */ + if (hw->sli.if_type == SLI4_INTF_IF_TYPE_2) { + /* + * Non-fatal error. In particular, we can disregard failure to + * set EFCT_HW_FDT_XFER_HINT on devices with legacy firmware + * that do not support EFCT_HW_FDT_XFER_HINT feature. + */ + efct_hw_config_set_fdt_xfer_hint(hw, EFCT_HW_FDT_XFER_HINT); + } + + /* zero the hashes */ + memset(hw->cq_hash, 0, sizeof(hw->cq_hash)); + efc_log_debug(hw->os, "Max CQs %d, hash size = %d\n", + EFCT_HW_MAX_NUM_CQ, EFCT_HW_Q_HASH_SIZE); + + memset(hw->rq_hash, 0, sizeof(hw->rq_hash)); + efc_log_debug(hw->os, "Max RQs %d, hash size = %d\n", + EFCT_HW_MAX_NUM_RQ, EFCT_HW_Q_HASH_SIZE); + + memset(hw->wq_hash, 0, sizeof(hw->wq_hash)); + efc_log_debug(hw->os, "Max WQs %d, hash size = %d\n", + EFCT_HW_MAX_NUM_WQ, EFCT_HW_Q_HASH_SIZE); + + rc = efct_hw_init_queues(hw); + if (rc) + return rc; + + efct_hw_map_wq_cpu(hw); + + /* Allocate and p_st RQ buffers */ + rc = efct_hw_rx_allocate(hw); + if (rc) { + efc_log_err(hw->os, "rx_allocate failed\n"); + return rc; + } + + rc = efct_hw_rx_post(hw); + if (rc) { + efc_log_err(hw->os, "WARNING - error posting RQ buffers\n"); + return rc; + } + + if (hw->config.n_eq == 1) { + rc = efct_hw_config_rq(hw); + if (rc) { + efc_log_err(hw->os, "config rq failed %d\n", rc); + return rc; + } + } else { + rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_FCFI_MODE, 0); + if (rc != EFCT_HW_RTN_SUCCESS) { + efc_log_err(hw->os, "REG_FCFI_MRQ FCFI reg failed\n"); + return rc; + } + + rc = efct_hw_config_mrq(hw, SLI4_CMD_REG_FCFI_SET_MRQ_MODE, 0); + if (rc != EFCT_HW_RTN_SUCCESS) { + efc_log_err(hw->os, "REG_FCFI_MRQ MRQ reg failed\n"); + return rc; + } + + } + + /* + * Allocate the WQ request tag pool, if not previously allocated + * (the request tag value is 16 bits, thus the pool allocation size + * of 64k) + */ + hw->wq_reqtag_pool = efct_hw_reqtag_pool_alloc(hw); + if (!hw->wq_reqtag_pool) { + efc_log_err(hw->os, "efct_hw_reqtag_init failed %d\n", rc); + return rc; + } + + rc = efct_hw_setup_io(hw); + if (rc) { + efc_log_err(hw->os, "IO allocation failure\n"); + return rc; + } + + rc = efct_hw_init_io(hw); + if (rc) { + efc_log_err(hw->os, "IO initialization failure\n"); + return rc; + } + + dma = &hw->loop_map; + dma->size = SLI4_MIN_LOOP_MAP_BYTES; + dma->virt = dma_alloc_coherent(&hw->os->pci->dev, dma->size, &dma->phys, + GFP_DMA); + if (!dma->virt) + return EFCT_HW_RTN_ERROR; + + /* + * Arming the EQ allows (e.g.) interrupts when CQ completions write EQ + * entries + */ + for (i = 0; i < hw->eq_count; i++) + sli_queue_arm(&hw->sli, &hw->eq[i], true); + + /* + * Initialize RQ hash + */ + for (i = 0; i < hw->rq_count; i++) + efct_hw_queue_hash_add(hw->rq_hash, hw->rq[i].id, i); + + /* + * Initialize WQ hash + */ + for (i = 0; i < hw->wq_count; i++) + efct_hw_queue_hash_add(hw->wq_hash, hw->wq[i].id, i); + + /* + * Arming the CQ allows (e.g.) MQ completions to write CQ entries + */ + for (i = 0; i < hw->cq_count; i++) { + efct_hw_queue_hash_add(hw->cq_hash, hw->cq[i].id, i); + sli_queue_arm(&hw->sli, &hw->cq[i], true); + } + + /* Set RQ process limit*/ + for (i = 0; i < hw->hw_rq_count; i++) { + struct hw_rq *rq = hw->hw_rq[i]; + + hw->cq[rq->cq->instance].proc_limit = hw->config.n_io / 2; + } + + /* record the fact that the queues are functional */ + hw->state = EFCT_HW_STATE_ACTIVE; + /* + * Allocate a HW IOs for send frame. + */ + hw->hw_wq[0]->send_frame_io = efct_hw_io_alloc(hw); + if (!hw->hw_wq[0]->send_frame_io) + efc_log_err(hw->os, "alloc for send_frame_io failed\n"); + + /* Initialize send frame sequence id */ + atomic_set(&hw->send_frame_seq_id, 0); + + return EFCT_HW_RTN_SUCCESS; +} + +enum efct_hw_rtn +efct_hw_parse_filter(struct efct_hw *hw, void *value) +{ + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + char *p = NULL; + char *token; + u32 idx = 0; + + for (idx = 0; idx < ARRAY_SIZE(hw->config.filter_def); idx++) + hw->config.filter_def[idx] = 0; + + p = kstrdup(value, GFP_KERNEL); + if (!p || !*p) { + efc_log_err(hw->os, "p is NULL\n"); + return EFCT_HW_RTN_NO_MEMORY; + } + + idx = 0; + while ((token = strsep(&p, ",")) && *token) { + if (kstrtou32(token, 0, &hw->config.filter_def[idx++])) + efc_log_err(hw->os, "kstrtoint failed\n"); + + if (!p || !*p) + break; + + if (idx == ARRAY_SIZE(hw->config.filter_def)) + break; + } + kfree(p); + + return rc; +} + +u64 +efct_get_wwnn(struct efct_hw *hw) +{ + struct sli4 *sli = &hw->sli; + u8 p[8]; + + memcpy(p, sli->wwnn, sizeof(p)); + return get_unaligned_be64(p); +} + +u64 +efct_get_wwpn(struct efct_hw *hw) +{ + struct sli4 *sli = &hw->sli; + u8 p[8]; + + memcpy(p, sli->wwpn, sizeof(p)); + return get_unaligned_be64(p); +} diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index 59557834a2bc..513be4525867 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -600,4 +600,19 @@ struct efct_hw_grp_hdr { u8 revision[32]; }; +static inline int +efct_hw_get_link_speed(struct efct_hw *hw) { + return hw->link.speed; +} + +enum efct_hw_rtn +efct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev); +enum efct_hw_rtn efct_hw_init(struct efct_hw *hw); +enum efct_hw_rtn +efct_hw_parse_filter(struct efct_hw *hw, void *value); +uint64_t +efct_get_wwnn(struct efct_hw *hw); +uint64_t +efct_get_wwpn(struct efct_hw *hw); + #endif /* __EFCT_H__ */ diff --git a/drivers/scsi/elx/efct/efct_xport.c b/drivers/scsi/elx/efct/efct_xport.c new file mode 100644 index 000000000000..b5066c0f340d --- /dev/null +++ b/drivers/scsi/elx/efct/efct_xport.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efct_driver.h" +#include "efct_unsol.h" + +struct efct_xport_post_node_event { + struct completion done; + atomic_t refcnt; + struct efc_node *node; + u32 evt; + void *context; +}; + +static struct dentry *efct_debugfs_root; +static atomic_t efct_debugfs_count; + +static struct scsi_host_template efct_template = { + .module = THIS_MODULE, + .name = EFCT_DRIVER_NAME, + .supported_mode = MODE_TARGET, +}; + +/* globals */ +static struct fc_function_template efct_xport_functions; +static struct fc_function_template efct_vport_functions; + +static struct scsi_transport_template *efct_xport_fc_tt; +static struct scsi_transport_template *efct_vport_fc_tt; + +struct efct_xport * +efct_xport_alloc(struct efct *efct) +{ + struct efct_xport *xport; + + xport = kzalloc(sizeof(*xport), GFP_KERNEL); + if (!xport) + return xport; + + xport->efct = efct; + return xport; +} + +static int +efct_xport_init_debugfs(struct efct *efct) +{ + /* Setup efct debugfs root directory */ + if (!efct_debugfs_root) { + efct_debugfs_root = debugfs_create_dir("efct", NULL); + atomic_set(&efct_debugfs_count, 0); + if (!efct_debugfs_root) { + efc_log_err(efct, "failed to create debugfs entry\n"); + goto debugfs_fail; + } + } + + /* Create a directory for sessions in root */ + if (!efct->sess_debugfs_dir) { + efct->sess_debugfs_dir = debugfs_create_dir("sessions", NULL); + if (!efct->sess_debugfs_dir) { + efc_log_err(efct, + "failed to create debugfs entry for sessions\n"); + goto debugfs_fail; + } + atomic_inc(&efct_debugfs_count); + } + + return EFC_SUCCESS; + +debugfs_fail: + return EFC_FAIL; +} + +static void efct_xport_delete_debugfs(struct efct *efct) +{ + /* Remove session debugfs directory */ + debugfs_remove(efct->sess_debugfs_dir); + efct->sess_debugfs_dir = NULL; + atomic_dec(&efct_debugfs_count); + + if (atomic_read(&efct_debugfs_count) == 0) { + /* remove root debugfs directory */ + debugfs_remove(efct_debugfs_root); + efct_debugfs_root = NULL; + } +} + +int +efct_xport_attach(struct efct_xport *xport) +{ + struct efct *efct = xport->efct; + int rc; + + rc = efct_hw_setup(&efct->hw, efct, efct->pci); + if (rc) { + efc_log_err(efct, "%s: Can't setup hardware\n", efct->desc); + return rc; + } + + efct_hw_parse_filter(&efct->hw, (void *)efct->filter_def); + + xport->io_pool = efct_io_pool_create(efct, efct->hw.config.n_sgl); + if (!xport->io_pool) { + efc_log_err(efct, "Can't allocate IO pool\n"); + return EFC_FAIL; + } + + return EFC_SUCCESS; +} + +static void +efct_xport_link_stats_cb(int status, u32 num_counters, + struct efct_hw_link_stat_counts *counters, void *arg) +{ + union efct_xport_stats_u *result = arg; + + result->stats.link_stats.link_failure_error_count = + counters[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter; + result->stats.link_stats.loss_of_sync_error_count = + counters[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter; + result->stats.link_stats.primitive_sequence_error_count = + counters[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter; + result->stats.link_stats.invalid_transmission_word_error_count = + counters[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter; + result->stats.link_stats.crc_error_count = + counters[EFCT_HW_LINK_STAT_CRC_COUNT].counter; + + complete(&result->stats.done); +} + +static void +efct_xport_host_stats_cb(int status, u32 num_counters, + struct efct_hw_host_stat_counts *counters, void *arg) +{ + union efct_xport_stats_u *result = arg; + + result->stats.host_stats.transmit_kbyte_count = + counters[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter; + result->stats.host_stats.receive_kbyte_count = + counters[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter; + result->stats.host_stats.transmit_frame_count = + counters[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter; + result->stats.host_stats.receive_frame_count = + counters[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter; + + complete(&result->stats.done); +} + +static void +efct_xport_async_link_stats_cb(int status, u32 num_counters, + struct efct_hw_link_stat_counts *counters, + void *arg) +{ + union efct_xport_stats_u *result = arg; + + result->stats.link_stats.link_failure_error_count = + counters[EFCT_HW_LINK_STAT_LINK_FAILURE_COUNT].counter; + result->stats.link_stats.loss_of_sync_error_count = + counters[EFCT_HW_LINK_STAT_LOSS_OF_SYNC_COUNT].counter; + result->stats.link_stats.primitive_sequence_error_count = + counters[EFCT_HW_LINK_STAT_PRIMITIVE_SEQ_COUNT].counter; + result->stats.link_stats.invalid_transmission_word_error_count = + counters[EFCT_HW_LINK_STAT_INVALID_XMIT_WORD_COUNT].counter; + result->stats.link_stats.crc_error_count = + counters[EFCT_HW_LINK_STAT_CRC_COUNT].counter; +} + +static void +efct_xport_async_host_stats_cb(int status, u32 num_counters, + struct efct_hw_host_stat_counts *counters, + void *arg) +{ + union efct_xport_stats_u *result = arg; + + result->stats.host_stats.transmit_kbyte_count = + counters[EFCT_HW_HOST_STAT_TX_KBYTE_COUNT].counter; + result->stats.host_stats.receive_kbyte_count = + counters[EFCT_HW_HOST_STAT_RX_KBYTE_COUNT].counter; + result->stats.host_stats.transmit_frame_count = + counters[EFCT_HW_HOST_STAT_TX_FRAME_COUNT].counter; + result->stats.host_stats.receive_frame_count = + counters[EFCT_HW_HOST_STAT_RX_FRAME_COUNT].counter; +} + +static void +efct_xport_config_stats_timer(struct efct *efct); + +static void +efct_xport_stats_timer_cb(struct timer_list *t) +{ + struct efct_xport *xport = from_timer(xport, t, stats_timer); + struct efct *efct = xport->efct; + + efct_xport_config_stats_timer(efct); +} + +static void +efct_xport_config_stats_timer(struct efct *efct) +{ + u32 timeout = 3 * 1000; + struct efct_xport *xport = NULL; + + if (!efct) { + pr_err("%s: failed to locate EFCT device\n", __func__); + return; + } + + xport = efct->xport; + efct_hw_get_link_stats(&efct->hw, 0, 0, 0, + efct_xport_async_link_stats_cb, + &xport->fc_xport_stats); + efct_hw_get_host_stats(&efct->hw, 0, efct_xport_async_host_stats_cb, + &xport->fc_xport_stats); + + timer_setup(&xport->stats_timer, + &efct_xport_stats_timer_cb, 0); + mod_timer(&xport->stats_timer, + jiffies + msecs_to_jiffies(timeout)); +} + +int +efct_xport_initialize(struct efct_xport *xport) +{ + struct efct *efct = xport->efct; + int rc = 0; + + /* Initialize io lists */ + spin_lock_init(&xport->io_pending_lock); + INIT_LIST_HEAD(&xport->io_pending_list); + atomic_set(&xport->io_active_count, 0); + atomic_set(&xport->io_pending_count, 0); + atomic_set(&xport->io_total_free, 0); + atomic_set(&xport->io_total_pending, 0); + atomic_set(&xport->io_alloc_failed_count, 0); + atomic_set(&xport->io_pending_recursing, 0); + + rc = efct_hw_init(&efct->hw); + if (rc) { + efc_log_err(efct, "efct_hw_init failure\n"); + goto out; + } + + rc = efct_scsi_tgt_new_device(efct); + if (rc) { + efc_log_err(efct, "failed to initialize target\n"); + goto hw_init_out; + } + + rc = efct_scsi_new_device(efct); + if (rc) { + efc_log_err(efct, "failed to initialize initiator\n"); + goto tgt_dev_out; + } + + /* Get FC link and host statistics perodically*/ + efct_xport_config_stats_timer(efct); + + efct_xport_init_debugfs(efct); + + return rc; + +tgt_dev_out: + efct_scsi_tgt_del_device(efct); + +hw_init_out: + efct_hw_teardown(&efct->hw); +out: + return rc; +} + +int +efct_xport_status(struct efct_xport *xport, enum efct_xport_status cmd, + union efct_xport_stats_u *result) +{ + u32 rc = 0; + struct efct *efct = NULL; + union efct_xport_stats_u value; + + efct = xport->efct; + + switch (cmd) { + case EFCT_XPORT_CONFIG_PORT_STATUS: + if (xport->configured_link_state == 0) { + /* + * Initial state is offline. configured_link_state is + * set to online explicitly when port is brought online + */ + xport->configured_link_state = EFCT_XPORT_PORT_OFFLINE; + } + result->value = xport->configured_link_state; + break; + + case EFCT_XPORT_PORT_STATUS: + /* Determine port status based on link speed. */ + value.value = efct_hw_get_link_speed(&efct->hw); + if (value.value == 0) + result->value = 0; + else + result->value = 1; + rc = 0; + break; + + case EFCT_XPORT_LINK_SPEED: { + result->value = efct_hw_get_link_speed(&efct->hw); + + break; + } + case EFCT_XPORT_LINK_STATISTICS: + memcpy((void *)result, &efct->xport->fc_xport_stats, + sizeof(union efct_xport_stats_u)); + break; + case EFCT_XPORT_LINK_STAT_RESET: { + /* Create a completion to synchronize the stat reset process */ + init_completion(&result->stats.done); + + /* First reset the link stats */ + rc = efct_hw_get_link_stats(&efct->hw, 0, 1, 1, + efct_xport_link_stats_cb, result); + if (rc) + break; + + /* Wait for completion to be signaled when the cmd completes */ + if (wait_for_completion_interruptible(&result->stats.done)) { + /* Undefined failure */ + efc_log_test(efct, "sem wait failed\n"); + rc = EFC_FAIL; + break; + } + + /* Next reset the host stats */ + rc = efct_hw_get_host_stats(&efct->hw, 1, + efct_xport_host_stats_cb, result); + + if (rc) + break; + + /* Wait for completion to be signaled when the cmd completes */ + if (wait_for_completion_interruptible(&result->stats.done)) { + /* Undefined failure */ + efc_log_test(efct, "sem wait failed\n"); + rc = EFC_FAIL; + break; + } + break; + } + default: + rc = EFC_FAIL; + break; + } + + return rc; +} + +int +efct_scsi_new_device(struct efct *efct) +{ + struct Scsi_Host *shost = NULL; + int error = 0; + struct efct_vport *vport = NULL; + union efct_xport_stats_u speed; + u32 supported_speeds = 0; + + shost = scsi_host_alloc(&efct_template, sizeof(*vport)); + if (!shost) { + efc_log_err(efct, "failed to allocate Scsi_Host struct\n"); + return EFC_FAIL; + } + + /* save shost to initiator-client context */ + efct->shost = shost; + + /* save efct information to shost LLD-specific space */ + vport = (struct efct_vport *)shost->hostdata; + vport->efct = efct; + + /* + * Set initial can_queue value to the max SCSI IOs. This is the maximum + * global queue depth (as opposed to the per-LUN queue depth -- + * .cmd_per_lun This may need to be adjusted for I+T mode. + */ + shost->can_queue = efct->hw.config.n_io; + shost->max_cmd_len = 16; /* 16-byte CDBs */ + shost->max_id = 0xffff; + shost->max_lun = 0xffffffff; + + /* + * can only accept (from mid-layer) as many SGEs as we've + * pre-registered + */ + shost->sg_tablesize = sli_get_max_sgl(&efct->hw.sli); + + /* attach FC Transport template to shost */ + shost->transportt = efct_xport_fc_tt; + efc_log_debug(efct, "transport template=%p\n", efct_xport_fc_tt); + + /* get pci_dev structure and add host to SCSI ML */ + error = scsi_add_host_with_dma(shost, &efct->pci->dev, + &efct->pci->dev); + if (error) { + efc_log_test(efct, "failed scsi_add_host_with_dma\n"); + return EFC_FAIL; + } + + /* Set symbolic name for host port */ + snprintf(fc_host_symbolic_name(shost), + sizeof(fc_host_symbolic_name(shost)), + "Emulex %s FV%s DV%s", efct->model, + efct->hw.sli.fw_name[0], EFCT_DRIVER_VERSION); + + /* Set host port supported classes */ + fc_host_supported_classes(shost) = FC_COS_CLASS3; + + speed.value = 1000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_1GBIT; + } + speed.value = 2000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_2GBIT; + } + speed.value = 4000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_4GBIT; + } + speed.value = 8000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_8GBIT; + } + speed.value = 10000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_10GBIT; + } + speed.value = 16000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_16GBIT; + } + speed.value = 32000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_32GBIT; + } + + fc_host_supported_speeds(shost) = supported_speeds; + + fc_host_node_name(shost) = efct_get_wwnn(&efct->hw); + fc_host_port_name(shost) = efct_get_wwpn(&efct->hw); + fc_host_max_npiv_vports(shost) = 128; + + return EFC_SUCCESS; +} + +struct scsi_transport_template * +efct_attach_fc_transport(void) +{ + struct scsi_transport_template *efct_fc_template = NULL; + + efct_fc_template = fc_attach_transport(&efct_xport_functions); + + if (!efct_fc_template) + pr_err("failed to attach EFCT with fc transport\n"); + + return efct_fc_template; +} + +struct scsi_transport_template * +efct_attach_vport_fc_transport(void) +{ + struct scsi_transport_template *efct_fc_template = NULL; + + efct_fc_template = fc_attach_transport(&efct_vport_functions); + + if (!efct_fc_template) + pr_err("failed to attach EFCT with fc transport\n"); + + return efct_fc_template; +} + +int +efct_scsi_reg_fc_transport(void) +{ + /* attach to appropriate scsi_tranport_* module */ + efct_xport_fc_tt = efct_attach_fc_transport(); + if (!efct_xport_fc_tt) { + pr_err("%s: failed to attach to scsi_transport_*", __func__); + return EFC_FAIL; + } + + efct_vport_fc_tt = efct_attach_vport_fc_transport(); + if (!efct_vport_fc_tt) { + pr_err("%s: failed to attach to scsi_transport_*", __func__); + efct_release_fc_transport(efct_xport_fc_tt); + efct_xport_fc_tt = NULL; + return EFC_FAIL; + } + + return EFC_SUCCESS; +} + +int +efct_scsi_release_fc_transport(void) +{ + /* detach from scsi_transport_* */ + efct_release_fc_transport(efct_xport_fc_tt); + efct_xport_fc_tt = NULL; + if (efct_vport_fc_tt) + efct_release_fc_transport(efct_vport_fc_tt); + efct_vport_fc_tt = NULL; + + return EFC_SUCCESS; +} diff --git a/drivers/scsi/elx/efct/efct_xport.h b/drivers/scsi/elx/efct/efct_xport.h new file mode 100644 index 000000000000..64b87ca83e44 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_xport.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#if !defined(__EFCT_XPORT_H__) +#define __EFCT_XPORT_H__ + +enum efct_xport_ctrl { + EFCT_XPORT_PORT_ONLINE = 1, + EFCT_XPORT_PORT_OFFLINE, + EFCT_XPORT_SHUTDOWN, + EFCT_XPORT_POST_NODE_EVENT, + EFCT_XPORT_WWNN_SET, + EFCT_XPORT_WWPN_SET, +}; + +enum efct_xport_status { + EFCT_XPORT_PORT_STATUS, + EFCT_XPORT_CONFIG_PORT_STATUS, + EFCT_XPORT_LINK_SPEED, + EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + EFCT_XPORT_LINK_STATISTICS, + EFCT_XPORT_LINK_STAT_RESET, + EFCT_XPORT_IS_QUIESCED +}; + +struct efct_xport_link_stats { + bool rec; + bool gec; + bool w02of; + bool w03of; + bool w04of; + bool w05of; + bool w06of; + bool w07of; + bool w08of; + bool w09of; + bool w10of; + bool w11of; + bool w12of; + bool w13of; + bool w14of; + bool w15of; + bool w16of; + bool w17of; + bool w18of; + bool w19of; + bool w20of; + bool w21of; + bool clrc; + bool clof1; + u32 link_failure_error_count; + u32 loss_of_sync_error_count; + u32 loss_of_signal_error_count; + u32 primitive_sequence_error_count; + u32 invalid_transmission_word_error_count; + u32 crc_error_count; + u32 primitive_sequence_event_timeout_count; + u32 elastic_buffer_overrun_error_count; + u32 arbitration_fc_al_timeout_count; + u32 advertised_receive_bufftor_to_buffer_credit; + u32 current_receive_buffer_to_buffer_credit; + u32 advertised_transmit_buffer_to_buffer_credit; + u32 current_transmit_buffer_to_buffer_credit; + u32 received_eofa_count; + u32 received_eofdti_count; + u32 received_eofni_count; + u32 received_soff_count; + u32 received_dropped_no_aer_count; + u32 received_dropped_no_available_rpi_resources_count; + u32 received_dropped_no_available_xri_resources_count; +}; + +struct efct_xport_host_stats { + bool cc; + u32 transmit_kbyte_count; + u32 receive_kbyte_count; + u32 transmit_frame_count; + u32 receive_frame_count; + u32 transmit_sequence_count; + u32 receive_sequence_count; + u32 total_exchanges_originator; + u32 total_exchanges_responder; + u32 receive_p_bsy_count; + u32 receive_f_bsy_count; + u32 dropped_frames_due_to_no_rq_buffer_count; + u32 empty_rq_timeout_count; + u32 dropped_frames_due_to_no_xri_count; + u32 empty_xri_pool_count; +}; + +struct efct_xport_host_statistics { + struct completion done; + struct efct_xport_link_stats link_stats; + struct efct_xport_host_stats host_stats; +}; + +union efct_xport_stats_u { + u32 value; + struct efct_xport_host_statistics stats; +}; + +struct efct_xport_fcp_stats { + u64 input_bytes; + u64 output_bytes; + u64 input_requests; + u64 output_requests; + u64 control_requests; +}; + +struct efct_xport { + struct efct *efct; + /* wwpn requested by user for primary nport */ + u64 req_wwpn; + /* wwnn requested by user for primary nport */ + u64 req_wwnn; + + /* Nodes */ + /* number of allocated nodes */ + u32 nodes_count; + /* used to track how often IO pool is empty */ + atomic_t io_alloc_failed_count; + /* array of pointers to nodes */ + struct efc_node **nodes; + + /* Io pool and counts */ + /* pointer to IO pool */ + struct efct_io_pool *io_pool; + /* lock for io_pending_list */ + spinlock_t io_pending_lock; + /* list of IOs waiting for HW resources + * lock: xport->io_pending_lock + * link: efct_io_s->io_pending_link + */ + struct list_head io_pending_list; + /* count of totals IOS allocated */ + atomic_t io_total_alloc; + /* count of totals IOS free'd */ + atomic_t io_total_free; + /* count of totals IOS that were pended */ + atomic_t io_total_pending; + /* count of active IOS */ + atomic_t io_active_count; + /* count of pending IOS */ + atomic_t io_pending_count; + /* non-zero if efct_scsi_check_pending is executing */ + atomic_t io_pending_recursing; + + /* Port */ + /* requested link state */ + u32 configured_link_state; + + /* Timer for Statistics */ + struct timer_list stats_timer; + union efct_xport_stats_u fc_xport_stats; + struct efct_xport_fcp_stats fcp_stats; +}; + +struct efct_rport_data { + struct efc_node *node; +}; + +struct efct_xport * +efct_xport_alloc(struct efct *efct); +int +efct_xport_attach(struct efct_xport *xport); +int +efct_xport_initialize(struct efct_xport *xport); +int +efct_xport_detach(struct efct_xport *xport); +int +efct_xport_control(struct efct_xport *xport, enum efct_xport_ctrl cmd, ...); +int +efct_xport_status(struct efct_xport *xport, enum efct_xport_status cmd, + union efct_xport_stats_u *result); +void +efct_xport_free(struct efct_xport *xport); + +struct scsi_transport_template *efct_attach_fc_transport(void); +struct scsi_transport_template *efct_attach_vport_fc_transport(void); +void +efct_release_fc_transport(struct scsi_transport_template *transport_template); + +#endif /* __EFCT_XPORT_H__ */ From patchwork Mon Oct 12 22:51:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269820 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9BC02C4363D for ; Mon, 12 Oct 2020 22:52:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4F5BE20709 for ; Mon, 12 Oct 2020 22:52:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="OB0HGzh4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390831AbgJLWwe (ORCPT ); Mon, 12 Oct 2020 18:52:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60612 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390685AbgJLWwY (ORCPT ); Mon, 12 Oct 2020 18:52:24 -0400 Received: from mail-pg1-x542.google.com (mail-pg1-x542.google.com [IPv6:2607:f8b0:4864:20::542]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 701F7C0613D2 for ; Mon, 12 Oct 2020 15:52:24 -0700 (PDT) Received: by mail-pg1-x542.google.com with SMTP id x16so15906591pgj.3 for ; Mon, 12 Oct 2020 15:52:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=tbfEcG4QyWtBF7ZADYqki9N7NJ5eAjU1CsIt+FQKZnk=; b=OB0HGzh4paJMhNGxn1Q7ZG2K/31O+SfHU8lZc1tolwtYpuq9NBRrHZr+BSlClxrUH6 IZ9qJwRw5K9PQTKcR1EcyU5sbS2y5J1aBvtZWdbvqngLGJF6815soqD4eTZLgRiiesIh oGgsFEklN//mFJhf4osFweE7tRKP/p9ar1yoc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=tbfEcG4QyWtBF7ZADYqki9N7NJ5eAjU1CsIt+FQKZnk=; b=JBRutzq/KOdEk2SlmC6OsNfeufE4sl5tDkWVIWpTvFHyuqg8JjKSUZaebSnvJqgUg/ 30feLFvxOrQg3najhXJk8f7CAxruExZa8IKM2g/UyMMNL/s7mZsxWhRFFXLF4XotWnZ7 3TB8Hjm19zaekF8SKTVafNcN6RyVQV/5sQEK8536pj+zj9GC2ZE60mrCnGb0EkLWUKj2 SKo0r0l3F3aYdtDSI5I/epWEGaRyDduWyT5U/l+oEVqnFWwj3Oy7EdKZX3dElKjEvm/O YmNIEMCBJDCf07arv9mnbDwEOBb8MPT1DC5W/HqXnv+G3tDRDR0SZ/JFBaeKlsyJVm/9 Dn5A== X-Gm-Message-State: AOAM532QAd6iXL5vMC8RJMlKqRI97h/rriZihOm8wxVugH6o2ice6AtE f8KGzdaR8KIRCAMpqERmSre4KZ+/ISnFb8Pq2YFGRPrlCJXzDUbec/3n2IO8I3DdTb7XsnhLNCQ Dv668lBXFADxxg+W+toj9N9gUEnOUZ8O2wfsRnEnRBU0jKFNHTlvAk01BF6D+jnjGzPJHygoN8c mcdzQ= X-Google-Smtp-Source: ABdhPJyaqKzK4dUyiMDGH0mxdBA0i9b3EVgzkT7f5uoDIaQRcJzsPZWd/Xsklo1BwYVqfVW0bDjUww== X-Received: by 2002:a17:90a:1bc3:: with SMTP id r3mr22896928pjr.196.1602543142936; Mon, 12 Oct 2020 15:52:22 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:22 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 19/31] elx: efct: Hardware queues creation and deletion Date: Mon, 12 Oct 2020 15:51:35 -0700 Message-Id: <20201012225147.54404-20-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: Routines for queue creation, deletion, and configuration. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke --- v4: Changes for MRQ support. Mbox request API. --- drivers/scsi/elx/efct/efct_hw.h | 4 + drivers/scsi/elx/efct/efct_hw_queues.c | 677 +++++++++++++++++++++++++ 2 files changed, 681 insertions(+) create mode 100644 drivers/scsi/elx/efct/efct_hw_queues.c diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index 513be4525867..44649d649229 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -610,6 +610,10 @@ efct_hw_setup(struct efct_hw *hw, void *os, struct pci_dev *pdev); enum efct_hw_rtn efct_hw_init(struct efct_hw *hw); enum efct_hw_rtn efct_hw_parse_filter(struct efct_hw *hw, void *value); +enum efct_hw_rtn +efct_hw_init_queues(struct efct_hw *hw); +void +efct_hw_map_wq_cpu(struct efct_hw *hw); uint64_t efct_get_wwnn(struct efct_hw *hw); uint64_t diff --git a/drivers/scsi/elx/efct/efct_hw_queues.c b/drivers/scsi/elx/efct/efct_hw_queues.c new file mode 100644 index 000000000000..7586064e2da5 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_hw_queues.c @@ -0,0 +1,677 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efct_driver.h" +#include "efct_hw.h" +#include "efct_unsol.h" + +enum efct_hw_rtn +efct_hw_init_queues(struct efct_hw *hw) +{ + struct hw_eq *eq = NULL; + struct hw_cq *cq = NULL; + struct hw_wq *wq = NULL; + struct hw_mq *mq = NULL; + + struct hw_eq *eqs[EFCT_HW_MAX_NUM_EQ]; + struct hw_cq *cqs[EFCT_HW_MAX_NUM_EQ]; + struct hw_rq *rqs[EFCT_HW_MAX_NUM_EQ]; + u32 i = 0, j; + + hw->eq_count = 0; + hw->cq_count = 0; + hw->mq_count = 0; + hw->wq_count = 0; + hw->rq_count = 0; + hw->hw_rq_count = 0; + INIT_LIST_HEAD(&hw->eq_list); + + for (i = 0; i < hw->config.n_eq; i++) { + /* Create EQ */ + eq = efct_hw_new_eq(hw, EFCT_HW_EQ_DEPTH); + if (!eq) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_NO_MEMORY; + } + + eqs[i] = eq; + + /* Create one MQ */ + if (!i) { + cq = efct_hw_new_cq(eq, + hw->num_qentries[SLI4_QTYPE_CQ]); + if (!cq) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_NO_MEMORY; + } + + mq = efct_hw_new_mq(cq, EFCT_HW_MQ_DEPTH); + if (!mq) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_NO_MEMORY; + } + } + + /* Create WQ */ + cq = efct_hw_new_cq(eq, hw->num_qentries[SLI4_QTYPE_CQ]); + if (!cq) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_NO_MEMORY; + } + + wq = efct_hw_new_wq(cq, hw->num_qentries[SLI4_QTYPE_WQ]); + if (!wq) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_NO_MEMORY; + } + } + + /* Create CQ set */ + if (efct_hw_new_cq_set(eqs, cqs, i, hw->num_qentries[SLI4_QTYPE_CQ])) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_ERROR; + } + + /* Create RQ set */ + if (efct_hw_new_rq_set(cqs, rqs, i, EFCT_HW_RQ_ENTRIES_DEF)) { + efct_hw_queue_teardown(hw); + return EFCT_HW_RTN_ERROR; + } + + for (j = 0; j < i ; j++) { + rqs[j]->filter_mask = 0; + rqs[j]->is_mrq = true; + rqs[j]->base_mrq_id = rqs[0]->hdr->id; + } + + hw->hw_mrq_count = i; + + return EFCT_HW_RTN_SUCCESS; +} + +void +efct_hw_map_wq_cpu(struct efct_hw *hw) +{ + struct efct *efct = hw->os; + u32 cpu = 0, i; + + /* Init cpu_map array */ + for_each_possible_cpu(cpu) { + hw->wq_cpu_array[cpu] = NULL; + } + + for (i = 0; i < hw->config.n_eq; i++) { + const struct cpumask *maskp; + + /* Get a CPU mask for all CPUs affinitized to this vector */ + maskp = pci_irq_get_affinity(efct->pci, i); + if (!maskp) { + efc_log_debug(efct, "maskp null for vector:%d\n", i); + continue; + } + + /* Loop through all CPUs associated with vector idx */ + for_each_cpu_and(cpu, maskp, cpu_present_mask) { + efc_log_debug(efct, "CPU:%d irq vector:%d\n", cpu, i); + hw->wq_cpu_array[cpu] = hw->hw_wq[i]; + } + } + +} + +struct hw_eq * +efct_hw_new_eq(struct efct_hw *hw, u32 entry_count) +{ + struct hw_eq *eq = kzalloc(sizeof(*eq), GFP_KERNEL); + + if (!eq) + return NULL; + + eq->type = SLI4_QTYPE_EQ; + eq->hw = hw; + eq->entry_count = entry_count; + eq->instance = hw->eq_count++; + eq->queue = &hw->eq[eq->instance]; + INIT_LIST_HEAD(&eq->cq_list); + + if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_EQ, eq->queue, entry_count, + NULL)) { + efc_log_err(hw->os, "EQ[%d] alloc failure\n", eq->instance); + kfree(eq); + return NULL; + } + + sli_eq_modify_delay(&hw->sli, eq->queue, 1, 0, 8); + hw->hw_eq[eq->instance] = eq; + INIT_LIST_HEAD(&eq->list_entry); + list_add_tail(&eq->list_entry, &hw->eq_list); + efc_log_debug(hw->os, "create eq[%2d] id %3d len %4d\n", eq->instance, + eq->queue->id, eq->entry_count); + return eq; +} + +struct hw_cq * +efct_hw_new_cq(struct hw_eq *eq, u32 entry_count) +{ + struct efct_hw *hw = eq->hw; + struct hw_cq *cq = kzalloc(sizeof(*cq), GFP_KERNEL); + + if (!cq) + return NULL; + + cq->eq = eq; + cq->type = SLI4_QTYPE_CQ; + cq->instance = eq->hw->cq_count++; + cq->entry_count = entry_count; + cq->queue = &hw->cq[cq->instance]; + + INIT_LIST_HEAD(&cq->q_list); + + if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_CQ, cq->queue, + cq->entry_count, eq->queue)) { + efc_log_err(hw->os, "CQ[%d] allocation failure len=%d\n", + eq->instance, eq->entry_count); + kfree(cq); + return NULL; + } + + hw->hw_cq[cq->instance] = cq; + INIT_LIST_HEAD(&cq->list_entry); + list_add_tail(&cq->list_entry, &eq->cq_list); + efc_log_debug(hw->os, "create cq[%2d] id %3d len %4d\n", cq->instance, + cq->queue->id, cq->entry_count); + return cq; +} + +u32 +efct_hw_new_cq_set(struct hw_eq *eqs[], struct hw_cq *cqs[], + u32 num_cqs, u32 entry_count) +{ + u32 i; + struct efct_hw *hw = eqs[0]->hw; + struct sli4 *sli4 = &hw->sli; + struct hw_cq *cq = NULL; + struct sli4_queue *qs[SLI4_MAX_CQ_SET_COUNT]; + struct sli4_queue *assefct[SLI4_MAX_CQ_SET_COUNT]; + + /* Initialise CQS pointers to NULL */ + for (i = 0; i < num_cqs; i++) + cqs[i] = NULL; + + for (i = 0; i < num_cqs; i++) { + cq = kzalloc(sizeof(*cq), GFP_KERNEL); + if (!cq) + goto error; + + cqs[i] = cq; + cq->eq = eqs[i]; + cq->type = SLI4_QTYPE_CQ; + cq->instance = hw->cq_count++; + cq->entry_count = entry_count; + cq->queue = &hw->cq[cq->instance]; + qs[i] = cq->queue; + assefct[i] = eqs[i]->queue; + INIT_LIST_HEAD(&cq->q_list); + } + + if (sli_cq_alloc_set(sli4, qs, num_cqs, entry_count, assefct)) { + efc_log_err(hw->os, "Failed to create CQ Set.\n"); + goto error; + } + + for (i = 0; i < num_cqs; i++) { + hw->hw_cq[cqs[i]->instance] = cqs[i]; + INIT_LIST_HEAD(&cqs[i]->list_entry); + list_add_tail(&cqs[i]->list_entry, &cqs[i]->eq->cq_list); + } + + return EFC_SUCCESS; + +error: + for (i = 0; i < num_cqs; i++) { + kfree(cqs[i]); + cqs[i] = NULL; + } + return EFC_FAIL; +} + +struct hw_mq * +efct_hw_new_mq(struct hw_cq *cq, u32 entry_count) +{ + struct efct_hw *hw = cq->eq->hw; + struct hw_mq *mq = kzalloc(sizeof(*mq), GFP_KERNEL); + + if (!mq) + return NULL; + + mq->cq = cq; + mq->type = SLI4_QTYPE_MQ; + mq->instance = cq->eq->hw->mq_count++; + mq->entry_count = entry_count; + mq->entry_size = EFCT_HW_MQ_DEPTH; + mq->queue = &hw->mq[mq->instance]; + + if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_MQ, mq->queue, mq->entry_size, + cq->queue)) { + efc_log_err(hw->os, "MQ allocation failure\n"); + kfree(mq); + return NULL; + } + + hw->hw_mq[mq->instance] = mq; + INIT_LIST_HEAD(&mq->list_entry); + list_add_tail(&mq->list_entry, &cq->q_list); + efc_log_debug(hw->os, "create mq[%2d] id %3d len %4d\n", mq->instance, + mq->queue->id, mq->entry_count); + return mq; +} + +struct hw_wq * +efct_hw_new_wq(struct hw_cq *cq, u32 entry_count) +{ + struct efct_hw *hw = cq->eq->hw; + struct hw_wq *wq = kzalloc(sizeof(*wq), GFP_KERNEL); + + if (!wq) + return NULL; + + wq->hw = cq->eq->hw; + wq->cq = cq; + wq->type = SLI4_QTYPE_WQ; + wq->instance = cq->eq->hw->wq_count++; + wq->entry_count = entry_count; + wq->queue = &hw->wq[wq->instance]; + wq->wqec_set_count = EFCT_HW_WQEC_SET_COUNT; + wq->wqec_count = wq->wqec_set_count; + wq->free_count = wq->entry_count - 1; + INIT_LIST_HEAD(&wq->pending_list); + + if (sli_queue_alloc(&hw->sli, SLI4_QTYPE_WQ, wq->queue, + wq->entry_count, cq->queue)) { + efc_log_err(hw->os, "WQ allocation failure\n"); + kfree(wq); + return NULL; + } + + hw->hw_wq[wq->instance] = wq; + INIT_LIST_HEAD(&wq->list_entry); + list_add_tail(&wq->list_entry, &cq->q_list); + efc_log_debug(hw->os, "create wq[%2d] id %3d len %4d cls %d\n", + wq->instance, wq->queue->id, wq->entry_count, wq->class); + return wq; +} + +u32 +efct_hw_new_rq_set(struct hw_cq *cqs[], struct hw_rq *rqs[], + u32 num_rq_pairs, u32 entry_count) +{ + struct efct_hw *hw = cqs[0]->eq->hw; + struct hw_rq *rq = NULL; + struct sli4_queue *qs[SLI4_MAX_RQ_SET_COUNT * 2] = { NULL }; + u32 i, q_count, size; + + /* Initialise RQS pointers */ + for (i = 0; i < num_rq_pairs; i++) + rqs[i] = NULL; + + /* + * Allocate an RQ object SET, where each element in set + * encapsulates 2 SLI queues (for rq pair) + */ + for (i = 0, q_count = 0; i < num_rq_pairs; i++, q_count += 2) { + rq = kzalloc(sizeof(*rq), GFP_KERNEL); + if (!rq) + goto error; + + rqs[i] = rq; + rq->instance = hw->hw_rq_count++; + rq->cq = cqs[i]; + rq->type = SLI4_QTYPE_RQ; + rq->entry_count = entry_count; + + /* Header RQ */ + rq->hdr = &hw->rq[hw->rq_count]; + rq->hdr_entry_size = EFCT_HW_RQ_HEADER_SIZE; + hw->hw_rq_lookup[hw->rq_count] = rq->instance; + hw->rq_count++; + qs[q_count] = rq->hdr; + + /* Data RQ */ + rq->data = &hw->rq[hw->rq_count]; + rq->data_entry_size = hw->config.rq_default_buffer_size; + hw->hw_rq_lookup[hw->rq_count] = rq->instance; + hw->rq_count++; + qs[q_count + 1] = rq->data; + + rq->rq_tracker = NULL; + } + + if (sli_fc_rq_set_alloc(&hw->sli, num_rq_pairs, qs, + cqs[0]->queue->id, + rqs[0]->entry_count, + rqs[0]->hdr_entry_size, + rqs[0]->data_entry_size)) { + efc_log_err(hw->os, "RQ Set alloc failure for base CQ=%d\n", + cqs[0]->queue->id); + goto error; + } + + for (i = 0; i < num_rq_pairs; i++) { + hw->hw_rq[rqs[i]->instance] = rqs[i]; + INIT_LIST_HEAD(&rqs[i]->list_entry); + list_add_tail(&rqs[i]->list_entry, &cqs[i]->q_list); + size = sizeof(struct efc_hw_sequence *) * rqs[i]->entry_count; + rqs[i]->rq_tracker = kzalloc(size, GFP_KERNEL); + if (!rqs[i]->rq_tracker) + goto error; + } + + return EFC_SUCCESS; + +error: + for (i = 0; i < num_rq_pairs; i++) { + if (rqs[i]) { + kfree(rqs[i]->rq_tracker); + kfree(rqs[i]); + } + } + + return EFC_FAIL; +} + +void +efct_hw_del_eq(struct hw_eq *eq) +{ + struct hw_cq *cq; + struct hw_cq *cq_next; + + if (!eq) + return; + + list_for_each_entry_safe(cq, cq_next, &eq->cq_list, list_entry) + efct_hw_del_cq(cq); + list_del(&eq->list_entry); + eq->hw->hw_eq[eq->instance] = NULL; + kfree(eq); +} + +void +efct_hw_del_cq(struct hw_cq *cq) +{ + struct hw_q *q; + struct hw_q *q_next; + + if (!cq) + return; + + list_for_each_entry_safe(q, q_next, &cq->q_list, list_entry) { + switch (q->type) { + case SLI4_QTYPE_MQ: + efct_hw_del_mq((struct hw_mq *)q); + break; + case SLI4_QTYPE_WQ: + efct_hw_del_wq((struct hw_wq *)q); + break; + case SLI4_QTYPE_RQ: + efct_hw_del_rq((struct hw_rq *)q); + break; + default: + break; + } + } + list_del(&cq->list_entry); + cq->eq->hw->hw_cq[cq->instance] = NULL; + kfree(cq); +} + +void +efct_hw_del_mq(struct hw_mq *mq) +{ + if (!mq) + return; + + list_del(&mq->list_entry); + mq->cq->eq->hw->hw_mq[mq->instance] = NULL; + kfree(mq); +} + +void +efct_hw_del_wq(struct hw_wq *wq) +{ + if (!wq) + return; + + list_del(&wq->list_entry); + wq->cq->eq->hw->hw_wq[wq->instance] = NULL; + kfree(wq); +} + +void +efct_hw_del_rq(struct hw_rq *rq) +{ + struct efct_hw *hw = NULL; + + if (!rq) + return; + /* Free RQ tracker */ + kfree(rq->rq_tracker); + rq->rq_tracker = NULL; + list_del(&rq->list_entry); + hw = rq->cq->eq->hw; + hw->hw_rq[rq->instance] = NULL; + kfree(rq); +} + +void +efct_hw_queue_teardown(struct efct_hw *hw) +{ + struct hw_eq *eq; + struct hw_eq *eq_next; + + if (!hw->eq_list.next) + return; + + list_for_each_entry_safe(eq, eq_next, &hw->eq_list, list_entry) + efct_hw_del_eq(eq); +} + +static inline int +efct_hw_rqpair_find(struct efct_hw *hw, u16 rq_id) +{ + return efct_hw_queue_hash_find(hw->rq_hash, rq_id); +} + +static struct efc_hw_sequence * +efct_hw_rqpair_get(struct efct_hw *hw, u16 rqindex, u16 bufindex) +{ + struct sli4_queue *rq_hdr = &hw->rq[rqindex]; + struct efc_hw_sequence *seq = NULL; + struct hw_rq *rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]]; + unsigned long flags = 0; + + if (bufindex >= rq_hdr->length) { + efc_log_err(hw->os, + "RQidx %d bufidx %d exceed ring len %d for id %d\n", + rqindex, bufindex, rq_hdr->length, rq_hdr->id); + return NULL; + } + + /* rq_hdr lock also covers rqindex+1 queue */ + spin_lock_irqsave(&rq_hdr->lock, flags); + + seq = rq->rq_tracker[bufindex]; + rq->rq_tracker[bufindex] = NULL; + + if (!seq) { + efc_log_err(hw->os, + "RQbuf NULL, rqidx %d, bufidx %d, cur q idx = %d\n", + rqindex, bufindex, rq_hdr->index); + } + + spin_unlock_irqrestore(&rq_hdr->lock, flags); + return seq; +} + +int +efct_hw_rqpair_process_rq(struct efct_hw *hw, struct hw_cq *cq, + u8 *cqe) +{ + u16 rq_id; + u32 index; + int rqindex; + int rq_status; + u32 h_len; + u32 p_len; + struct efc_hw_sequence *seq; + struct hw_rq *rq; + + rq_status = sli_fc_rqe_rqid_and_index(&hw->sli, cqe, + &rq_id, &index); + if (rq_status != 0) { + switch (rq_status) { + case SLI4_FC_ASYNC_RQ_BUF_LEN_EXCEEDED: + case SLI4_FC_ASYNC_RQ_DMA_FAILURE: + /* just get RQ buffer then return to chip */ + rqindex = efct_hw_rqpair_find(hw, rq_id); + if (rqindex < 0) { + efc_log_test(hw->os, + "status=%#x: lookup fail id=%#x\n", + rq_status, rq_id); + break; + } + + /* get RQ buffer */ + seq = efct_hw_rqpair_get(hw, rqindex, index); + + /* return to chip */ + if (efct_hw_rqpair_sequence_free(hw, seq)) { + efc_log_test(hw->os, + "status=%#x,fail rtrn buf to RQ\n", + rq_status); + break; + } + break; + case SLI4_FC_ASYNC_RQ_INSUFF_BUF_NEEDED: + case SLI4_FC_ASYNC_RQ_INSUFF_BUF_FRM_DISC: + /* + * since RQ buffers were not consumed, cannot return + * them to chip + */ + efc_log_debug(hw->os, "Warning: RCQE status=%#x,\n", + rq_status); + fallthrough; + default: + break; + } + return EFC_FAIL; + } + + rqindex = efct_hw_rqpair_find(hw, rq_id); + if (rqindex < 0) { + efc_log_test(hw->os, "Error: rq_id lookup failed for id=%#x\n", + rq_id); + return EFC_FAIL; + } + + rq = hw->hw_rq[hw->hw_rq_lookup[rqindex]]; + rq->use_count++; + + seq = efct_hw_rqpair_get(hw, rqindex, index); + if (WARN_ON(!seq)) + return EFC_FAIL; + + seq->hw = hw; + seq->auto_xrdy = 0; + seq->out_of_xris = 0; + + sli_fc_rqe_length(&hw->sli, cqe, &h_len, &p_len); + seq->header->dma.len = h_len; + seq->payload->dma.len = p_len; + seq->fcfi = sli_fc_rqe_fcfi(&hw->sli, cqe); + seq->hw_priv = cq->eq; + + efct_unsolicited_cb(hw->os, seq); + + return EFC_SUCCESS; +} + +static int +efct_hw_rqpair_put(struct efct_hw *hw, struct efc_hw_sequence *seq) +{ + struct sli4_queue *rq_hdr = &hw->rq[seq->header->rqindex]; + struct sli4_queue *rq_payload = &hw->rq[seq->payload->rqindex]; + u32 hw_rq_index = hw->hw_rq_lookup[seq->header->rqindex]; + struct hw_rq *rq = hw->hw_rq[hw_rq_index]; + u32 phys_hdr[2]; + u32 phys_payload[2]; + int qindex_hdr; + int qindex_payload; + unsigned long flags = 0; + + /* Update the RQ verification lookup tables */ + phys_hdr[0] = upper_32_bits(seq->header->dma.phys); + phys_hdr[1] = lower_32_bits(seq->header->dma.phys); + phys_payload[0] = upper_32_bits(seq->payload->dma.phys); + phys_payload[1] = lower_32_bits(seq->payload->dma.phys); + + /* rq_hdr lock also covers payload / header->rqindex+1 queue */ + spin_lock_irqsave(&rq_hdr->lock, flags); + + /* + * Note: The header must be posted last for buffer pair mode because + * posting on the header queue posts the payload queue as well. + * We do not ring the payload queue independently in RQ pair mode. + */ + qindex_payload = sli_rq_write(&hw->sli, rq_payload, + (void *)phys_payload); + qindex_hdr = sli_rq_write(&hw->sli, rq_hdr, (void *)phys_hdr); + if (qindex_hdr < 0 || + qindex_payload < 0) { + efc_log_err(hw->os, "RQ_ID=%#x write failed\n", rq_hdr->id); + spin_unlock_irqrestore(&rq_hdr->lock, flags); + return EFCT_HW_RTN_ERROR; + } + + /* ensure the indexes are the same */ + WARN_ON(qindex_hdr != qindex_payload); + + /* Update the lookup table */ + if (!rq->rq_tracker[qindex_hdr]) { + rq->rq_tracker[qindex_hdr] = seq; + } else { + efc_log_test(hw->os, + "expected rq_tracker[%d][%d] buffer to be NULL\n", + hw_rq_index, qindex_hdr); + } + + spin_unlock_irqrestore(&rq_hdr->lock, flags); + return EFCT_HW_RTN_SUCCESS; +} + +enum efct_hw_rtn +efct_hw_rqpair_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq) +{ + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + + /* + * Post the data buffer first. Because in RQ pair mode, ringing the + * doorbell of the header ring will post the data buffer as well. + */ + if (efct_hw_rqpair_put(hw, seq)) { + efc_log_err(hw->os, "error writing buffers\n"); + return EFCT_HW_RTN_ERROR; + } + + return rc; +} + +int +efct_efc_hw_sequence_free(struct efc *efc, struct efc_hw_sequence *seq) +{ + struct efct *efct = efc->base; + + return efct_hw_rqpair_sequence_free(&efct->hw, seq); +} From patchwork Mon Oct 12 22:51:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269822 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DDB18C433DF for ; Mon, 12 Oct 2020 22:52:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8B0BA20838 for ; Mon, 12 Oct 2020 22:52:33 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="HI8MZ4xZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390774AbgJLWwd (ORCPT ); Mon, 12 Oct 2020 18:52:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60618 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390688AbgJLWw1 (ORCPT ); Mon, 12 Oct 2020 18:52:27 -0400 Received: from mail-pg1-x541.google.com (mail-pg1-x541.google.com [IPv6:2607:f8b0:4864:20::541]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 28100C0613D5 for ; Mon, 12 Oct 2020 15:52:26 -0700 (PDT) Received: by mail-pg1-x541.google.com with SMTP id l18so6803711pgg.0 for ; Mon, 12 Oct 2020 15:52:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=igo3CCZYDpzxvI1i0plDXiDdItLWmSO93tihtZvDcSQ=; b=HI8MZ4xZqJGpOVYf2ehAYNxyU3WXLT+6XNZQFb+E/um8i0NdTEbY+bjgrFnmZXowfc NHEsDQHCog66SKEwhANyWt0sG996F/zjMQPbmirt+zCvQ8RQ6+E8i1p4ZtXSe34NPYpa 7LcPlzQdBn17MTZwo6iR/psEni/rMq0sIZVjE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=igo3CCZYDpzxvI1i0plDXiDdItLWmSO93tihtZvDcSQ=; b=qsBscnkanGX464c3QNM5NoYrJylVZQLMncaoPxjh1gkDq32v2j986yJDFoLJq5g8m6 ZeO9g5e5Fyb6LureG60YcxaxsQD2D/jtDJCKMfEN2DPuWhVEy60uZUXyZJuYJddshiiK 6wHcCdJe8mBZvQWe8HWVvLWWHpHPlhubcEk5I9phCOWQ0Tn9hTId9CdjPJxkBT3lFhlN osEJxat9IwPF93fbO2/8GL019ODPwkzWF6vzuiZmhfNehVy//21jvh2qbhULtiFH66Z6 W102ak/72Qpn4QaunG5eQwv58kCpXbnqDdmr280YeWJAeumeg3ckygkC5NNnM225ODRv aIpA== X-Gm-Message-State: AOAM530zqiU0crWyv558aYMSS+1mH/8BBSheoyYn4BHuA3SPRxRPE9tn KO8orqbO5SLd9Ve4jubBpVopc5GTfIDRbSaG9JbQg6HmO4uGbX7NyPYUUWbkLP2dTwUZ3JxonXl Mx3a+T+Zp4610yIUCKemRSUH2e23mzA2vG2HHIl3rNwAGZLxI3yk8mu3yIkx2LvAxc0SP/aZafK Q6Fxk= X-Google-Smtp-Source: ABdhPJznTHFUM/+/WbHIBdeqMh6A9C/67JNcxslNYUzyAe/X0FLSiqsboKlaxXOTG1cO78zwqTGJ4w== X-Received: by 2002:a17:90a:7c0c:: with SMTP id v12mr21905669pjf.71.1602543144471; Mon, 12 Oct 2020 15:52:24 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:23 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 20/31] elx: efct: RQ buffer, memory pool allocation and deallocation APIs Date: Mon, 12 Oct 2020 15:51:36 -0700 Message-Id: <20201012225147.54404-21-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: RQ data buffer allocation and deallocate. Memory pool allocation and deallocation APIs. Mailbox command submission and completion routines. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- drivers/scsi/elx/efct/efct_hw.c | 411 ++++++++++++++++++++++++++++++++ drivers/scsi/elx/efct/efct_hw.h | 9 + 2 files changed, 420 insertions(+) diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 0af13aa12e35..fdde7f70bedf 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -1164,3 +1164,414 @@ efct_get_wwpn(struct efct_hw *hw) memcpy(p, sli->wwpn, sizeof(p)); return get_unaligned_be64(p); } + +static struct efc_hw_rq_buffer * +efct_hw_rx_buffer_alloc(struct efct_hw *hw, u32 rqindex, u32 count, + u32 size) +{ + struct efct *efct = hw->os; + struct efc_hw_rq_buffer *rq_buf = NULL; + struct efc_hw_rq_buffer *prq; + u32 i; + + if (count != 0) { + rq_buf = kmalloc_array(count, sizeof(*rq_buf), GFP_KERNEL); + if (!rq_buf) + return NULL; + memset(rq_buf, 0, sizeof(*rq_buf) * count); + + for (i = 0, prq = rq_buf; i < count; i ++, prq++) { + prq->rqindex = rqindex; + prq->dma.size = size; + prq->dma.virt = dma_alloc_coherent(&efct->pci->dev, + prq->dma.size, + &prq->dma.phys, + GFP_DMA); + if (!prq->dma.virt) { + efc_log_err(hw->os, "DMA allocation failed\n"); + kfree(rq_buf); + rq_buf = NULL; + break; + } + } + } + return rq_buf; +} + +static void +efct_hw_rx_buffer_free(struct efct_hw *hw, + struct efc_hw_rq_buffer *rq_buf, + u32 count) +{ + struct efct *efct = hw->os; + u32 i; + struct efc_hw_rq_buffer *prq; + + if (rq_buf) { + for (i = 0, prq = rq_buf; i < count; i++, prq++) { + dma_free_coherent(&efct->pci->dev, + prq->dma.size, prq->dma.virt, + prq->dma.phys); + memset(&prq->dma, 0, sizeof(struct efc_dma)); + } + + kfree(rq_buf); + } +} + +enum efct_hw_rtn +efct_hw_rx_allocate(struct efct_hw *hw) +{ + struct efct *efct = hw->os; + u32 i; + int rc = EFCT_HW_RTN_SUCCESS; + u32 rqindex = 0; + u32 hdr_size = EFCT_HW_RQ_SIZE_HDR; + u32 payload_size = hw->config.rq_default_buffer_size; + + rqindex = 0; + + for (i = 0; i < hw->hw_rq_count; i++) { + struct hw_rq *rq = hw->hw_rq[i]; + + /* Allocate header buffers */ + rq->hdr_buf = efct_hw_rx_buffer_alloc(hw, rqindex, + rq->entry_count, + hdr_size); + if (!rq->hdr_buf) { + efc_log_err(efct, + "efct_hw_rx_buffer_alloc hdr_buf failed\n"); + rc = EFCT_HW_RTN_ERROR; + break; + } + + efc_log_debug(hw->os, + "rq[%2d] rq_id %02d header %4d by %4d bytes\n", + i, rq->hdr->id, rq->entry_count, hdr_size); + + rqindex++; + + /* Allocate payload buffers */ + rq->payload_buf = efct_hw_rx_buffer_alloc(hw, rqindex, + rq->entry_count, + payload_size); + if (!rq->payload_buf) { + efc_log_err(efct, + "efct_hw_rx_buffer_alloc fb_buf failed\n"); + rc = EFCT_HW_RTN_ERROR; + break; + } + efc_log_debug(hw->os, + "rq[%2d] rq_id %02d default %4d by %4d bytes\n", + i, rq->data->id, rq->entry_count, payload_size); + rqindex++; + } + + return rc ? EFCT_HW_RTN_ERROR : EFCT_HW_RTN_SUCCESS; +} + +enum efct_hw_rtn +efct_hw_rx_post(struct efct_hw *hw) +{ + u32 i; + u32 idx; + u32 rq_idx; + int rc = 0; + + if (!hw->seq_pool) { + u32 count = 0; + + for (i = 0; i < hw->hw_rq_count; i++) + count += hw->hw_rq[i]->entry_count; + + hw->seq_pool = kmalloc_array(count, + sizeof(struct efc_hw_sequence), GFP_KERNEL); + if (!hw->seq_pool) + return EFCT_HW_RTN_NO_MEMORY; + } + + /* + * In RQ pair mode, we MUST post the header and payload buffer at the + * same time. + */ + for (rq_idx = 0, idx = 0; rq_idx < hw->hw_rq_count; rq_idx++) { + struct hw_rq *rq = hw->hw_rq[rq_idx]; + + for (i = 0; i < rq->entry_count - 1; i++) { + struct efc_hw_sequence *seq; + + seq = hw->seq_pool + idx; + if (!seq) { + rc = -1; + break; + } + idx++; + seq->header = &rq->hdr_buf[i]; + seq->payload = &rq->payload_buf[i]; + rc = efct_hw_sequence_free(hw, seq); + if (rc) + break; + } + if (rc) + break; + } + + if (rc && hw->seq_pool) + kfree(hw->seq_pool); + + return rc; +} + +void +efct_hw_rx_free(struct efct_hw *hw) +{ + u32 i; + + /* Free hw_rq buffers */ + for (i = 0; i < hw->hw_rq_count; i++) { + struct hw_rq *rq = hw->hw_rq[i]; + + if (rq) { + efct_hw_rx_buffer_free(hw, rq->hdr_buf, + rq->entry_count); + rq->hdr_buf = NULL; + efct_hw_rx_buffer_free(hw, rq->payload_buf, + rq->entry_count); + rq->payload_buf = NULL; + } + } +} + +static int +efct_hw_cmd_submit_pending(struct efct_hw *hw) +{ + struct efct_command_ctx *ctx = NULL; + int rc = 0; + + /* Assumes lock held */ + + /* Only submit MQE if there's room */ + while (hw->cmd_head_count < (EFCT_HW_MQ_DEPTH - 1) && + !list_empty(&hw->cmd_pending)) { + ctx = list_first_entry(&hw->cmd_pending, + struct efct_command_ctx, list_entry); + if (!ctx) + break; + + list_del(&ctx->list_entry); + + INIT_LIST_HEAD(&ctx->list_entry); + list_add_tail(&ctx->list_entry, &hw->cmd_head); + hw->cmd_head_count++; + if (sli_mq_write(&hw->sli, hw->mq, ctx->buf) < 0) { + efc_log_test(hw->os, + "sli_queue_write failed: %d\n", rc); + rc = -1; + break; + } + } + return rc; +} + +enum efct_hw_rtn +efct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb, void *arg) +{ + enum efct_hw_rtn rc = EFCT_HW_RTN_ERROR; + unsigned long flags = 0; + void *bmbx = NULL; + + /* + * If the chip is in an error state (UE'd) then reject this mailbox + * command. + */ + if (sli_fw_error_status(&hw->sli) > 0) { + efc_log_crit(hw->os, + "Chip is in an error state - reset needed\n"); + efc_log_crit(hw->os, + "status=%#x error1=%#x error2=%#x\n", + sli_reg_read_status(&hw->sli), + sli_reg_read_err1(&hw->sli), + sli_reg_read_err2(&hw->sli)); + + return EFCT_HW_RTN_ERROR; + } + + /* + * Send a mailbox command to the hardware, and either wait for + * a completion (EFCT_CMD_POLL) or get an optional asynchronous + * completion (EFCT_CMD_NOWAIT). + */ + + if (opts == EFCT_CMD_POLL) { + mutex_lock(&hw->bmbx_lock); + bmbx = hw->sli.bmbx.virt; + + memset(bmbx, 0, SLI4_BMBX_SIZE); + memcpy(bmbx, cmd, SLI4_BMBX_SIZE); + + if (sli_bmbx_command(&hw->sli) == 0) { + rc = EFCT_HW_RTN_SUCCESS; + memcpy(cmd, bmbx, SLI4_BMBX_SIZE); + } + mutex_unlock(&hw->bmbx_lock); + } else if (opts == EFCT_CMD_NOWAIT) { + struct efct_command_ctx *ctx = NULL; + + if (hw->state != EFCT_HW_STATE_ACTIVE) { + efc_log_err(hw->os, + "Can't send command, HW state=%d\n", + hw->state); + return EFCT_HW_RTN_ERROR; + } + + ctx = mempool_alloc(hw->cmd_ctx_pool, GFP_ATOMIC); + if (!ctx) + return EFCT_HW_RTN_NO_RESOURCES; + + memset(ctx, 0, sizeof(struct efct_command_ctx)); + + if (cb) { + ctx->cb = cb; + ctx->arg = arg; + } + + memcpy(ctx->buf, cmd, SLI4_BMBX_SIZE); + ctx->ctx = hw; + + spin_lock_irqsave(&hw->cmd_lock, flags); + + /* Add to pending list */ + INIT_LIST_HEAD(&ctx->list_entry); + list_add_tail(&ctx->list_entry, &hw->cmd_pending); + + /* Submit as much of the pending list as we can */ + rc = efct_hw_cmd_submit_pending(hw); + + spin_unlock_irqrestore(&hw->cmd_lock, flags); + } + + return rc; +} + +static int +efct_hw_command_process(struct efct_hw *hw, int status, u8 *mqe, + size_t size) +{ + struct efct_command_ctx *ctx = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&hw->cmd_lock, flags); + if (!list_empty(&hw->cmd_head)) { + ctx = list_first_entry(&hw->cmd_head, + struct efct_command_ctx, list_entry); + list_del(&ctx->list_entry); + } + if (!ctx) { + efc_log_err(hw->os, "no command context?!?\n"); + spin_unlock_irqrestore(&hw->cmd_lock, flags); + return EFC_FAIL; + } + + hw->cmd_head_count--; + + /* Post any pending requests */ + efct_hw_cmd_submit_pending(hw); + + spin_unlock_irqrestore(&hw->cmd_lock, flags); + + if (ctx->cb) { + if (ctx->buf) + memcpy(ctx->buf, mqe, size); + + ctx->cb(hw, status, ctx->buf, ctx->arg); + } + + mempool_free(ctx, hw->cmd_ctx_pool); + + return EFC_SUCCESS; +} + +static int +efct_hw_mq_process(struct efct_hw *hw, + int status, struct sli4_queue *mq) +{ + u8 mqe[SLI4_BMBX_SIZE]; + + if (!sli_mq_read(&hw->sli, mq, mqe)) + efct_hw_command_process(hw, status, mqe, mq->size); + + return EFC_SUCCESS; +} + +static int +efct_hw_command_cancel(struct efct_hw *hw) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&hw->cmd_lock, flags); + + /* + * Manually clean up remaining commands. Note: since this calls + * efct_hw_command_process(), we'll also process the cmd_pending + * list, so no need to manually clean that out. + */ + while (!list_empty(&hw->cmd_head)) { + u8 mqe[SLI4_BMBX_SIZE] = { 0 }; + struct efct_command_ctx *ctx = + list_first_entry(&hw->cmd_head, struct efct_command_ctx, list_entry); + + efc_log_test(hw->os, "hung command %08x\n", + !ctx ? U32_MAX : + (!ctx->buf ? U32_MAX : + *((u32 *)ctx->buf))); + spin_unlock_irqrestore(&hw->cmd_lock, flags); + efct_hw_command_process(hw, -1, mqe, SLI4_BMBX_SIZE); + spin_lock_irqsave(&hw->cmd_lock, flags); + } + + spin_unlock_irqrestore(&hw->cmd_lock, flags); + + return EFC_SUCCESS; +} + +static void +efct_mbox_rsp_cb(struct efct_hw *hw, int status, u8 *mqe, void *arg) +{ + struct efct_mbox_rqst_ctx *ctx = arg; + + if (ctx) { + if (ctx->callback) + (*ctx->callback)(hw->os->efcport, status, mqe, + ctx->arg); + + mempool_free(ctx, hw->mbox_rqst_pool); + } +} + +int +efct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg) +{ + int rc = 0; + struct efct_mbox_rqst_ctx *ctx; + struct efct *efct = base; + struct efct_hw *hw = &efct->hw; + + /* + * Allocate a callback context (which includes the mbox cmd buffer), + * we need this to be persistent as the mbox cmd submission may be + * queued and executed later execution. + */ + ctx = mempool_alloc(hw->mbox_rqst_pool, GFP_ATOMIC); + if (!ctx) + return EFC_FAIL; + + ctx->callback = cb; + ctx->arg = arg; + + if (efct_hw_command(hw, cmd, EFCT_CMD_NOWAIT, efct_mbox_rsp_cb, ctx)) { + efc_log_err(efct, "issue mbox rqst failure\n"); + mempool_free(ctx, hw->mbox_rqst_pool); + rc = -1; + } + return rc; +} diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index 44649d649229..03787da3f4f7 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -619,4 +619,13 @@ efct_get_wwnn(struct efct_hw *hw); uint64_t efct_get_wwpn(struct efct_hw *hw); +enum efct_hw_rtn efct_hw_rx_allocate(struct efct_hw *hw); +enum efct_hw_rtn efct_hw_rx_post(struct efct_hw *hw); +void efct_hw_rx_free(struct efct_hw *hw); +enum efct_hw_rtn +efct_hw_command(struct efct_hw *hw, u8 *cmd, u32 opts, void *cb, + void *arg); +int +efct_issue_mbox_rqst(void *base, void *cmd, void *cb, void *arg); + #endif /* __EFCT_H__ */ From patchwork Mon Oct 12 22:51:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269821 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6215DC43467 for ; Mon, 12 Oct 2020 22:52:35 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 040F92073A for ; Mon, 12 Oct 2020 22:52:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="VEdSu3Bu" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390827AbgJLWwe (ORCPT ); Mon, 12 Oct 2020 18:52:34 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60638 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390748AbgJLWwc (ORCPT ); Mon, 12 Oct 2020 18:52:32 -0400 Received: from mail-pf1-x444.google.com (mail-pf1-x444.google.com [IPv6:2607:f8b0:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A36FCC0613D7 for ; Mon, 12 Oct 2020 15:52:28 -0700 (PDT) Received: by mail-pf1-x444.google.com with SMTP id e10so15131017pfj.1 for ; Mon, 12 Oct 2020 15:52:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=IHjMNUnwzMR272Rkimlnir5XqvTsEzCjdAooWkr6HV4=; b=VEdSu3BuQrthw5BV3DmD2FzWNRwBwv7AyomyYvOxIV4WPPwzrNT4+KXWz9xT0oySw/ l+FkJpoAEqhf2kHhZ/Ob6qz8elylX7KECuK7ybJ9BcKlQIoUbeWNoNDWXHfPLFat9qY1 PkJYkiyab9atuppK1Y0ihOjAAMysYtvppb5Q0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=IHjMNUnwzMR272Rkimlnir5XqvTsEzCjdAooWkr6HV4=; b=BMsUkrEhmFomuJ+0sDpKpJEGS5zrWxc5hG0UWqIOqSw+gu9AKI1RDBw4kqIn+IGHFd ov2mf5AMe9MP07tBIZHf0cPXJsSsF99UUrR+Gsx0kq2agrI0fKUXkgVfl9Ky1/sPomff nJk2DSEbh8KoA67KoHE2RgKzbNP61YsccqrbxMgmM1kwNwdmj34zSOTRxe8OYzdl/KPf YgSX7i8BJ6hlNBr//3i5pl235sNiqn2x8DytAK2UeGHuuk0siiuIv2UJV/igPalJHPEx ITHJbXSphDXrAWjTYvN48i6DUXUFmcqtiYl2MYE9d/eQ2b9ahQoO/SFdICnSQUW8848r Qevg== X-Gm-Message-State: AOAM531b6QRnWbrrKxpHRVhbEWfix35rxU/hTdbyzadPx16b7XIbMbvu 4TwVbrtPCSP3Nsab/ovd0rvm1s+wbLo2RuJTaQCO/qVVnq8i/9dbrIEgtuKsRJ+96aOwBmJBMD1 klj6vUOC8EXIHK/bWd039T3nYTCn/SmsOVqZSF7yNeP8gw4dJIfY4RvtxfvOTOZlf0M3bopy1ww JZK+k= X-Google-Smtp-Source: ABdhPJzU1jIYiZR4lAFi5wuogjGNJwSYkDfIFrCMSg/MKd+X6QTWdtP1zsDG66XhXnj+wHqvho2T7A== X-Received: by 2002:a63:40c1:: with SMTP id n184mr7274928pga.215.1602543147251; Mon, 12 Oct 2020 15:52:27 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:26 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 22/31] elx: efct: Hardware queues processing Date: Mon, 12 Oct 2020 15:51:38 -0700 Message-Id: <20201012225147.54404-23-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: Routines for EQ, CQ, WQ and RQ processing. Routines for IO object pool allocation and deallocation. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke --- v4: Remove els entries from scsi io object, as discovery IO object handles all els. scsi io takes efct_node object refernce and releases when IO is done. --- drivers/scsi/elx/efct/efct_hw.c | 366 ++++++++++++++++++++++++++++++++ drivers/scsi/elx/efct/efct_hw.h | 38 ++++ drivers/scsi/elx/efct/efct_io.c | 191 +++++++++++++++++ drivers/scsi/elx/efct/efct_io.h | 166 +++++++++++++++ 4 files changed, 761 insertions(+) create mode 100644 drivers/scsi/elx/efct/efct_io.c create mode 100644 drivers/scsi/elx/efct/efct_io.h diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 3df46eb20fa1..7764f16b2bed 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -2165,3 +2165,369 @@ efct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index) return wqcb; } + +int +efct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id) +{ + int index = -1; + int i = id & (EFCT_HW_Q_HASH_SIZE - 1); + + /* + * Since the hash is always bigger than the maximum number of Qs, then + * we never have to worry about an infinite loop. We will always find + * an unused entry. + */ + do { + if (hash[i].in_use && hash[i].id == id) + index = hash[i].index; + else + i = (i + 1) & (EFCT_HW_Q_HASH_SIZE - 1); + } while (index == -1 && hash[i].in_use); + + return index; +} + +int +efct_hw_process(struct efct_hw *hw, u32 vector, + u32 max_isr_time_msec) +{ + struct hw_eq *eq; + int rc = 0; + + /* + * The caller should disable interrupts if they wish to prevent us + * from processing during a shutdown. The following states are defined: + * EFCT_HW_STATE_UNINITIALIZED - No queues allocated + * EFCT_HW_STATE_QUEUES_ALLOCATED - The state after a chip reset, + * queues are cleared. + * EFCT_HW_STATE_ACTIVE - Chip and queues are operational + * EFCT_HW_STATE_RESET_IN_PROGRESS - reset, we still want completions + * EFCT_HW_STATE_TEARDOWN_IN_PROGRESS - We still want mailbox + * completions. + */ + if (hw->state == EFCT_HW_STATE_UNINITIALIZED) + return EFC_SUCCESS; + + /* Get pointer to struct hw_eq */ + eq = hw->hw_eq[vector]; + if (!eq) + return EFC_SUCCESS; + + eq->use_count++; + + rc = efct_hw_eq_process(hw, eq, max_isr_time_msec); + + return rc; +} + +int +efct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq, + u32 max_isr_time_msec) +{ + u8 eqe[sizeof(struct sli4_eqe)] = { 0 }; + u32 tcheck_count; + u64 tstart; + u64 telapsed; + bool done = false; + + tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS; + tstart = jiffies_to_msecs(jiffies); + + while (!done && !sli_eq_read(&hw->sli, eq->queue, eqe)) { + u16 cq_id = 0; + int rc; + + rc = sli_eq_parse(&hw->sli, eqe, &cq_id); + if (unlikely(rc)) { + if (rc == SLI4_EQE_STATUS_EQ_FULL) { + u32 i; + + /* + * Received a sentinel EQE indicating the + * EQ is full. Process all CQs + */ + for (i = 0; i < hw->cq_count; i++) + efct_hw_cq_process(hw, hw->hw_cq[i]); + continue; + } else { + return rc; + } + } else { + int index; + + index = efct_hw_queue_hash_find(hw->cq_hash, cq_id); + + if (likely(index >= 0)) + efct_hw_cq_process(hw, hw->hw_cq[index]); + else + efc_log_err(hw->os, "bad CQ_ID %#06x\n", + cq_id); + } + + if (eq->queue->n_posted > eq->queue->posted_limit) + sli_queue_arm(&hw->sli, eq->queue, false); + + if (tcheck_count && (--tcheck_count == 0)) { + tcheck_count = EFCT_HW_TIMECHECK_ITERATIONS; + telapsed = jiffies_to_msecs(jiffies) - tstart; + if (telapsed >= max_isr_time_msec) + done = true; + } + } + sli_queue_eq_arm(&hw->sli, eq->queue, true); + + return EFC_SUCCESS; +} + +static int +_efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe) +{ + int queue_rc; + + /* Every so often, set the wqec bit to generate comsummed completions */ + if (wq->wqec_count) + wq->wqec_count--; + + if (wq->wqec_count == 0) { + struct sli4_generic_wqe *genwqe = (void *)wqe->wqebuf; + + genwqe->cmdtype_wqec_byte |= SLI4_GEN_WQE_WQEC; + wq->wqec_count = wq->wqec_set_count; + } + + /* Decrement WQ free count */ + wq->free_count--; + + queue_rc = sli_wq_write(&wq->hw->sli, wq->queue, wqe->wqebuf); + + return (queue_rc < 0) ? EFC_FAIL : EFC_SUCCESS; +} + +static void +hw_wq_submit_pending(struct hw_wq *wq, u32 update_free_count) +{ + struct efct_hw_wqe *wqe; + unsigned long flags = 0; + + spin_lock_irqsave(&wq->queue->lock, flags); + + /* Update free count with value passed in */ + wq->free_count += update_free_count; + + while ((wq->free_count > 0) && (!list_empty(&wq->pending_list))) { + wqe = list_first_entry(&wq->pending_list, + struct efct_hw_wqe, list_entry); + list_del(&wqe->list_entry); + _efct_hw_wq_write(wq, wqe); + + if (wqe->abort_wqe_submit_needed) { + wqe->abort_wqe_submit_needed = false; + efct_hw_fill_abort_wqe(wq->hw, wqe); + INIT_LIST_HEAD(&wqe->list_entry); + list_add_tail(&wqe->list_entry, &wq->pending_list); + wq->wq_pending_count++; + } + } + + spin_unlock_irqrestore(&wq->queue->lock, flags); +} + +void +efct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq) +{ + u8 cqe[sizeof(struct sli4_mcqe)]; + u16 rid = U16_MAX; + /* completion type */ + enum sli4_qentry ctype; + u32 n_processed = 0; + u32 tstart, telapsed; + + tstart = jiffies_to_msecs(jiffies); + + while (!sli_cq_read(&hw->sli, cq->queue, cqe)) { + int status; + + status = sli_cq_parse(&hw->sli, cq->queue, cqe, &ctype, &rid); + /* + * The sign of status is significant. If status is: + * == 0 : call completed correctly and + * the CQE indicated success + * > 0 : call completed correctly and + * the CQE indicated an error + * < 0 : call failed and no information is available about the + * CQE + */ + if (status < 0) { + if (status == SLI4_MCQE_STATUS_NOT_COMPLETED) + /* + * Notification that an entry was consumed, + * but not completed + */ + continue; + + break; + } + + switch (ctype) { + case SLI4_QENTRY_ASYNC: + sli_cqe_async(&hw->sli, cqe); + break; + case SLI4_QENTRY_MQ: + /* + * Process MQ entry. Note there is no way to determine + * the MQ_ID from the completion entry. + */ + efct_hw_mq_process(hw, status, hw->mq); + break; + case SLI4_QENTRY_WQ: + efct_hw_wq_process(hw, cq, cqe, status, rid); + break; + case SLI4_QENTRY_WQ_RELEASE: { + u32 wq_id = rid; + int index; + struct hw_wq *wq = NULL; + + index = efct_hw_queue_hash_find(hw->wq_hash, wq_id); + + if (likely(index >= 0)) { + wq = hw->hw_wq[index]; + } else { + efc_log_err(hw->os, "bad WQ_ID %#06x\n", wq_id); + break; + } + /* Submit any HW IOs that are on the WQ pending list */ + hw_wq_submit_pending(wq, wq->wqec_set_count); + + break; + } + + case SLI4_QENTRY_RQ: + efct_hw_rqpair_process_rq(hw, cq, cqe); + break; + case SLI4_QENTRY_XABT: { + efct_hw_xabt_process(hw, cq, cqe, rid); + break; + } + default: + efc_log_test(hw->os, + "unhandled ctype=%#x rid=%#x\n", + ctype, rid); + break; + } + + n_processed++; + if (n_processed == cq->queue->proc_limit) + break; + + if (cq->queue->n_posted >= cq->queue->posted_limit) + sli_queue_arm(&hw->sli, cq->queue, false); + } + + sli_queue_arm(&hw->sli, cq->queue, true); + + if (n_processed > cq->queue->max_num_processed) + cq->queue->max_num_processed = n_processed; + telapsed = jiffies_to_msecs(jiffies) - tstart; + if (telapsed > cq->queue->max_process_time) + cq->queue->max_process_time = telapsed; +} + +void +efct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq, + u8 *cqe, int status, u16 rid) +{ + struct hw_wq_callback *wqcb; + + if (rid == EFCT_HW_REQUE_XRI_REGTAG) { + if (status) + efc_log_err(hw->os, "reque xri failed, status = %d\n", + status); + return; + } + + wqcb = efct_hw_reqtag_get_instance(hw, rid); + if (!wqcb) { + efc_log_err(hw->os, "invalid request tag: x%x\n", rid); + return; + } + + if (!wqcb->callback) { + efc_log_err(hw->os, "wqcb callback is NULL\n"); + return; + } + + (*wqcb->callback)(wqcb->arg, cqe, status); +} + +void +efct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq, + u8 *cqe, u16 rid) +{ + /* search IOs wait free list */ + struct efct_hw_io *io = NULL; + unsigned long flags = 0; + + io = efct_hw_io_lookup(hw, rid); + if (!io) { + /* IO lookup failure should never happen */ + efc_log_err(hw->os, + "Error: xabt io lookup failed rid=%#x\n", rid); + return; + } + + if (!io->xbusy) + efc_log_debug(hw->os, "xabt io not busy rid=%#x\n", rid); + else + /* mark IO as no longer busy */ + io->xbusy = false; + + /* + * For IOs that were aborted internally, we need to issue any pending + * callback here. + */ + if (io->done) { + efct_hw_done_t done = io->done; + void *arg = io->arg; + + /* + * Use latched status as this is always saved for an internal + * abort + */ + int status = io->saved_status; + u32 len = io->saved_len; + u32 ext = io->saved_ext; + + io->done = NULL; + io->status_saved = false; + + done(io, len, status, ext, arg); + } + + spin_lock_irqsave(&hw->io_lock, flags); + if (io->state == EFCT_HW_IO_STATE_INUSE || + io->state == EFCT_HW_IO_STATE_WAIT_FREE) { + /* if on wait_free list, caller has already freed IO; + * remove from wait_free list and add to free list. + * if on in-use list, already marked as no longer busy; + * just leave there and wait for caller to free. + */ + if (io->state == EFCT_HW_IO_STATE_WAIT_FREE) { + io->state = EFCT_HW_IO_STATE_FREE; + list_del(&io->list_entry); + efct_hw_io_free_move_correct_list(hw, io); + } + } + spin_unlock_irqrestore(&hw->io_lock, flags); +} + +static int +efct_hw_flush(struct efct_hw *hw) +{ + u32 i = 0; + + /* Process any remaining completions */ + for (i = 0; i < hw->eq_count; i++) + efct_hw_process(hw, i, ~0); + + return EFC_SUCCESS; +} diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index f90f78ab73d8..ab9a86ded815 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -669,4 +669,42 @@ efct_hw_reqtag_free(struct efct_hw *hw, struct hw_wq_callback *wqcb); struct hw_wq_callback *efct_hw_reqtag_get_instance(struct efct_hw *hw, u32 instance_index); +/* RQ completion handlers for RQ pair mode */ +int +efct_hw_rqpair_process_rq(struct efct_hw *hw, + struct hw_cq *cq, u8 *cqe); +enum efct_hw_rtn +efct_hw_rqpair_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq); +static inline void +efct_hw_sequence_copy(struct efc_hw_sequence *dst, + struct efc_hw_sequence *src) +{ + /* Copy src to dst, then zero out the linked list link */ + *dst = *src; +} + +int +efct_efc_hw_sequence_free(struct efc *efc, struct efc_hw_sequence *seq); + +static inline enum efct_hw_rtn +efct_hw_sequence_free(struct efct_hw *hw, struct efc_hw_sequence *seq) +{ + /* Only RQ pair mode is supported */ + return efct_hw_rqpair_sequence_free(hw, seq); +} +int +efct_hw_eq_process(struct efct_hw *hw, struct hw_eq *eq, + u32 max_isr_time_msec); +void efct_hw_cq_process(struct efct_hw *hw, struct hw_cq *cq); +void +efct_hw_wq_process(struct efct_hw *hw, struct hw_cq *cq, + u8 *cqe, int status, u16 rid); +void +efct_hw_xabt_process(struct efct_hw *hw, struct hw_cq *cq, + u8 *cqe, u16 rid); +int +efct_hw_process(struct efct_hw *hw, u32 vector, u32 max_isr_time_msec); +int +efct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id); + #endif /* __EFCT_H__ */ diff --git a/drivers/scsi/elx/efct/efct_io.c b/drivers/scsi/elx/efct/efct_io.c new file mode 100644 index 000000000000..3398e0240d2e --- /dev/null +++ b/drivers/scsi/elx/efct/efct_io.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include "efct_driver.h" +#include "efct_hw.h" +#include "efct_io.h" + +struct efct_io_pool { + struct efct *efct; + spinlock_t lock; /* IO pool lock */ + u32 io_num_ios; /* Total IOs allocated */ + struct efct_io *ios[EFCT_NUM_SCSI_IOS]; + struct list_head freelist; + +}; + +struct efct_io_pool * +efct_io_pool_create(struct efct *efct, u32 num_sgl) +{ + u32 i = 0; + struct efct_io_pool *io_pool; + struct efct_io *io; + + /* Allocate the IO pool */ + io_pool = kzalloc(sizeof(*io_pool), GFP_KERNEL); + if (!io_pool) + return NULL; + + io_pool->efct = efct; + INIT_LIST_HEAD(&io_pool->freelist); + /* initialize IO pool lock */ + spin_lock_init(&io_pool->lock); + + for (i = 0; i < EFCT_NUM_SCSI_IOS; i++) { + io = kzalloc(sizeof(*io), GFP_KERNEL); + if (!io) + break; + + io_pool->io_num_ios++; + io_pool->ios[i] = io; + io->tag = i; + io->instance_index = i; + + /* Allocate a response buffer */ + io->rspbuf.size = SCSI_RSP_BUF_LENGTH; + io->rspbuf.virt = dma_alloc_coherent(&efct->pci->dev, + io->rspbuf.size, + &io->rspbuf.phys, GFP_DMA); + if (!io->rspbuf.virt) { + efc_log_err(efct, "dma_alloc rspbuf failed\n"); + efct_io_pool_free(io_pool); + return NULL; + } + + /* Allocate SGL */ + io->sgl = kzalloc(sizeof(*io->sgl) * num_sgl, GFP_KERNEL); + if (!io->sgl) { + efct_io_pool_free(io_pool); + return NULL; + } + + memset(io->sgl, 0, sizeof(*io->sgl) * num_sgl); + io->sgl_allocated = num_sgl; + io->sgl_count = 0; + + INIT_LIST_HEAD(&io->list_entry); + list_add_tail(&io->list_entry, &io_pool->freelist); + } + + return io_pool; +} + +int +efct_io_pool_free(struct efct_io_pool *io_pool) +{ + struct efct *efct; + u32 i; + struct efct_io *io; + + if (io_pool) { + efct = io_pool->efct; + + for (i = 0; i < io_pool->io_num_ios; i++) { + io = io_pool->ios[i]; + if (!io) + continue; + + kfree(io->sgl); + dma_free_coherent(&efct->pci->dev, + io->rspbuf.size, io->rspbuf.virt, + io->rspbuf.phys); + memset(&io->rspbuf, 0, sizeof(struct efc_dma)); + } + + kfree(io_pool); + efct->xport->io_pool = NULL; + } + + return EFC_SUCCESS; +} + +struct efct_io * +efct_io_pool_io_alloc(struct efct_io_pool *io_pool) +{ + struct efct_io *io = NULL; + struct efct *efct; + unsigned long flags = 0; + + efct = io_pool->efct; + + spin_lock_irqsave(&io_pool->lock, flags); + + if (!list_empty(&io_pool->freelist)) { + io = list_first_entry(&io_pool->freelist, struct efct_io, + list_entry); + list_del(&io->list_entry); + } + + spin_unlock_irqrestore(&io_pool->lock, flags); + + if (!io) + return NULL; + + io->io_type = EFCT_IO_TYPE_MAX; + io->hio_type = EFCT_HW_IO_MAX; + io->hio = NULL; + io->transferred = 0; + io->efct = efct; + io->timeout = 0; + io->sgl_count = 0; + io->tgt_task_tag = 0; + io->init_task_tag = 0; + io->hw_tag = 0; + io->display_name = "pending"; + io->seq_init = 0; + io->io_free = 0; + io->release = NULL; + atomic_add_return(1, &efct->xport->io_active_count); + atomic_add_return(1, &efct->xport->io_total_alloc); + return io; +} + +/* Free an object used to track an IO */ +void +efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io) +{ + struct efct *efct; + struct efct_hw_io *hio = NULL; + unsigned long flags = 0; + + efct = io_pool->efct; + + spin_lock_irqsave(&io_pool->lock, flags); + hio = io->hio; + io->hio = NULL; + io->io_free = 1; + INIT_LIST_HEAD(&io->list_entry); + list_add(&io->list_entry, &io_pool->freelist); + spin_unlock_irqrestore(&io_pool->lock, flags); + + if (hio) + efct_hw_io_free(&efct->hw, hio); + + atomic_sub_return(1, &efct->xport->io_active_count); + atomic_add_return(1, &efct->xport->io_total_free); +} + +/* Find an I/O given it's node and ox_id */ +struct efct_io * +efct_io_find_tgt_io(struct efct *efct, struct efct_node *node, + u16 ox_id, u16 rx_id) +{ + struct efct_io *io = NULL; + unsigned long flags = 0; + u8 found = false; + + spin_lock_irqsave(&node->active_ios_lock, flags); + list_for_each_entry(io, &node->active_ios, list_entry) { + if ((io->cmd_tgt && io->init_task_tag == ox_id) && + (rx_id == 0xffff || io->tgt_task_tag == rx_id)) { + if (kref_get_unless_zero(&io->ref)) + found = true; + break; + } + } + spin_unlock_irqrestore(&node->active_ios_lock, flags); + return found ? io : NULL; +} diff --git a/drivers/scsi/elx/efct/efct_io.h b/drivers/scsi/elx/efct/efct_io.h new file mode 100644 index 000000000000..8e4df9fcf9df --- /dev/null +++ b/drivers/scsi/elx/efct/efct_io.h @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#if !defined(__EFCT_IO_H__) +#define __EFCT_IO_H__ + +#include "efct_lio.h" + +#define EFCT_LOG_ENABLE_IO_ERRORS(efct) \ + (((efct) != NULL) ? (((efct)->logmask & (1U << 6)) != 0) : 0) + +#define io_error_log(io, fmt, ...) \ + do { \ + if (EFCT_LOG_ENABLE_IO_ERRORS(io->efct)) \ + efc_log_warn(io->efct, fmt, ##__VA_ARGS__); \ + } while (0) + +#define SCSI_CMD_BUF_LENGTH 48 +#define SCSI_RSP_BUF_LENGTH (FCP_RESP_WITH_EXT + SCSI_SENSE_BUFFERSIZE) +#define EFCT_NUM_SCSI_IOS 8192 + +enum efct_io_type { + EFCT_IO_TYPE_IO = 0, + EFCT_IO_TYPE_ELS, + EFCT_IO_TYPE_CT, + EFCT_IO_TYPE_CT_RESP, + EFCT_IO_TYPE_BLS_RESP, + EFCT_IO_TYPE_ABORT, + + EFCT_IO_TYPE_MAX, +}; + +enum efct_els_state { + EFCT_ELS_REQUEST = 0, + EFCT_ELS_REQUEST_DELAYED, + EFCT_ELS_REQUEST_DELAY_ABORT, + EFCT_ELS_REQ_ABORT, + EFCT_ELS_REQ_ABORTED, + EFCT_ELS_ABORT_IO_COMPL, +}; + +struct efct_io { + struct list_head list_entry; + struct list_head io_pending_link; + /* reference counter and callback function */ + struct kref ref; + void (*release)(struct kref *arg); + /* pointer back to efct */ + struct efct *efct; + /* unique instance index value */ + u32 instance_index; + /* display name */ + const char *display_name; + /* pointer to node */ + struct efct_node *node; + /* (io_pool->io_free_list) free list link */ + /* initiator task tag (OX_ID) for back-end and SCSI logging */ + u32 init_task_tag; + /* target task tag (RX_ID) - for back-end and SCSI logging */ + u32 tgt_task_tag; + /* HW layer unique IO id - for back-end and SCSI logging */ + u32 hw_tag; + /* unique IO identifier */ + u32 tag; + /* SGL */ + struct efct_scsi_sgl *sgl; + /* Number of allocated SGEs */ + u32 sgl_allocated; + /* Number of SGEs in this SGL */ + u32 sgl_count; + /* backend target private IO data */ + struct efct_scsi_tgt_io tgt_io; + /* expected data transfer length, based on FC header */ + u32 exp_xfer_len; + + /* Declarations private to HW/SLI */ + void *hw_priv; + + /* indicates what this struct efct_io structure is used for */ + enum efct_io_type io_type; + struct efct_hw_io *hio; + size_t transferred; + + /* set if auto_trsp was set */ + bool auto_resp; + /* set if low latency request */ + bool low_latency; + /* selected WQ steering request */ + u8 wq_steering; + /* selected WQ class if steering is class */ + u8 wq_class; + /* transfer size for current request */ + u64 xfer_req; + /* target callback function */ + efct_scsi_io_cb_t scsi_tgt_cb; + /* target callback function argument */ + void *scsi_tgt_cb_arg; + /* abort callback function */ + efct_scsi_io_cb_t abort_cb; + /* abort callback function argument */ + void *abort_cb_arg; + /* BLS callback function */ + efct_scsi_io_cb_t bls_cb; + /* BLS callback function argument */ + void *bls_cb_arg; + /* TMF command being processed */ + enum efct_scsi_tmf_cmd tmf_cmd; + /* rx_id from the ABTS that initiated the command abort */ + u16 abort_rx_id; + + /* True if this is a Target command */ + bool cmd_tgt; + /* when aborting, indicates ABTS is to be sent */ + bool send_abts; + /* True if this is an Initiator command */ + bool cmd_ini; + /* True if local node has sequence initiative */ + bool seq_init; + /* iparams for hw io send call */ + union efct_hw_io_param_u iparam; + /* HW IO type */ + enum efct_hw_io_type hio_type; + /* wire length */ + u64 wire_len; + /* saved HW callback */ + void *hw_cb; + + /* for abort handling */ + /* pointer to IO to abort */ + struct efct_io *io_to_abort; + + /* SCSI Response buffer */ + struct efc_dma rspbuf; + /* Timeout value in seconds for this IO */ + u32 timeout; + /* CS_CTL priority for this IO */ + u8 cs_ctl; + /* Is io object in freelist > */ + u8 io_free; + u32 app_id; +}; + +struct efct_io_cb_arg { + int status; /* completion status */ + int ext_status; /* extended completion status */ + void *app; /* application argument */ +}; + +struct efct_io_pool * +efct_io_pool_create(struct efct *efct, u32 num_sgl); +int +efct_io_pool_free(struct efct_io_pool *io_pool); +u32 +efct_io_pool_allocated(struct efct_io_pool *io_pool); + +struct efct_io * +efct_io_pool_io_alloc(struct efct_io_pool *io_pool); +void +efct_io_pool_io_free(struct efct_io_pool *io_pool, struct efct_io *io); +struct efct_io * +efct_io_find_tgt_io(struct efct *efct, struct efct_node *node, + u16 ox_id, u16 rx_id); +#endif /* __EFCT_IO_H__ */ From patchwork Mon Oct 12 22:51:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269815 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BC12BC4363A for ; Mon, 12 Oct 2020 22:52:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 53A1520838 for ; Mon, 12 Oct 2020 22:52:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="WL8HCWXr" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390877AbgJLWwv (ORCPT ); Mon, 12 Oct 2020 18:52:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60646 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390723AbgJLWwd (ORCPT ); Mon, 12 Oct 2020 18:52:33 -0400 Received: from mail-pg1-x52d.google.com (mail-pg1-x52d.google.com [IPv6:2607:f8b0:4864:20::52d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 893A6C0613D0 for ; Mon, 12 Oct 2020 15:52:33 -0700 (PDT) Received: by mail-pg1-x52d.google.com with SMTP id u24so15918576pgi.1 for ; Mon, 12 Oct 2020 15:52:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=C8mmemcQ3R8P4/4oOhb0Ee9QCaUa2eNoPt9mKAbeVj8=; b=WL8HCWXrbZO4WL/821cWUxJrtW+tWicEgJLA9M9rJQoJEmw1zbEITgn0J5WvrnTqTH 5dp9+t2fd8f90PacDAMTjcmfrHNxz9xcpQe+Gu2qlvyfuhvDsX3TfFVBfeZzOU3z1M5w hhsmmF36sskJOMmkXweL3yyvk5wCThiTRAAAo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=C8mmemcQ3R8P4/4oOhb0Ee9QCaUa2eNoPt9mKAbeVj8=; b=HE146c5Fy5hrO9VorGDvUa2kouPwcnSmM62CcxVbN7krWzVQ8K6lQ8k9SsJhdjeRdD HOtXby6Mj+IFHook1OncHJsIsT6zuZEuItEpyADr2+j6ze1YMb8dX06p+nm+/tCHgD3f b/HmDaxw0Rnx89Q405WTJBYSaNG9mim+RvNUOnxPdpcZWVWxNPd4DfKQ4UYFj76mfUtU XzXfupM7G1iA2qzMr7VZKMAaDSaNaUbqodVU2c3ERqMQMJXVWSbTdN/bbUXWwn/0IYtc JRyICkdTniYCwzJ45jrnxkiqYCaiY1u8keGlvxyOH3IYuIbmC1rdoXDn62i/9Aouyr/A jC3w== X-Gm-Message-State: AOAM531KUN8akVF96gAMdbR8HxGqMO9ErkkWiqMaJJPa93hfoW9cBvOJ FDWyVw50DVBfQfRBuepDS/v4+eliNj8d3AgJYy5zCX5pjVVqji57aAVmkp9FFMTOS1/ObbKa3IN 1xwA2gX70TYslX1CLjeItn8YNq+jASWX6rU3kaUewND0yYLSX2R+HMl8v7rm9vsSqzZ3Nh2K9aF xXyD0= X-Google-Smtp-Source: ABdhPJwSOYxsCuJu+eI/D4v2YiOaRSAA85TXHODDnI4lzTKSwNQCXbdBU48Us6FLFHKVmP1UMoy5dg== X-Received: by 2002:a17:90a:dc0c:: with SMTP id i12mr23529686pjv.191.1602543151562; Mon, 12 Oct 2020 15:52:31 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:30 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 25/31] elx: efct: LIO backend interface routines Date: Mon, 12 Oct 2020 15:51:41 -0700 Message-Id: <20201012225147.54404-26-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: LIO backend template registration and template functions. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke --- v4: Changes to create efct_node(user driver specific node) and store lookup id. Removed debugfs interface. --- drivers/scsi/elx/efct/efct_lio.c | 1697 ++++++++++++++++++++++++++++++ drivers/scsi/elx/efct/efct_lio.h | 189 ++++ 2 files changed, 1886 insertions(+) create mode 100644 drivers/scsi/elx/efct/efct_lio.c create mode 100644 drivers/scsi/elx/efct/efct_lio.h diff --git a/drivers/scsi/elx/efct/efct_lio.c b/drivers/scsi/elx/efct/efct_lio.c new file mode 100644 index 000000000000..a18cb3c61601 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_lio.c @@ -0,0 +1,1697 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#include +#include +#include "efct_driver.h" +#include "efct_lio.h" + +/* + * lio_wq is used to call the LIO backed during creation or deletion of + * sessions. This brings serialization to the session management as we create + * single threaded work queue. + */ +static struct workqueue_struct *lio_wq; + +static int +efct_format_wwn(char *str, size_t len, const char *pre, u64 wwn) +{ + u8 a[8]; + + put_unaligned_be64(wwn, a); + return snprintf(str, len, "%s%8phC", pre, a); +} + +static int +efct_lio_parse_wwn(const char *name, u64 *wwp, u8 npiv) +{ + int num; + u8 b[8]; + + if (npiv) { + num = sscanf(name, + "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], + &b[7]); + } else { + num = sscanf(name, + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &b[0], &b[1], &b[2], &b[3], &b[4], &b[5], &b[6], + &b[7]); + } + + if (num != 8) + return -EINVAL; + + *wwp = get_unaligned_be64(b); + return EFC_SUCCESS; +} + +static int +efct_lio_parse_npiv_wwn(const char *name, size_t size, u64 *wwpn, u64 *wwnn) +{ + unsigned int cnt = size; + int rc; + + *wwpn = *wwnn = 0; + if (name[cnt - 1] == '\n' || name[cnt - 1] == 0) + cnt--; + + /* validate we have enough characters for WWPN */ + if ((cnt != (16 + 1 + 16)) || (name[16] != ':')) + return -EINVAL; + + rc = efct_lio_parse_wwn(&name[0], wwpn, 1); + if (rc) + return rc; + + rc = efct_lio_parse_wwn(&name[17], wwnn, 1); + if (rc) + return rc; + + return EFC_SUCCESS; +} + +static ssize_t +efct_lio_tpg_enable_show(struct config_item *item, char *page) +{ + struct se_portal_group *se_tpg = to_tpg(item); + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return snprintf(page, PAGE_SIZE, "%d\n", atomic_read(&tpg->enabled)); +} + +static ssize_t +efct_lio_tpg_enable_store(struct config_item *item, const char *page, + size_t count) +{ + struct se_portal_group *se_tpg = to_tpg(item); + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + struct efct *efct; + struct efc *efc; + unsigned long op; + + if (!tpg->nport || !tpg->nport->efct) { + pr_err("%s: Unable to find EFCT device\n", __func__); + return -EINVAL; + } + + efct = tpg->nport->efct; + efc = efct->efcport; + + if (kstrtoul(page, 0, &op) < 0) + return -EINVAL; + + if (op == 1) { + int ret; + + atomic_set(&tpg->enabled, 1); + efc_log_debug(efct, "enable portal group %d\n", tpg->tpgt); + + ret = efct_xport_control(efct->xport, EFCT_XPORT_PORT_ONLINE); + if (ret) { + efct->tgt_efct.lio_nport = NULL; + efc_log_test(efct, "cannot bring port online\n"); + return ret; + } + } else if (op == 0) { + efc_log_debug(efct, "disable portal group %d\n", tpg->tpgt); + + if (efc->domain && efc->domain->nport) + efct_scsi_tgt_del_nport(efc, efc->domain->nport); + + atomic_set(&tpg->enabled, 0); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t +efct_lio_npiv_tpg_enable_show(struct config_item *item, char *page) +{ + struct se_portal_group *se_tpg = to_tpg(item); + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return snprintf(page, PAGE_SIZE, "%d\n", atomic_read(&tpg->enabled)); +} + +static ssize_t +efct_lio_npiv_tpg_enable_store(struct config_item *item, const char *page, + size_t count) +{ + struct se_portal_group *se_tpg = to_tpg(item); + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + struct efct_lio_vport *lio_vport = tpg->vport; + struct efct *efct; + struct efc *efc; + unsigned long op; + + if (kstrtoul(page, 0, &op) < 0) + return -EINVAL; + + if (!lio_vport) { + pr_err("Unable to find vport\n"); + return -EINVAL; + } + + efct = lio_vport->efct; + efc = efct->efcport; + + if (op == 1) { + atomic_set(&tpg->enabled, 1); + efc_log_debug(efct, "enable portal group %d\n", tpg->tpgt); + + if (efc->domain) { + int ret; + + ret = efc_nport_vport_new(efc->domain, + lio_vport->npiv_wwpn, + lio_vport->npiv_wwnn, + U32_MAX, false, true, + NULL, NULL); + if (ret != 0) { + efc_log_err(efct, "Failed to create Vport\n"); + return ret; + } + return count; + } + + if (!(efc_vport_create_spec(efc, lio_vport->npiv_wwnn, + lio_vport->npiv_wwpn, U32_MAX, + false, true, NULL, NULL))) + return -ENOMEM; + + } else if (op == 0) { + efc_log_debug(efct, "disable portal group %d\n", tpg->tpgt); + + atomic_set(&tpg->enabled, 0); + /* only physical nport should exist, free lio_nport + * allocated in efct_lio_make_nport + */ + if (efc->domain) { + efc_nport_vport_del(efct->efcport, efc->domain, + lio_vport->npiv_wwpn, + lio_vport->npiv_wwnn); + return count; + } + } else { + return -EINVAL; + } + return count; +} + +static char *efct_lio_get_fabric_wwn(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->nport->wwpn_str; +} + +static char *efct_lio_get_npiv_fabric_wwn(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->vport->wwpn_str; +} + +static u16 efct_lio_get_tag(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->tpgt; +} + +static u16 efct_lio_get_npiv_tag(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->tpgt; +} + +static int efct_lio_check_demo_mode(struct se_portal_group *se_tpg) +{ + return 1; +} + +static int efct_lio_check_demo_mode_cache(struct se_portal_group *se_tpg) +{ + return 1; +} + +static int efct_lio_check_demo_write_protect(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->tpg_attrib.demo_mode_write_protect; +} + +static int +efct_lio_npiv_check_demo_write_protect(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->tpg_attrib.demo_mode_write_protect; +} + +static int efct_lio_check_prod_write_protect(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->tpg_attrib.prod_mode_write_protect; +} + +static int +efct_lio_npiv_check_prod_write_protect(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + return tpg->tpg_attrib.prod_mode_write_protect; +} + +static u32 efct_lio_tpg_get_inst_index(struct se_portal_group *se_tpg) +{ + return EFC_SUCCESS; +} + +static int efct_lio_check_stop_free(struct se_cmd *se_cmd) +{ + struct efct_scsi_tgt_io *ocp = + container_of(se_cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *io = container_of(ocp, struct efct_io, tgt_io); + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_CHK_STOP_FREE); + return target_put_sess_cmd(se_cmd); +} + +static int +efct_lio_abort_tgt_cb(struct efct_io *io, + enum efct_scsi_io_status scsi_status, + u32 flags, void *arg) +{ + efct_lio_io_printf(io, "%s\n", __func__); + return EFC_SUCCESS; +} + +static void +efct_lio_aborted_task(struct se_cmd *se_cmd) +{ + struct efct_scsi_tgt_io *ocp = + container_of(se_cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *io = container_of(ocp, struct efct_io, tgt_io); + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_ABORTED_TASK); + + if (!(se_cmd->transport_state & CMD_T_ABORTED) || ocp->rsp_sent) + return; + + /* command has been aborted, cleanup here */ + ocp->aborting = true; + ocp->err = EFCT_SCSI_STATUS_ABORTED; + /* terminate the exchange */ + efct_scsi_tgt_abort_io(io, efct_lio_abort_tgt_cb, NULL); +} + +static void efct_lio_release_cmd(struct se_cmd *se_cmd) +{ + struct efct_scsi_tgt_io *ocp = + container_of(se_cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *io = container_of(ocp, struct efct_io, tgt_io); + struct efct *efct = io->efct; + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_RELEASE_CMD); + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_CMPL_CMD); + efct_scsi_io_complete(io); + atomic_sub_return(1, &efct->tgt_efct.ios_in_use); +} + +static void efct_lio_close_session(struct se_session *se_sess) +{ + struct efc_node *node = se_sess->fabric_sess_ptr; + struct efct *efct = NULL; + int rc; + + pr_debug("se_sess=%p node=%p", se_sess, node); + + if (!node) { + pr_debug("node is NULL"); + return; + } + + efct = node->efc->base; + rc = efct_xport_control(efct->xport, EFCT_XPORT_POST_NODE_EVENT, node, + EFCT_XPORT_SHUTDOWN); + if (rc != 0) { + efc_log_test(efct, + "Failed to shutdown session %p node %p\n", + se_sess, node); + return; + } +} + +static u32 efct_lio_sess_get_index(struct se_session *se_sess) +{ + return EFC_SUCCESS; +} + +static void efct_lio_set_default_node_attrs(struct se_node_acl *nacl) +{ +} + +static int efct_lio_get_cmd_state(struct se_cmd *se_cmd) +{ + return EFC_SUCCESS; +} + +static int +efct_lio_sg_map(struct efct_io *io) +{ + struct efct_scsi_tgt_io *ocp = &io->tgt_io; + struct se_cmd *cmd = &ocp->cmd; + + ocp->seg_map_cnt = pci_map_sg(io->efct->pci, cmd->t_data_sg, + cmd->t_data_nents, cmd->data_direction); + if (ocp->seg_map_cnt == 0) + return -EFAULT; + return EFC_SUCCESS; +} + +static void +efct_lio_sg_unmap(struct efct_io *io) +{ + struct efct_scsi_tgt_io *ocp = &io->tgt_io; + struct se_cmd *cmd = &ocp->cmd; + + if (WARN_ON(!ocp->seg_map_cnt || !cmd->t_data_sg)) + return; + + pci_unmap_sg(io->efct->pci, cmd->t_data_sg, + ocp->seg_map_cnt, cmd->data_direction); + ocp->seg_map_cnt = 0; +} + +static int +efct_lio_status_done(struct efct_io *io, + enum efct_scsi_io_status scsi_status, + u32 flags, void *arg) +{ + struct efct_scsi_tgt_io *ocp = &io->tgt_io; + + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_RSP_DONE); + if (scsi_status != EFCT_SCSI_STATUS_GOOD) { + efct_lio_io_printf(io, "callback completed with error=%d\n", + scsi_status); + ocp->err = scsi_status; + } + if (ocp->seg_map_cnt) + efct_lio_sg_unmap(io); + + efct_lio_io_printf(io, "status=%d, err=%d flags=0x%x, dir=%d\n", + scsi_status, ocp->err, flags, ocp->ddir); + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE); + transport_generic_free_cmd(&io->tgt_io.cmd, 0); + return EFC_SUCCESS; +} + +static int +efct_lio_datamove_done(struct efct_io *io, enum efct_scsi_io_status scsi_status, + u32 flags, void *arg); + +static int +efct_lio_write_pending(struct se_cmd *cmd) +{ + struct efct_scsi_tgt_io *ocp = + container_of(cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *io = container_of(ocp, struct efct_io, tgt_io); + struct efct_scsi_sgl *sgl = io->sgl; + struct scatterlist *sg; + u32 flags = 0, cnt, curcnt; + u64 length = 0; + int rc = 0; + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_WRITE_PENDING); + efct_lio_io_printf(io, "trans_state=0x%x se_cmd_flags=0x%x\n", + cmd->transport_state, cmd->se_cmd_flags); + + if (ocp->seg_cnt == 0) { + ocp->seg_cnt = cmd->t_data_nents; + ocp->cur_seg = 0; + if (efct_lio_sg_map(io)) { + efct_lio_io_printf(io, "efct_lio_sg_map failed\n"); + return -EFAULT; + } + } + curcnt = (ocp->seg_map_cnt - ocp->cur_seg); + curcnt = (curcnt < io->sgl_allocated) ? curcnt : io->sgl_allocated; + /* find current sg */ + for (cnt = 0, sg = cmd->t_data_sg; cnt < ocp->cur_seg; cnt++, + sg = sg_next(sg)) + ;/* do nothing */ + + for (cnt = 0; cnt < curcnt; cnt++, sg = sg_next(sg)) { + sgl[cnt].addr = sg_dma_address(sg); + sgl[cnt].dif_addr = 0; + sgl[cnt].len = sg_dma_len(sg); + length += sgl[cnt].len; + ocp->cur_seg++; + } + if (ocp->cur_seg == ocp->seg_cnt) + flags = EFCT_SCSI_LAST_DATAPHASE; + rc = efct_scsi_recv_wr_data(io, flags, sgl, curcnt, length, + efct_lio_datamove_done, NULL); + return rc; +} + +static int +efct_lio_queue_data_in(struct se_cmd *cmd) +{ + struct efct_scsi_tgt_io *ocp = + container_of(cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *io = container_of(ocp, struct efct_io, tgt_io); + struct efct_scsi_sgl *sgl = io->sgl; + struct scatterlist *sg = NULL; + uint flags = 0, cnt = 0, curcnt = 0; + u64 length = 0; + int rc = 0; + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_QUEUE_DATA_IN); + + if (ocp->seg_cnt == 0) { + if (cmd->data_length) { + ocp->seg_cnt = cmd->t_data_nents; + ocp->cur_seg = 0; + if (efct_lio_sg_map(io)) { + efct_lio_io_printf(io, + "efct_lio_sg_map failed\n"); + return -EAGAIN; + } + } else { + /* If command length is 0, send the response status */ + struct efct_scsi_cmd_resp rsp; + + memset(&rsp, 0, sizeof(rsp)); + efct_lio_io_printf(io, + "cmd : %p length 0, send status\n", + cmd); + return efct_scsi_send_resp(io, 0, &rsp, + efct_lio_status_done, NULL); + } + } + curcnt = min(ocp->seg_map_cnt - ocp->cur_seg, io->sgl_allocated); + + while (cnt < curcnt) { + sg = &cmd->t_data_sg[ocp->cur_seg]; + sgl[cnt].addr = sg_dma_address(sg); + sgl[cnt].dif_addr = 0; + if (ocp->transferred_len + sg_dma_len(sg) >= cmd->data_length) + sgl[cnt].len = cmd->data_length - ocp->transferred_len; + else + sgl[cnt].len = sg_dma_len(sg); + + ocp->transferred_len += sgl[cnt].len; + length += sgl[cnt].len; + ocp->cur_seg++; + cnt++; + if (ocp->transferred_len == cmd->data_length) + break; + } + + if (ocp->transferred_len == cmd->data_length) { + flags = EFCT_SCSI_LAST_DATAPHASE; + ocp->seg_cnt = ocp->cur_seg; + } + + /* If there is residual, disable Auto Good Response */ + if (cmd->residual_count) + flags |= EFCT_SCSI_NO_AUTO_RESPONSE; + + rc = efct_scsi_send_rd_data(io, flags, sgl, curcnt, length, + efct_lio_datamove_done, NULL); + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_SEND_RD_DATA); + return rc; +} + +static void +efct_lio_send_resp(struct efct_io *io, enum efct_scsi_io_status scsi_status, + u32 flags) +{ + struct efct_scsi_cmd_resp rsp; + struct efct_scsi_tgt_io *ocp = &io->tgt_io; + struct se_cmd *cmd = &io->tgt_io.cmd; + int rc; + + if (flags & EFCT_SCSI_IO_CMPL_RSP_SENT) { + ocp->rsp_sent = true; + efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE); + transport_generic_free_cmd(&io->tgt_io.cmd, 0); + return; + } + + /* send check condition if an error occurred */ + memset(&rsp, 0, sizeof(rsp)); + rsp.scsi_status = cmd->scsi_status; + rsp.sense_data = (uint8_t *)io->tgt_io.sense_buffer; + rsp.sense_data_length = cmd->scsi_sense_length; + + /* Check for residual underrun or overrun */ + if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) + rsp.residual = -cmd->residual_count; + else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) + rsp.residual = cmd->residual_count; + + rc = efct_scsi_send_resp(io, 0, &rsp, efct_lio_status_done, NULL); + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_SEND_RSP); + if (rc != 0) { + efct_lio_io_printf(io, "Read done, send rsp failed %d\n", rc); + efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE); + transport_generic_free_cmd(&io->tgt_io.cmd, 0); + } else { + ocp->rsp_sent = true; + } +} + +static int +efct_lio_datamove_done(struct efct_io *io, enum efct_scsi_io_status scsi_status, + u32 flags, void *arg) +{ + struct efct_scsi_tgt_io *ocp = &io->tgt_io; + + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_DATA_DONE); + if (scsi_status != EFCT_SCSI_STATUS_GOOD) { + efct_lio_io_printf(io, "callback completed with error=%d\n", + scsi_status); + ocp->err = scsi_status; + } + efct_lio_io_printf(io, "seg_map_cnt=%d\n", ocp->seg_map_cnt); + if (ocp->seg_map_cnt) { + if (ocp->err == EFCT_SCSI_STATUS_GOOD && + ocp->cur_seg < ocp->seg_cnt) { + int rc; + + efct_lio_io_printf(io, "continuing cmd at segm=%d\n", + ocp->cur_seg); + if (ocp->ddir == DMA_TO_DEVICE) + rc = efct_lio_write_pending(&ocp->cmd); + else + rc = efct_lio_queue_data_in(&ocp->cmd); + if (!rc) + return EFC_SUCCESS; + + ocp->err = EFCT_SCSI_STATUS_ERROR; + efct_lio_io_printf(io, "could not continue command\n"); + } + efct_lio_sg_unmap(io); + } + + if (io->tgt_io.aborting) { + efct_lio_io_printf(io, "IO done aborted\n"); + return EFC_SUCCESS; + } + + if (ocp->ddir == DMA_TO_DEVICE) { + efct_lio_io_printf(io, "Write done, trans_state=0x%x\n", + io->tgt_io.cmd.transport_state); + if (scsi_status != EFCT_SCSI_STATUS_GOOD) { + transport_generic_request_failure(&io->tgt_io.cmd, + TCM_CHECK_CONDITION_ABORT_CMD); + efct_set_lio_io_state(io, + EFCT_LIO_STATE_TGT_GENERIC_REQ_FAILURE); + } else { + efct_set_lio_io_state(io, + EFCT_LIO_STATE_TGT_EXECUTE_CMD); + target_execute_cmd(&io->tgt_io.cmd); + } + } else { + efct_lio_send_resp(io, scsi_status, flags); + } + return EFC_SUCCESS; +} + +static int +efct_lio_tmf_done(struct efct_io *io, enum efct_scsi_io_status scsi_status, + u32 flags, void *arg) +{ + efct_lio_tmfio_printf(io, "cmd=%p status=%d, flags=0x%x\n", + &io->tgt_io.cmd, scsi_status, flags); + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_GENERIC_FREE); + transport_generic_free_cmd(&io->tgt_io.cmd, 0); + return EFC_SUCCESS; +} + +static int +efct_lio_null_tmf_done(struct efct_io *tmfio, + enum efct_scsi_io_status scsi_status, + u32 flags, void *arg) +{ + efct_lio_tmfio_printf(tmfio, "cmd=%p status=%d, flags=0x%x\n", + &tmfio->tgt_io.cmd, scsi_status, flags); + + /* free struct efct_io only, no active se_cmd */ + efct_scsi_io_complete(tmfio); + return EFC_SUCCESS; +} + +static int +efct_lio_queue_status(struct se_cmd *cmd) +{ + struct efct_scsi_cmd_resp rsp; + struct efct_scsi_tgt_io *ocp = + container_of(cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *io = container_of(ocp, struct efct_io, tgt_io); + int rc = 0; + + efct_set_lio_io_state(io, EFCT_LIO_STATE_TFO_QUEUE_STATUS); + efct_lio_io_printf(io, + "status=0x%x trans_state=0x%x se_cmd_flags=0x%x sns_len=%d\n", + cmd->scsi_status, cmd->transport_state, cmd->se_cmd_flags, + cmd->scsi_sense_length); + + memset(&rsp, 0, sizeof(rsp)); + rsp.scsi_status = cmd->scsi_status; + rsp.sense_data = (u8 *)io->tgt_io.sense_buffer; + rsp.sense_data_length = cmd->scsi_sense_length; + + /* Check for residual underrun or overrun, mark negitive value for + * underrun to recognize in HW + */ + if (cmd->se_cmd_flags & SCF_OVERFLOW_BIT) + rsp.residual = -cmd->residual_count; + else if (cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) + rsp.residual = cmd->residual_count; + + rc = efct_scsi_send_resp(io, 0, &rsp, efct_lio_status_done, NULL); + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_SEND_RSP); + if (rc == 0) + ocp->rsp_sent = true; + return rc; +} + +static void efct_lio_queue_tm_rsp(struct se_cmd *cmd) +{ + struct efct_scsi_tgt_io *ocp = + container_of(cmd, struct efct_scsi_tgt_io, cmd); + struct efct_io *tmfio = container_of(ocp, struct efct_io, tgt_io); + struct se_tmr_req *se_tmr = cmd->se_tmr_req; + u8 rspcode; + + efct_lio_tmfio_printf(tmfio, "cmd=%p function=0x%x tmr->response=%d\n", + cmd, se_tmr->function, se_tmr->response); + switch (se_tmr->response) { + case TMR_FUNCTION_COMPLETE: + rspcode = EFCT_SCSI_TMF_FUNCTION_COMPLETE; + break; + case TMR_TASK_DOES_NOT_EXIST: + rspcode = EFCT_SCSI_TMF_FUNCTION_IO_NOT_FOUND; + break; + case TMR_LUN_DOES_NOT_EXIST: + rspcode = EFCT_SCSI_TMF_INCORRECT_LOGICAL_UNIT_NUMBER; + break; + case TMR_FUNCTION_REJECTED: + default: + rspcode = EFCT_SCSI_TMF_FUNCTION_REJECTED; + break; + } + efct_scsi_send_tmf_resp(tmfio, rspcode, NULL, efct_lio_tmf_done, NULL); +} + +static struct efct *efct_find_wwpn(u64 wwpn) +{ + int efctidx; + struct efct *efct; + + /* Search for the HBA that has this WWPN */ + for (efctidx = 0; efctidx < MAX_EFCT_DEVICES; efctidx++) { + + efct = efct_devices[efctidx]; + if (!efct) + continue; + + if (wwpn == efct_get_wwpn(&efct->hw)) + break; + } + + if (efctidx == MAX_EFCT_DEVICES) + return NULL; + + return efct_devices[efctidx]; +} + +static struct se_wwn * +efct_lio_make_nport(struct target_fabric_configfs *tf, + struct config_group *group, const char *name) +{ + struct efct_lio_nport *lio_nport; + struct efct *efct; + int ret; + u64 wwpn; + + ret = efct_lio_parse_wwn(name, &wwpn, 0); + if (ret) + return ERR_PTR(ret); + + efct = efct_find_wwpn(wwpn); + if (!efct) { + pr_err("cannot find EFCT for base wwpn %s\n", name); + return ERR_PTR(-ENXIO); + } + + lio_nport = kzalloc(sizeof(*lio_nport), GFP_KERNEL); + if (!lio_nport) + return ERR_PTR(-ENOMEM); + + lio_nport->efct = efct; + lio_nport->wwpn = wwpn; + efct_format_wwn(lio_nport->wwpn_str, sizeof(lio_nport->wwpn_str), + "naa.", wwpn); + efct->tgt_efct.lio_nport = lio_nport; + + return &lio_nport->nport_wwn; +} + +static struct se_wwn * +efct_lio_npiv_make_nport(struct target_fabric_configfs *tf, + struct config_group *group, const char *name) +{ + struct efct_lio_vport *lio_vport; + struct efct *efct; + int ret = -1; + u64 p_wwpn, npiv_wwpn, npiv_wwnn; + char *p, *pbuf, tmp[128]; + struct efct_lio_vport_list_t *vport_list; + struct fc_vport *new_fc_vport; + struct fc_vport_identifiers vport_id; + unsigned long flags = 0; + + snprintf(tmp, sizeof(tmp), "%s", name); + pbuf = &tmp[0]; + + p = strsep(&pbuf, "@"); + + if (!p || !pbuf) { + pr_err("Unable to find separator operator(@)\n"); + return ERR_PTR(-EINVAL); + } + + ret = efct_lio_parse_wwn(p, &p_wwpn, 0); + if (ret) + return ERR_PTR(ret); + + ret = efct_lio_parse_npiv_wwn(pbuf, strlen(pbuf), &npiv_wwpn, + &npiv_wwnn); + if (ret) + return ERR_PTR(ret); + + efct = efct_find_wwpn(p_wwpn); + if (!efct) { + pr_err("cannot find EFCT for base wwpn %s\n", name); + return ERR_PTR(-ENXIO); + } + + lio_vport = kzalloc(sizeof(*lio_vport), GFP_KERNEL); + if (!lio_vport) + return ERR_PTR(-ENOMEM); + + lio_vport->efct = efct; + lio_vport->wwpn = p_wwpn; + lio_vport->npiv_wwpn = npiv_wwpn; + lio_vport->npiv_wwnn = npiv_wwnn; + + efct_format_wwn(lio_vport->wwpn_str, sizeof(lio_vport->wwpn_str), + "naa.", npiv_wwpn); + + vport_list = kzalloc(sizeof(*vport_list), GFP_KERNEL); + if (!vport_list) { + kfree(lio_vport); + return ERR_PTR(-ENOMEM); + } + + vport_list->lio_vport = lio_vport; + spin_lock_irqsave(&efct->tgt_efct.efct_lio_lock, flags); + INIT_LIST_HEAD(&vport_list->list_entry); + list_add_tail(&vport_list->list_entry, &efct->tgt_efct.vport_list); + spin_unlock_irqrestore(&efct->tgt_efct.efct_lio_lock, flags); + + memset(&vport_id, 0, sizeof(vport_id)); + vport_id.port_name = npiv_wwpn; + vport_id.node_name = npiv_wwnn; + vport_id.roles = FC_PORT_ROLE_FCP_INITIATOR; + vport_id.vport_type = FC_PORTTYPE_NPIV; + vport_id.disable = false; + + new_fc_vport = fc_vport_create(efct->shost, 0, &vport_id); + if (!new_fc_vport) { + efc_log_err(efct, "fc_vport_create failed\n"); + kfree(lio_vport); + kfree(vport_list); + return ERR_PTR(-ENOMEM); + } + + lio_vport->fc_vport = new_fc_vport; + + return &lio_vport->vport_wwn; +} + +static void +efct_lio_drop_nport(struct se_wwn *wwn) +{ + struct efct_lio_nport *lio_nport = + container_of(wwn, struct efct_lio_nport, nport_wwn); + struct efct *efct = lio_nport->efct; + + /* only physical nport should exist, free lio_nport allocated + * in efct_lio_make_nport. + */ + kfree(efct->tgt_efct.lio_nport); + efct->tgt_efct.lio_nport = NULL; +} + +static void +efct_lio_npiv_drop_nport(struct se_wwn *wwn) +{ + struct efct_lio_vport *lio_vport = + container_of(wwn, struct efct_lio_vport, vport_wwn); + struct efct_lio_vport_list_t *vport, *next_vport; + struct efct *efct = lio_vport->efct; + unsigned long flags = 0; + + spin_lock_irqsave(&efct->tgt_efct.efct_lio_lock, flags); + + if (lio_vport->fc_vport) + fc_vport_terminate(lio_vport->fc_vport); + + list_for_each_entry_safe(vport, next_vport, &efct->tgt_efct.vport_list, + list_entry) { + if (vport->lio_vport == lio_vport) { + list_del(&vport->list_entry); + kfree(vport->lio_vport); + kfree(vport); + break; + } + } + spin_unlock_irqrestore(&efct->tgt_efct.efct_lio_lock, flags); +} + +static struct se_portal_group * +efct_lio_make_tpg(struct se_wwn *wwn, const char *name) +{ + struct efct_lio_nport *lio_nport = + container_of(wwn, struct efct_lio_nport, nport_wwn); + struct efct_lio_tpg *tpg; + struct efct *efct; + unsigned long n; + int ret; + + if (strstr(name, "tpgt_") != name) + return ERR_PTR(-EINVAL); + if (kstrtoul(name + 5, 10, &n) || n > USHRT_MAX) + return ERR_PTR(-EINVAL); + + tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); + if (!tpg) + return ERR_PTR(-ENOMEM); + + tpg->nport = lio_nport; + tpg->tpgt = n; + atomic_set(&tpg->enabled, 0); + + tpg->tpg_attrib.generate_node_acls = 1; + tpg->tpg_attrib.demo_mode_write_protect = 1; + tpg->tpg_attrib.cache_dynamic_acls = 1; + tpg->tpg_attrib.demo_mode_login_only = 1; + tpg->tpg_attrib.session_deletion_wait = 1; + + ret = core_tpg_register(wwn, &tpg->tpg, SCSI_PROTOCOL_FCP); + if (ret < 0) { + kfree(tpg); + return NULL; + } + efct = lio_nport->efct; + efct->tgt_efct.tpg = tpg; + efc_log_debug(efct, "create portal group %d\n", tpg->tpgt); + + xa_init(&efct->lookup); + return &tpg->tpg; +} + +static void +efct_lio_drop_tpg(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + struct efct *efct = tpg->nport->efct; + + efc_log_debug(efct, "drop portal group %d\n", tpg->tpgt); + tpg->nport->efct->tgt_efct.tpg = NULL; + core_tpg_deregister(se_tpg); + xa_destroy(&efct->lookup); + kfree(tpg); +} + +static struct se_portal_group * +efct_lio_npiv_make_tpg(struct se_wwn *wwn, const char *name) +{ + struct efct_lio_vport *lio_vport = + container_of(wwn, struct efct_lio_vport, vport_wwn); + struct efct_lio_tpg *tpg; + struct efct *efct; + unsigned long n; + int ret; + + efct = lio_vport->efct; + if (strstr(name, "tpgt_") != name) + return ERR_PTR(-EINVAL); + if (kstrtoul(name + 5, 10, &n) || n > USHRT_MAX) + return ERR_PTR(-EINVAL); + + if (n != 1) { + efc_log_err(efct, "Invalid tpgt index: %ld provided\n", n); + return ERR_PTR(-EINVAL); + } + + tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); + if (!tpg) + return ERR_PTR(-ENOMEM); + + tpg->vport = lio_vport; + tpg->tpgt = n; + atomic_set(&tpg->enabled, 0); + + tpg->tpg_attrib.generate_node_acls = 1; + tpg->tpg_attrib.demo_mode_write_protect = 1; + tpg->tpg_attrib.cache_dynamic_acls = 1; + tpg->tpg_attrib.demo_mode_login_only = 1; + tpg->tpg_attrib.session_deletion_wait = 1; + + ret = core_tpg_register(wwn, &tpg->tpg, SCSI_PROTOCOL_FCP); + + if (ret < 0) { + kfree(tpg); + return NULL; + } + lio_vport->tpg = tpg; + efc_log_debug(efct, "create vport portal group %d\n", tpg->tpgt); + + return &tpg->tpg; +} + +static void +efct_lio_npiv_drop_tpg(struct se_portal_group *se_tpg) +{ + struct efct_lio_tpg *tpg = + container_of(se_tpg, struct efct_lio_tpg, tpg); + + efc_log_debug(tpg->vport->efct, "drop npiv portal group %d\n", + tpg->tpgt); + core_tpg_deregister(se_tpg); + kfree(tpg); +} + +static int +efct_lio_init_nodeacl(struct se_node_acl *se_nacl, const char *name) +{ + struct efct_lio_nacl *nacl; + u64 wwnn; + + if (efct_lio_parse_wwn(name, &wwnn, 0) < 0) + return -EINVAL; + + nacl = container_of(se_nacl, struct efct_lio_nacl, se_node_acl); + nacl->nport_wwnn = wwnn; + + efct_format_wwn(nacl->nport_name, sizeof(nacl->nport_name), "", wwnn); + return EFC_SUCCESS; +} + +static int efct_lio_check_demo_mode_login_only(struct se_portal_group *stpg) +{ + struct efct_lio_tpg *tpg = container_of(stpg, struct efct_lio_tpg, tpg); + + return tpg->tpg_attrib.demo_mode_login_only; +} + +static int +efct_lio_npiv_check_demo_mode_login_only(struct se_portal_group *stpg) +{ + struct efct_lio_tpg *tpg = container_of(stpg, struct efct_lio_tpg, tpg); + + return tpg->tpg_attrib.demo_mode_login_only; +} + +static struct efct_lio_tpg * +efct_get_vport_tpg(struct efc_node *node) +{ + struct efct *efct; + u64 wwpn = node->nport->wwpn; + struct efct_lio_vport_list_t *vport, *next; + struct efct_lio_vport *lio_vport = NULL; + struct efct_lio_tpg *tpg = NULL; + unsigned long flags = 0; + + efct = node->efc->base; + spin_lock_irqsave(&efct->tgt_efct.efct_lio_lock, flags); + list_for_each_entry_safe(vport, next, &efct->tgt_efct.vport_list, + list_entry) { + lio_vport = vport->lio_vport; + if (wwpn && lio_vport && lio_vport->npiv_wwpn == wwpn) { + efc_log_test(efct, "found tpg on vport\n"); + tpg = lio_vport->tpg; + break; + } + } + spin_unlock_irqrestore(&efct->tgt_efct.efct_lio_lock, flags); + return tpg; +} + +static void +_efct_tgt_node_free(struct kref *arg) +{ + struct efct_node *tgt_node = container_of(arg, struct efct_node, ref); + struct efc_node *node = tgt_node->node; + + efc_scsi_del_initiator_complete(node->efc, node); + kfree(tgt_node); +} + +static int efct_session_cb(struct se_portal_group *se_tpg, + struct se_session *se_sess, void *private) +{ + struct efc_node *node = private; + struct efct_node *tgt_node = NULL; + struct efct *efct = node->efc->base; + u64 id; + u32 rc; + + tgt_node = kzalloc(sizeof(*tgt_node), GFP_KERNEL); + if (!tgt_node) + return EFC_FAIL; + + /* initialize refcount */ + kref_init(&tgt_node->ref); + tgt_node->release = _efct_tgt_node_free; + + tgt_node->session = se_sess; + node->tgt_node = tgt_node; + tgt_node->efct = efct; + + tgt_node->node = node; + + tgt_node->node_fc_id = node->rnode.fc_id; + tgt_node->port_fc_id = node->nport->fc_id; + tgt_node->vpi = node->nport->indicator; + tgt_node->rpi = node->rnode.indicator; + + spin_lock_init(&tgt_node->active_ios_lock); + INIT_LIST_HEAD(&tgt_node->active_ios); + + id = (u64) tgt_node->port_fc_id << 32 | tgt_node->node_fc_id; + efc_log_err(efct, "New Node id: %llx node:%p\n", id, tgt_node); + rc = xa_err(xa_store(&efct->lookup, id, tgt_node, GFP_KERNEL)); + if (rc) + efc_log_err(efct, "Node lookup store failed: %d\n", rc); + + efc_scsi_sess_reg_complete(node, EFC_SUCCESS); + return EFC_SUCCESS; +} + +int efct_scsi_tgt_new_device(struct efct *efct) +{ + int rc = 0; + u32 total_ios; + + /* Get the max settings */ + efct->tgt_efct.max_sge = sli_get_max_sge(&efct->hw.sli); + efct->tgt_efct.max_sgl = sli_get_max_sgl(&efct->hw.sli); + + /* initialize IO watermark fields */ + atomic_set(&efct->tgt_efct.ios_in_use, 0); + total_ios = efct->hw.config.n_io; + efc_log_debug(efct, "total_ios=%d\n", total_ios); + efct->tgt_efct.watermark_min = + (total_ios * EFCT_WATERMARK_LOW_PCT) / 100; + efct->tgt_efct.watermark_max = + (total_ios * EFCT_WATERMARK_HIGH_PCT) / 100; + atomic_set(&efct->tgt_efct.io_high_watermark, + efct->tgt_efct.watermark_max); + atomic_set(&efct->tgt_efct.watermark_hit, 0); + atomic_set(&efct->tgt_efct.initiator_count, 0); + + lio_wq = create_singlethread_workqueue("efct_lio_worker"); + if (!lio_wq) { + efc_log_err(efct, "workqueue create failed\n"); + return -ENOMEM; + } + + spin_lock_init(&efct->tgt_efct.efct_lio_lock); + INIT_LIST_HEAD(&efct->tgt_efct.vport_list); + + return rc; +} + +int efct_scsi_tgt_del_device(struct efct *efct) +{ + flush_workqueue(lio_wq); + + return EFC_SUCCESS; +} + +int +efct_scsi_tgt_new_nport(struct efc *efc, struct efc_nport *nport) +{ + struct efct *efct = nport->efc->base; + + efc_log_debug(efct, "New SPORT: %s bound to %s\n", nport->display_name, + efct->tgt_efct.lio_nport->wwpn_str); + + return EFC_SUCCESS; +} + +void +efct_scsi_tgt_del_nport(struct efc *efc, struct efc_nport *nport) +{ + efc_log_debug(efc, "Del SPORT: %s\n", nport->display_name); +} + +static void efct_lio_setup_session(struct work_struct *work) +{ + struct efct_lio_wq_data *wq_data = + container_of(work, struct efct_lio_wq_data, work); + struct efct *efct = wq_data->efct; + struct efc_node *node = wq_data->ptr; + char wwpn[WWN_NAME_LEN]; + struct efct_lio_tpg *tpg = NULL; + struct se_portal_group *se_tpg; + struct se_session *se_sess; + int watermark; + int ini_count; + + /* Check to see if it's belongs to vport, + * if not get physical port + */ + tpg = efct_get_vport_tpg(node); + if (tpg) { + se_tpg = &tpg->tpg; + } else if (efct->tgt_efct.tpg) { + tpg = efct->tgt_efct.tpg; + se_tpg = &tpg->tpg; + } else { + efc_log_err(efct, "failed to init session\n"); + return; + } + + /* + * Format the FCP Initiator port_name into colon + * separated values to match the format by our explicit + * ConfigFS NodeACLs. + */ + efct_format_wwn(wwpn, sizeof(wwpn), "", efc_node_get_wwpn(node)); + + se_sess = target_setup_session(se_tpg, 0, 0, TARGET_PROT_NORMAL, wwpn, + node, efct_session_cb); + if (IS_ERR(se_sess)) { + efc_log_err(efct, "failed to setup session\n"); + kfree(wq_data); + efc_scsi_sess_reg_complete(node, EFC_FAIL); + return; + } + + efc_log_debug(efct, "new initiator sess=%p node=%p\n", se_sess, node); + + /* update IO watermark: increment initiator count */ + ini_count = atomic_add_return(1, &efct->tgt_efct.initiator_count); + watermark = efct->tgt_efct.watermark_max - + ini_count * EFCT_IO_WATERMARK_PER_INITIATOR; + watermark = (efct->tgt_efct.watermark_min > watermark) ? + efct->tgt_efct.watermark_min : watermark; + atomic_set(&efct->tgt_efct.io_high_watermark, watermark); + + kfree(wq_data); +} + +int efct_scsi_new_initiator(struct efc *efc, struct efc_node *node) +{ + struct efct *efct = node->efc->base; + struct efct_lio_wq_data *wq_data; + + /* + * Since LIO only supports initiator validation at thread level, + * we are open minded and accept all callers. + */ + wq_data = kzalloc(sizeof(*wq_data), GFP_ATOMIC); + if (!wq_data) + return -ENOMEM; + + wq_data->ptr = node; + wq_data->efct = efct; + INIT_WORK(&wq_data->work, efct_lio_setup_session); + queue_work(lio_wq, &wq_data->work); + return EFCT_SCSI_CALL_ASYNC; +} + +static void efct_lio_remove_session(struct work_struct *work) +{ + struct efct_lio_wq_data *wq_data = + container_of(work, struct efct_lio_wq_data, work); + struct efct *efct = wq_data->efct; + struct efc_node *node = wq_data->ptr; + struct efct_node *tgt_node = NULL; + struct se_session *se_sess; + + tgt_node = node->tgt_node; + se_sess = tgt_node->session; + + if (!se_sess) { + /* base driver has sent back-to-back requests + * to unreg session with no intervening + * register + */ + efc_log_err(efct, "unreg session for NULL session\n"); + efc_scsi_del_initiator_complete(node->efc, node); + return; + } + + efc_log_debug(efct, "unreg session se_sess=%p node=%p\n", + se_sess, node); + + /* first flag all session commands to complete */ + target_sess_cmd_list_set_waiting(se_sess); + + /* now wait for session commands to complete */ + target_wait_for_sess_cmds(se_sess); + target_remove_session(se_sess); + + kref_put(&tgt_node->ref, tgt_node->release); + + kfree(wq_data); +} + +int efct_scsi_del_initiator(struct efc *efc, struct efc_node *node, int reason) +{ + struct efct *efct = node->efc->base; + struct efct_node *tgt_node = node->tgt_node; + struct efct_lio_wq_data *wq_data; + int watermark; + int ini_count; + u64 id; + + if (reason == EFCT_SCSI_INITIATOR_MISSING) + return EFCT_SCSI_CALL_COMPLETE; + + wq_data = kzalloc(sizeof(*wq_data), GFP_ATOMIC); + if (!wq_data) + return EFCT_SCSI_CALL_COMPLETE; + + id = (u64) tgt_node->port_fc_id << 32 | tgt_node->node_fc_id; + xa_erase(&efct->lookup, id); + + wq_data->ptr = node; + wq_data->efct = efct; + INIT_WORK(&wq_data->work, efct_lio_remove_session); + queue_work(lio_wq, &wq_data->work); + + /* + * update IO watermark: decrement initiator count + */ + ini_count = atomic_sub_return(1, &efct->tgt_efct.initiator_count); + + watermark = efct->tgt_efct.watermark_max - + ini_count * EFCT_IO_WATERMARK_PER_INITIATOR; + watermark = (efct->tgt_efct.watermark_min > watermark) ? + efct->tgt_efct.watermark_min : watermark; + atomic_set(&efct->tgt_efct.io_high_watermark, watermark); + + return EFCT_SCSI_CALL_ASYNC; +} + +int efct_scsi_recv_cmd(struct efct_io *io, uint64_t lun, u8 *cdb, + u32 cdb_len, u32 flags) +{ + struct efct_scsi_tgt_io *ocp = &io->tgt_io; + struct efct *efct = io->efct; + char *ddir; + struct efct_node *tgt_node = NULL; + struct se_session *se_sess; + int rc = 0; + + memset(ocp, 0, sizeof(struct efct_scsi_tgt_io)); + efct_set_lio_io_state(io, EFCT_LIO_STATE_SCSI_RECV_CMD); + atomic_add_return(1, &efct->tgt_efct.ios_in_use); + + /* set target timeout */ + io->timeout = efct->target_io_timer_sec; + + if (flags & EFCT_SCSI_CMD_SIMPLE) + ocp->task_attr = TCM_SIMPLE_TAG; + else if (flags & EFCT_SCSI_CMD_HEAD_OF_QUEUE) + ocp->task_attr = TCM_HEAD_TAG; + else if (flags & EFCT_SCSI_CMD_ORDERED) + ocp->task_attr = TCM_ORDERED_TAG; + else if (flags & EFCT_SCSI_CMD_ACA) + ocp->task_attr = TCM_ACA_TAG; + + switch (flags & (EFCT_SCSI_CMD_DIR_IN | EFCT_SCSI_CMD_DIR_OUT)) { + case EFCT_SCSI_CMD_DIR_IN: + ddir = "FROM_INITIATOR"; + ocp->ddir = DMA_TO_DEVICE; + break; + case EFCT_SCSI_CMD_DIR_OUT: + ddir = "TO_INITIATOR"; + ocp->ddir = DMA_FROM_DEVICE; + break; + case EFCT_SCSI_CMD_DIR_IN | EFCT_SCSI_CMD_DIR_OUT: + ddir = "BIDIR"; + ocp->ddir = DMA_BIDIRECTIONAL; + break; + default: + ddir = "NONE"; + ocp->ddir = DMA_NONE; + break; + } + + ocp->lun = lun; + efct_lio_io_printf(io, "new cmd=0x%x ddir=%s dl=%u\n", + cdb[0], ddir, io->exp_xfer_len); + + tgt_node = io->node; + se_sess = tgt_node->session; + if (se_sess) { + efct_set_lio_io_state(io, EFCT_LIO_STATE_TGT_SUBMIT_CMD); + rc = target_submit_cmd(&io->tgt_io.cmd, se_sess, + cdb, &io->tgt_io.sense_buffer[0], + ocp->lun, io->exp_xfer_len, + ocp->task_attr, ocp->ddir, + TARGET_SCF_ACK_KREF); + if (rc) { + efc_log_err(efct, "failed to submit cmd se_cmd: %p\n", + &ocp->cmd); + efct_scsi_io_free(io); + } + } + + return rc; +} + +int +efct_scsi_recv_tmf(struct efct_io *tmfio, u32 lun, enum efct_scsi_tmf_cmd cmd, + struct efct_io *io_to_abort, u32 flags) +{ + unsigned char tmr_func; + struct efct *efct = tmfio->efct; + struct efct_scsi_tgt_io *ocp = &tmfio->tgt_io; + struct efct_node *tgt_node = NULL; + struct se_session *se_sess; + int rc; + + memset(ocp, 0, sizeof(struct efct_scsi_tgt_io)); + efct_set_lio_io_state(tmfio, EFCT_LIO_STATE_SCSI_RECV_TMF); + atomic_add_return(1, &efct->tgt_efct.ios_in_use); + efct_lio_tmfio_printf(tmfio, "%s: new tmf %x lun=%u\n", + tmfio->display_name, cmd, lun); + + switch (cmd) { + case EFCT_SCSI_TMF_ABORT_TASK: + tmr_func = TMR_ABORT_TASK; + break; + case EFCT_SCSI_TMF_ABORT_TASK_SET: + tmr_func = TMR_ABORT_TASK_SET; + break; + case EFCT_SCSI_TMF_CLEAR_TASK_SET: + tmr_func = TMR_CLEAR_TASK_SET; + break; + case EFCT_SCSI_TMF_LOGICAL_UNIT_RESET: + tmr_func = TMR_LUN_RESET; + break; + case EFCT_SCSI_TMF_CLEAR_ACA: + tmr_func = TMR_CLEAR_ACA; + break; + case EFCT_SCSI_TMF_TARGET_RESET: + tmr_func = TMR_TARGET_WARM_RESET; + break; + case EFCT_SCSI_TMF_QUERY_ASYNCHRONOUS_EVENT: + case EFCT_SCSI_TMF_QUERY_TASK_SET: + default: + goto tmf_fail; + } + + tmfio->tgt_io.tmf = tmr_func; + tmfio->tgt_io.lun = lun; + tmfio->tgt_io.io_to_abort = io_to_abort; + + tgt_node = tmfio->node; + + se_sess = tgt_node->session; + if (!se_sess) + return EFC_SUCCESS; + + rc = target_submit_tmr(&ocp->cmd, se_sess, NULL, lun, ocp, tmr_func, + GFP_ATOMIC, tmfio->init_task_tag, TARGET_SCF_ACK_KREF); + + efct_set_lio_io_state(tmfio, EFCT_LIO_STATE_TGT_SUBMIT_TMR); + if (rc) + goto tmf_fail; + + return EFC_SUCCESS; + +tmf_fail: + efct_scsi_send_tmf_resp(tmfio, EFCT_SCSI_TMF_FUNCTION_REJECTED, + NULL, efct_lio_null_tmf_done, NULL); + return EFC_SUCCESS; +} + +/* Start items for efct_lio_tpg_attrib_cit */ + +#define DEF_EFCT_TPG_ATTRIB(name) \ + \ +static ssize_t efct_lio_tpg_attrib_##name##_show( \ + struct config_item *item, char *page) \ +{ \ + struct se_portal_group *se_tpg = to_tpg(item); \ + struct efct_lio_tpg *tpg = container_of(se_tpg, \ + struct efct_lio_tpg, tpg); \ + \ + return sprintf(page, "%u\n", tpg->tpg_attrib.name); \ +} \ + \ +static ssize_t efct_lio_tpg_attrib_##name##_store( \ + struct config_item *item, const char *page, size_t count) \ +{ \ + struct se_portal_group *se_tpg = to_tpg(item); \ + struct efct_lio_tpg *tpg = container_of(se_tpg, \ + struct efct_lio_tpg, tpg); \ + struct efct_lio_tpg_attrib *a = &tpg->tpg_attrib; \ + unsigned long val; \ + int ret; \ + \ + ret = kstrtoul(page, 0, &val); \ + if (ret < 0) { \ + pr_err("kstrtoul() failed with ret: %d\n", ret); \ + return ret; \ + } \ + \ + if (val != 0 && val != 1) { \ + pr_err("Illegal boolean value %lu\n", val); \ + return -EINVAL; \ + } \ + \ + a->name = val; \ + \ + return count; \ +} \ +CONFIGFS_ATTR(efct_lio_tpg_attrib_, name) + +DEF_EFCT_TPG_ATTRIB(generate_node_acls); +DEF_EFCT_TPG_ATTRIB(cache_dynamic_acls); +DEF_EFCT_TPG_ATTRIB(demo_mode_write_protect); +DEF_EFCT_TPG_ATTRIB(prod_mode_write_protect); +DEF_EFCT_TPG_ATTRIB(demo_mode_login_only); +DEF_EFCT_TPG_ATTRIB(session_deletion_wait); + +static struct configfs_attribute *efct_lio_tpg_attrib_attrs[] = { + &efct_lio_tpg_attrib_attr_generate_node_acls, + &efct_lio_tpg_attrib_attr_cache_dynamic_acls, + &efct_lio_tpg_attrib_attr_demo_mode_write_protect, + &efct_lio_tpg_attrib_attr_prod_mode_write_protect, + &efct_lio_tpg_attrib_attr_demo_mode_login_only, + &efct_lio_tpg_attrib_attr_session_deletion_wait, + NULL, +}; + +#define DEF_EFCT_NPIV_TPG_ATTRIB(name) \ + \ +static ssize_t efct_lio_npiv_tpg_attrib_##name##_show( \ + struct config_item *item, char *page) \ +{ \ + struct se_portal_group *se_tpg = to_tpg(item); \ + struct efct_lio_tpg *tpg = container_of(se_tpg, \ + struct efct_lio_tpg, tpg); \ + \ + return sprintf(page, "%u\n", tpg->tpg_attrib.name); \ +} \ + \ +static ssize_t efct_lio_npiv_tpg_attrib_##name##_store( \ + struct config_item *item, const char *page, size_t count) \ +{ \ + struct se_portal_group *se_tpg = to_tpg(item); \ + struct efct_lio_tpg *tpg = container_of(se_tpg, \ + struct efct_lio_tpg, tpg); \ + struct efct_lio_tpg_attrib *a = &tpg->tpg_attrib; \ + unsigned long val; \ + int ret; \ + \ + ret = kstrtoul(page, 0, &val); \ + if (ret < 0) { \ + pr_err("kstrtoul() failed with ret: %d\n", ret); \ + return ret; \ + } \ + \ + if (val != 0 && val != 1) { \ + pr_err("Illegal boolean value %lu\n", val); \ + return -EINVAL; \ + } \ + \ + a->name = val; \ + \ + return count; \ +} \ +CONFIGFS_ATTR(efct_lio_npiv_tpg_attrib_, name) + +DEF_EFCT_NPIV_TPG_ATTRIB(generate_node_acls); +DEF_EFCT_NPIV_TPG_ATTRIB(cache_dynamic_acls); +DEF_EFCT_NPIV_TPG_ATTRIB(demo_mode_write_protect); +DEF_EFCT_NPIV_TPG_ATTRIB(prod_mode_write_protect); +DEF_EFCT_NPIV_TPG_ATTRIB(demo_mode_login_only); +DEF_EFCT_NPIV_TPG_ATTRIB(session_deletion_wait); + +static struct configfs_attribute *efct_lio_npiv_tpg_attrib_attrs[] = { + &efct_lio_npiv_tpg_attrib_attr_generate_node_acls, + &efct_lio_npiv_tpg_attrib_attr_cache_dynamic_acls, + &efct_lio_npiv_tpg_attrib_attr_demo_mode_write_protect, + &efct_lio_npiv_tpg_attrib_attr_prod_mode_write_protect, + &efct_lio_npiv_tpg_attrib_attr_demo_mode_login_only, + &efct_lio_npiv_tpg_attrib_attr_session_deletion_wait, + NULL, +}; + +CONFIGFS_ATTR(efct_lio_tpg_, enable); +static struct configfs_attribute *efct_lio_tpg_attrs[] = { + &efct_lio_tpg_attr_enable, NULL }; +CONFIGFS_ATTR(efct_lio_npiv_tpg_, enable); +static struct configfs_attribute *efct_lio_npiv_tpg_attrs[] = { + &efct_lio_npiv_tpg_attr_enable, NULL }; + +static const struct target_core_fabric_ops efct_lio_ops = { + .module = THIS_MODULE, + .fabric_name = "efct", + .node_acl_size = sizeof(struct efct_lio_nacl), + .max_data_sg_nents = 65535, + .tpg_get_wwn = efct_lio_get_fabric_wwn, + .tpg_get_tag = efct_lio_get_tag, + .fabric_init_nodeacl = efct_lio_init_nodeacl, + .tpg_check_demo_mode = efct_lio_check_demo_mode, + .tpg_check_demo_mode_cache = efct_lio_check_demo_mode_cache, + .tpg_check_demo_mode_write_protect = efct_lio_check_demo_write_protect, + .tpg_check_prod_mode_write_protect = efct_lio_check_prod_write_protect, + .tpg_get_inst_index = efct_lio_tpg_get_inst_index, + .check_stop_free = efct_lio_check_stop_free, + .aborted_task = efct_lio_aborted_task, + .release_cmd = efct_lio_release_cmd, + .close_session = efct_lio_close_session, + .sess_get_index = efct_lio_sess_get_index, + .write_pending = efct_lio_write_pending, + .set_default_node_attributes = efct_lio_set_default_node_attrs, + .get_cmd_state = efct_lio_get_cmd_state, + .queue_data_in = efct_lio_queue_data_in, + .queue_status = efct_lio_queue_status, + .queue_tm_rsp = efct_lio_queue_tm_rsp, + .fabric_make_wwn = efct_lio_make_nport, + .fabric_drop_wwn = efct_lio_drop_nport, + .fabric_make_tpg = efct_lio_make_tpg, + .fabric_drop_tpg = efct_lio_drop_tpg, + .tpg_check_demo_mode_login_only = efct_lio_check_demo_mode_login_only, + .tpg_check_prot_fabric_only = NULL, + .sess_get_initiator_sid = NULL, + .tfc_tpg_base_attrs = efct_lio_tpg_attrs, + .tfc_tpg_attrib_attrs = efct_lio_tpg_attrib_attrs, +}; + +static const struct target_core_fabric_ops efct_lio_npiv_ops = { + .module = THIS_MODULE, + .fabric_name = "efct_npiv", + .node_acl_size = sizeof(struct efct_lio_nacl), + .max_data_sg_nents = 65535, + .tpg_get_wwn = efct_lio_get_npiv_fabric_wwn, + .tpg_get_tag = efct_lio_get_npiv_tag, + .fabric_init_nodeacl = efct_lio_init_nodeacl, + .tpg_check_demo_mode = efct_lio_check_demo_mode, + .tpg_check_demo_mode_cache = efct_lio_check_demo_mode_cache, + .tpg_check_demo_mode_write_protect = + efct_lio_npiv_check_demo_write_protect, + .tpg_check_prod_mode_write_protect = + efct_lio_npiv_check_prod_write_protect, + .tpg_get_inst_index = efct_lio_tpg_get_inst_index, + .check_stop_free = efct_lio_check_stop_free, + .aborted_task = efct_lio_aborted_task, + .release_cmd = efct_lio_release_cmd, + .close_session = efct_lio_close_session, + .sess_get_index = efct_lio_sess_get_index, + .write_pending = efct_lio_write_pending, + .set_default_node_attributes = efct_lio_set_default_node_attrs, + .get_cmd_state = efct_lio_get_cmd_state, + .queue_data_in = efct_lio_queue_data_in, + .queue_status = efct_lio_queue_status, + .queue_tm_rsp = efct_lio_queue_tm_rsp, + .fabric_make_wwn = efct_lio_npiv_make_nport, + .fabric_drop_wwn = efct_lio_npiv_drop_nport, + .fabric_make_tpg = efct_lio_npiv_make_tpg, + .fabric_drop_tpg = efct_lio_npiv_drop_tpg, + .tpg_check_demo_mode_login_only = + efct_lio_npiv_check_demo_mode_login_only, + .tpg_check_prot_fabric_only = NULL, + .sess_get_initiator_sid = NULL, + .tfc_tpg_base_attrs = efct_lio_npiv_tpg_attrs, + .tfc_tpg_attrib_attrs = efct_lio_npiv_tpg_attrib_attrs, +}; + +int efct_scsi_tgt_driver_init(void) +{ + int rc; + + /* Register the top level struct config_item_type with TCM core */ + rc = target_register_template(&efct_lio_ops); + if (rc < 0) { + pr_err("target_fabric_configfs_register failed with %d\n", rc); + return rc; + } + rc = target_register_template(&efct_lio_npiv_ops); + if (rc < 0) { + pr_err("target_fabric_configfs_register failed with %d\n", rc); + target_unregister_template(&efct_lio_ops); + return rc; + } + return EFC_SUCCESS; +} + +int efct_scsi_tgt_driver_exit(void) +{ + target_unregister_template(&efct_lio_ops); + target_unregister_template(&efct_lio_npiv_ops); + return EFC_SUCCESS; +} diff --git a/drivers/scsi/elx/efct/efct_lio.h b/drivers/scsi/elx/efct/efct_lio.h new file mode 100644 index 000000000000..64e6bd5dce78 --- /dev/null +++ b/drivers/scsi/elx/efct/efct_lio.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 Broadcom. All Rights Reserved. The term + * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. + */ + +#ifndef __EFCT_LIO_H__ +#define __EFCT_LIO_H__ + +#include "efct_scsi.h" +#include + +#define efct_lio_io_printf(io, fmt, ...) \ + efc_log_debug(io->efct, \ + "[%s] [%04x][i:%04x t:%04x h:%04x][c:%02x]" fmt,\ + io->node->display_name, io->instance_index, \ + io->init_task_tag, io->tgt_task_tag, io->hw_tag,\ + io->tgt_io.cmd.t_task_cdb[0], ##__VA_ARGS__) + +#define efct_lio_tmfio_printf(io, fmt, ...) \ + efc_log_debug(io->efct, \ + "[%s] [%04x][i:%04x t:%04x h:%04x][f:%02x]" fmt,\ + io->node->display_name, io->instance_index, \ + io->init_task_tag, io->tgt_task_tag, io->hw_tag,\ + io->tgt_io.tmf, ##__VA_ARGS__) + +#define efct_set_lio_io_state(io, value) (io->tgt_io.state |= value) + +struct efct_lio_wq_data { + struct efct *efct; + void *ptr; + struct work_struct work; +}; + +/* Target private efct structure */ +struct efct_scsi_tgt { + u32 max_sge; + u32 max_sgl; + + /* + * Variables used to send task set full. We are using a high watermark + * method to send task set full. We will reserve a fixed number of IOs + * per initiator plus a fudge factor. Once we reach this number, + * then the target will start sending task set full/busy responses. + */ + atomic_t initiator_count; + atomic_t ios_in_use; + atomic_t io_high_watermark; + + atomic_t watermark_hit; + int watermark_min; + int watermark_max; + + struct efct_lio_nport *lio_nport; + struct efct_lio_tpg *tpg; + + struct list_head vport_list; + /* Protects vport list*/ + spinlock_t efct_lio_lock; + + u64 wwnn; +}; + +struct efct_scsi_tgt_nport { + struct efct_lio_nport *lio_nport; +}; + +struct efct_node { + struct list_head list_entry; + struct kref ref; + void (*release)(struct kref *arg); + struct efct *efct; + struct efc_node *node; + struct se_session *session; + spinlock_t active_ios_lock; + struct list_head active_ios; + char display_name[EFC_NAME_LENGTH]; + u32 port_fc_id; + u32 node_fc_id; + u32 vpi; + u32 rpi; + u32 abort_cnt; +}; + +#define EFCT_LIO_STATE_SCSI_RECV_CMD (1 << 0) +#define EFCT_LIO_STATE_TGT_SUBMIT_CMD (1 << 1) +#define EFCT_LIO_STATE_TFO_QUEUE_DATA_IN (1 << 2) +#define EFCT_LIO_STATE_TFO_WRITE_PENDING (1 << 3) +#define EFCT_LIO_STATE_TGT_EXECUTE_CMD (1 << 4) +#define EFCT_LIO_STATE_SCSI_SEND_RD_DATA (1 << 5) +#define EFCT_LIO_STATE_TFO_CHK_STOP_FREE (1 << 6) +#define EFCT_LIO_STATE_SCSI_DATA_DONE (1 << 7) +#define EFCT_LIO_STATE_TFO_QUEUE_STATUS (1 << 8) +#define EFCT_LIO_STATE_SCSI_SEND_RSP (1 << 9) +#define EFCT_LIO_STATE_SCSI_RSP_DONE (1 << 10) +#define EFCT_LIO_STATE_TGT_GENERIC_FREE (1 << 11) +#define EFCT_LIO_STATE_SCSI_RECV_TMF (1 << 12) +#define EFCT_LIO_STATE_TGT_SUBMIT_TMR (1 << 13) +#define EFCT_LIO_STATE_TFO_WRITE_PEND_STATUS (1 << 14) +#define EFCT_LIO_STATE_TGT_GENERIC_REQ_FAILURE (1 << 15) + +#define EFCT_LIO_STATE_TFO_ABORTED_TASK (1 << 29) +#define EFCT_LIO_STATE_TFO_RELEASE_CMD (1 << 30) +#define EFCT_LIO_STATE_SCSI_CMPL_CMD (1u << 31) + +struct efct_scsi_tgt_io { + struct se_cmd cmd; + unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER]; + enum dma_data_direction ddir; + int task_attr; + u64 lun; + + u32 state; + u8 tmf; + struct efct_io *io_to_abort; + u32 seg_map_cnt; + u32 seg_cnt; + u32 cur_seg; + enum efct_scsi_io_status err; + bool aborting; + bool rsp_sent; + uint32_t transferred_len; +}; + +/* Handler return codes */ +enum { + SCSI_HANDLER_DATAPHASE_STARTED = 1, + SCSI_HANDLER_RESP_STARTED, + SCSI_HANDLER_VALIDATED_DATAPHASE_STARTED, + SCSI_CMD_NOT_SUPPORTED, +}; + +#define WWN_NAME_LEN 32 +struct efct_lio_vport { + u64 wwpn; + u64 npiv_wwpn; + u64 npiv_wwnn; + unsigned char wwpn_str[WWN_NAME_LEN]; + struct se_wwn vport_wwn; + struct efct_lio_tpg *tpg; + struct efct *efct; + struct Scsi_Host *shost; + struct fc_vport *fc_vport; + atomic_t enable; +}; + +struct efct_lio_nport { + u64 wwpn; + unsigned char wwpn_str[WWN_NAME_LEN]; + struct se_wwn nport_wwn; + struct efct_lio_tpg *tpg; + struct efct *efct; + atomic_t enable; +}; + +struct efct_lio_tpg_attrib { + u32 generate_node_acls; + u32 cache_dynamic_acls; + u32 demo_mode_write_protect; + u32 prod_mode_write_protect; + u32 demo_mode_login_only; + bool session_deletion_wait; +}; + +struct efct_lio_tpg { + struct se_portal_group tpg; + struct efct_lio_nport *nport; + struct efct_lio_vport *vport; + struct efct_lio_tpg_attrib tpg_attrib; + unsigned short tpgt; + atomic_t enabled; +}; + +struct efct_lio_nacl { + u64 nport_wwnn; + char nport_name[WWN_NAME_LEN]; + struct se_session *session; + struct se_node_acl se_node_acl; +}; + +struct efct_lio_vport_list_t { + struct list_head list_entry; + struct efct_lio_vport *lio_vport; +}; + +int efct_scsi_tgt_driver_init(void); +int efct_scsi_tgt_driver_exit(void); + +#endif /*__EFCT_LIO_H__ */ From patchwork Mon Oct 12 22:51:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269817 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49975C43467 for ; Mon, 12 Oct 2020 22:52:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 053C92073A for ; Mon, 12 Oct 2020 22:52:50 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="HuVvJOBz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390875AbgJLWwu (ORCPT ); Mon, 12 Oct 2020 18:52:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60648 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390798AbgJLWwe (ORCPT ); Mon, 12 Oct 2020 18:52:34 -0400 Received: from mail-pg1-x544.google.com (mail-pg1-x544.google.com [IPv6:2607:f8b0:4864:20::544]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 36A72C0613D1 for ; Mon, 12 Oct 2020 15:52:34 -0700 (PDT) Received: by mail-pg1-x544.google.com with SMTP id q21so4673264pgi.13 for ; Mon, 12 Oct 2020 15:52:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=9qhuBLNBk2DTpojX7fUC4DTJI29iey0zKTFXRApWwE0=; b=HuVvJOBzLKl3oqIL6kPO3YIOrSAh22vHKALeujnCUgHjXyvPRJJPiqawwX9cf6WAFH x0D3zNO4mXcYTvfv9gy0dM7uKIp/iwal/GAuYI+nYvG0WPIu4Ap6Ialo1xFY8Abv6Q2L c6fSWT5R5ScrEpEspqoIRAZKgNkikzGn1b4cg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=9qhuBLNBk2DTpojX7fUC4DTJI29iey0zKTFXRApWwE0=; b=BkNrWIf62qILoU00hiM6PUe0rtDK9BmCh8ZcPCKb5ZTUnfebRuVIgVNoZniVYSXDJv FKEwLtil5XWYjlom1a2qoywe3JId3DSw5D7h2vruZMBV6+mQ+ISvbigc2md4hi35tALy Z+nL/lKPTjE4pXTlQhUY/79PqIxKKt8gshVdwOA0MRDAQ1jRBhKq8A4gDkWrMkqemc1v AWPqflS+IleHHQPBM8Uwx4vKPDIZNNGljXppNPdpkqCF8ygRu8Kx/ZbvJ38CkHphtBlZ Nri4Wl3PAizzsOxItf0NOX8VjreyyALfBFhknernM/SDTEGz+mux5O5NET8t3fAY7NH1 XIgQ== X-Gm-Message-State: AOAM533ExzPblKh5/DxSJOGe7eGD9ipK0tYY4JM/6PvNFQh/XhydKZvI ZezBPk2m8onsAXHr2uPj8032s7SdaVkAzJVjSeTmBXb49DtQaS7c9TQH3kx2GVU/HlwoD3GyjBC ILNPf7ma6NbMIIILvbREva37zUAHNV+6ztO9IBJpAlh8lx8fN0TzgFJd0vBk5psgApsKXVJm+ua YTGzY= X-Google-Smtp-Source: ABdhPJyHWBeTB3TqGGtS6RXJFswmaOxypa6YsU0qBzWZXOBECHbVpYh6i+J4RUi4EUw/UdXe7NcBvg== X-Received: by 2002:a17:90a:4f01:: with SMTP id p1mr21659741pjh.144.1602543152924; Mon, 12 Oct 2020 15:52:32 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:32 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 26/31] elx: efct: Hardware IO submission routines Date: Mon, 12 Oct 2020 15:51:42 -0700 Message-Id: <20201012225147.54404-27-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: Routines that write IO to Work queue, send SRRs and raw frames. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reviewed-by: Hannes Reinecke --- v4: Reduced the argument list for WQE filling routines. --- drivers/scsi/elx/efct/efct_hw.c | 515 ++++++++++++++++++++++++++++++++ drivers/scsi/elx/efct/efct_hw.h | 14 + 2 files changed, 529 insertions(+) diff --git a/drivers/scsi/elx/efct/efct_hw.c b/drivers/scsi/elx/efct/efct_hw.c index 7764f16b2bed..6e92e86cbb84 100644 --- a/drivers/scsi/elx/efct/efct_hw.c +++ b/drivers/scsi/elx/efct/efct_hw.c @@ -2531,3 +2531,518 @@ efct_hw_flush(struct efct_hw *hw) return EFC_SUCCESS; } + +int +efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe) +{ + int rc = 0; + unsigned long flags = 0; + + spin_lock_irqsave(&wq->queue->lock, flags); + if (list_empty(&wq->pending_list)) { + if (wq->free_count > 0) { + rc = _efct_hw_wq_write(wq, wqe); + } else { + INIT_LIST_HEAD(&wqe->list_entry); + list_add_tail(&wqe->list_entry, &wq->pending_list); + wq->wq_pending_count++; + } + + spin_unlock_irqrestore(&wq->queue->lock, flags); + return rc; + } + + INIT_LIST_HEAD(&wqe->list_entry); + list_add_tail(&wqe->list_entry, &wq->pending_list); + wq->wq_pending_count++; + while (wq->free_count > 0) { + wqe = list_first_entry(&wq->pending_list, struct efct_hw_wqe, + list_entry); + if (!wqe) + break; + + list_del(&wqe->list_entry); + rc = _efct_hw_wq_write(wq, wqe); + if (rc) + break; + + if (wqe->abort_wqe_submit_needed) { + wqe->abort_wqe_submit_needed = false; + efct_hw_fill_abort_wqe(wq->hw, wqe); + + INIT_LIST_HEAD(&wqe->list_entry); + list_add_tail(&wqe->list_entry, &wq->pending_list); + wq->wq_pending_count++; + } + } + + spin_unlock_irqrestore(&wq->queue->lock, flags); + + return rc; +} + +int +efct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls) +{ + struct efct *efct = efc->base; + + return efct_hw_bls_send(efct, type, bls, NULL, NULL); +} + +int +efct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params, + void *cb, void *arg) +{ + struct efct_hw *hw = &efct->hw; + struct efct_hw_io *hio; + struct sli_bls_payload bls; + int rc; + + if (hw->state != EFCT_HW_STATE_ACTIVE) { + efc_log_err(hw->os, + "cannot send BLS, HW state=%d\n", hw->state); + return EFCT_HW_RTN_ERROR; + } + + hio = efct_hw_io_alloc(hw); + if (!hio) { + efc_log_err(hw->os, "HIO allocation failed\n"); + return EFCT_HW_RTN_ERROR; + } + + hio->done = cb; + hio->arg = arg; + + bls_params->xri = hio->indicator; + bls_params->tag = hio->reqtag; + + if (type == FC_RCTL_BA_ACC) { + hio->type = EFCT_HW_BLS_ACC; + bls.type = SLI4_SLI_BLS_ACC; + memcpy(&bls.u.acc, bls_params->payload, sizeof(bls.u.acc)); + } else { + hio->type = EFCT_HW_BLS_RJT; + bls.type = SLI4_SLI_BLS_RJT; + memcpy(&bls.u.rjt, bls_params->payload, sizeof(bls.u.rjt)); + } + + bls.ox_id = cpu_to_le16(bls_params->ox_id); + bls.rx_id = cpu_to_le16(bls_params->rx_id); + + if (sli_xmit_bls_rsp64_wqe(&hw->sli, hio->wqe.wqebuf, + &bls, bls_params)) { + efc_log_err(hw->os, "XMIT_BLS_RSP64 WQE error\n"); + return EFCT_HW_RTN_ERROR; + } + + hio->xbusy = true; + + /* + * Add IO to active io wqe list before submitting, in case the + * wcqe processing preempts this thread. + */ + hio->wq->use_count++; + rc = efct_hw_wq_write(hio->wq, &hio->wqe); + if (rc >= 0) { + /* non-negative return is success */ + rc = EFC_SUCCESS; + } else { + /* failed to write wqe, remove from active wqe list */ + efc_log_err(hw->os, + "sli_queue_write failed: %d\n", rc); + hio->xbusy = false; + } + + return rc; +} + +static int +efct_els_ssrs_send_cb(struct efct_hw_io *hio, u32 length, int status, + u32 ext_status, void *arg) +{ + struct efc_disc_io *io = arg; + + efc_disc_io_complete(io, length, status, ext_status); + return 0; +} + +static inline void +efct_fill_els_params(struct efc_disc_io *io, struct sli_els_params *params) +{ + u8 *cmd = io->req.virt; + + params->cmd = *cmd; + params->s_id = io->s_id; + params->d_id = io->d_id; + params->ox_id = io->iparam.els.ox_id; + params->rpi = io->rpi; + params->vpi = io->vpi; + params->rpi_registered = io->rpi_registered; + params->xmit_len = io->xmit_len; + params->rsp_len = io->rsp_len; + params->timeout = io->iparam.els.timeout; +} + +static inline void +efct_fill_ct_params(struct efc_disc_io *io, struct sli_ct_params *params) +{ + params->r_ctl = io->iparam.ct.r_ctl; + params->type = io->iparam.ct.type; + params->df_ctl = io->iparam.ct.df_ctl; + params->d_id = io->d_id; + params->ox_id = io->iparam.ct.ox_id; + params->rpi = io->rpi; + params->vpi = io->vpi; + params->rpi_registered = io->rpi_registered; + params->xmit_len = io->xmit_len; + params->rsp_len = io->rsp_len; + params->timeout = io->iparam.ct.timeout; +} + +/** + * efct_els_hw_srrs_send() - Send a single request and response cmd. + * @efc: efc library structure + * @io: Discovery IO used to hold els and ct cmd context. + * + * This routine supports communication sequences consisting of a single + * request and single response between two endpoints. Examples include: + * - Sending an ELS request. + * - Sending an ELS response - To send an ELS response, the caller must provide + * the OX_ID from the received request. + * - Sending a FC Common Transport (FC-CT) request - To send a FC-CT request, + * the caller must provide the R_CTL, TYPE, and DF_CTL + * values to place in the FC frame header. + * + * Return: Status of the request. + */ +int +efct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io) +{ + struct efct *efct = efc->base; + struct efct_hw_io *hio; + struct efct_hw *hw = &efct->hw; + struct efc_dma *send = &io->req; + struct efc_dma *receive = &io->rsp; + struct sli4_sge *sge = NULL; + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + u32 len = io->xmit_len; + u32 sge0_flags; + u32 sge1_flags; + + hio = efct_hw_io_alloc(hw); + if (!hio) { + pr_err("HIO alloc failed\n"); + return EFCT_HW_RTN_ERROR; + } + + if (hw->state != EFCT_HW_STATE_ACTIVE) { + efc_log_test(hw->os, + "cannot send SRRS, HW state=%d\n", hw->state); + return EFCT_HW_RTN_ERROR; + } + + hio->done = efct_els_ssrs_send_cb; + hio->arg = io; + + sge = hio->sgl->virt; + + /* clear both SGE */ + memset(hio->sgl->virt, 0, 2 * sizeof(struct sli4_sge)); + + sge0_flags = le32_to_cpu(sge[0].dw2_flags); + sge1_flags = le32_to_cpu(sge[1].dw2_flags); + if (send->size) { + sge[0].buffer_address_high = + cpu_to_le32(upper_32_bits(send->phys)); + sge[0].buffer_address_low = + cpu_to_le32(lower_32_bits(send->phys)); + + sge0_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT); + + sge[0].buffer_length = cpu_to_le32(len); + } + + if (io->io_type == EFC_DISC_IO_ELS_REQ || + io->io_type == EFC_DISC_IO_CT_REQ) { + sge[1].buffer_address_high = + cpu_to_le32(upper_32_bits(receive->phys)); + sge[1].buffer_address_low = + cpu_to_le32(lower_32_bits(receive->phys)); + + sge1_flags |= (SLI4_SGE_TYPE_DATA << SLI4_SGE_TYPE_SHIFT); + sge1_flags |= SLI4_SGE_LAST; + + sge[1].buffer_length = cpu_to_le32(receive->size); + } else { + sge0_flags |= SLI4_SGE_LAST; + } + + sge[0].dw2_flags = cpu_to_le32(sge0_flags); + sge[1].dw2_flags = cpu_to_le32(sge1_flags); + + switch (io->io_type) { + case EFC_DISC_IO_ELS_REQ: { + struct sli_els_params els_params; + + hio->type = EFCT_HW_ELS_REQ; + efct_fill_els_params(io, &els_params); + els_params.xri = hio->indicator; + els_params.tag = hio->reqtag; + + if (sli_els_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl, + &els_params)) { + efc_log_err(hw->os, "REQ WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + break; + } + case EFC_DISC_IO_ELS_RESP: { + struct sli_els_params els_params; + + hio->type = EFCT_HW_ELS_RSP; + efct_fill_els_params(io, &els_params); + els_params.xri = hio->indicator; + els_params.tag = hio->reqtag; + if (sli_xmit_els_rsp64_wqe(&hw->sli, hio->wqe.wqebuf, send, + &els_params)){ + efc_log_err(hw->os, "RSP WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + break; + } + case EFC_DISC_IO_CT_REQ: { + struct sli_ct_params ct_params; + + hio->type = EFCT_HW_FC_CT; + efct_fill_ct_params(io, &ct_params); + ct_params.xri = hio->indicator; + ct_params.tag = hio->reqtag; + if (sli_gen_request64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl, + &ct_params)){ + efc_log_err(hw->os, "GEN WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + break; + } + case EFC_DISC_IO_CT_RESP: { + struct sli_ct_params ct_params; + + hio->type = EFCT_HW_FC_CT_RSP; + efct_fill_ct_params(io, &ct_params); + ct_params.xri = hio->indicator; + ct_params.tag = hio->reqtag; + if (sli_xmit_sequence64_wqe(&hw->sli, hio->wqe.wqebuf, hio->sgl, + &ct_params)){ + efc_log_err(hw->os, "XMIT SEQ WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + break; + } + default: + efc_log_err(hw->os, "bad SRRS type %#x\n", io->io_type); + rc = EFCT_HW_RTN_ERROR; + } + + if (rc == EFCT_HW_RTN_SUCCESS) { + + hio->xbusy = true; + + /* + * Add IO to active io wqe list before submitting, in case the + * wcqe processing preempts this thread. + */ + hio->wq->use_count++; + rc = efct_hw_wq_write(hio->wq, &hio->wqe); + if (rc >= 0) { + /* non-negative return is success */ + rc = 0; + } else { + /* failed to write wqe, remove from active wqe list */ + efc_log_err(hw->os, + "sli_queue_write failed: %d\n", rc); + hio->xbusy = false; + } + } + + return rc; +} + +enum efct_hw_rtn +efct_hw_io_send(struct efct_hw *hw, enum efct_hw_io_type type, + struct efct_hw_io *io, union efct_hw_io_param_u *iparam, + void *cb, void *arg) +{ + enum efct_hw_rtn rc = EFCT_HW_RTN_SUCCESS; + bool send_wqe = true; + + if (!io) { + pr_err("bad parm hw=%p io=%p\n", hw, io); + return EFCT_HW_RTN_ERROR; + } + + if (hw->state != EFCT_HW_STATE_ACTIVE) { + efc_log_err(hw->os, "cannot send IO, HW state=%d\n", hw->state); + return EFCT_HW_RTN_ERROR; + } + + + /* + * Save state needed during later stages + */ + io->type = type; + io->done = cb; + io->arg = arg; + + /* + * Format the work queue entry used to send the IO + */ + switch (type) { + case EFCT_HW_IO_TARGET_WRITE: { + u16 flags = iparam->fcp_tgt.flags; + struct fcp_txrdy *xfer = io->xfer_rdy.virt; + + /* + * Fill in the XFER_RDY for IF_TYPE 0 devices + */ + xfer->ft_data_ro = cpu_to_be32(iparam->fcp_tgt.offset); + xfer->ft_burst_len = cpu_to_be32(iparam->fcp_tgt.xmit_len); + + if (io->xbusy) + flags |= SLI4_IO_CONTINUATION; + else + flags &= ~SLI4_IO_CONTINUATION; + iparam->fcp_tgt.xri = io->indicator; + iparam->fcp_tgt.tag = io->reqtag; + + if (sli_fcp_treceive64_wqe(&hw->sli, io->wqe.wqebuf, + &io->def_sgl, io->first_data_sge, + SLI4_CQ_DEFAULT, + 0, 0, &iparam->fcp_tgt)) { + efc_log_err(hw->os, "TRECEIVE WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + break; + } + case EFCT_HW_IO_TARGET_READ: { + u16 flags = iparam->fcp_tgt.flags; + + if (io->xbusy) + flags |= SLI4_IO_CONTINUATION; + else + flags &= ~SLI4_IO_CONTINUATION; + + iparam->fcp_tgt.xri = io->indicator; + iparam->fcp_tgt.tag = io->reqtag; + + if (sli_fcp_tsend64_wqe(&hw->sli, io->wqe.wqebuf, + &io->def_sgl, io->first_data_sge, + SLI4_CQ_DEFAULT, + 0, 0, &iparam->fcp_tgt)) { + efc_log_err(hw->os, "TSEND WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + break; + } + case EFCT_HW_IO_TARGET_RSP: { + u16 flags = iparam->fcp_tgt.flags; + + if (io->xbusy) + flags |= SLI4_IO_CONTINUATION; + else + flags &= ~SLI4_IO_CONTINUATION; + + iparam->fcp_tgt.xri = io->indicator; + iparam->fcp_tgt.tag = io->reqtag; + + if (sli_fcp_trsp64_wqe(&hw->sli, io->wqe.wqebuf, + &io->def_sgl, SLI4_CQ_DEFAULT, + 0, &iparam->fcp_tgt)) { + efc_log_err(hw->os, "TRSP WQE error\n"); + rc = EFCT_HW_RTN_ERROR; + } + + break; + } + default: + efc_log_err(hw->os, "unsupported IO type %#x\n", type); + rc = EFCT_HW_RTN_ERROR; + } + + if (send_wqe && rc == EFCT_HW_RTN_SUCCESS) { + + io->xbusy = true; + + /* + * Add IO to active io wqe list before submitting, in case the + * wcqe processing preempts this thread. + */ + hw->tcmd_wq_submit[io->wq->instance]++; + io->wq->use_count++; + rc = efct_hw_wq_write(io->wq, &io->wqe); + if (rc >= 0) { + /* non-negative return is success */ + rc = 0; + } else { + /* failed to write wqe, remove from active wqe list */ + efc_log_err(hw->os, + "sli_queue_write failed: %d\n", rc); + io->xbusy = false; + } + } + + return rc; +} + +enum efct_hw_rtn +efct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr, + u8 sof, u8 eof, struct efc_dma *payload, + struct efct_hw_send_frame_context *ctx, + void (*callback)(void *arg, u8 *cqe, int status), + void *arg) +{ + int rc; + struct efct_hw_wqe *wqe; + u32 xri; + struct hw_wq *wq; + + wqe = &ctx->wqe; + + /* populate the callback object */ + ctx->hw = hw; + + /* Fetch and populate request tag */ + ctx->wqcb = efct_hw_reqtag_alloc(hw, callback, arg); + if (!ctx->wqcb) { + efc_log_err(hw->os, "can't allocate request tag\n"); + return EFCT_HW_RTN_NO_RESOURCES; + } + + wq = hw->hw_wq[0]; + + /* Set XRI and RX_ID in the header based on which WQ, and which + * send_frame_io we are using + */ + xri = wq->send_frame_io->indicator; + + /* Build the send frame WQE */ + rc = sli_send_frame_wqe(&hw->sli, wqe->wqebuf, + sof, eof, (u32 *)hdr, payload, payload->len, + EFCT_HW_SEND_FRAME_TIMEOUT, xri, + ctx->wqcb->instance_index); + if (rc) { + efc_log_err(hw->os, "sli_send_frame_wqe failed: %d\n", + rc); + return EFCT_HW_RTN_ERROR; + } + + /* Write to WQ */ + rc = efct_hw_wq_write(wq, wqe); + if (rc) { + efc_log_err(hw->os, "efct_hw_wq_write failed: %d\n", rc); + return EFCT_HW_RTN_ERROR; + } + + wq->use_count++; + + return EFCT_HW_RTN_SUCCESS; +} diff --git a/drivers/scsi/elx/efct/efct_hw.h b/drivers/scsi/elx/efct/efct_hw.h index ab9a86ded815..bcd77b5095e8 100644 --- a/drivers/scsi/elx/efct/efct_hw.h +++ b/drivers/scsi/elx/efct/efct_hw.h @@ -706,5 +706,19 @@ int efct_hw_process(struct efct_hw *hw, u32 vector, u32 max_isr_time_msec); int efct_hw_queue_hash_find(struct efct_queue_hash *hash, u16 id); +int efct_hw_wq_write(struct hw_wq *wq, struct efct_hw_wqe *wqe); +enum efct_hw_rtn +efct_hw_send_frame(struct efct_hw *hw, struct fc_frame_header *hdr, + u8 sof, u8 eof, struct efc_dma *payload, + struct efct_hw_send_frame_context *ctx, + void (*callback)(void *arg, u8 *cqe, int status), + void *arg); +int +efct_els_hw_srrs_send(struct efc *efc, struct efc_disc_io *io); +int +efct_efc_bls_send(struct efc *efc, u32 type, struct sli_bls_params *bls); +int +efct_hw_bls_send(struct efct *efct, u32 type, struct sli_bls_params *bls_params, + void *cb, void *arg); #endif /* __EFCT_H__ */ From patchwork Mon Oct 12 22:51:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269818 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00FA6C433E7 for ; Mon, 12 Oct 2020 22:52:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 97FFD2073A for ; Mon, 12 Oct 2020 22:52:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="aVCA4ry7" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390870AbgJLWwt (ORCPT ); Mon, 12 Oct 2020 18:52:49 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60668 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390548AbgJLWwi (ORCPT ); Mon, 12 Oct 2020 18:52:38 -0400 Received: from mail-pg1-x543.google.com (mail-pg1-x543.google.com [IPv6:2607:f8b0:4864:20::543]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 648E2C0613D6 for ; Mon, 12 Oct 2020 15:52:38 -0700 (PDT) Received: by mail-pg1-x543.google.com with SMTP id r10so15881495pgb.10 for ; Mon, 12 Oct 2020 15:52:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=QYrRXIq96A6CYdmj3d8p0RiftZXfzexb+EMmvj0k5M0=; b=aVCA4ry7QdGnrXQTTGRiARiIaoAWrvV/N5+sicxctTpMk0ga9SzgICp+henMxNZxku NhQWP+pUNf7AU0bXb96Hjg/L3hw0CBfiGHHy8JhhUOYL7M2I48omlPfO8uwNx9DRoHsu XwrMRjg+LrktqfOAuZm9v3P2UHjR+0IkPvk/Y= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=QYrRXIq96A6CYdmj3d8p0RiftZXfzexb+EMmvj0k5M0=; b=in+J26mh+VB40wUBgtaLp80VsWy7vFxJPWN/luLQYd3VxDIjfndYn2ZQrPJ1SfWOwZ 1qGAzEiJyLZveBpZkxhesggRYp2iRm+PDKcdssjitptNKiNHSP9v/vUfBFo1FrhWN7dW Rf1kblWUyjLb12dhONyLP3uJb+XAKEbVgc7fpG60hJZd3mgLUNGWO1PA6aNrTEF7D/I+ OZOD1KRr4Wp3wAY4BA7iDIC50Ew9pJWTrEdPQ/6YY1xm6/1bI7u9PDFssWAlZQwff8k9 qqOOGaFUXbUQhWz/1Zg+i068uHIQWkVXTOyq3wVPJ+zmYU8sLlYqWfbNdBxUlYdRiHua E0qw== X-Gm-Message-State: AOAM532i5aW6L+Gex8L79wtG72ShKq2XpqDWQCRmsfblWdHEZFXq1zPr rHJGIBEg6YssS/WS4OW7yFTgpMfAIUO4WXoB7x13+4qEXOMIQVF+9/e1KasgbHTre6H0eZXI2XA cK2Bp01dpZUCHuyob8ehB+N/ijZ4TGuxEKNJhy2bBxu1Z0iVnIcyFnqwMuBXw+GnxrtkvC/xUEe PjfQE= X-Google-Smtp-Source: ABdhPJyuH1ha9gUfC+cuD7hXCEMz8dQwUAiDcmeNZdJ5ewG4UkbjT/+pGLkvYeetFExrgCdlqhm9PA== X-Received: by 2002:a63:3dc7:: with SMTP id k190mr14514171pga.291.1602543157030; Mon, 12 Oct 2020 15:52:37 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:36 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 29/31] elx: efct: scsi_transport_fc host interface support Date: Mon, 12 Oct 2020 15:51:45 -0700 Message-Id: <20201012225147.54404-30-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This patch continues the efct driver population. This patch adds driver definitions for: Integration with the scsi_fc_transport host interfaces Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart --- drivers/scsi/elx/efct/efct_xport.c | 500 +++++++++++++++++++++++++++++ 1 file changed, 500 insertions(+) diff --git a/drivers/scsi/elx/efct/efct_xport.c b/drivers/scsi/elx/efct/efct_xport.c index 02d958344a8f..05be629f8985 100644 --- a/drivers/scsi/elx/efct/efct_xport.c +++ b/drivers/scsi/elx/efct/efct_xport.c @@ -778,3 +778,503 @@ int efct_scsi_del_device(struct efct *efct) } return EFC_SUCCESS; } + +static void +efct_get_host_port_id(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + struct efc *efc = efct->efcport; + struct efc_nport *nport; + + if (efc->domain && efc->domain->nport) { + nport = efc->domain->nport; + fc_host_port_id(shost) = nport->fc_id; + } +} + +static void +efct_get_host_port_type(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + struct efc *efc = efct->efcport; + int type = FC_PORTTYPE_UNKNOWN; + + if (efc->domain && efc->domain->nport) { + if (efc->domain->is_loop) { + type = FC_PORTTYPE_LPORT; + } else { + struct efc_nport *nport = efc->domain->nport; + + if (nport->is_vport) + type = FC_PORTTYPE_NPIV; + else if (nport->topology == EFC_SPORT_TOPOLOGY_P2P) + type = FC_PORTTYPE_PTP; + else if (nport->topology == EFC_SPORT_TOPOLOGY_UNKNOWN) + type = FC_PORTTYPE_UNKNOWN; + else + type = FC_PORTTYPE_NPORT; + } + } + fc_host_port_type(shost) = type; +} + +static void +efct_get_host_vport_type(struct Scsi_Host *shost) +{ + fc_host_port_type(shost) = FC_PORTTYPE_NPIV; +} + +static void +efct_get_host_port_state(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + struct efc *efc = efct->efcport; + + if (efc->domain) + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; + else + fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; +} + +static void +efct_get_host_speed(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + struct efc *efc = efct->efcport; + union efct_xport_stats_u speed; + u32 fc_speed = FC_PORTSPEED_UNKNOWN; + int rc; + + if (!efc->domain || !efc->domain->nport) { + fc_host_speed(shost) = fc_speed; + return; + } + + rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_SPEED, &speed); + if (rc == 0) { + switch (speed.value) { + case 1000: + fc_speed = FC_PORTSPEED_1GBIT; + break; + case 2000: + fc_speed = FC_PORTSPEED_2GBIT; + break; + case 4000: + fc_speed = FC_PORTSPEED_4GBIT; + break; + case 8000: + fc_speed = FC_PORTSPEED_8GBIT; + break; + case 10000: + fc_speed = FC_PORTSPEED_10GBIT; + break; + case 16000: + fc_speed = FC_PORTSPEED_16GBIT; + break; + case 32000: + fc_speed = FC_PORTSPEED_32GBIT; + break; + } + } + + fc_host_speed(shost) = fc_speed; +} + +static void +efct_get_host_fabric_name(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + struct efc *efc = efct->efcport; + + if (efc->domain) { + struct fc_els_flogi *sp = + (struct fc_els_flogi *) + efc->domain->flogi_service_params; + + fc_host_fabric_name(shost) = be64_to_cpu(sp->fl_wwnn); + } +} + +static struct fc_host_statistics * +efct_get_stats(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + union efct_xport_stats_u stats; + struct efct_xport *xport = efct->xport; + u32 rc = 1; + + rc = efct_xport_status(xport, EFCT_XPORT_LINK_STATISTICS, &stats); + if (rc != 0) { + pr_err("efct_xport_status returned non 0 - %d\n", rc); + return NULL; + } + + vport->fc_host_stats.loss_of_sync_count = + stats.stats.link_stats.loss_of_sync_error_count; + vport->fc_host_stats.link_failure_count = + stats.stats.link_stats.link_failure_error_count; + vport->fc_host_stats.prim_seq_protocol_err_count = + stats.stats.link_stats.primitive_sequence_error_count; + vport->fc_host_stats.invalid_tx_word_count = + stats.stats.link_stats.invalid_transmission_word_error_count; + vport->fc_host_stats.invalid_crc_count = + stats.stats.link_stats.crc_error_count; + /* mbox returns kbyte count so we need to convert to words */ + vport->fc_host_stats.tx_words = + stats.stats.host_stats.transmit_kbyte_count * 256; + /* mbox returns kbyte count so we need to convert to words */ + vport->fc_host_stats.rx_words = + stats.stats.host_stats.receive_kbyte_count * 256; + vport->fc_host_stats.tx_frames = + stats.stats.host_stats.transmit_frame_count; + vport->fc_host_stats.rx_frames = + stats.stats.host_stats.receive_frame_count; + + vport->fc_host_stats.fcp_input_requests = + xport->fcp_stats.input_requests; + vport->fc_host_stats.fcp_output_requests = + xport->fcp_stats.output_requests; + vport->fc_host_stats.fcp_output_megabytes = + xport->fcp_stats.output_bytes >> 20; + vport->fc_host_stats.fcp_input_megabytes = + xport->fcp_stats.input_bytes >> 20; + vport->fc_host_stats.fcp_control_requests = + xport->fcp_stats.control_requests; + + return &vport->fc_host_stats; +} + +static void +efct_reset_stats(struct Scsi_Host *shost) +{ + struct efct_vport *vport = (struct efct_vport *)shost->hostdata; + struct efct *efct = vport->efct; + /* argument has no purpose for this action */ + union efct_xport_stats_u dummy; + u32 rc = 0; + + rc = efct_xport_status(efct->xport, EFCT_XPORT_LINK_STAT_RESET, &dummy); + if (rc != 0) + pr_err("efct_xport_status returned non 0 - %d\n", rc); +} + +static void +efct_get_starget_port_id(struct scsi_target *starget) +{ + pr_err("%s\n", __func__); +} + +static void +efct_get_starget_node_name(struct scsi_target *starget) +{ + pr_err("%s\n", __func__); +} + +static void +efct_get_starget_port_name(struct scsi_target *starget) +{ + pr_err("%s\n", __func__); +} + +static void +efct_set_vport_symbolic_name(struct fc_vport *fc_vport) +{ + pr_err("%s\n", __func__); +} + +static int +efct_issue_lip(struct Scsi_Host *shost) +{ + struct efct_vport *vport = + shost ? (struct efct_vport *)shost->hostdata : NULL; + struct efct *efct = vport ? vport->efct : NULL; + + if (!shost || !vport || !efct) { + pr_err("%s: shost=%p vport=%p efct=%p\n", __func__, + shost, vport, efct); + return -EPERM; + } + + /* + * Bring the link down gracefully then re-init the link. + * The firmware will re-initialize the Fibre Channel interface as + * required. It does not issue a LIP. + */ + + if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_OFFLINE)) + efc_log_test(efct, "EFCT_XPORT_PORT_OFFLINE failed\n"); + + if (efct_xport_control(efct->xport, EFCT_XPORT_PORT_ONLINE)) + efc_log_test(efct, "EFCT_XPORT_PORT_ONLINE failed\n"); + + return EFC_SUCCESS; +} + +struct efct_vport * +efct_scsi_new_vport(struct efct *efct, struct device *dev) +{ + struct Scsi_Host *shost = NULL; + int error = 0; + struct efct_vport *vport = NULL; + union efct_xport_stats_u speed; + u32 supported_speeds = 0; + + shost = scsi_host_alloc(&efct_template, sizeof(*vport)); + if (!shost) { + efc_log_err(efct, "failed to allocate Scsi_Host struct\n"); + return NULL; + } + + /* save efct information to shost LLD-specific space */ + vport = (struct efct_vport *)shost->hostdata; + vport->efct = efct; + vport->is_vport = true; + + shost->can_queue = efct->hw.config.n_io; + shost->max_cmd_len = 16; /* 16-byte CDBs */ + shost->max_id = 0xffff; + shost->max_lun = 0xffffffff; + + /* can only accept (from mid-layer) as many SGEs as we've pre-regited*/ + shost->sg_tablesize = sli_get_max_sgl(&efct->hw.sli); + + /* attach FC Transport template to shost */ + shost->transportt = efct_vport_fc_tt; + efc_log_debug(efct, "vport transport template=%p\n", + efct_vport_fc_tt); + + /* get pci_dev structure and add host to SCSI ML */ + error = scsi_add_host_with_dma(shost, dev, &efct->pci->dev); + if (error) { + efc_log_test(efct, "failed scsi_add_host_with_dma\n"); + return NULL; + } + + /* Set symbolic name for host port */ + snprintf(fc_host_symbolic_name(shost), + sizeof(fc_host_symbolic_name(shost)), + "Emulex %s FV%s DV%s", efct->model, efct->hw.sli.fw_name[0], + EFCT_DRIVER_VERSION); + + /* Set host port supported classes */ + fc_host_supported_classes(shost) = FC_COS_CLASS3; + + speed.value = 1000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_1GBIT; + } + speed.value = 2000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_2GBIT; + } + speed.value = 4000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_4GBIT; + } + speed.value = 8000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_8GBIT; + } + speed.value = 10000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_10GBIT; + } + speed.value = 16000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_16GBIT; + } + speed.value = 32000; + if (efct_xport_status(efct->xport, EFCT_XPORT_IS_SUPPORTED_LINK_SPEED, + &speed)) { + supported_speeds |= FC_PORTSPEED_32GBIT; + } + + fc_host_supported_speeds(shost) = supported_speeds; + vport->shost = shost; + + return vport; +} + +int efct_scsi_del_vport(struct efct *efct, struct Scsi_Host *shost) +{ + if (shost) { + efc_log_debug(efct, + "Unregistering vport with Transport Layer\n"); + efct_xport_remove_host(shost); + efc_log_debug(efct, "Unregistering vport with SCSI Midlayer\n"); + scsi_remove_host(shost); + scsi_host_put(shost); + return EFC_SUCCESS; + } + return EFC_FAIL; +} + +static int +efct_vport_create(struct fc_vport *fc_vport, bool disable) +{ + struct Scsi_Host *shost = fc_vport ? fc_vport->shost : NULL; + struct efct_vport *pport = shost ? + (struct efct_vport *)shost->hostdata : + NULL; + struct efct *efct = pport ? pport->efct : NULL; + struct efct_vport *vport = NULL; + + if (!fc_vport || !shost || !efct) + goto fail; + + vport = efct_scsi_new_vport(efct, &fc_vport->dev); + if (!vport) { + efc_log_err(efct, "failed to create vport\n"); + goto fail; + } + + vport->fc_vport = fc_vport; + vport->npiv_wwpn = fc_vport->port_name; + vport->npiv_wwnn = fc_vport->node_name; + fc_host_node_name(vport->shost) = vport->npiv_wwnn; + fc_host_port_name(vport->shost) = vport->npiv_wwpn; + *(struct efct_vport **)fc_vport->dd_data = vport; + + return EFC_SUCCESS; + +fail: + return EFC_FAIL; +} + +static int +efct_vport_delete(struct fc_vport *fc_vport) +{ + struct efct_vport *vport = *(struct efct_vport **)fc_vport->dd_data; + struct Scsi_Host *shost = vport ? vport->shost : NULL; + struct efct *efct = vport ? vport->efct : NULL; + int rc = -1; + + rc = efct_scsi_del_vport(efct, shost); + + if (rc) + pr_err("%s: vport delete failed\n", __func__); + + return rc; +} + +static int +efct_vport_disable(struct fc_vport *fc_vport, bool disable) +{ + return EFC_SUCCESS; +} + +static struct fc_function_template efct_xport_functions = { + .get_starget_node_name = efct_get_starget_node_name, + .get_starget_port_name = efct_get_starget_port_name, + .get_starget_port_id = efct_get_starget_port_id, + + .get_host_port_id = efct_get_host_port_id, + .get_host_port_type = efct_get_host_port_type, + .get_host_port_state = efct_get_host_port_state, + .get_host_speed = efct_get_host_speed, + .get_host_fabric_name = efct_get_host_fabric_name, + + .get_fc_host_stats = efct_get_stats, + .reset_fc_host_stats = efct_reset_stats, + + .issue_fc_host_lip = efct_issue_lip, + + .set_vport_symbolic_name = efct_set_vport_symbolic_name, + .vport_disable = efct_vport_disable, + + /* allocation lengths for host-specific data */ + .dd_fcrport_size = sizeof(struct efct_rport_data), + .dd_fcvport_size = 128, /* should be sizeof(...) */ + + /* remote port fixed attributes */ + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_rport_dev_loss_tmo = 1, + + /* target dynamic attributes */ + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + + /* host fixed attributes */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + + /* host dynamic attributes */ + .show_host_port_id = 1, + .show_host_port_type = 1, + .show_host_port_state = 1, + /* active_fc4s is shown but doesn't change (thus no get function) */ + .show_host_active_fc4s = 1, + .show_host_speed = 1, + .show_host_fabric_name = 1, + .show_host_symbolic_name = 1, + .vport_create = efct_vport_create, + .vport_delete = efct_vport_delete, +}; + +static struct fc_function_template efct_vport_functions = { + .get_starget_node_name = efct_get_starget_node_name, + .get_starget_port_name = efct_get_starget_port_name, + .get_starget_port_id = efct_get_starget_port_id, + + .get_host_port_id = efct_get_host_port_id, + .get_host_port_type = efct_get_host_vport_type, + .get_host_port_state = efct_get_host_port_state, + .get_host_speed = efct_get_host_speed, + .get_host_fabric_name = efct_get_host_fabric_name, + + .get_fc_host_stats = efct_get_stats, + .reset_fc_host_stats = efct_reset_stats, + + .issue_fc_host_lip = efct_issue_lip, + .set_vport_symbolic_name = efct_set_vport_symbolic_name, + + /* allocation lengths for host-specific data */ + .dd_fcrport_size = sizeof(struct efct_rport_data), + .dd_fcvport_size = 128, /* should be sizeof(...) */ + + /* remote port fixed attributes */ + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + .show_rport_dev_loss_tmo = 1, + + /* target dynamic attributes */ + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + + /* host fixed attributes */ + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_supported_speeds = 1, + .show_host_maxframe_size = 1, + + /* host dynamic attributes */ + .show_host_port_id = 1, + .show_host_port_type = 1, + .show_host_port_state = 1, + /* active_fc4s is shown but doesn't change (thus no get function) */ + .show_host_active_fc4s = 1, + .show_host_speed = 1, + .show_host_fabric_name = 1, + .show_host_symbolic_name = 1, +}; From patchwork Mon Oct 12 22:51:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Smart X-Patchwork-Id: 269816 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5E0F4C4363D for ; Mon, 12 Oct 2020 22:52:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DC59520838 for ; Mon, 12 Oct 2020 22:52:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.b="Cgsf1xfU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2390876AbgJLWww (ORCPT ); Mon, 12 Oct 2020 18:52:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60698 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2390858AbgJLWwt (ORCPT ); Mon, 12 Oct 2020 18:52:49 -0400 Received: from mail-pf1-x444.google.com (mail-pf1-x444.google.com [IPv6:2607:f8b0:4864:20::444]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1DD75C0613D8 for ; Mon, 12 Oct 2020 15:52:41 -0700 (PDT) Received: by mail-pf1-x444.google.com with SMTP id c20so4511640pfr.8 for ; Mon, 12 Oct 2020 15:52:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version; bh=U71v0y9A/yeUSfRSWTIePZ0qq3wUA511pDnY3SEsJw8=; b=Cgsf1xfUMJ+KXjKFl0ZJa+3x6PVE5yGgA2GzWSTNeGORC01xm5tQ+yZb0QWG7g2zYk 52kLG5zb7/cdvnOV6rS31FfGrQmC/VA7z8Q56A1/Hzqjb5dqybE+3K/cLOpZaJtjkRER QqM46pMBXJfz1RLO/YKCdpa4xTxZ5qKkkN4XI= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version; bh=U71v0y9A/yeUSfRSWTIePZ0qq3wUA511pDnY3SEsJw8=; b=UMgXs8DPGPYka+JPLyMFx4RbAbexyRu87M4YsFScY2sjB+b0p8PrLywlG3I4/27oFE VWF+MuurD01ynW2S8gy6TTEHCiXALoRAe178Jb2z/KjLZDmxwu6v+SsbKNDPgTYUsYnl qcF51Ppc+SC/JGHtjEZI1X+w8zcGIwuU4bXyIYnv0aX9UuWXPvf3+dHjCG7SQO5aZHs0 4AigzJTS1B6puUN2ISstiqsihDxDVOCRHYSd0adnAxOxDydT1fbLiAmHEqZrQJJeLske OSHD9qwQt5LwECFMZBFWZ2HdhKsfb0eXmO3Wmtbclxf4h72af3UbGNxwXIq3KzQC+X6e uMJQ== X-Gm-Message-State: AOAM531IMbBzUbllndMS/cVNSlxbM67RW+Y9oJyVurZtYRdIn2yzOvhf JWhapnCBJWLmyYMEKXEGELcDBR5Jd+yUHR/3FydygyWm6I7ElK0WmQX1LXVgREAPmW86pFDAyii z/4uQvDM7hrY3jHoG7KEKJf89wiwbA2v0g3kCd2w+Vwto9/iJq5RpY07eNVu2tYK6Jb5pOD5cm0 jY4y4= X-Google-Smtp-Source: ABdhPJyjz17CgXY2xZhhJ8l6OYbTvTLX9BR3YbWB7AsL1ugvLX13mffvbNc+kI7AVDJI4mWxAPN4yg== X-Received: by 2002:a63:7843:: with SMTP id t64mr14907507pgc.117.1602543159906; Mon, 12 Oct 2020 15:52:39 -0700 (PDT) Received: from localhost.localdomain ([192.19.223.252]) by smtp.gmail.com with ESMTPSA id x5sm21222287pfr.83.2020.10.12.15.52.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Oct 2020 15:52:38 -0700 (PDT) From: James Smart To: linux-scsi@vger.kernel.org Cc: James Smart , Ram Vegesna Subject: [PATCH v4 31/31] elx: efct: Tie into kernel Kconfig and build process Date: Mon, 12 Oct 2020 15:51:47 -0700 Message-Id: <20201012225147.54404-32-james.smart@broadcom.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201012225147.54404-1-james.smart@broadcom.com> References: <20201012225147.54404-1-james.smart@broadcom.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org This final patch ties the efct driver into the kernel Kconfig and build linkages in the drivers/scsi directory. Co-developed-by: Ram Vegesna Signed-off-by: Ram Vegesna Signed-off-by: James Smart Reported-by: kernel test robot Reported-by: kernel test robot Reviewed-by: Hannes Reinecke --- drivers/scsi/Kconfig | 2 ++ drivers/scsi/Makefile | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 701b61ec76ee..f2d47bf55f97 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1170,6 +1170,8 @@ config SCSI_LPFC_DEBUG_FS This makes debugging information from the lpfc driver available via the debugfs filesystem. +source "drivers/scsi/elx/Kconfig" + config SCSI_SIM710 tristate "Simple 53c710 SCSI support (Compaq, NCR machines)" depends on EISA && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index c00e3dd57990..844db573283c 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx/ obj-$(CONFIG_SCSI_QLA_ISCSI) += libiscsi.o qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ +obj-$(CONFIG_SCSI_EFCT) += elx/ obj-$(CONFIG_SCSI_BFA_FC) += bfa/ obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/ obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o