summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin K. Petersen <martin.petersen@oracle.com>2026-07-26 16:49:52 -0400
committerMartin K. Petersen <martin.petersen@oracle.com>2026-07-26 16:49:52 -0400
commit1cd82d710eedab0140f21940c8eeca42648713a7 (patch)
tree577a4ac7bf8f7f7ef85b5279102916118e998933
parent0cad7bd51301678ad5c9aad701be29eab2ab4e7a (diff)
parent4857949b58cdb61f8cd41ca4ffc674b85896ef61 (diff)
downloadlinux-next-1cd82d710eedab0140f21940c8eeca42648713a7.tar.gz
linux-next-1cd82d710eedab0140f21940c8eeca42648713a7.zip
Merge patch series "ibmvfc: NVMe/FC support over IBM Virtual FC"
Tyrel Datwyler <tyreld@linux.ibm.com> says: This series adds NVMe/FC initiator support to the ibmvfc driver, enabling IBM POWER virtual machines to discover and use NVMe namespaces presented by the IBM Virtual I/O Server (VIOS) over the existing NPIV transport. The ibmvfc driver communicates with the VIOS via a CRQ-based protocol. With this series the VIOS can present both SCSI/FCP and NVMe/FC targets through parallel sets of protocol-specific MAD opcodes, fabric login flows, and sub-CRQ channels. The series is organized into three phases: Patches 1-5: Bug fixes and preparatory refactoring Four pre-existing bugs are fixed before any NVMe/FC work is introduced: a deadlock in the MAD send-failure path (locked done variant called with host_lock already held), a race during driver teardown where rport_add_work_q work items can outlive the FC host, a NULL event dereference in ibmvfc_tgt_implicit_logout_and_del, and an allocator mismatch where mempool-allocated ibmvfc_target structs are freed via kfree rather than mempool_free. Patch 5 moves the target list and count from struct ibmvfc_host into struct ibmvfc_channels as the structural prerequisite for independent per-protocol target tracking. Patches 6-27: Protocol interface and driver scaffolding Patch 6 extends ibmvfc.h with NVMe/FC protocol definitions: MAD opcodes, capability flags, the v3 command layout, the fabric login MAD, async sub-CRQ event format, and updated channel enquiry/setup fields. Patch 7 splits ibmvfc.c into ibmvfc-core.c and the new ibmvfc-nvme.c/h, registers an nvme_fc_port_template with stub callbacks, and adds NVMe module parameters. The remaining patches build the NVMe/FC plumbing: NVMe channel-group initialization, sub-CRQ lifecycle management, protocol- specific fabric login flow, target discovery, PLOGI/PRLI/query-target, implicit logout, move-login, protocol-driven target allocation, NVMe target deletion, state machine updates, and local/remote port registration with the NVMe-FC transport layer. Patches 28-33: NVMe-FC LLDD callbacks and I/O path Implements the full nvme_fc_port_template: create_queue/delete_queue map NVMe controller queues to sub-CRQ handles; ls_req submits FC-LS frames via the ibmvfc passthru MAD; fcp_io builds and submits NVMe FCP commands via the NVMe sub-CRQ and completes them via nvme_fc_rcv_fcp_rsp(); ls_abort and fcp_abort cancel outstanding requests via NVMF cancel MADs. Patch 33 extends the purge path to fail outstanding NVMe FCP and LS requests during host reset and link-down events. Link: https://patch.msgid.link/20260723000149.969416-1-tyreld@linux.ibm.com Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
-rw-r--r--drivers/scsi/ibmvscsi/Makefile2
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc-core.c (renamed from drivers/scsi/ibmvscsi/ibmvfc.c)819
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc-nvme.c573
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc-nvme.h49
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.h219
5 files changed, 1466 insertions, 196 deletions
diff --git a/drivers/scsi/ibmvscsi/Makefile b/drivers/scsi/ibmvscsi/Makefile
index 5eb1cb1a0028..9408c7f4cdee 100644
--- a/drivers/scsi/ibmvscsi/Makefile
+++ b/drivers/scsi/ibmvscsi/Makefile
@@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+ibmvfc-objs := ibmvfc-core.o ibmvfc-nvme.o
+
obj-$(CONFIG_SCSI_IBMVSCSI) += ibmvscsi.o
obj-$(CONFIG_SCSI_IBMVFC) += ibmvfc.o
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 3dd2adda195e..dfa231b6cc47 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -1,10 +1,11 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
/*
* ibmvfc.c -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
- * Copyright (C) IBM Corporation, 2008
+ * Copyright (C) IBM Corporation, 2008-2026
*/
#include <linux/module.h>
@@ -40,15 +41,19 @@ static unsigned int max_requests = IBMVFC_MAX_REQUESTS_DEFAULT;
static u16 max_sectors = IBMVFC_MAX_SECTORS;
static u16 scsi_qdepth = IBMVFC_SCSI_QDEPTH;
static unsigned int disc_threads = IBMVFC_MAX_DISC_THREADS;
-static unsigned int ibmvfc_debug = IBMVFC_DEBUG;
static unsigned int log_level = IBMVFC_DEFAULT_LOG_LEVEL;
static unsigned int cls3_error = IBMVFC_CLS3_ERROR;
static unsigned int mq_enabled = IBMVFC_MQ;
static unsigned int nr_scsi_hw_queues = IBMVFC_SCSI_HW_QUEUES;
static unsigned int nr_scsi_channels = IBMVFC_SCSI_CHANNELS;
+static unsigned int nvme_enabled = IBMVFC_NVME;
+static unsigned int nr_nvme_hw_queues = IBMVFC_NVME_HW_QUEUES;
+static unsigned int nr_nvme_channels = IBMVFC_NVME_CHANNELS;
static unsigned int mig_channels_only = IBMVFC_MIG_NO_SUB_TO_CRQ;
static unsigned int mig_no_less_channels = IBMVFC_MIG_NO_N_TO_M;
+unsigned int ibmvfc_debug = IBMVFC_DEBUG;
+
static LIST_HEAD(ibmvfc_head);
static DEFINE_SPINLOCK(ibmvfc_driver_lock);
static struct scsi_transport_template *ibmvfc_transport_template;
@@ -74,6 +79,16 @@ module_param_named(mig_no_less_channels, mig_no_less_channels, uint, S_IRUGO);
MODULE_PARM_DESC(mig_no_less_channels, "Prevent migration to system with less channels. "
"[Default=" __stringify(IBMVFC_MIG_NO_N_TO_M) "]");
+module_param_named(nvme, nvme_enabled, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme, "Enable NVMe over FC support. "
+ "[Default=" __stringify(IBMVFC_NVME) "]");
+module_param_named(nvme_host_queues, nr_nvme_hw_queues, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme_host_queues, "Number of NVMeoF Host submission queues. "
+ "[Default=" __stringify(IBMVFC_NVME_HW_QUEUES) "]");
+module_param_named(nvme_hw_channels, nr_nvme_channels, uint, S_IRUGO);
+MODULE_PARM_DESC(nvme_hw_channels, "Number of hw NVMeoF channels to request. "
+ "[Default=" __stringify(IBMVFC_NVME_CHANNELS) "]");
+
module_param_named(init_timeout, init_timeout, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(init_timeout, "Initialization timeout in seconds. "
"[Default=" __stringify(IBMVFC_INIT_TIMEOUT) "]");
@@ -159,6 +174,11 @@ static const struct {
{ IBMVFC_FC_SCSI_ERROR, IBMVFC_COMMAND_FAILED, DID_ERROR, 0, 1, "PRLI to device failed." },
};
+static const char *proto_type[] = {
+ "SCSI",
+ "NVMe",
+};
+
static void ibmvfc_npiv_login(struct ibmvfc_host *);
static void ibmvfc_tgt_send_prli(struct ibmvfc_target *);
static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *);
@@ -186,17 +206,18 @@ static long h_reg_sub_crq(unsigned long unit_address, unsigned long ioba,
return rc;
}
-static int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
+static int ibmvfc_nvme_active(struct ibmvfc_host *vhost)
{
- u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
-
- return (host_caps & cap_flags) ? 1 : 0;
+ return (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF) &&
+ vhost->nvme_scrqs.active_queues);
}
static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
struct ibmvfc_cmd *vfc_cmd)
{
- if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ return &vfc_cmd->v3scsi.iu;
+ else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
return &vfc_cmd->v2.iu;
else
return &vfc_cmd->v1.iu;
@@ -205,7 +226,9 @@ static struct ibmvfc_fcp_cmd_iu *ibmvfc_get_fcp_iu(struct ibmvfc_host *vhost,
static struct ibmvfc_fcp_rsp *ibmvfc_get_fcp_rsp(struct ibmvfc_host *vhost,
struct ibmvfc_cmd *vfc_cmd)
{
- if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ return &vfc_cmd->v3scsi.rsp;
+ else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
return &vfc_cmd->v2.rsp;
else
return &vfc_cmd->v1.rsp;
@@ -324,7 +347,7 @@ static int ibmvfc_get_err_index(u16 status, u16 error)
* Return value:
* error description string
**/
-static const char *ibmvfc_get_cmd_error(u16 status, u16 error)
+const char *ibmvfc_get_cmd_error(u16 status, u16 error)
{
int rc = ibmvfc_get_err_index(status, error);
if (rc >= 0)
@@ -468,6 +491,7 @@ static const struct {
{ IBMVFC_FABRIC_BUSY, "fabric busy" },
{ IBMVFC_PORT_BUSY, "port busy" },
{ IBMVFC_BASIC_REJECT, "basic reject" },
+ { IBMVFC_FC4_LS_REJECT, "fc4 ls reject" },
};
static const char *unknown_fc_type = "unknown fc type";
@@ -682,7 +706,9 @@ static void ibmvfc_link_down(struct ibmvfc_host *vhost,
ENTER;
scsi_block_requests(vhost->host);
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+ ibmvfc_del_tgt(tgt);
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
ibmvfc_del_tgt(tgt);
ibmvfc_set_host_state(vhost, state);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_TGT_DEL);
@@ -715,7 +741,13 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE);
vhost->async_crq.cur = 0;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (vhost->client_migrated)
+ tgt->need_login = 1;
+ else
+ ibmvfc_del_tgt(tgt);
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (vhost->client_migrated)
tgt->need_login = 1;
else
@@ -930,6 +962,7 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
unsigned long flags;
ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
/* Re-enable the CRQ */
do {
@@ -945,10 +978,13 @@ static int ibmvfc_reenable_crq_queue(struct ibmvfc_host *vhost)
spin_lock(vhost->crq.q_lock);
vhost->do_enquiry = 1;
vhost->using_channels = 0;
+ vhost->do_scsi_login = 0;
+ vhost->do_nvme_login = 0;
spin_unlock(vhost->crq.q_lock);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
return rc;
}
@@ -968,6 +1004,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
struct ibmvfc_queue *crq = &vhost->crq;
ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
/* Close the CRQ */
do {
@@ -982,6 +1019,8 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
vhost->logged_in = 0;
vhost->do_enquiry = 1;
vhost->using_channels = 0;
+ vhost->do_scsi_login = 0;
+ vhost->do_nvme_login = 0;
/* Clean out the queue */
memset(crq->msgs.crq, 0, PAGE_SIZE);
@@ -1001,6 +1040,7 @@ static int ibmvfc_reset_crq(struct ibmvfc_host *vhost)
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
return rc;
}
@@ -1029,7 +1069,7 @@ static int ibmvfc_valid_event(struct ibmvfc_event_pool *pool,
* @evt: ibmvfc_event to be freed
*
**/
-static void ibmvfc_free_event(struct ibmvfc_event *evt)
+void ibmvfc_free_event(struct ibmvfc_event *evt)
{
struct ibmvfc_event_pool *pool = &evt->queue->evt_pool;
unsigned long flags;
@@ -1052,19 +1092,29 @@ static void ibmvfc_free_event(struct ibmvfc_event *evt)
}
/**
- * ibmvfc_scsi_eh_done - EH done function for queuecommand commands
+ * ibmvfc_vfc_eh_done - EH done function for queued IO
* @evt: ibmvfc event struct
*
- * This function does not setup any error status, that must be done
- * before this function gets called.
+ * This function does not setup any error status for scsi commands, that must be
+ * done before this function gets called.
**/
-static void ibmvfc_scsi_eh_done(struct ibmvfc_event *evt)
+static void ibmvfc_vfc_eh_done(struct ibmvfc_event *evt)
{
struct scsi_cmnd *cmnd = evt->cmnd;
+ struct nvmefc_ls_req *ls_req = evt->ls_req;
+ struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
if (cmnd) {
scsi_dma_unmap(cmnd);
scsi_done(cmnd);
+ } else if (fcp_req) {
+ fcp_req->rcv_rsplen = 0;
+ fcp_req->transferred_length = 0;
+ fcp_req->status = NVME_SC_INTERNAL;
+ fcp_req->done(fcp_req);
+ } else if (ls_req) {
+ ls_req->done(ls_req, -EIO);
+ kref_put(&evt->tgt->kref, ibmvfc_release_tgt);
}
ibmvfc_free_event(evt);
@@ -1106,8 +1156,10 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
BUG_ON(!atomic_dec_and_test(&evt->active));
if (evt->cmnd) {
evt->cmnd->result = (error_code << 16);
- evt->done = ibmvfc_scsi_eh_done;
- } else
+ evt->done = ibmvfc_vfc_eh_done;
+ } else if (evt->fcp_req || evt->ls_req)
+ evt->done = ibmvfc_vfc_eh_done;
+ else
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_DRIVER_FAILED);
timer_delete(&evt->timer);
@@ -1124,13 +1176,16 @@ static void ibmvfc_fail_request(struct ibmvfc_event *evt, int error_code)
static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
{
struct ibmvfc_event *evt, *pos;
- struct ibmvfc_queue *queues = vhost->scsi_scrqs.scrqs;
+ struct ibmvfc_queue *scsi_q = vhost->scsi_scrqs.scrqs;
+ struct ibmvfc_queue *nvme_q = vhost->nvme_scrqs.scrqs;
unsigned long flags;
- int hwqs = 0;
+ int shwqs, nhwqs = 0;
int i;
- if (vhost->using_channels)
- hwqs = vhost->scsi_scrqs.active_queues;
+ if (vhost->using_channels) {
+ shwqs = vhost->scsi_scrqs.active_queues;
+ nhwqs = vhost->nvme_scrqs.active_queues;
+ }
ibmvfc_dbg(vhost, "Purging all requests\n");
spin_lock_irqsave(&vhost->crq.l_lock, flags);
@@ -1139,14 +1194,24 @@ static void ibmvfc_purge_requests(struct ibmvfc_host *vhost, int error_code)
list_splice_init(&vhost->crq.sent, &vhost->purge);
spin_unlock_irqrestore(&vhost->crq.l_lock, flags);
- for (i = 0; i < hwqs; i++) {
- spin_lock_irqsave(queues[i].q_lock, flags);
- spin_lock(&queues[i].l_lock);
- list_for_each_entry_safe(evt, pos, &queues[i].sent, queue_list)
+ for (i = 0; i < shwqs; i++) {
+ spin_lock_irqsave(scsi_q[i].q_lock, flags);
+ spin_lock(&scsi_q[i].l_lock);
+ list_for_each_entry_safe(evt, pos, &scsi_q[i].sent, queue_list)
ibmvfc_fail_request(evt, error_code);
- list_splice_init(&queues[i].sent, &vhost->purge);
- spin_unlock(&queues[i].l_lock);
- spin_unlock_irqrestore(queues[i].q_lock, flags);
+ list_splice_init(&scsi_q[i].sent, &vhost->purge);
+ spin_unlock(&scsi_q[i].l_lock);
+ spin_unlock_irqrestore(scsi_q[i].q_lock, flags);
+ }
+
+ for (i = 0; i < nhwqs; i++) {
+ spin_lock_irqsave(nvme_q[i].q_lock, flags);
+ spin_lock(&nvme_q[i].l_lock);
+ list_for_each_entry_safe(evt, pos, &nvme_q[i].sent, queue_list)
+ ibmvfc_fail_request(evt, error_code);
+ list_splice_init(&nvme_q[i].sent, &vhost->purge);
+ spin_unlock(&nvme_q[i].l_lock);
+ spin_unlock_irqrestore(nvme_q[i].q_lock, flags);
}
}
@@ -1232,7 +1297,7 @@ static struct ibmvfc_target *__ibmvfc_get_target(struct scsi_target *starget)
struct ibmvfc_host *vhost = shost_priv(shost);
struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->target_id == starget->id) {
kref_get(&tgt->kref);
return tgt;
@@ -1363,10 +1428,10 @@ static void ibmvfc_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
* @kref: kref struct
*
**/
-static void ibmvfc_release_tgt(struct kref *kref)
+void ibmvfc_release_tgt(struct kref *kref)
{
struct ibmvfc_target *tgt = container_of(kref, struct ibmvfc_target, kref);
- kfree(tgt);
+ mempool_free(tgt, tgt->vhost->tgt_pool);
}
/**
@@ -1493,13 +1558,13 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
if (mq_enabled)
max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
- vhost->scsi_scrqs.desired_queues;
+ (vhost->scsi_scrqs.desired_queues + vhost->nvme_scrqs.desired_queues);
memset(login_info, 0, sizeof(*login_info));
login_info->ostype = cpu_to_be32(IBMVFC_OS_LINUX);
login_info->max_dma_len = cpu_to_be64(max_sectors << 9);
- login_info->max_payload = cpu_to_be32(sizeof(struct ibmvfc_fcp_cmd_iu));
+ login_info->max_payload = cpu_to_be32(sizeof(struct nvme_fc_cmd_iu));
login_info->max_response = cpu_to_be32(sizeof(struct ibmvfc_fcp_rsp));
login_info->partition_num = cpu_to_be32(vhost->partition_number);
login_info->vfc_frame_version = cpu_to_be32(1);
@@ -1511,8 +1576,14 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
login_info->max_cmds = cpu_to_be32(max_cmds);
login_info->capabilities = cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN);
- if (vhost->mq_enabled || vhost->using_channels)
+ if (vhost->mq_enabled || vhost->using_channels) {
login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
+ if (vhost->nvme_enabled) {
+ login_info->capabilities |= cpu_to_be64(IBMVFC_YES_NVMEOF);
+ login_info->capabilities |= cpu_to_be64(IBMVFC_YES_SCSI);
+ login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_WWPN_ALL);
+ }
+ }
login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
login_info->async.len = cpu_to_be32(async_crq->size *
@@ -1535,7 +1606,7 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
*
* Returns a free event from the pool.
**/
-static struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
+struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved)
{
struct ibmvfc_event *evt = NULL;
unsigned long flags;
@@ -1559,9 +1630,6 @@ out:
return evt;
}
-#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
-#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
-
/**
* ibmvfc_locked_done - Calls evt completion with host_lock held
* @evt: ibmvfc evt to complete
@@ -1586,10 +1654,12 @@ static void ibmvfc_locked_done(struct ibmvfc_event *evt)
* @done: Routine to call when the event is responded to
* @format: SRP or MAD format
**/
-static void ibmvfc_init_event(struct ibmvfc_event *evt,
+void ibmvfc_init_event(struct ibmvfc_event *evt,
void (*done) (struct ibmvfc_event *), u8 format)
{
evt->cmnd = NULL;
+ evt->fcp_req = NULL;
+ evt->ls_req = NULL;
evt->sync_iu = NULL;
evt->eh_comp = NULL;
evt->crq.format = format;
@@ -1711,7 +1781,7 @@ static void ibmvfc_timeout(struct timer_list *t)
*
* Returns the value returned from ibmvfc_send_crq(). (Zero for success)
**/
-static int ibmvfc_send_event(struct ibmvfc_event *evt,
+int ibmvfc_send_event(struct ibmvfc_event *evt,
struct ibmvfc_host *vhost, unsigned long timeout)
{
__be64 *crq_as_u64 = (__be64 *) &evt->crq;
@@ -1773,9 +1843,13 @@ static int ibmvfc_send_event(struct ibmvfc_event *evt,
dev_err(vhost->dev, "Send error (rc=%d)\n", rc);
if (evt->cmnd) {
evt->cmnd->result = DID_ERROR << 16;
- evt->done = ibmvfc_scsi_eh_done;
- } else
+ evt->done = ibmvfc_vfc_eh_done;
+ } else if (evt->fcp_req || evt->ls_req) {
+ evt->done = ibmvfc_vfc_eh_done;
+ } else {
evt->xfer_iu->mad_common.status = cpu_to_be16(IBMVFC_MAD_CRQ_ERROR);
+ evt->done = evt->_done;
+ }
evt->done(evt);
} else {
@@ -1824,7 +1898,7 @@ static void ibmvfc_log_error(struct ibmvfc_event *evt)
* @sdev: scsi device struct
*
**/
-static void ibmvfc_relogin(struct scsi_device *sdev)
+static void ibmvfc_scsi_relogin(struct scsi_device *sdev)
{
struct ibmvfc_host *vhost = shost_priv(sdev->host);
struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
@@ -1832,7 +1906,7 @@ static void ibmvfc_relogin(struct scsi_device *sdev)
unsigned long flags;
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (rport == tgt->rport) {
ibmvfc_del_tgt(tgt);
break;
@@ -1876,7 +1950,7 @@ static void ibmvfc_scsi_done(struct ibmvfc_event *evt)
memcpy(cmnd->sense_buffer, rsp->data.sense + rsp_len, sense_len);
if ((be16_to_cpu(vfc_cmd->status) & IBMVFC_VIOS_FAILURE) &&
(be16_to_cpu(vfc_cmd->error) == IBMVFC_PLOGI_REQUIRED))
- ibmvfc_relogin(cmnd->device);
+ ibmvfc_scsi_relogin(cmnd->device);
if (!cmnd->result && (!scsi_get_resid(cmnd) || (rsp->flags & FCP_RESID_OVER)))
cmnd->result = (DID_ERROR << 16);
@@ -1935,11 +2009,13 @@ static struct ibmvfc_cmd *ibmvfc_init_vfc_cmd(struct ibmvfc_event *evt, struct s
size_t offset;
memset(vfc_cmd, 0, sizeof(*vfc_cmd));
- if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN)) {
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ offset = offsetof(struct ibmvfc_cmd, v3scsi.rsp);
+ else if (ibmvfc_check_caps(vhost, IBMVFC_HANDLE_VF_WWPN))
offset = offsetof(struct ibmvfc_cmd, v2.rsp);
- vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
- } else
+ else
offset = offsetof(struct ibmvfc_cmd, v1.rsp);
+ vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
vfc_cmd->resp.va = cpu_to_be64(be64_to_cpu(evt->crq.ioba) + offset);
vfc_cmd->resp.len = cpu_to_be32(sizeof(*rsp));
vfc_cmd->frame_type = cpu_to_be32(IBMVFC_SCSI_FCP_TYPE);
@@ -2130,7 +2206,7 @@ static int ibmvfc_bsg_plogi(struct ibmvfc_host *vhost, unsigned int port_id)
ENTER;
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->scsi_id == port_id) {
issue_login = 0;
break;
@@ -3102,7 +3178,7 @@ static void ibmvfc_terminate_rport_io(struct fc_rport *rport)
spin_lock_irqsave(shost->host_lock, flags);
found = 0;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->scsi_id == rport->port_id) {
found++;
break;
@@ -3242,7 +3318,23 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
case IBMVFC_AE_ELS_LOGO:
case IBMVFC_AE_ELS_PRLO:
case IBMVFC_AE_ELS_PLOGI:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
+ break;
+ if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
+ continue;
+ if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+ continue;
+ if (crq->node_name && cpu_to_be64(tgt->ids.node_name) != crq->node_name)
+ continue;
+ if (tgt->need_login && be64_to_cpu(crq->event) == IBMVFC_AE_ELS_LOGO)
+ tgt->logo_rcvd = 1;
+ if (!tgt->need_login || be64_to_cpu(crq->event) == IBMVFC_AE_ELS_PLOGI) {
+ ibmvfc_del_tgt(tgt);
+ ibmvfc_reinit_host(vhost);
+ }
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (!crq->scsi_id && !crq->wwpn && !crq->node_name)
break;
if (crq->scsi_id && cpu_to_be64(tgt->scsi_id) != crq->scsi_id)
@@ -4042,11 +4134,12 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Process Login succeeded: %X %02X %04X\n",
- parms->type, parms->flags, parms->service_parms);
+ tgt_dbg(tgt, "%s Process Login succeeded: %X %02X %04X\n",
+ proto_type[tgt->protocol], parms->type, parms->flags,
+ parms->service_parms);
+ index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
if (parms->type == IBMVFC_SCSI_FCP_TYPE) {
- index = ibmvfc_get_prli_rsp(be16_to_cpu(parms->flags));
if (prli_rsp[index].logged_in) {
if (be16_to_cpu(parms->flags) & IBMVFC_PRLI_EST_IMG_PAIR) {
tgt->need_login = 0;
@@ -4062,6 +4155,22 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
else
ibmvfc_del_tgt(tgt);
+ } else if (parms->type == IBMVFC_NVME_FCP_TYPE) {
+ if (prli_rsp[index].logged_in) {
+ /* For FC-NVMe PRLI the Image Pair field is always set to zero (see 6.3.3) */
+ tgt->need_login = 0;
+ tgt->ids.roles = 0;
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_TARGET)
+ tgt->ids.roles |= FC_PORT_ROLE_NVME_TARGET;
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_INITIATOR)
+ tgt->ids.roles |= FC_PORT_ROLE_NVME_INITIATOR;
+ if (be32_to_cpu(parms->service_parms) & IBMVFC_PRLI_NVME_DISCOVERY)
+ tgt->ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY;
+ tgt->add_rport = 1;
+ } else if (prli_rsp[index].retry)
+ ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli);
+ else
+ ibmvfc_del_tgt(tgt);
} else
ibmvfc_del_tgt(tgt);
break;
@@ -4082,9 +4191,10 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt)
else
ibmvfc_del_tgt(tgt);
- tgt_log(tgt, level, "Process Login failed: %s (%x:%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- be16_to_cpu(rsp->status), be16_to_cpu(rsp->error), status);
+ tgt_log(tgt, level, "%s Process Login failed: %s (%x:%x) rc=0x%02X\n",
+ proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error), status);
break;
}
@@ -4126,25 +4236,30 @@ static void ibmvfc_tgt_send_prli(struct ibmvfc_target *tgt)
} else {
prli->common.version = cpu_to_be32(1);
}
- prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI) {
+ prli->common.opcode = cpu_to_be32(IBMVFC_PROCESS_LOGIN);
+ prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
+ prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
+ prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
+ prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
+
+ if (cls3_error)
+ prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
+ } else {
+ prli->common.opcode = cpu_to_be32(IBMVFC_NVMF_PROCESS_LOGIN);
+ prli->parms.type = IBMVFC_NVME_FCP_TYPE;
+ prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_NVME_INITIATOR);
+ }
prli->common.length = cpu_to_be16(sizeof(*prli));
prli->scsi_id = cpu_to_be64(tgt->scsi_id);
- prli->parms.type = IBMVFC_SCSI_FCP_TYPE;
- prli->parms.flags = cpu_to_be16(IBMVFC_PRLI_EST_IMG_PAIR);
- prli->parms.service_parms = cpu_to_be32(IBMVFC_PRLI_INITIATOR_FUNC);
- prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED);
-
- if (cls3_error)
- prli->parms.service_parms |= cpu_to_be32(IBMVFC_PRLI_RETRY);
-
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent process login\n");
+ tgt_dbg(tgt, "%s Sent process login\n", proto_type[tgt->protocol]);
}
/**
@@ -4164,7 +4279,7 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Port Login succeeded\n");
+ tgt_dbg(tgt, "%s Port Login succeeded\n", proto_type[tgt->protocol]);
if (tgt->ids.port_name &&
tgt->ids.port_name != wwn_to_u64(rsp->service_parms.port_name)) {
vhost->reinit = 1;
@@ -4192,9 +4307,9 @@ static void ibmvfc_tgt_plogi_done(struct ibmvfc_event *evt)
else
ibmvfc_del_tgt(tgt);
- tgt_log(tgt, level, "Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ tgt_log(tgt, level, "%s Port Login failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
ibmvfc_get_ls_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain), status);
break;
@@ -4240,7 +4355,10 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
} else {
plogi->common.version = cpu_to_be32(1);
}
- plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ plogi->common.opcode = cpu_to_be32(IBMVFC_PORT_LOGIN);
+ else
+ plogi->common.opcode = cpu_to_be32(IBMVFC_NVMF_PORT_LOGIN);
plogi->common.length = cpu_to_be16(sizeof(*plogi));
plogi->scsi_id = cpu_to_be64(tgt->scsi_id);
@@ -4249,7 +4367,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent port login\n");
+ tgt_dbg(tgt, "Sent %s port login\n", proto_type[tgt->protocol]);
}
/**
@@ -4270,7 +4388,7 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Implicit Logout succeeded\n");
+ tgt_dbg(tgt, "%s Implicit Logout succeeded\n", proto_type[tgt->protocol]);
break;
case IBMVFC_MAD_DRIVER_FAILED:
kref_put(&tgt->kref, ibmvfc_release_tgt);
@@ -4278,7 +4396,8 @@ static void ibmvfc_tgt_implicit_logout_done(struct ibmvfc_event *evt)
return;
case IBMVFC_MAD_FAILED:
default:
- tgt_err(tgt, "Implicit Logout failed: rc=0x%02X\n", status);
+ tgt_err(tgt, "%s Implicit Logout failed: rc=0x%02X\n",
+ proto_type[tgt->protocol], status);
break;
}
@@ -4311,7 +4430,10 @@ static struct ibmvfc_event *__ibmvfc_tgt_get_implicit_logout_evt(struct ibmvfc_t
mad = &evt->iu.implicit_logout;
memset(mad, 0, sizeof(*mad));
mad->common.version = cpu_to_be32(1);
- mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ mad->common.opcode = cpu_to_be32(IBMVFC_IMPLICIT_LOGOUT);
+ else
+ mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_IMPLICIT_LOGOUT);
mad->common.length = cpu_to_be16(sizeof(*mad));
mad->old_scsi_id = cpu_to_be64(tgt->scsi_id);
return evt;
@@ -4347,7 +4469,7 @@ static void ibmvfc_tgt_implicit_logout(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Implicit Logout\n");
+ tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]);
}
/**
@@ -4377,7 +4499,8 @@ static void ibmvfc_tgt_implicit_logout_and_del_done(struct ibmvfc_event *evt)
else
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT);
- tgt_dbg(tgt, "Implicit Logout %s\n", (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
+ tgt_dbg(tgt, "%s Implicit Logout %s\n", proto_type[tgt->protocol],
+ (status == IBMVFC_MAD_SUCCESS) ? "succeeded" : "failed");
kref_put(&tgt->kref, ibmvfc_release_tgt);
wake_up(&vhost->work_wait_q);
}
@@ -4404,13 +4527,21 @@ static void ibmvfc_tgt_implicit_logout_and_del(struct ibmvfc_target *tgt)
evt = __ibmvfc_tgt_get_implicit_logout_evt(tgt,
ibmvfc_tgt_implicit_logout_and_del_done);
+ if (!evt) {
+ vhost->discovery_threads--;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ __ibmvfc_reset_host(vhost);
+ return;
+ }
+
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT);
if (ibmvfc_send_event(evt, vhost, default_timeout)) {
vhost->discovery_threads--;
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Implicit Logout\n");
+ tgt_dbg(tgt, "%s Sent Implicit Logout\n", proto_type[tgt->protocol]);
}
/**
@@ -4430,7 +4561,8 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Move Login succeeded for new scsi_id: %llX\n", tgt->new_scsi_id);
+ tgt_dbg(tgt, "%s Move Login succeeded for new scsi_id: %llX\n",
+ proto_type[tgt->protocol], tgt->new_scsi_id);
tgt->ids.node_name = wwn_to_u64(rsp->service_parms.node_name);
tgt->ids.port_name = wwn_to_u64(rsp->service_parms.port_name);
tgt->scsi_id = tgt->new_scsi_id;
@@ -4451,8 +4583,9 @@ static void ibmvfc_tgt_move_login_done(struct ibmvfc_event *evt)
level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_move_login);
tgt_log(tgt, level,
- "Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
- tgt->new_scsi_id, be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
+ "%s Move Login failed: new scsi_id: %llX, flags:%x, vios_flags:%x, rc=0x%02X\n",
+ proto_type[tgt->protocol], tgt->new_scsi_id,
+ be32_to_cpu(rsp->flags), be16_to_cpu(rsp->vios_flags),
status);
break;
}
@@ -4492,7 +4625,10 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
move = &evt->iu.move_login;
memset(move, 0, sizeof(*move));
move->common.version = cpu_to_be32(1);
- move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ move->common.opcode = cpu_to_be32(IBMVFC_MOVE_LOGIN);
+ else
+ move->common.opcode = cpu_to_be32(IBMVFC_NVMF_MOVE_LOGIN);
move->common.length = cpu_to_be16(sizeof(*move));
move->old_scsi_id = cpu_to_be64(tgt->scsi_id);
@@ -4505,7 +4641,8 @@ static void ibmvfc_tgt_move_login(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Move Login for new scsi_id: %llX\n", tgt->new_scsi_id);
+ tgt_dbg(tgt, "Sent %s Move Login for new scsi_id: %llX\n",
+ proto_type[tgt->protocol], tgt->new_scsi_id);
}
/**
@@ -4766,7 +4903,7 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
switch (status) {
case IBMVFC_MAD_SUCCESS:
- tgt_dbg(tgt, "Query Target succeeded\n");
+ tgt_dbg(tgt, "%s Query Target succeeded\n", proto_type[tgt->protocol]);
if (be64_to_cpu(rsp->scsi_id) != tgt->scsi_id)
ibmvfc_del_tgt(tgt);
else
@@ -4788,9 +4925,9 @@ static void ibmvfc_tgt_query_target_done(struct ibmvfc_event *evt)
else
ibmvfc_del_tgt(tgt);
- tgt_log(tgt, level, "Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
- ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
- be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
+ tgt_log(tgt, level, "%s Query Target failed: %s (%x:%x) %s (%x) %s (%x) rc=0x%02X\n",
+ proto_type[tgt->protocol], ibmvfc_get_cmd_error(be16_to_cpu(rsp->status),
+ be16_to_cpu(rsp->error)), be16_to_cpu(rsp->status), be16_to_cpu(rsp->error),
ibmvfc_get_fc_type(be16_to_cpu(rsp->fc_type)), be16_to_cpu(rsp->fc_type),
ibmvfc_get_gs_explain(be16_to_cpu(rsp->fc_explain)), be16_to_cpu(rsp->fc_explain),
status);
@@ -4830,7 +4967,10 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
query_tgt = &evt->iu.query_tgt;
memset(query_tgt, 0, sizeof(*query_tgt));
query_tgt->common.version = cpu_to_be32(1);
- query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+ if (tgt->protocol == IBMVFC_PROTO_SCSI)
+ query_tgt->common.opcode = cpu_to_be32(IBMVFC_QUERY_TARGET);
+ else
+ query_tgt->common.opcode = cpu_to_be32(IBMVFC_NVMF_QUERY_TARGET);
query_tgt->common.length = cpu_to_be16(sizeof(*query_tgt));
query_tgt->wwpn = cpu_to_be64(tgt->ids.port_name);
@@ -4840,7 +4980,7 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
kref_put(&tgt->kref, ibmvfc_release_tgt);
} else
- tgt_dbg(tgt, "Sent Query Target\n");
+ tgt_dbg(tgt, "Sent %s Query Target\n", proto_type[tgt->protocol]);
}
/**
@@ -4852,25 +4992,32 @@ static void ibmvfc_tgt_query_target(struct ibmvfc_target *tgt)
* 0 on success / other on failure
**/
static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
- struct ibmvfc_discover_targets_entry *target)
+ struct ibmvfc_discover_targets_entry *target,
+ enum ibmvfc_protocol protocol)
{
struct ibmvfc_target *stgt = NULL;
struct ibmvfc_target *wtgt = NULL;
struct ibmvfc_target *tgt;
+ struct ibmvfc_channels *channels;
unsigned long flags;
u64 scsi_id = be32_to_cpu(target->scsi_id) & IBMVFC_DISC_TGT_SCSI_ID_MASK;
u64 wwpn = be64_to_cpu(target->wwpn);
+ if (protocol == IBMVFC_PROTO_SCSI)
+ channels = &vhost->scsi_scrqs;
+ else
+ channels = &vhost->nvme_scrqs;
+
/* Look to see if we already have a target allocated for this SCSI ID or WWPN */
spin_lock_irqsave(vhost->host->host_lock, flags);
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &channels->targets, queue) {
if (tgt->wwpn == wwpn) {
wtgt = tgt;
break;
}
}
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &channels->targets, queue) {
if (tgt->scsi_id == scsi_id) {
stgt = tgt;
break;
@@ -4918,6 +5065,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
tgt = mempool_alloc(vhost->tgt_pool, GFP_NOIO);
memset(tgt, 0, sizeof(*tgt));
+ tgt->protocol = protocol;
tgt->scsi_id = scsi_id;
tgt->wwpn = wwpn;
tgt->vhost = vhost;
@@ -4927,7 +5075,7 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost,
ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
spin_lock_irqsave(vhost->host->host_lock, flags);
tgt->cancel_key = vhost->task_set++;
- list_add_tail(&tgt->queue, &vhost->targets);
+ list_add_tail(&tgt->queue, &channels->targets);
unlock_out:
spin_unlock_irqrestore(vhost->host->host_lock, flags);
@@ -4945,8 +5093,12 @@ static int ibmvfc_alloc_targets(struct ibmvfc_host *vhost)
{
int i, rc;
- for (i = 0, rc = 0; !rc && i < vhost->num_targets; i++)
- rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i]);
+ for (i = 0, rc = 0; !rc && i < vhost->scsi_scrqs.num_targets; i++)
+ rc = ibmvfc_alloc_target(vhost, &vhost->scsi_scrqs.disc_buf[i],
+ vhost->scsi_scrqs.protocol);
+ for (i = 0; !rc && i < vhost->nvme_scrqs.num_targets; i++)
+ rc = ibmvfc_alloc_target(vhost, &vhost->nvme_scrqs.disc_buf[i],
+ vhost->nvme_scrqs.protocol);
return rc;
}
@@ -4960,19 +5112,30 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_discover_targets *rsp = &evt->xfer_iu->discover_targets;
+ struct ibmvfc_channels *channels;
u32 mad_status = be16_to_cpu(rsp->common.status);
+ u32 opcode = be32_to_cpu(rsp->common.opcode);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
+ if (opcode == IBMVFC_DISC_TARGETS)
+ channels = &vhost->scsi_scrqs;
+ else
+ channels = &vhost->nvme_scrqs;
+
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
- ibmvfc_dbg(vhost, "Discover Targets succeeded\n");
- vhost->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
- max_targets);
+ ibmvfc_dbg(vhost, "Discover %s Targets succeeded\n",
+ proto_type[channels->protocol]);
+ channels->num_targets = min_t(u32, be32_to_cpu(rsp->num_written),
+ max_targets);
+ ibmvfc_dbg(vhost, "%d %s targets found\n", channels->num_targets,
+ proto_type[channels->protocol]);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_ALLOC_TGTS);
break;
case IBMVFC_MAD_FAILED:
level += ibmvfc_retry_host_init(vhost);
- ibmvfc_log(vhost, level, "Discover Targets failed: %s (%x:%x)\n",
+ ibmvfc_log(vhost, level, "Discover %s Targets failed: %s (%x:%x)\n",
+ proto_type[channels->protocol],
ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
break;
@@ -4988,6 +5151,32 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
wake_up(&vhost->work_wait_q);
}
+static struct ibmvfc_event *ibmvfc_get_disc_event(struct ibmvfc_channels *channels)
+{
+ struct ibmvfc_discover_targets *mad;
+ struct ibmvfc_host *vhost = ibmvfc_channels_to_vhost(channels);
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt)
+ return NULL;
+
+ ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.discover_targets;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ if (channels->protocol == IBMVFC_PROTO_SCSI)
+ mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
+ else
+ mad->common.opcode = cpu_to_be32(IBMVFC_DISC_NVMF_TARGETS);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+ mad->bufflen = cpu_to_be32(channels->disc_buf_sz);
+ mad->buffer.va = cpu_to_be64(channels->disc_buf_dma);
+ mad->buffer.len = cpu_to_be32(channels->disc_buf_sz);
+ mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
+
+ return evt;
+}
+
/**
* ibmvfc_discover_targets - Send Discover Targets MAD
* @vhost: ibmvfc host struct
@@ -4995,30 +5184,168 @@ static void ibmvfc_discover_targets_done(struct ibmvfc_event *evt)
**/
static void ibmvfc_discover_targets(struct ibmvfc_host *vhost)
{
- struct ibmvfc_discover_targets *mad;
- struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+ struct ibmvfc_event *evt = ibmvfc_get_disc_event(&vhost->scsi_scrqs);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
if (!evt) {
- ibmvfc_log(vhost, level, "Discover Targets failed: no available events\n");
+ ibmvfc_log(vhost, level, "Discover SCSI Targets failed: no available events\n");
ibmvfc_hard_reset_host(vhost);
return;
}
- ibmvfc_init_event(evt, ibmvfc_discover_targets_done, IBMVFC_MAD_FORMAT);
- mad = &evt->iu.discover_targets;
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Sent discover SCSI targets\n");
+ else
+ goto link_down;
+
+ if (!ibmvfc_nvme_active(vhost))
+ return;
+
+ evt = ibmvfc_get_disc_event(&vhost->nvme_scrqs);
+ if (!evt) {
+ ibmvfc_log(vhost, level, "Discover NVMe Targets failed: no available events\n");
+ ibmvfc_hard_reset_host(vhost);
+ return;
+ }
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Sent discover NVMe targets\n");
+ else
+ goto link_down;
+
+ return;
+
+link_down:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+static void ibmvfc_fabric_login_nvme_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
+ ibmvfc_nvme_register(vhost);
+ ibmvfc_dbg(vhost, "NVMe fabric login succeeded\n");
+ break;
+ case IBMVFC_MAD_FAILED:
+ if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
+ level += ibmvfc_retry_host_init(vhost);
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_log(vhost, level, "NVMe fabric login failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
+ ibmvfc_free_event(evt);
+ return;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ ibmvfc_free_event(evt);
+ return;
+ default:
+ dev_err(vhost->dev, "Invalid NVMe fabric login response: 0x%x\n", mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ }
+
+ ibmvfc_free_event(evt);
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+}
+
+static void ibmvfc_fabric_login_nvme(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_fabric_login_mad *mad;
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt) {
+ ibmvfc_retry_host_init(vhost);
+ return;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_fabric_login_nvme_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.fabric_login;
memset(mad, 0, sizeof(*mad));
mad->common.version = cpu_to_be32(1);
- mad->common.opcode = cpu_to_be32(IBMVFC_DISC_TARGETS);
+ mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_FABRIC_LOGIN);
mad->common.length = cpu_to_be16(sizeof(*mad));
- mad->bufflen = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
- mad->buffer.va = cpu_to_be64(vhost->scsi_scrqs.disc_buf_dma);
- mad->buffer.len = cpu_to_be32(vhost->scsi_scrqs.disc_buf_sz);
- mad->flags = cpu_to_be32(IBMVFC_DISC_TGT_PORT_ID_WWPN_LIST);
+
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
+
+ if (!ibmvfc_send_event(evt, vhost, default_timeout))
+ ibmvfc_dbg(vhost, "Send NVMe fabric login\n");
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+}
+
+static void ibmvfc_fabric_login_scsi_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_host *vhost = evt->vhost;
+ struct ibmvfc_fabric_login_mad *rsp = &evt->xfer_iu->fabric_login;
+ u32 mad_status = be16_to_cpu(rsp->common.status);
+ int level = IBMVFC_DEFAULT_LOG_LEVEL;
+
+ switch (mad_status) {
+ case IBMVFC_MAD_SUCCESS:
+ fc_host_port_id(vhost->host) = be64_to_cpu(rsp->nport_id);
+ ibmvfc_dbg(vhost, "SCSI fabric login succeeded\n");
+ break;
+ case IBMVFC_MAD_FAILED:
+ if (ibmvfc_retry_cmd(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)))
+ level += ibmvfc_retry_host_init(vhost);
+ else
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ ibmvfc_log(vhost, level, "SCSI fabric login failed: %s (%x:%x)\n",
+ ibmvfc_get_cmd_error(be16_to_cpu(rsp->status), be16_to_cpu(rsp->error)),
+ be16_to_cpu(rsp->status), be16_to_cpu(rsp->error));
+ ibmvfc_free_event(evt);
+ return;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ ibmvfc_free_event(evt);
+ return;
+ default:
+ dev_err(vhost->dev, "Invalid SCSI fabric login response: 0x%x\n", mad_status);
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ }
+
+ ibmvfc_free_event(evt);
+
+ if (vhost->do_nvme_login) {
+ ibmvfc_fabric_login_nvme(vhost);
+ } else {
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+ }
+}
+
+static void ibmvfc_fabric_login_scsi(struct ibmvfc_host *vhost)
+{
+ struct ibmvfc_fabric_login_mad *mad;
+ struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
+
+ if (!evt) {
+ ibmvfc_retry_host_init(vhost);
+ return;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_fabric_login_scsi_done, IBMVFC_MAD_FORMAT);
+ mad = &evt->iu.fabric_login;
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(1);
+ mad->common.opcode = cpu_to_be32(IBMVFC_FABRIC_LOGIN);
+ mad->common.length = cpu_to_be16(sizeof(*mad));
+
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT_WAIT);
if (!ibmvfc_send_event(evt, vhost, default_timeout))
- ibmvfc_dbg(vhost, "Sent discover targets\n");
+ ibmvfc_dbg(vhost, "Send SCSI fabric login\n");
else
ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
}
@@ -5027,10 +5354,11 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
{
struct ibmvfc_host *vhost = evt->vhost;
struct ibmvfc_channel_setup *setup = vhost->channel_setup_buf;
- struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
+ struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+ struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;
u32 mad_status = be16_to_cpu(evt->xfer_iu->channel_setup.common.status);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
- int flags, active_queues, i;
+ int flags, i;
ibmvfc_free_event(evt);
@@ -5039,22 +5367,28 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
ibmvfc_dbg(vhost, "Channel Setup succeeded\n");
flags = be32_to_cpu(setup->flags);
vhost->do_enquiry = 0;
- active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
- scrqs->active_queues = active_queues;
+ scsi->active_queues = be32_to_cpu(setup->num_scsi_subq_channels);
+ nvme->active_queues = be32_to_cpu(setup->num_nvme_subq_channels);
if (flags & IBMVFC_CHANNELS_CANCELED) {
ibmvfc_dbg(vhost, "Channels Canceled\n");
vhost->using_channels = 0;
- } else {
- if (active_queues)
- vhost->using_channels = 1;
- for (i = 0; i < active_queues; i++)
- scrqs->scrqs[i].vios_cookie =
- be64_to_cpu(setup->channel_handles[i]);
-
- ibmvfc_dbg(vhost, "Using %u channels\n",
- vhost->scsi_scrqs.active_queues);
+ break;
}
+
+ if (scsi->active_queues || nvme->active_queues)
+ vhost->using_channels = 1;
+ for (i = 0; i < scsi->active_queues; i++)
+ scsi->scrqs[i].vios_cookie =
+ be64_to_cpu(setup->channel_handles[i]);
+ for (i = 0; i < nvme->active_queues; i++)
+ nvme->scrqs[i].vios_cookie =
+ be64_to_cpu(setup->channel_handles[scsi->active_queues + i]);
+
+ ibmvfc_dbg(vhost, "Using %u SCSI channels\n",
+ scsi->active_queues);
+ ibmvfc_dbg(vhost, "Using %u NVMe channels\n",
+ nvme->active_queues);
break;
case IBMVFC_MAD_FAILED:
level += ibmvfc_retry_host_init(vhost);
@@ -5069,8 +5403,12 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
return;
}
- ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
- wake_up(&vhost->work_wait_q);
+ if (vhost->do_scsi_login) {
+ ibmvfc_fabric_login_scsi(vhost);
+ } else {
+ ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
+ wake_up(&vhost->work_wait_q);
+ }
}
static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
@@ -5078,9 +5416,12 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
struct ibmvfc_channel_setup_mad *mad;
struct ibmvfc_channel_setup *setup_buf = vhost->channel_setup_buf;
struct ibmvfc_event *evt = ibmvfc_get_reserved_event(&vhost->crq);
- struct ibmvfc_channels *scrqs = &vhost->scsi_scrqs;
- unsigned int num_channels =
- min(scrqs->desired_queues, vhost->max_vios_scsi_channels);
+ struct ibmvfc_channels *scsi = &vhost->scsi_scrqs;
+ struct ibmvfc_channels *nvme = &vhost->nvme_scrqs;
+ unsigned int scsi_channels =
+ min(scsi->desired_queues, vhost->max_vios_scsi_channels);
+ unsigned int nvme_channels =
+ min(nvme->desired_queues, vhost->max_vios_nvme_channels);
int level = IBMVFC_DEFAULT_LOG_LEVEL;
int i;
@@ -5091,12 +5432,17 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
}
memset(setup_buf, 0, sizeof(*setup_buf));
- if (num_channels == 0)
+ if (!scsi_channels && !nvme_channels)
setup_buf->flags = cpu_to_be32(IBMVFC_CANCEL_CHANNELS);
else {
- setup_buf->num_scsi_subq_channels = cpu_to_be32(num_channels);
- for (i = 0; i < num_channels; i++)
- setup_buf->channel_handles[i] = cpu_to_be64(scrqs->scrqs[i].cookie);
+ setup_buf->num_scsi_subq_channels = cpu_to_be32(scsi_channels);
+ setup_buf->num_nvme_subq_channels = cpu_to_be32(nvme_channels);
+ for (i = 0; i < scsi_channels; i++)
+ setup_buf->channel_handles[i] =
+ cpu_to_be64(scsi->scrqs[i].cookie);
+ for (i = 0; i < nvme_channels; i++)
+ setup_buf->channel_handles[scsi_channels + i] =
+ cpu_to_be64(nvme->scrqs[i].cookie);
}
ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
@@ -5127,6 +5473,7 @@ static void ibmvfc_channel_enquiry_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
ibmvfc_dbg(vhost, "Channel Enquiry succeeded\n");
vhost->max_vios_scsi_channels = be32_to_cpu(rsp->num_scsi_subq_channels);
+ vhost->max_vios_nvme_channels = be32_to_cpu(rsp->num_nvme_subq_channels);
ibmvfc_free_event(evt);
break;
case IBMVFC_MAD_FAILED:
@@ -5261,8 +5608,14 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
vhost->host->can_queue = be32_to_cpu(rsp->max_cmds) - IBMVFC_NUM_INTERNAL_REQ;
vhost->host->max_sectors = npiv_max_sectors;
- if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS) && vhost->do_enquiry) {
- ibmvfc_channel_enquiry(vhost);
+
+ if (ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPORT_CHANNELS)) {
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_SCSI))
+ vhost->do_scsi_login = 1;
+ if (ibmvfc_check_caps(vhost, IBMVFC_SUPPORT_NVMEOF))
+ vhost->do_nvme_login = 1;
+ if (vhost->do_enquiry)
+ ibmvfc_channel_enquiry(vhost);
} else {
vhost->do_enquiry = 0;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
@@ -5323,6 +5676,7 @@ static void ibmvfc_npiv_logout_done(struct ibmvfc_event *evt)
case IBMVFC_MAD_SUCCESS:
if (list_empty(&vhost->crq.sent) &&
vhost->action == IBMVFC_HOST_ACTION_LOGO_WAIT) {
+ ibmvfc_nvme_unregister(vhost);
ibmvfc_init_host(vhost);
return;
}
@@ -5383,7 +5737,12 @@ static int ibmvfc_dev_init_to_do(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
+ tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+ return 1;
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT ||
tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 1;
@@ -5403,7 +5762,12 @@ static int ibmvfc_dev_logo_to_do(struct ibmvfc_host *vhost)
{
struct ibmvfc_target *tgt;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
+ tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+ return 1;
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT ||
tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
return 1;
@@ -5433,10 +5797,16 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_QUERY_TGTS:
if (vhost->discovery_threads == disc_threads)
return 0;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_INIT)
return 1;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT)
+ return 1;
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
+ return 0;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_INIT_WAIT)
return 0;
return 1;
@@ -5444,10 +5814,16 @@ static int __ibmvfc_work_to_do(struct ibmvfc_host *vhost)
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
if (vhost->discovery_threads == disc_threads)
return 0;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
+ return 1;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT)
return 1;
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
+ return 0;
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT_WAIT)
return 0;
return 1;
@@ -5501,6 +5877,51 @@ static void ibmvfc_log_ae(struct ibmvfc_host *vhost, int events)
}
/**
+ * ibmvfc_tgt_add_nvme_rport - Tell the FC transport about a new remote port
+ * @tgt: ibmvfc target struct
+ *
+ **/
+static void ibmvfc_tgt_add_nvme_rport(struct ibmvfc_target *tgt)
+{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct nvme_fc_remote_port *rport;
+ unsigned long flags;
+
+ tgt_dbg(tgt, "Adding NVMe rport\n");
+ ibmvfc_nvme_register_remoteport(tgt);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ rport = tgt->nvme_remote_port;
+
+ if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+ tgt_dbg(tgt, "Deleting NVMe rport\n");
+ list_del(&tgt->queue);
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ timer_delete_sync(&tgt->timer);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+ tgt->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DELETED_RPORT) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ return;
+ }
+
+ if (rport) {
+ tgt_dbg(tgt, "NVMe rport add succeeded\n");
+ tgt->target_id = tgt->nvme_remote_port->port_id;
+ } else
+ tgt_dbg(tgt, "NVMe rport add failed\n");
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+}
+
+/**
* ibmvfc_tgt_add_rport - Tell the FC transport about a new remote port
* @tgt: ibmvfc target struct
*
@@ -5564,6 +5985,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
struct ibmvfc_target *tgt;
unsigned long flags;
struct fc_rport *rport;
+ struct nvme_fc_remote_port *nvme_rport;
LIST_HEAD(purge);
int rc;
@@ -5579,6 +6001,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
list_splice_init(&vhost->purge, &purge);
spin_unlock_irqrestore(vhost->host->host_lock, flags);
ibmvfc_complete_purge(&purge);
+ ibmvfc_nvme_unregister(vhost);
rc = ibmvfc_reset_crq(vhost);
spin_lock_irqsave(vhost->host->host_lock, flags);
@@ -5635,12 +6058,20 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
vhost->job_step(vhost);
break;
case IBMVFC_HOST_ACTION_QUERY:
- list_for_each_entry(tgt, &vhost->targets, queue)
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue)
+ ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue)
ibmvfc_init_tgt(tgt, ibmvfc_tgt_query_target);
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY_TGTS);
break;
case IBMVFC_HOST_ACTION_QUERY_TGTS:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
tgt->job_step(tgt);
break;
@@ -5652,7 +6083,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
break;
case IBMVFC_HOST_ACTION_TGT_DEL:
case IBMVFC_HOST_ACTION_TGT_DEL_FAILED:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_LOGOUT_RPORT) {
tgt->job_step(tgt);
break;
@@ -5664,7 +6101,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
return;
}
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
tgt_dbg(tgt, "Deleting rport\n");
rport = tgt->rport;
@@ -5702,6 +6139,30 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
}
}
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_DEL_RPORT) {
+ tgt_dbg(tgt, "Deleteing NVMe rport\n");
+ nvme_rport = tgt->nvme_remote_port;
+ list_del(&tgt->queue);
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DELETED_RPORT);
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (nvme_rport)
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ timer_delete_sync(&tgt->timer);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return;
+ } else if (rport && tgt->action == IBMVFC_TGT_ACTION_DEL_AND_LOGOUT_RPORT) {
+ tgt_dbg(tgt, "Deleting NVMe rport with outstanding I/O\n");
+ nvme_rport = tgt->nvme_remote_port;
+ ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_LOGOUT_DELETED_RPORT);
+ tgt->init_retries = 0;
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (nvme_rport)
+ ibmvfc_nvme_unregister_remoteport(tgt);
+ return;
+ }
+ }
+
if (vhost->state == IBMVFC_INITIALIZING) {
if (vhost->action == IBMVFC_HOST_ACTION_TGT_DEL_FAILED) {
if (vhost->reinit) {
@@ -5739,7 +6200,13 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
spin_lock_irqsave(vhost->host->host_lock, flags);
break;
case IBMVFC_HOST_ACTION_TGT_INIT:
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
+ if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
+ tgt->job_step(tgt);
+ break;
+ }
+ }
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
if (tgt->action == IBMVFC_TGT_ACTION_INIT) {
tgt->job_step(tgt);
break;
@@ -6094,6 +6561,13 @@ static void ibmvfc_init_sub_crqs(struct ibmvfc_host *vhost)
ibmvfc_reg_sub_crqs(vhost, &vhost->scsi_scrqs);
+ if (vhost->nvme_enabled) {
+ if (ibmvfc_alloc_channels(vhost, &vhost->nvme_scrqs))
+ vhost->nvme_enabled = 0;
+ else
+ ibmvfc_reg_sub_crqs(vhost, &vhost->nvme_scrqs);
+ }
+
LEAVE;
}
@@ -6122,8 +6596,10 @@ static void ibmvfc_release_sub_crqs(struct ibmvfc_host *vhost)
return;
ibmvfc_dereg_sub_crqs(vhost, &vhost->scsi_scrqs);
-
ibmvfc_release_channels(vhost, &vhost->scsi_scrqs);
+
+ ibmvfc_dereg_sub_crqs(vhost, &vhost->nvme_scrqs);
+ ibmvfc_release_channels(vhost, &vhost->nvme_scrqs);
LEAVE;
}
@@ -6148,6 +6624,7 @@ static void ibmvfc_free_mem(struct ibmvfc_host *vhost)
mempool_destroy(vhost->tgt_pool);
kfree(vhost->trace);
ibmvfc_free_disc_buf(vhost->dev, &vhost->scsi_scrqs);
+ ibmvfc_free_disc_buf(vhost->dev, &vhost->nvme_scrqs);
dma_free_coherent(vhost->dev, sizeof(*vhost->login_buf),
vhost->login_buf, vhost->login_buf_dma);
dma_free_coherent(vhost->dev, sizeof(*vhost->channel_setup_buf),
@@ -6210,12 +6687,15 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
if (ibmvfc_alloc_disc_buf(dev, &vhost->scsi_scrqs))
goto free_login_buffer;
+ if (ibmvfc_alloc_disc_buf(dev, &vhost->nvme_scrqs))
+ goto free_scsi_disc_buffer;
+
vhost->trace = kzalloc_objs(struct ibmvfc_trace_entry,
IBMVFC_NUM_TRACE_ENTRIES);
atomic_set(&vhost->trace_index, -1);
if (!vhost->trace)
- goto free_scsi_disc_buffer;
+ goto free_nvme_disc_buffer;
vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
sizeof(struct ibmvfc_target));
@@ -6241,6 +6721,8 @@ free_tgt_pool:
mempool_destroy(vhost->tgt_pool);
free_trace:
kfree(vhost->trace);
+free_nvme_disc_buffer:
+ ibmvfc_free_disc_buf(dev, &vhost->nvme_scrqs);
free_scsi_disc_buffer:
ibmvfc_free_disc_buf(dev, &vhost->scsi_scrqs);
free_login_buffer:
@@ -6266,6 +6748,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
rport_add_work_q);
struct ibmvfc_target *tgt;
struct fc_rport *rport;
+ struct nvme_fc_remote_port *nvme_rport;
unsigned long flags;
int did_work;
@@ -6276,7 +6759,7 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
if (vhost->state != IBMVFC_ACTIVE)
break;
- list_for_each_entry(tgt, &vhost->targets, queue) {
+ list_for_each_entry(tgt, &vhost->scsi_scrqs.targets, queue) {
if (tgt->add_rport) {
did_work = 1;
tgt->add_rport = 0;
@@ -6299,7 +6782,28 @@ static void ibmvfc_rport_add_thread(struct work_struct *work)
break;
}
}
- } while(did_work);
+
+ list_for_each_entry(tgt, &vhost->nvme_scrqs.targets, queue) {
+ if (tgt->add_rport) {
+ did_work = 1;
+ tgt->add_rport = 0;
+ kref_get(&tgt->kref);
+ nvme_rport = tgt->nvme_remote_port;
+ if (!nvme_rport) {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_tgt_add_nvme_rport(tgt);
+ } else {
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ if (IS_ENABLED(CONFIG_NVME_FC))
+ nvme_fc_rescan_remoteport(nvme_rport);
+ }
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ break;
+ }
+ }
+ } while (did_work);
if (vhost->state == IBMVFC_ACTIVE)
vhost->scan_complete = 1;
@@ -6322,7 +6826,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
struct device *dev = &vdev->dev;
int rc = -ENOMEM;
unsigned int online_cpus = num_online_cpus();
- unsigned int max_scsi_queues = min((unsigned int)IBMVFC_MAX_SCSI_QUEUES, online_cpus);
+ unsigned int max_scsi_queues = min_t(unsigned int, IBMVFC_MAX_SCSI_QUEUES, online_cpus);
+ unsigned int max_nvme_queues = min_t(unsigned int, IBMVFC_MAX_NVME_QUEUES, online_cpus);
ENTER;
shost = scsi_host_alloc(&driver_template, sizeof(*vhost));
@@ -6341,7 +6846,8 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
shost->nr_hw_queues = mq_enabled ? min(max_scsi_queues, nr_scsi_hw_queues) : 1;
vhost = shost_priv(shost);
- INIT_LIST_HEAD(&vhost->targets);
+ INIT_LIST_HEAD(&vhost->scsi_scrqs.targets);
+ INIT_LIST_HEAD(&vhost->nvme_scrqs.targets);
INIT_LIST_HEAD(&vhost->purge);
sprintf(vhost->name, IBMVFC_NAME);
vhost->host = shost;
@@ -6356,6 +6862,10 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
vhost->scsi_scrqs.protocol = IBMVFC_PROTO_SCSI;
vhost->using_channels = 0;
vhost->do_enquiry = 1;
+ vhost->nvme_enabled = mq_enabled ? nvme_enabled : 0;
+ vhost->nvme_scrqs.desired_queues = min(max_nvme_queues, nr_nvme_channels);
+ vhost->nvme_scrqs.max_queues = max_nvme_queues;
+ vhost->nvme_scrqs.protocol = IBMVFC_PROTO_NVME;
vhost->scan_timeout = 0;
strcpy(vhost->partition_name, "UNKNOWN");
@@ -6441,6 +6951,7 @@ static void ibmvfc_remove(struct vio_dev *vdev)
ibmvfc_wait_while_resetting(vhost);
kthread_stop(vhost->work_thread);
+ flush_work(&vhost->rport_add_work_q);
fc_remove_host(vhost->host);
scsi_remove_host(vhost->host);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.c b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
new file mode 100644
index 000000000000..54f80b6265d4
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.c
@@ -0,0 +1,573 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * ibmvfc-nvme.c -- IBM Power Virtual Fibre Channel NVMeoF HBA driver
+ *
+ * Written By: Tyrel Datwyler <tyreld@linux.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2026
+ */
+
+#include <linux/dmapool.h>
+#include <scsi/scsi_transport_fc.h>
+
+#include "ibmvfc-nvme.h"
+
+static unsigned int default_timeout = IBMVFC_DEFAULT_TIMEOUT;
+
+static void ibmvfc_nvme_localport_delete(struct nvme_fc_local_port *lport)
+{
+ struct ibmvfc_host *vhost = lport->private;
+
+ vhost->nvme_local_port = NULL;
+ complete(&vhost->nvme_delete_done);
+}
+
+static void ibmvfc_nvme_remoteport_delete(struct nvme_fc_remote_port *rport)
+{
+ struct ibmvfc_target *tgt = rport->private;
+
+ tgt->nvme_remote_port = NULL;
+ complete(&tgt->nvme_delete_done);
+}
+
+static int ibmvfc_nvme_create_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
+ u16 qsize, void **handle)
+{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_nvme_qhandle *qhandle;
+
+ if (!vhost->nvme_scrqs.active_queues)
+ return -ENODEV;
+
+ qhandle = kzalloc_obj(struct ibmvfc_nvme_qhandle);
+ if (!qhandle)
+ return -ENOMEM;
+
+ qhandle->cpu_id = raw_smp_processor_id();
+ qhandle->qidx = qidx;
+
+ /* Admin and first IO queue are both mapped to index 0 */
+ if (qidx)
+ qhandle->index = (qidx - 1) % vhost->nvme_scrqs.active_queues;
+ else
+ qhandle->index = qidx;
+
+ qhandle->queue = &vhost->nvme_scrqs.scrqs[qhandle->index];
+
+ *handle = qhandle;
+ return 0;
+}
+
+static void ibmvfc_nvme_delete_queue(struct nvme_fc_local_port *lport, unsigned int qidx,
+ void *handle)
+{
+ kfree(handle);
+}
+
+static void ibmvfc_ls_req_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_target *tgt = evt->tgt;
+ struct ibmvfc_passthru_mad *mad = &evt->xfer_iu->passthru;
+ struct fcnvme_ls_rqst_w0 *ls_rqst;
+ struct fcnvme_ls_cr_assoc_acc *ls_resp;
+ u32 status = be16_to_cpu(mad->common.status);
+ int rc = 0;
+
+ ls_rqst = (struct fcnvme_ls_rqst_w0 *)evt->ls_req->rqstaddr;
+ ls_resp = (struct fcnvme_ls_cr_assoc_acc *)evt->ls_req->rspaddr;
+
+ switch (status) {
+ case IBMVFC_MAD_SUCCESS:
+ tgt_dbg(tgt, "ls_req succeeded\n");
+ if ((ls_rqst->ls_cmd == FCNVME_LS_CREATE_ASSOCIATION) &&
+ (ls_resp->hdr.w0.ls_cmd == FCNVME_LS_ACC)) {
+ tgt->assoc_id = be64_to_cpu(ls_resp->associd.association_id);
+ tgt_dbg(tgt, "assoc_id 0x%llx\n", tgt->assoc_id);
+ }
+ break;
+ case IBMVFC_MAD_DRIVER_FAILED:
+ break;
+ case IBMVFC_MAD_FAILED:
+ default:
+ tgt_info(tgt, "ls_req failed: %s (%x:%x) rc=0x%02X\n",
+ ibmvfc_get_cmd_error(be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error)),
+ be16_to_cpu(mad->iu.status), be16_to_cpu(mad->iu.error), status);
+ break;
+ }
+
+ if (status)
+ rc = -EIO;
+
+ evt->ls_req->done(evt->ls_req, rc);
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ ibmvfc_free_event(evt);
+}
+
+static void ibmvfc_init_ls_req(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_req)
+{
+ struct ibmvfc_passthru_mad *mad = &evt->iu.passthru;
+
+ memset(mad, 0, sizeof(*mad));
+ mad->common.version = cpu_to_be32(2);
+ mad->common.opcode = cpu_to_be32(IBMVFC_NVMF_PASSTHRU);
+ mad->common.length = cpu_to_be16(sizeof(*mad) - sizeof(mad->fc_iu) - sizeof(mad->iu));
+ mad->cmd_ioba.va = cpu_to_be64((u64)be64_to_cpu(evt->crq.ioba) +
+ offsetof(struct ibmvfc_passthru_mad, iu));
+ mad->cmd_ioba.len = cpu_to_be32(sizeof(mad->iu));
+ mad->iu.cmd_len = cpu_to_be32(ls_req->rqstlen);
+ mad->iu.rsp_len = cpu_to_be32(ls_req->rsplen);
+ mad->iu.cmd.va = cpu_to_be64(ls_req->rqstdma);
+ mad->iu.cmd.len = cpu_to_be32(ls_req->rqstlen);
+ mad->iu.rsp.va = cpu_to_be64(ls_req->rspdma);
+ mad->iu.rsp.len = cpu_to_be32(ls_req->rsplen);
+}
+
+static int ibmvfc_nvme_ls_req(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_ls_req *ls_req)
+{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_passthru_mad *mad;
+ struct ibmvfc_event *evt;
+
+ kref_get(&tgt->kref);
+ evt = ibmvfc_get_event(&vhost->crq);
+ if (!evt) {
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return -EBUSY;
+ }
+
+ ibmvfc_init_event(evt, ibmvfc_ls_req_done, IBMVFC_MAD_FORMAT);
+ evt->tgt = tgt;
+ evt->ls_req = ls_req;
+ ls_req->private = evt;
+
+ ibmvfc_init_ls_req(evt, ls_req);
+ mad = &evt->iu.passthru;
+ mad->iu.flags = cpu_to_be32(IBMVFC_FC4_LS_DSC_CTRL);
+ mad->iu.scsi_id = cpu_to_be64(tgt->scsi_id);
+ mad->iu.cancel_key = cpu_to_be32((u64)evt);
+ mad->iu.target_wwpn = cpu_to_be64(tgt->wwpn);
+
+ ibmvfc_dbg(vhost, "nvme_ls_req\n");
+ if (ibmvfc_send_event(evt, vhost, IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT)) {
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void ibmvfc_sync_nvme_completion(struct ibmvfc_event *evt)
+{
+ /* copy the response back */
+ if (evt->sync_iu)
+ *evt->sync_iu = *evt->xfer_iu;
+
+ complete(&evt->comp);
+}
+
+static void ibmvfc_init_ls_abort(struct ibmvfc_event *evt, struct nvmefc_ls_req *ls_abort)
+{
+ struct ibmvfc_tmf *tmf;
+ struct ibmvfc_event *abt_evt = ls_abort->private;
+ struct ibmvfc_target *tgt = abt_evt->tgt;
+ struct ibmvfc_host *vhost = evt->vhost;
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = cpu_to_be32(2);
+ tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+ tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
+ tmf->common.length = cpu_to_be16(sizeof(*tmf));
+ if (vhost->state != IBMVFC_ACTIVE)
+ if (!ibmvfc_check_caps(vhost, IBMVFC_CAN_SUPPRESS_ABTS))
+ tmf->flags = cpu_to_be32(IBMVFC_TMF_SUPPRESS_ABTS);
+ tmf->cancel_key = cpu_to_be32((u64)abt_evt);
+ tmf->my_cancel_key = cpu_to_be32((u64)evt);
+ tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+ init_completion(&evt->comp);
+}
+
+static void ibmvfc_nvme_ls_abort(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_ls_req *ls_abort)
+{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_event *evt;
+ union ibmvfc_iu rsp;
+ unsigned long flags;
+ u16 status;
+
+ evt = ibmvfc_get_event(&vhost->crq);
+ if (!vhost->logged_in || !evt)
+ return;
+
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ kref_get(&tgt->kref);
+ ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+ ibmvfc_init_ls_abort(evt, ls_abort);
+ evt->sync_iu = &rsp;
+
+ if (ibmvfc_send_event(evt, vhost, default_timeout))
+ goto out;
+
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+
+ wait_for_completion(&evt->comp);
+ status = be16_to_cpu(rsp.mad_common.status);
+ spin_lock_irqsave(vhost->host->host_lock, flags);
+ ibmvfc_free_event(evt);
+out:
+ spin_unlock_irqrestore(vhost->host->host_lock, flags);
+ ibmvfc_dbg(vhost, "ls_abort: cancel failed with rc=%x\n", status);
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+}
+
+static void ibmvfc_nvme_done(struct ibmvfc_event *evt)
+{
+ struct ibmvfc_cmd *vfc_cmd = &evt->xfer_iu->cmd;
+ struct nvmefc_fcp_req *fcp_req = evt->fcp_req;
+ struct nvme_fc_ersp_iu *ersp = (struct nvme_fc_ersp_iu *)fcp_req->rspaddr;
+ struct nvme_completion *cqe = &ersp->cqe;
+ struct nvme_command *sqe = &((struct nvme_fc_cmd_iu *)fcp_req->cmdaddr)->sqe;
+
+ ibmvfc_dbg(evt->vhost, "fc_done: (%x:%x)\n", be16_to_cpu(vfc_cmd->status),
+ be16_to_cpu(vfc_cmd->error));
+ ibmvfc_dbg(evt->vhost, "fc_done: cmdlen: %d, rsplen %d, payload_len %d\n",
+ fcp_req->cmdlen, fcp_req->rsplen, fcp_req->payload_length);
+
+ fcp_req->status = 0;
+ if (!vfc_cmd->status) {
+ fcp_req->rcv_rsplen = NVME_FC_SIZEOF_ZEROS_RSP;
+ fcp_req->transferred_length = fcp_req->payload_length;
+ } else if (be16_to_cpu(vfc_cmd->status) & IBMVFC_FC_NVME_STATUS) {
+ fcp_req->rcv_rsplen = sizeof(struct nvme_fc_ersp_iu);
+ fcp_req->transferred_length = be32_to_cpu(ersp->xfrd_len);
+ if (be16_to_cpu(vfc_cmd->error) & IBMVFC_NVMS_VALID_NODMA_CQE)
+ cqe->command_id = sqe->common.command_id;
+ } else {
+ fcp_req->rcv_rsplen = 0;
+ fcp_req->transferred_length = 0;
+ fcp_req->status = NVME_SC_INTERNAL;
+ }
+
+ fcp_req->done(fcp_req);
+ ibmvfc_free_event(evt);
+}
+
+static struct ibmvfc_cmd *ibmvfc_nvme_init_vfc_cmd(struct ibmvfc_event *evt,
+ struct nvme_fc_remote_port *rport,
+ struct nvmefc_fcp_req *fcp_req)
+{
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_cmd *vfc_cmd = &evt->iu.cmd;
+
+ memset(vfc_cmd, 0, sizeof(*vfc_cmd));
+
+ vfc_cmd->resp.va = cpu_to_be64(fcp_req->rspdma);
+ vfc_cmd->resp.len = cpu_to_be32(fcp_req->rsplen);
+ vfc_cmd->frame_type = cpu_to_be32(IBMVFC_NVME_FCP_TYPE);
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_NVMEOF_PROTOCOL);
+ vfc_cmd->payload_len = cpu_to_be32(fcp_req->cmdlen);
+ vfc_cmd->resp_len = cpu_to_be32(fcp_req->rsplen);
+ vfc_cmd->cancel_key = cpu_to_be32((u64)evt);
+ vfc_cmd->target_wwpn = cpu_to_be64(rport->port_name);
+ vfc_cmd->tgt_scsi_id = cpu_to_be64(rport->port_id);
+ vfc_cmd->assoc_id = cpu_to_be64(tgt->assoc_id);
+
+ memcpy(&vfc_cmd->v3nvme.iu, fcp_req->cmdaddr, fcp_req->cmdlen);
+
+ return vfc_cmd;
+}
+
+static void ibmvfc_nvme_map_sg_list(struct nvmefc_fcp_req *fcp_req,
+ struct srp_direct_buf *md)
+{
+ int i;
+ struct scatterlist *sg;
+
+ for_each_sg(fcp_req->first_sgl, sg, fcp_req->sg_cnt, i) {
+ md[i].va = cpu_to_be64(sg_dma_address(sg));
+ md[i].len = cpu_to_be32(sg_dma_len(sg));
+ md[i].key = 0;
+ }
+}
+
+static int ibmvfc_nvme_map_sg_data(struct nvmefc_fcp_req *fcp_req,
+ struct ibmvfc_event *evt,
+ struct ibmvfc_cmd *vfc_cmd)
+{
+ struct srp_direct_buf *data = &vfc_cmd->ioba;
+ struct ibmvfc_host *vhost = evt->vhost;
+
+ if (!fcp_req->sg_cnt) {
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_NO_MEM_DESC);
+ return 0;
+ }
+
+ if (fcp_req->io_dir == NVMEFC_FCP_WRITE)
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_WRITE);
+ else
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_READ);
+
+ if (fcp_req->sg_cnt == 1) {
+ ibmvfc_nvme_map_sg_list(fcp_req, data);
+ return 0;
+ }
+
+ vfc_cmd->flags |= cpu_to_be16(IBMVFC_SCATTERLIST);
+
+ if (!evt->ext_list) {
+ evt->ext_list = dma_pool_alloc(vhost->sg_pool, GFP_ATOMIC,
+ &evt->ext_list_token);
+
+ if (!evt->ext_list)
+ return -ENOMEM;
+ }
+
+ ibmvfc_nvme_map_sg_list(fcp_req, evt->ext_list);
+
+ data->va = cpu_to_be64(evt->ext_list_token);
+ data->len = cpu_to_be32(fcp_req->sg_cnt * sizeof(struct srp_direct_buf));
+ data->key = 0;
+ return 0;
+}
+
+static int ibmvfc_nvme_fcp_io(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ void *hw_queue_handle,
+ struct nvmefc_fcp_req *fcp_req)
+{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_nvme_qhandle *qhandle = hw_queue_handle;
+ struct ibmvfc_cmd *vfc_cmd;
+ struct ibmvfc_event *evt;
+ int rc;
+
+ ibmvfc_dbg(vhost, "nvme_fcp_io\n");
+ evt = ibmvfc_get_event(qhandle->queue);
+ if (!evt)
+ return -EBUSY;
+
+ evt->hwq = qhandle->index;
+ ibmvfc_dbg(vhost, "vfc-nvme-mq-%d\n", evt->hwq);
+
+ ibmvfc_init_event(evt, ibmvfc_nvme_done, IBMVFC_CMD_FORMAT);
+ evt->fcp_req = fcp_req;
+ fcp_req->private = evt;
+
+ vfc_cmd = ibmvfc_nvme_init_vfc_cmd(evt, rport, fcp_req);
+
+ vfc_cmd->correlation = cpu_to_be64((u64)evt);
+
+ if (likely(!(rc = ibmvfc_nvme_map_sg_data(fcp_req, evt, vfc_cmd))))
+ return ibmvfc_send_event(evt, vhost, 0);
+
+ ibmvfc_free_event(evt);
+
+ return rc;
+}
+
+static void ibmvfc_init_fcp_abort(struct ibmvfc_event *evt,
+ struct nvmefc_fcp_req *abort_req)
+{
+ struct ibmvfc_tmf *tmf;
+ struct ibmvfc_event *abt_evt = abort_req->private;
+ struct ibmvfc_target *tgt = abt_evt->tgt;
+
+ tmf = &evt->iu.tmf;
+ memset(tmf, 0, sizeof(*tmf));
+ tmf->common.version = cpu_to_be32(2);
+ tmf->common.opcode = cpu_to_be32(IBMVFC_NVMF_TMF_MAD);
+ tmf->common.length = cpu_to_be16(sizeof(*tmf));
+ tmf->flags = cpu_to_be32(IBMVFC_TMF_ABORT_TASK | IBMVFC_TMF_NVMF_ASSOC);
+ tmf->cancel_key = cpu_to_be32((u64)abt_evt);
+ tmf->my_cancel_key = cpu_to_be32((u64)evt);
+ tmf->target_wwpn = cpu_to_be64(tgt->wwpn);
+ tmf->assoc_id = cpu_to_be64(tgt->assoc_id);
+ tmf->task_tag = cpu_to_be64((u64)abt_evt);
+
+ init_completion(&evt->comp);
+}
+
+static void ibmvfc_nvme_fcp_abort(struct nvme_fc_local_port *lport,
+ struct nvme_fc_remote_port *rport,
+ void *hw_queue_handle,
+ struct nvmefc_fcp_req *abort_req)
+{
+ struct ibmvfc_host *vhost = lport->private;
+ struct ibmvfc_target *tgt = rport->private;
+ struct ibmvfc_event *evt, *abt_evt = abort_req->private;
+ struct ibmvfc_queue *queue;
+ union ibmvfc_iu rsp;
+ unsigned long flags;
+ u16 status = 0;
+
+ if (!abt_evt)
+ return;
+
+ queue = abt_evt->queue;
+ if (!vhost->logged_in || !queue)
+ return;
+
+ evt = ibmvfc_get_event(queue);
+ if (!evt)
+ return;
+
+ spin_lock_irqsave(queue->q_lock, flags);
+ kref_get(&tgt->kref);
+ ibmvfc_init_event(evt, ibmvfc_sync_nvme_completion, IBMVFC_MAD_FORMAT);
+ ibmvfc_init_fcp_abort(evt, abort_req);
+ evt->sync_iu = &rsp;
+
+ if (ibmvfc_send_event(evt, vhost, default_timeout))
+ goto out;
+
+ spin_unlock_irqrestore(queue->q_lock, flags);
+
+ wait_for_completion(&evt->comp);
+ status = be16_to_cpu(rsp.mad_common.status);
+
+ spin_lock_irqsave(queue->q_lock, flags);
+ ibmvfc_free_event(evt);
+out:
+ spin_unlock_irqrestore(queue->q_lock, flags);
+
+ if (status)
+ ibmvfc_dbg(vhost, "fcp_abort: cancel failed with rc=%x\n", status);
+
+ kref_put(&tgt->kref, ibmvfc_release_tgt);
+}
+
+static struct nvme_fc_port_template ibmvfc_nvme_fc_transport = {
+ .localport_delete = ibmvfc_nvme_localport_delete,
+ .remoteport_delete = ibmvfc_nvme_remoteport_delete,
+ .create_queue = ibmvfc_nvme_create_queue,
+ .delete_queue = ibmvfc_nvme_delete_queue,
+ .ls_req = ibmvfc_nvme_ls_req,
+ .ls_abort = ibmvfc_nvme_ls_abort,
+ .fcp_io = ibmvfc_nvme_fcp_io,
+ .fcp_abort = ibmvfc_nvme_fcp_abort,
+ .map_queues = NULL,
+ .max_hw_queues = IBMVFC_NVME_HW_QUEUES,
+ .max_sgl_segments = 1024,
+ .max_dif_sgl_segments = 64,
+ .dma_boundary = 0xFFFFFFFF,
+ .local_priv_sz = sizeof(struct ibmvfc_host *),
+ .remote_priv_sz = sizeof(struct ibmvfc_target *),
+ .lsrqst_priv_sz = sizeof(struct ibmvfc_event *),
+ .fcprqst_priv_sz = sizeof(struct ibmvfc_event *),
+};
+
+int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt)
+{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct nvme_fc_port_info pinfo;
+ int rc;
+
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return 0;
+
+ if (!vhost->nvme_local_port) {
+ dev_err(vhost->dev, "Attempt to register NVMe fc remoteport without valid localport\n");
+ return -EINVAL;
+ }
+
+ memset(&pinfo, 0, sizeof(struct nvme_fc_port_info));
+ pinfo.node_name = tgt->ids.node_name;
+ pinfo.port_name = tgt->ids.port_name;
+ pinfo.port_id = tgt->ids.port_id;
+ pinfo.port_role = FC_PORT_ROLE_NVME_TARGET;
+
+ rc = nvme_fc_register_remoteport(vhost->nvme_local_port, &pinfo,
+ &tgt->nvme_remote_port);
+
+ if (!rc) {
+ ibmvfc_log(vhost, 2, "register_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
+ pinfo.node_name, pinfo.port_name, pinfo.port_id);
+ tgt->nvme_remote_port->private = tgt;
+ }
+
+ return rc;
+}
+
+void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt)
+{
+ struct ibmvfc_host *vhost = tgt->vhost;
+ struct nvme_fc_remote_port *rport = tgt->nvme_remote_port;
+ int rc;
+
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return;
+
+ if (!tgt->nvme_remote_port)
+ return;
+
+ ibmvfc_log(vhost, 2, "unregister_remoteport: traddr=nn-0x%llx:pn-0x%llx PortID:%x\n",
+ rport->node_name, rport->port_name, rport->port_id);
+ init_completion(&tgt->nvme_delete_done);
+ rc = nvme_fc_unregister_remoteport(tgt->nvme_remote_port);
+
+ if (!rc) {
+ wait_for_completion(&tgt->nvme_delete_done);
+ }
+}
+
+int ibmvfc_nvme_register(struct ibmvfc_host *vhost)
+{
+ struct nvme_fc_port_info pinfo;
+ int rc;
+
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return 0;
+
+ pinfo.node_name = fc_host_node_name(vhost->host);
+ pinfo.port_name = fc_host_port_name(vhost->host);
+ pinfo.port_id = fc_host_port_id(vhost->host);
+ pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR;
+ pinfo.dev_loss_tmo = 0;
+
+ rc = nvme_fc_register_localport(&pinfo, &ibmvfc_nvme_fc_transport,
+ get_device(vhost->dev),
+ &vhost->nvme_local_port);
+
+ if (!rc) {
+ ibmvfc_log(vhost, 2, "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+ pinfo.node_name, pinfo.port_name, pinfo.port_id);
+ vhost->nvme_local_port->private = vhost;
+ } else {
+ dev_err(vhost->dev, "Failed to register NVMe fc localport (%d)\n", rc);
+ put_device(vhost->dev);
+ }
+
+ return rc;
+}
+
+void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost)
+{
+ int rc;
+
+ if (!IS_ENABLED(CONFIG_NVME_FC))
+ return;
+
+ if (vhost->nvme_local_port) {
+ ibmvfc_log(vhost, 2, "unregister_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+ vhost->nvme_local_port->node_name,
+ vhost->nvme_local_port->port_name,
+ vhost->nvme_local_port->port_id);
+ init_completion(&vhost->nvme_delete_done);
+ rc = nvme_fc_unregister_localport(vhost->nvme_local_port);
+ if (!rc) {
+ wait_for_completion(&vhost->nvme_delete_done);
+ } else
+ dev_err(vhost->dev, "Failed to unregister NVMe fc localport (%d)\n", rc);
+
+ put_device(vhost->dev);
+ }
+}
diff --git a/drivers/scsi/ibmvscsi/ibmvfc-nvme.h b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
new file mode 100644
index 000000000000..e245b6ed0875
--- /dev/null
+++ b/drivers/scsi/ibmvscsi/ibmvfc-nvme.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * ibmvfc-nvme.h -- IBM Power Virtual Fibre Channel NVMeoF HBA driver
+ *
+ * Written By: Tyrel Datwyler <tyreld@linux.ibm.com>, IBM Corporation
+ *
+ * Copyright (C) IBM Corporation, 2026
+ */
+
+#ifndef _IBMVFC_NVME_H
+#define _IBMVFC_NVME_H
+
+#include <scsi/fc/fc_fs.h>
+#include <scsi/fc/fc_els.h>
+#include <linux/nvme-fc-driver.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+
+#include "ibmvfc.h"
+
+#define IBMVFC_NVME 0
+#define IBMVFC_NVME_HW_QUEUES 8
+#define IBMVFC_MAX_NVME_QUEUES 16
+#define IBMVFC_NVME_CHANNELS 8
+
+#define IBMVFC_FC4_LS_TIMEOUT 15
+#define IBMVFC_FC4_LS_CANCEL_TIMEOUT 45
+#define IBMVFC_FC4_LS_PLUS_CANCEL_TIMEOUT \
+ (IBMVFC_FC4_LS_TIMEOUT + IBMVFC_FC4_LS_CANCEL_TIMEOUT)
+
+extern unsigned int ibmvfc_debug;
+
+struct ibmvfc_host;
+struct ibmvfc_target;
+struct ibmvfc_queue;
+
+struct ibmvfc_nvme_qhandle {
+ unsigned int qidx;
+ u16 cpu_id;
+ unsigned long index;
+ struct ibmvfc_queue *queue;
+};
+
+int ibmvfc_nvme_register_remoteport(struct ibmvfc_target *tgt);
+void ibmvfc_nvme_unregister_remoteport(struct ibmvfc_target *tgt);
+int ibmvfc_nvme_register(struct ibmvfc_host *vhost);
+void ibmvfc_nvme_unregister(struct ibmvfc_host *vhost);
+#endif
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index c73ed2314ad0..ca80ceffe53a 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -1,20 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+
/*
* ibmvfc.h -- driver for IBM Power Virtual Fibre Channel Adapter
*
* Written By: Brian King <brking@linux.vnet.ibm.com>, IBM Corporation
*
- * Copyright (C) IBM Corporation, 2008
+ * Copyright (C) IBM Corporation, 2008-2026
*/
#ifndef _IBMVFC_H
#define _IBMVFC_H
+#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/types.h>
+#include <scsi/scsi_device.h>
#include <scsi/viosrp.h>
+#include <linux/nvme.h>
+#include <linux/nvme-fc.h>
+
+#include "ibmvfc-nvme.h"
-#define IBMVFC_NAME "ibmvfc"
+#define IBMVFC_NAME "ibmvfc"
#define IBMVFC_DRIVER_VERSION "1.0.11"
#define IBMVFC_DRIVER_DATE "(April 12, 2013)"
@@ -92,6 +99,7 @@ enum ibmvfc_cmd_status_flags {
IBMVFC_FC_SCSI_ERROR = 0x0008,
IBMVFC_HW_EVENT_LOGGED = 0x0010,
IBMVFC_VIOS_LOGGED = 0x0020,
+ IBMVFC_FC_NVME_STATUS = 0x0040,
};
enum ibmvfc_fabric_mapped_errors {
@@ -124,20 +132,37 @@ enum ibmvfc_vios_errors {
IBMVFC_COMMAND_FAILED = 0x8000,
};
+enum ibmvfc_fc_nvme_errors {
+ IBMVFC_NVMS_VALID_ERSP = 0x0001,
+ IBMVFC_NVMS_VALID_NODMA_CQE = 0x0002,
+};
+
enum ibmvfc_mad_types {
IBMVFC_NPIV_LOGIN = 0x0001,
- IBMVFC_DISC_TARGETS = 0x0002,
+ IBMVFC_DISC_TARGETS = 0x0002,
+ IBMVFC_DISC_NVMF_TARGETS = 0x0003,
IBMVFC_PORT_LOGIN = 0x0004,
- IBMVFC_PROCESS_LOGIN = 0x0008,
- IBMVFC_QUERY_TARGET = 0x0010,
+ IBMVFC_NVMF_PORT_LOGIN = 0x0005,
+ IBMVFC_PROCESS_LOGIN = 0x0008,
+ IBMVFC_NVMF_PROCESS_LOGIN = 0x0009,
+ IBMVFC_QUERY_TARGET = 0x0010,
+ IBMVFC_NVMF_QUERY_TARGET = 0x0011,
IBMVFC_MOVE_LOGIN = 0x0020,
- IBMVFC_IMPLICIT_LOGOUT = 0x0040,
- IBMVFC_PASSTHRU = 0x0200,
- IBMVFC_TMF_MAD = 0x0100,
- IBMVFC_NPIV_LOGOUT = 0x0800,
- IBMVFC_CHANNEL_ENQUIRY = 0x1000,
- IBMVFC_CHANNEL_SETUP = 0x2000,
- IBMVFC_CONNECTION_INFO = 0x4000,
+ IBMVFC_NVMF_MOVE_LOGIN = 0x0021,
+ IBMVFC_IMPLICIT_LOGOUT = 0x0040,
+ IBMVFC_NVMF_IMPLICIT_LOGOUT = 0x0041,
+ IBMVFC_RNID = 0x0080,
+ IBMVFC_NVMF_RNID = 0x0081,
+ IBMVFC_TMF_MAD = 0x0100,
+ IBMVFC_NVMF_TMF_MAD = 0x0101,
+ IBMVFC_PASSTHRU = 0x0200,
+ IBMVFC_NVMF_PASSTHRU = 0x0201,
+ IBMVFC_NPIV_LOGOUT = 0x0800,
+ IBMVFC_CHANNEL_ENQUIRY = 0x1000,
+ IBMVFC_CHANNEL_SETUP = 0x2000,
+ IBMVFC_CONNECTION_INFO = 0x4000,
+ IBMVFC_FABRIC_LOGIN = 0x8000,
+ IBMVFC_NVMF_FABRIC_LOGIN = 0x8001,
};
struct ibmvfc_mad_common {
@@ -175,11 +200,16 @@ struct ibmvfc_npiv_login {
#define IBMVFC_FLUSH_ON_HALT 0x02
__be32 max_cmds;
__be64 capabilities;
-#define IBMVFC_CAN_MIGRATE 0x01
-#define IBMVFC_CAN_USE_CHANNELS 0x02
-#define IBMVFC_CAN_HANDLE_FPIN 0x04
-#define IBMVFC_CAN_USE_MAD_VERSION 0x08
-#define IBMVFC_CAN_SEND_VF_WWPN 0x10
+#define IBMVFC_CAN_MIGRATE 0x001
+#define IBMVFC_CAN_USE_CHANNELS 0x002
+#define IBMVFC_CAN_HANDLE_FPIN 0x004
+#define IBMVFC_CAN_USE_MAD_VERSION 0x008
+#define IBMVFC_CAN_SEND_VF_WWPN 0x010
+#define IBMVFC_YES_NVMEOF 0x020
+#define IBMVFC_YES_SCSI 0x040
+#define IBMVFC_CAN_USE_WWPN_ALL 0x080
+#define IBMVFC_USE_ASYNC_SUBQ 0x100
+#define IBMVFC_CAN_USE_NOOP_CMD 0x200
__be64 node_name;
struct srp_direct_buf async;
u8 partition_name[IBMVFC_MAX_NAME];
@@ -219,13 +249,18 @@ struct ibmvfc_npiv_login_resp {
__be16 error;
__be32 flags;
#define IBMVFC_NATIVE_FC 0x01
- __be32 reserved;
+ __be32 possible_nports;
__be64 capabilities;
-#define IBMVFC_CAN_FLUSH_ON_HALT 0x08
-#define IBMVFC_CAN_SUPPRESS_ABTS 0x10
-#define IBMVFC_MAD_VERSION_CAP 0x20
-#define IBMVFC_HANDLE_VF_WWPN 0x40
-#define IBMVFC_CAN_SUPPORT_CHANNELS 0x80
+#define IBMVFC_CAN_FLUSH_ON_HALT 0x0008
+#define IBMVFC_CAN_SUPPRESS_ABTS 0x0010
+#define IBMVFC_MAD_VERSION_CAP 0x0020
+#define IBMVFC_HANDLE_VF_WWPN 0x0040
+#define IBMVFC_CAN_SUPPORT_CHANNELS 0x0080
+#define IBMVFC_SUPPORT_NVMEOF 0x0100
+#define IBMVFC_SUPPORT_SCSI 0x0200
+#define IBMVFC_SUPPORT_WWPN_ALL 0x0400
+#define IBMVFC_ASYNC_SUBQ 0x0800
+#define IBMVFC_SUPPORT_NOOP_CMD 0x1000
__be32 max_cmds;
__be32 scsi_id_sz;
__be64 max_dma_len;
@@ -238,7 +273,7 @@ struct ibmvfc_npiv_login_resp {
u8 port_loc_code[IBMVFC_MAX_NAME];
u8 drc_name[IBMVFC_MAX_NAME];
struct ibmvfc_service_parms service_parms;
- __be64 reserved2;
+ __be64 reserved;
} __packed __aligned(8);
union ibmvfc_npiv_login_data {
@@ -246,6 +281,17 @@ union ibmvfc_npiv_login_data {
struct ibmvfc_npiv_login_resp resp;
} __packed __aligned(8);
+struct ibmvfc_fabric_login_mad {
+ struct ibmvfc_mad_common common;
+ __be64 flags;
+ __be64 capabilities;
+ __be64 nport_id;
+ __be16 status;
+ __be16 error;
+ __be32 reserved;
+ __be64 reserved2[16];
+} __packed __aligned(8);
+
struct ibmvfc_discover_targets_entry {
__be32 scsi_id;
__be32 pad;
@@ -287,6 +333,7 @@ enum ibmvfc_fc_type {
IBMVFC_FABRIC_BUSY = 0x04,
IBMVFC_PORT_BUSY = 0x05,
IBMVFC_BASIC_REJECT = 0x06,
+ IBMVFC_FC4_LS_REJECT = 0x07,
};
enum ibmvfc_gs_explain {
@@ -334,6 +381,7 @@ struct ibmvfc_move_login {
struct ibmvfc_prli_svc_parms {
u8 type;
#define IBMVFC_SCSI_FCP_TYPE 0x08
+#define IBMVFC_NVME_FCP_TYPE 0x28
u8 type_ext;
__be16 flags;
#define IBMVFC_PRLI_ORIG_PA_VALID 0x8000
@@ -349,6 +397,11 @@ struct ibmvfc_prli_svc_parms {
#define IBMVFC_PRLI_TARGET_FUNC 0x00000010
#define IBMVFC_PRLI_READ_FCP_XFER_RDY_DISABLED 0x00000002
#define IBMVFC_PRLI_WR_FCP_XFER_RDY_DISABLED 0x00000001
+#define IBMVFC_PRLI_NVME_PI_CTRL 0x00000200
+#define IBMVFC_PRLI_NVME_SLER 0x00000100
+#define IBMVFC_PRLI_NVME_INITIATOR 0x00000020
+#define IBMVFC_PRLI_NVME_TARGET 0x00000010
+#define IBMVFC_PRLI_NVME_DISCOVERY 0x00000008
} __packed __aligned(4);
struct ibmvfc_process_login {
@@ -377,20 +430,27 @@ struct ibmvfc_query_tgt {
struct ibmvfc_implicit_logout {
struct ibmvfc_mad_common common;
__be64 old_scsi_id;
- __be64 reserved[2];
+ __be64 reserved[8];
+ __be64 target_wwpn;
} __packed __aligned(8);
struct ibmvfc_tmf {
struct ibmvfc_mad_common common;
__be64 scsi_id;
- struct scsi_lun lun;
+ union {
+ struct scsi_lun lun;
+ __be64 assoc_id;
+ };
__be32 flags;
-#define IBMVFC_TMF_ABORT_TASK 0x02
-#define IBMVFC_TMF_ABORT_TASK_SET 0x04
-#define IBMVFC_TMF_LUN_RESET 0x10
-#define IBMVFC_TMF_TGT_RESET 0x20
-#define IBMVFC_TMF_LUA_VALID 0x40
-#define IBMVFC_TMF_SUPPRESS_ABTS 0x80
+#define IBMVFC_TMF_ABORT_TASK 0x002
+#define IBMVFC_TMF_ABORT_TASK_SET 0x004
+#define IBMVFC_TMF_LUN_RESET 0x010
+#define IBMVFC_TMF_TGT_RESET 0x020
+#define IBMVFC_TMF_LUA_VALID 0x040
+#define IBMVFC_TMF_SUPPRESS_ABTS 0x080
+#define IBMVFC_TMF_NVMF_ASSOC 0x100
+#define IBMVFC_TMF_NVMF_TARGET 0x200
+#define IBMVFC_TMF_BITMASK_VALID 0x400
__be32 cancel_key;
__be32 my_cancel_key;
__be32 pad;
@@ -446,6 +506,8 @@ enum ibmvfc_cmd_flags {
IBMVFC_WRITE = 0x0008,
IBMVFC_TMF = 0x0080,
IBMVFC_CLASS_3_ERR = 0x0100,
+ IBMVFC_NVMEOF_PROTOCOL = 0x0200,
+ IBMVFC_NVMF_SLER = 0x0400,
};
enum ibmvfc_fc_task_attr {
@@ -493,7 +555,7 @@ struct ibmvfc_cmd {
__be64 tgt_scsi_id;
__be64 tag;
__be64 target_wwpn;
- __be64 reserved3;
+ __be64 assoc_id;
union {
struct {
struct ibmvfc_fcp_cmd_iu iu;
@@ -504,6 +566,15 @@ struct ibmvfc_cmd {
struct ibmvfc_fcp_cmd_iu iu;
struct ibmvfc_fcp_rsp rsp;
} v2;
+ struct {
+ __be64 reserved5[4];
+ struct ibmvfc_fcp_cmd_iu iu;
+ struct ibmvfc_fcp_rsp rsp;
+ } v3scsi;
+ struct {
+ __be64 reserved[4];
+ struct nvme_fc_cmd_iu iu;
+ } v3nvme;
};
} __packed __aligned(8);
@@ -522,6 +593,9 @@ struct ibmvfc_passthru_iu {
__be32 flags;
#define IBMVFC_FC_ELS 0x01
#define IBMVFC_FC_CT_IU 0x02
+#define IBMVFC_PT_PRLI 0x04
+#define IBMVFC_FC4_LS_OTH 0x08
+#define IBMVFC_FC4_LS_DSC_CTRL 0x10
__be32 cancel_key;
#define IBMVFC_PASSTHRU_CANCEL_KEY 0x80000000
#define IBMVFC_INTERNAL_CANCEL_KEY 0x80000001
@@ -549,9 +623,9 @@ struct ibmvfc_channel_enquiry {
#define IBMVFC_SUPPORT_VARIABLE_SUBQ_MSG 0x02
#define IBMVFC_NO_N_TO_M_CHANNELS_SUPPORT 0x04
__be32 num_scsi_subq_channels;
- __be32 num_nvmeof_subq_channels;
+ __be32 num_nvme_subq_channels;
__be32 num_scsi_vas_channels;
- __be32 num_nvmeof_vas_channels;
+ __be32 num_nvme_vas_channels;
} __packed __aligned(8);
struct ibmvfc_channel_setup_mad {
@@ -559,7 +633,7 @@ struct ibmvfc_channel_setup_mad {
struct srp_direct_buf buffer;
} __packed __aligned(8);
-#define IBMVFC_MAX_CHANNELS 502
+#define IBMVFC_MAX_CHANNELS 501
struct ibmvfc_channel_setup {
__be32 flags;
@@ -568,12 +642,13 @@ struct ibmvfc_channel_setup {
#define IBMVFC_CHANNELS_CANCELED 0x04
__be32 reserved;
__be32 num_scsi_subq_channels;
- __be32 num_nvmeof_subq_channels;
+ __be32 num_nvme_subq_channels;
__be32 num_scsi_vas_channels;
- __be32 num_nvmeof_vas_channels;
+ __be32 num_nvme_vas_channels;
struct srp_direct_buf buffer;
__be64 reserved2[5];
__be64 channel_handles[IBMVFC_MAX_CHANNELS];
+ __be64 async_sub_crq_handle;
} __packed __aligned(8);
struct ibmvfc_connection_info {
@@ -620,7 +695,8 @@ struct ibmvfc_trace_entry {
enum ibmvfc_crq_formats {
IBMVFC_CMD_FORMAT = 0x01,
- IBMVFC_ASYNC_EVENT = 0x02,
+ IBMVFC_ASYNC_EVENT = 0x02,
+ IBMVFC_NOOP = 0x03,
IBMVFC_MAD_FORMAT = 0x04,
};
@@ -639,6 +715,9 @@ enum ibmvfc_async_event {
IBMVFC_AE_RESUME = 0x0800,
IBMVFC_AE_ADAPTER_FAILED = 0x1000,
IBMVFC_AE_FPIN = 0x2000,
+ IBMVFC_NVME_DISCONNECT = 0x4000,
+ IBMVFC_NVMEOF_PROTO_AVAIL = 0x40000000,
+ IBMVFC_SCSI_PROTO_AVAIL = 0x80000000,
};
struct ibmvfc_async_desc {
@@ -683,13 +762,30 @@ struct ibmvfc_async_crq {
volatile __be64 scsi_id;
volatile __be64 wwpn;
volatile __be64 node_name;
- __be64 reserved;
+ __be64 assoc_id;
+} __packed __aligned(8);
+
+struct ibmvfc_async_sub_crq {
+ volatile u8 valid;
+ u8 flags;
+#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01
+ u8 link_state;
+ u8 fpin_status;
+ __be16 event;
+ __be16 pad;
+ __be64 wwpn;
+ __be64 nport_id;
+ union {
+ __be64 node_name;
+ __be64 assoc_id;
+ } id;
} __packed __aligned(8);
union ibmvfc_iu {
struct ibmvfc_mad_common mad_common;
struct ibmvfc_npiv_login_mad npiv_login;
struct ibmvfc_npiv_logout_mad npiv_logout;
+ struct ibmvfc_fabric_login_mad fabric_login;
struct ibmvfc_discover_targets discover_targets;
struct ibmvfc_port_login plogi;
struct ibmvfc_process_login prli;
@@ -728,6 +824,7 @@ struct ibmvfc_target {
u64 scsi_id;
u64 wwpn;
u64 new_scsi_id;
+ u64 assoc_id;
struct fc_rport *rport;
int target_id;
enum ibmvfc_target_action action;
@@ -743,6 +840,8 @@ struct ibmvfc_target {
void (*job_step) (struct ibmvfc_target *);
struct timer_list timer;
struct kref kref;
+ struct completion nvme_delete_done;
+ struct nvme_fc_remote_port *nvme_remote_port;
};
/* a unit of work for the hosting partition */
@@ -753,6 +852,8 @@ struct ibmvfc_event {
struct ibmvfc_queue *queue;
struct ibmvfc_target *tgt;
struct scsi_cmnd *cmnd;
+ struct nvmefc_ls_req *ls_req;
+ struct nvmefc_fcp_req *fcp_req;
atomic_t free;
atomic_t active;
union ibmvfc_iu *xfer_iu;
@@ -828,6 +929,8 @@ struct ibmvfc_channels {
unsigned int active_queues;
unsigned int desired_queues;
unsigned int max_queues;
+ int num_targets;
+ struct list_head targets;
int disc_buf_sz;
struct ibmvfc_discover_targets_entry *disc_buf;
dma_addr_t disc_buf_dma;
@@ -871,8 +974,6 @@ struct ibmvfc_host {
#define IBMVFC_TRACE_SIZE (sizeof(struct ibmvfc_trace_entry) * IBMVFC_NUM_TRACE_ENTRIES)
struct ibmvfc_trace_entry *trace;
atomic_t trace_index;
- int num_targets;
- struct list_head targets;
struct list_head purge;
struct device *dev;
struct dma_pool *sg_pool;
@@ -880,6 +981,7 @@ struct ibmvfc_host {
struct ibmvfc_queue crq;
struct ibmvfc_queue async_crq;
struct ibmvfc_channels scsi_scrqs;
+ struct ibmvfc_channels nvme_scrqs;
struct ibmvfc_npiv_login login_info;
union ibmvfc_npiv_login_data *login_buf;
dma_addr_t login_buf_dma;
@@ -888,6 +990,7 @@ struct ibmvfc_host {
int log_level;
struct mutex passthru_mutex;
unsigned int max_vios_scsi_channels;
+ unsigned int max_vios_nvme_channels;
int task_set;
int init_retries;
int discovery_threads;
@@ -899,6 +1002,9 @@ struct ibmvfc_host {
unsigned int mq_enabled:1;
unsigned int using_channels:1;
unsigned int do_enquiry:1;
+ unsigned int nvme_enabled:1;
+ unsigned int do_scsi_login:1;
+ unsigned int do_nvme_login:1;
unsigned int aborting_passthru:1;
unsigned int scan_complete:1;
int scan_timeout;
@@ -914,8 +1020,37 @@ struct ibmvfc_host {
struct work_struct rport_add_work_q;
wait_queue_head_t init_wait_q;
wait_queue_head_t work_wait_q;
+ struct nvme_fc_local_port *nvme_local_port;
+ struct completion nvme_delete_done;
};
+struct ibmvfc_event *__ibmvfc_get_event(struct ibmvfc_queue *queue, int reserved);
+#define ibmvfc_get_event(queue) __ibmvfc_get_event(queue, 0)
+#define ibmvfc_get_reserved_event(queue) __ibmvfc_get_event(queue, 1)
+
+void ibmvfc_init_event(struct ibmvfc_event *evt, void (*done) (struct ibmvfc_event *), u8 format);
+void ibmvfc_free_event(struct ibmvfc_event *evt);
+void ibmvfc_release_tgt(struct kref *kref);
+int ibmvfc_send_event(struct ibmvfc_event *evt, struct ibmvfc_host *vhost, unsigned long timeout);
+const char *ibmvfc_get_cmd_error(u16 status, u16 error);
+
+static inline int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap_flags)
+{
+ u64 host_caps = be64_to_cpu(vhost->login_buf->resp.capabilities);
+
+ return (host_caps & cap_flags) ? 1 : 0;
+}
+
+static inline struct ibmvfc_host *ibmvfc_channels_to_vhost(struct ibmvfc_channels *channels)
+{
+ if (channels->protocol == IBMVFC_PROTO_SCSI)
+ return container_of(channels, struct ibmvfc_host, scsi_scrqs);
+ else if (channels->protocol == IBMVFC_PROTO_NVME)
+ return container_of(channels, struct ibmvfc_host, nvme_scrqs);
+
+ return NULL;
+}
+
#define DBG_CMD(CMD) do { if (ibmvfc_debug) CMD; } while (0)
#define tgt_dbg(t, fmt, ...) \