[PATCH 4/5] ibmvfc: use async sub-queue for FPIN messages
From: Dave Marquardt via B4 Relay
Date: Wed Apr 08 2026 - 13:11:41 EST
From: Dave Marquardt <davemarq@xxxxxxxxxxxxx>
- allocate async sub-queue
- allocate interrupt and set up handler
- negotiate use of async sub-queue with NPIV (VIOS)
- refactor ibmvfc_basic_fpin_to_desc() and ibmvfc_full_fpin_to_desc()
into common routine
- add KUnit test to verify async sub-queue is allocated
---
drivers/scsi/ibmvscsi/ibmvfc.c | 325 ++++++++++++++++++++++++++++++++---
drivers/scsi/ibmvscsi/ibmvfc.h | 29 +++-
drivers/scsi/ibmvscsi/ibmvfc_kunit.c | 52 +++---
3 files changed, 363 insertions(+), 43 deletions(-)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 803fc3caa14d..26e39b367022 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -1471,6 +1471,13 @@ static void ibmvfc_gather_partition_info(struct ibmvfc_host *vhost)
of_node_put(rootdn);
}
+static __be64 ibmvfc_npiv_chan_caps[] = {
+ cpu_to_be64(IBMVFC_CAN_USE_CHANNELS | IBMVFC_USE_ASYNC_SUBQ |
+ IBMVFC_YES_SCSI | IBMVFC_CAN_HANDLE_FPIN),
+ cpu_to_be64(IBMVFC_CAN_USE_CHANNELS),
+};
+#define IBMVFC_NPIV_CHAN_CAPS_SIZE (sizeof(ibmvfc_npiv_chan_caps)/sizeof(__be64))
+
/**
* ibmvfc_set_login_info - Setup info for NPIV login
* @vhost: ibmvfc host struct
@@ -1486,6 +1493,8 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
const char *location;
u16 max_cmds;
+ ENTER;
+
max_cmds = scsi_qdepth + IBMVFC_NUM_INTERNAL_REQ;
if (mq_enabled)
max_cmds += (scsi_qdepth + IBMVFC_NUM_INTERNAL_SUBQ_REQ) *
@@ -1509,8 +1518,12 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
cpu_to_be64(IBMVFC_CAN_MIGRATE | IBMVFC_CAN_SEND_VF_WWPN |
IBMVFC_CAN_USE_NOOP_CMD);
- if (vhost->mq_enabled || vhost->using_channels)
- login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
+ if (vhost->mq_enabled || vhost->using_channels) {
+ if (vhost->login_cap_index >= IBMVFC_NPIV_CHAN_CAPS_SIZE)
+ login_info->capabilities |= cpu_to_be64(IBMVFC_CAN_USE_CHANNELS);
+ else
+ login_info->capabilities |= ibmvfc_npiv_chan_caps[vhost->login_cap_index];
+ }
login_info->async.va = cpu_to_be64(vhost->async_crq.msg_token);
login_info->async.len = cpu_to_be32(async_crq->size *
@@ -1524,6 +1537,8 @@ static void ibmvfc_set_login_info(struct ibmvfc_host *vhost)
location = of_get_property(of_node, "ibm,loc-code", NULL);
location = location ? location : dev_name(vhost->dev);
strscpy(login_info->drc_name, location, sizeof(login_info->drc_name));
+
+ LEAVE;
}
/**
@@ -3323,7 +3338,7 @@ ibmvfc_common_fpin_to_desc(u8 fpin_status, __be64 wwpn, __be16 modifier,
* non-NULL - pointer to populated struct fc_els_fpin
*/
static struct fc_els_fpin *
-/*XXX*/ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq)
+ibmvfc_basic_fpin_to_desc(struct ibmvfc_async_crq *crq)
{
return ibmvfc_common_fpin_to_desc(crq->fpin_status, crq->wwpn,
cpu_to_be16(0),
@@ -3332,6 +3347,29 @@ static struct fc_els_fpin *
cpu_to_be32(1));
}
+/**
+ * ibmvfc_full_fpin_to_desc(): allocate and populate a struct fc_els_fpin struct
+ * containing a descriptor.
+ * @ibmvfc_fpin: Pointer to async subq FPIN data
+ *
+ * Allocate a struct fc_els_fpin containing a descriptor and populate
+ * based on data from *ibmvfc_fpin.
+ *
+ * Return:
+ * NULL - unable to allocate structure
+ * non-NULL - pointer to populated struct fc_els_fpin
+ */
+static struct fc_els_fpin *
+ibmvfc_full_fpin_to_desc(struct ibmvfc_async_subq *ibmvfc_fpin)
+{
+ return ibmvfc_common_fpin_to_desc(ibmvfc_fpin->fpin_status,
+ ibmvfc_fpin->wwpn,
+ cpu_to_be16(0),
+ cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_PERIOD),
+ cpu_to_be32(IBMVFC_FPIN_DEFAULT_EVENT_THRESHOLD),
+ cpu_to_be32(1));
+}
+
/**
* ibmvfc_handle_async - Handle an async event from the adapter
* @crq: crq to process
@@ -3449,6 +3487,120 @@ VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq,
}
EXPORT_SYMBOL_IF_KUNIT(ibmvfc_handle_async);
+VISIBLE_IF_KUNIT void ibmvfc_handle_asyncq(struct ibmvfc_crq *crq_instance,
+ struct ibmvfc_host *vhost,
+ struct list_head *evt_doneq)
+{
+ struct ibmvfc_async_subq *crq = (struct ibmvfc_async_subq *)crq_instance;
+ const struct ibmvfc_async_desc *desc = ibmvfc_get_ae_desc(be16_to_cpu(crq->event));
+ struct ibmvfc_target *tgt;
+ struct fc_els_fpin *fpin;
+
+ ibmvfc_log(vhost, desc->log_level,
+ "%s event received. wwpn: %llx, node_name: %llx%s event 0x%x\n",
+ desc->desc, be64_to_cpu(crq->wwpn), be64_to_cpu(crq->id.node_name),
+ ibmvfc_get_link_state(crq->link_state), be16_to_cpu(crq->event));
+
+ switch (be16_to_cpu(crq->event)) {
+ case IBMVFC_AE_RESUME:
+ switch (crq->link_state) {
+ case IBMVFC_AE_LS_LINK_DOWN:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ break;
+ case IBMVFC_AE_LS_LINK_DEAD:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ case IBMVFC_AE_LS_LINK_UP:
+ case IBMVFC_AE_LS_LINK_BOUNCED:
+ default:
+ vhost->events_to_log |= IBMVFC_AE_LINKUP;
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ break;
+ }
+
+ break;
+ case IBMVFC_AE_LINK_UP:
+ vhost->events_to_log |= IBMVFC_AE_LINKUP;
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ break;
+ case IBMVFC_AE_SCN_FABRIC:
+ case IBMVFC_AE_SCN_DOMAIN:
+ vhost->events_to_log |= IBMVFC_AE_RSCN;
+ if (vhost->state < IBMVFC_HALTED) {
+ vhost->delay_init = 1;
+ __ibmvfc_reset_host(vhost);
+ }
+ break;
+ case IBMVFC_AE_SCN_NPORT:
+ case IBMVFC_AE_SCN_GROUP:
+ vhost->events_to_log |= IBMVFC_AE_RSCN;
+ ibmvfc_reinit_host(vhost);
+ break;
+ case IBMVFC_AE_ELS_LOGO:
+ case IBMVFC_AE_ELS_PRLO:
+ case IBMVFC_AE_ELS_PLOGI:
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (!crq->wwpn && !crq->id.node_name)
+ break;
+ #ifdef notyet
+ if (cpu_to_be64(tgt->scsi_id) != acrq->scsi_id)
+ continue;
+ #endif
+ if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+ continue;
+ if (crq->id.node_name &&
+ cpu_to_be64(tgt->ids.node_name) != crq->id.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);
+ }
+ }
+ break;
+ case IBMVFC_AE_LINK_DOWN:
+ case IBMVFC_AE_ADAPTER_FAILED:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ break;
+ case IBMVFC_AE_LINK_DEAD:
+ ibmvfc_link_down(vhost, IBMVFC_LINK_DEAD);
+ break;
+ case IBMVFC_AE_HALT:
+ ibmvfc_link_down(vhost, IBMVFC_HALTED);
+ break;
+ case IBMVFC_AE_FPIN:
+ if (!crq->wwpn && !crq->id.node_name)
+ break;
+ list_for_each_entry(tgt, &vhost->targets, queue) {
+ if (crq->wwpn && cpu_to_be64(tgt->ids.port_name) != crq->wwpn)
+ continue;
+ if (crq->id.node_name &&
+ cpu_to_be64(tgt->ids.node_name) != crq->id.node_name)
+ continue;
+ if (!tgt->rport)
+ continue;
+ fpin = ibmvfc_full_fpin_to_desc(crq);
+ if (fpin) {
+ fc_host_fpin_rcv(tgt->vhost->host,
+ sizeof(*fpin) + be32_to_cpu(fpin->desc_len),
+ (char *)fpin, 0);
+ kfree(fpin);
+ } else
+ dev_err(vhost->dev,
+ "FPIN event %u received, unable to process\n",
+ crq->fpin_status);
+ }
+ break;
+ default:
+ dev_err(vhost->dev, "Unknown async event received: %d\n", crq->event);
+ break;
+ }
+}
+EXPORT_SYMBOL_IF_KUNIT(ibmvfc_handle_asyncq);
+
/**
* ibmvfc_handle_crq - Handles and frees received events in the CRQ
* @crq: Command/Response queue
@@ -3500,6 +3652,7 @@ VISIBLE_IF_KUNIT void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_ho
dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format);
ibmvfc_purge_requests(vhost, DID_ERROR);
ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
+ vhost->login_cap_index++;
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_RESET);
} else {
dev_err(vhost->dev, "Received unknown transport event from partner (rc=%d)\n", crq->format);
@@ -4078,6 +4231,13 @@ static void ibmvfc_handle_scrq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost
spin_unlock(&evt->queue->l_lock);
}
+/**
+ * ibmvfc_next_scrq - Returns the next entry in message subqueue
+ * @scrq: Pointer to message subqueue
+ *
+ * Returns:
+ * Pointer to next entry in queue / NULL if empty
+ **/
static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
{
struct ibmvfc_crq *crq;
@@ -4093,6 +4253,65 @@ static struct ibmvfc_crq *ibmvfc_next_scrq(struct ibmvfc_queue *scrq)
return crq;
}
+static void ibmvfc_drain_async_subq(struct ibmvfc_queue *scrq)
+{
+ struct ibmvfc_crq *crq;
+ struct ibmvfc_event *evt, *temp;
+ unsigned long flags;
+ int done = 0;
+ LIST_HEAD(evt_doneq);
+
+ ENTER;
+
+ spin_lock_irqsave(scrq->q_lock, flags);
+ while (!done) {
+ while ((crq = ibmvfc_next_scrq(scrq)) != NULL) {
+ ibmvfc_handle_asyncq(crq, scrq->vhost, &evt_doneq);
+ crq->valid = 0;
+ wmb();
+ }
+
+ ibmvfc_toggle_scrq_irq(scrq, 1);
+ crq = ibmvfc_next_scrq(scrq);
+ if (crq != NULL) {
+ ibmvfc_toggle_scrq_irq(scrq, 0);
+ ibmvfc_handle_asyncq(crq, scrq->vhost, &evt_doneq);
+ crq->valid = 0;
+ wmb();
+ } else
+ done = 1;
+ }
+ spin_unlock_irqrestore(scrq->q_lock, flags);
+
+ list_for_each_entry_safe(evt, temp, &evt_doneq, queue_list) {
+ timer_delete(&evt->timer);
+ list_del(&evt->queue_list);
+ ibmvfc_trc_end(evt);
+ evt->done(evt);
+ }
+ LEAVE;
+}
+
+/**
+ * ibmvfc_interrupt_asyncq - Handle an async event from the adapter
+ * @irq: interrupt request
+ * @scrq_instance: async subq
+ *
+ **/
+static irqreturn_t ibmvfc_interrupt_asyncq(int irq, void *scrq_instance)
+{
+ struct ibmvfc_queue *scrq = (struct ibmvfc_queue *)scrq_instance;
+
+ ENTER;
+
+ ibmvfc_toggle_scrq_irq(scrq, 0);
+ ibmvfc_drain_async_subq(scrq);
+
+ LEAVE;
+
+ return IRQ_HANDLED;
+}
+
static void ibmvfc_drain_sub_crq(struct ibmvfc_queue *scrq)
{
struct ibmvfc_crq *crq;
@@ -5316,6 +5535,8 @@ static void ibmvfc_channel_setup_done(struct ibmvfc_event *evt)
for (i = 0; i < active_queues; i++)
scrqs->scrqs[i].vios_cookie =
be64_to_cpu(setup->channel_handles[i]);
+ scrqs->async_scrq->vios_cookie =
+ be64_to_cpu(setup->asyncSubqHandle);
ibmvfc_dbg(vhost, "Using %u channels\n",
vhost->scsi_scrqs.active_queues);
@@ -5366,6 +5587,7 @@ static void ibmvfc_channel_setup(struct ibmvfc_host *vhost)
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->asyncSubqHandle = cpu_to_be64(scrqs->async_scrq->cookie);
}
ibmvfc_init_event(evt, ibmvfc_channel_setup_done, IBMVFC_MAD_FORMAT);
@@ -5461,6 +5683,8 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
unsigned int npiv_max_sectors;
int level = IBMVFC_DEFAULT_LOG_LEVEL;
+ ENTER;
+
switch (mad_status) {
case IBMVFC_MAD_SUCCESS:
ibmvfc_free_event(evt);
@@ -5540,6 +5764,8 @@ static void ibmvfc_npiv_login_done(struct ibmvfc_event *evt)
ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_QUERY);
wake_up(&vhost->work_wait_q);
}
+
+ LEAVE;
}
/**
@@ -6188,14 +6414,26 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost)
return retrc;
}
-static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
- struct ibmvfc_channels *channels,
- int index)
+static inline char *ibmvfc_channel_index(struct ibmvfc_channels *channels,
+ struct ibmvfc_queue *scrq,
+ char *buf, size_t bufsize)
+{
+ if (scrq < channels->scrqs || scrq >= channels->scrqs + channels->active_queues)
+ strscpy(buf, "async", 6);
+ else
+ snprintf(buf, bufsize, "%ld", scrq - channels->scrqs);
+ return buf;
+}
+
+static int ibmvfc_register_channel_handler(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels,
+ struct ibmvfc_queue *scrq,
+ irq_handler_t irq)
{
struct device *dev = vhost->dev;
struct vio_dev *vdev = to_vio_dev(dev);
- struct ibmvfc_queue *scrq = &channels->scrqs[index];
int rc = -ENOMEM;
+ char buf[16];
ENTER;
@@ -6214,20 +6452,23 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
if (!scrq->irq) {
rc = -EINVAL;
- dev_err(dev, "Error mapping sub-crq[%d] irq\n", index);
+ dev_err(dev, "Error mapping sub-crq[%s] irq\n",
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
goto irq_failed;
}
switch (channels->protocol) {
case IBMVFC_PROTO_SCSI:
- snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%d",
- vdev->unit_address, index);
- scrq->handler = ibmvfc_interrupt_mq;
+ snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-scsi%s",
+ vdev->unit_address,
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
+ scrq->handler = irq;
break;
case IBMVFC_PROTO_NVME:
- snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-nvmf%d",
- vdev->unit_address, index);
- scrq->handler = ibmvfc_interrupt_mq;
+ snprintf(scrq->name, sizeof(scrq->name), "ibmvfc-%x-nvmf%s",
+ vdev->unit_address,
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
+ scrq->handler = irq;
break;
default:
dev_err(dev, "Unknown channel protocol (%d)\n",
@@ -6238,12 +6479,14 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
rc = request_irq(scrq->irq, scrq->handler, 0, scrq->name, scrq);
if (rc) {
- dev_err(dev, "Couldn't register sub-crq[%d] irq\n", index);
+ dev_err(dev, "Couldn't register sub-crq[%s] irq\n",
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)));
irq_dispose_mapping(scrq->irq);
goto irq_failed;
}
- scrq->hwq_id = index;
+ if (scrq >= channels->scrqs && scrq < channels->scrqs + channels->active_queues)
+ scrq->hwq_id = scrq - channels->scrqs;
LEAVE;
return 0;
@@ -6257,13 +6500,21 @@ static int ibmvfc_register_channel(struct ibmvfc_host *vhost,
return rc;
}
+static inline int
+ibmvfc_register_channel(struct ibmvfc_host *vhost,
+ struct ibmvfc_channels *channels,
+ struct ibmvfc_queue *scrq)
+{
+ return ibmvfc_register_channel_handler(vhost, channels, scrq, ibmvfc_interrupt_mq);
+}
+
static void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,
struct ibmvfc_channels *channels,
- int index)
+ struct ibmvfc_queue *scrq)
{
struct device *dev = vhost->dev;
struct vio_dev *vdev = to_vio_dev(dev);
- struct ibmvfc_queue *scrq = &channels->scrqs[index];
+ char buf[16];
long rc;
ENTER;
@@ -6278,7 +6529,8 @@ static void ibmvfc_deregister_channel(struct ibmvfc_host *vhost,
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
if (rc)
- dev_err(dev, "Failed to free sub-crq[%d]: rc=%ld\n", index, rc);
+ dev_err(dev, "Failed to free sub-crq[%s]: rc=%ld\n",
+ ibmvfc_channel_index(channels, scrq, buf, sizeof(buf)), rc);
/* Clean out the queue */
memset(scrq->msgs.crq, 0, PAGE_SIZE);
@@ -6296,10 +6548,19 @@ static void ibmvfc_reg_sub_crqs(struct ibmvfc_host *vhost,
if (!vhost->mq_enabled || !channels->scrqs)
return;
+ if (ibmvfc_register_channel_handler(vhost, channels,
+ channels->async_scrq,
+ ibmvfc_interrupt_asyncq))
+ return;
+
for (i = 0; i < channels->max_queues; i++) {
- if (ibmvfc_register_channel(vhost, channels, i)) {
+ if (ibmvfc_register_channel(vhost, channels, &channels->scrqs[i])) {
for (j = i; j > 0; j--)
- ibmvfc_deregister_channel(vhost, channels, j - 1);
+ ibmvfc_deregister_channel(
+ vhost, channels, &channels->scrqs[j - 1]);
+ ibmvfc_deregister_channel(vhost, channels,
+ channels->async_scrq);
+
vhost->do_enquiry = 0;
return;
}
@@ -6318,7 +6579,8 @@ static void ibmvfc_dereg_sub_crqs(struct ibmvfc_host *vhost,
return;
for (i = 0; i < channels->max_queues; i++)
- ibmvfc_deregister_channel(vhost, channels, i);
+ ibmvfc_deregister_channel(vhost, channels, &channels->scrqs[i]);
+ ibmvfc_deregister_channel(vhost, channels, channels->async_scrq);
LEAVE;
}
@@ -6334,6 +6596,21 @@ static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost,
if (!channels->scrqs)
return -ENOMEM;
+ channels->async_scrq = kzalloc_obj(*channels->async_scrq, GFP_KERNEL);
+
+ if (!channels->async_scrq) {
+ kfree(channels->scrqs);
+ return -ENOMEM;
+ }
+
+ rc = ibmvfc_alloc_queue(vhost, channels->async_scrq,
+ IBMVFC_SUB_CRQ_FMT);
+ if (rc) {
+ kfree(channels->scrqs);
+ kfree(channels->async_scrq);
+ return rc;
+ }
+
for (i = 0; i < channels->max_queues; i++) {
scrq = &channels->scrqs[i];
rc = ibmvfc_alloc_queue(vhost, scrq, IBMVFC_SUB_CRQ_FMT);
@@ -6345,6 +6622,9 @@ static int ibmvfc_alloc_channels(struct ibmvfc_host *vhost,
kfree(channels->scrqs);
channels->scrqs = NULL;
channels->active_queues = 0;
+ ibmvfc_free_queue(vhost, channels->async_scrq);
+ kfree(channels->async_scrq);
+ channels->async_scrq = NULL;
return rc;
}
}
@@ -6629,6 +6909,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
vhost->using_channels = 0;
vhost->do_enquiry = 1;
vhost->scan_timeout = 0;
+ vhost->login_cap_index = 0;
strcpy(vhost->partition_name, "UNKNOWN");
init_waitqueue_head(&vhost->work_wait_q);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h
index 4f680c5d9558..b9f22613d144 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.h
+++ b/drivers/scsi/ibmvscsi/ibmvfc.h
@@ -182,6 +182,9 @@ struct ibmvfc_npiv_login {
#define IBMVFC_CAN_HANDLE_FPIN 0x04
#define IBMVFC_CAN_USE_MAD_VERSION 0x08
#define IBMVFC_CAN_SEND_VF_WWPN 0x10
+#define IBMVFC_YES_NVMEOF 0x20
+#define IBMVFC_YES_SCSI 0x40
+#define IBMVFC_USE_ASYNC_SUBQ 0x100
#define IBMVFC_CAN_USE_NOOP_CMD 0x200
__be64 node_name;
struct srp_direct_buf async;
@@ -231,6 +234,7 @@ struct ibmvfc_npiv_login_resp {
#define IBMVFC_CAN_SUPPORT_CHANNELS 0x80
#define IBMVFC_SUPPORT_NVMEOF 0x100
#define IBMVFC_SUPPORT_SCSI 0x200
+#define IBMVFC_SUPPORT_ASYNC_SUBQ 0x800
#define IBMVFC_SUPPORT_NOOP_CMD 0x1000
__be32 max_cmds;
__be32 scsi_id_sz;
@@ -565,7 +569,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;
@@ -580,6 +584,7 @@ struct ibmvfc_channel_setup {
struct srp_direct_buf buffer;
__be64 reserved2[5];
__be64 channel_handles[IBMVFC_MAX_CHANNELS];
+ __be64 asyncSubqHandle;
} __packed __aligned(8);
struct ibmvfc_connection_info {
@@ -710,6 +715,25 @@ struct ibmvfc_async_crq {
__be64 reserved;
} __packed __aligned(8);
+struct ibmvfc_async_subq {
+ volatile u8 valid;
+#define IBMVFC_ASYNC_ID_IS_ASSOC_ID 0x01
+#define IBMVFC_FC_EEH 0x04
+#define IBMVFC_FC_FW_UPDATE 0x08
+#define IBMVFC_FC_FW_DUMP 0x10
+ u8 flags;
+ u8 link_state;
+ u8 fpin_status;
+ __be16 event;
+ __be16 pad;
+ volatile __be64 wwpn;
+ volatile __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;
@@ -849,6 +873,7 @@ struct ibmvfc_queue {
struct ibmvfc_channels {
struct ibmvfc_queue *scrqs;
+ struct ibmvfc_queue *async_scrq;
enum ibmvfc_protocol protocol;
unsigned int active_queues;
unsigned int desired_queues;
@@ -989,6 +1014,8 @@ static inline int ibmvfc_check_caps(struct ibmvfc_host *vhost, unsigned long cap
#ifdef VISIBLE_IF_KUNIT
VISIBLE_IF_KUNIT void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, struct ibmvfc_host *vhost);
+VISIBLE_IF_KUNIT void ibmvfc_handle_asyncq(struct ibmvfc_crq *crq_instance,
+ struct ibmvfc_host *vhost, struct list_head *evt_doneq);
VISIBLE_IF_KUNIT struct list_head *ibmvfc_get_headp(void);
VISIBLE_IF_KUNIT void ibmvfc_handle_crq(struct ibmvfc_crq *crq,
struct ibmvfc_host *vhost,
diff --git a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
index 3359e4ebebe2..3a41127c4e81 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc_kunit.c
@@ -22,14 +22,14 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static void ibmvfc_handle_fpin_event_test(struct kunit *test)
{
u64 *stats[IBMVFC_AE_FPIN_CONGESTION_CLEARED + 1] = { NULL };
- u64 post[IBMVFC_AE_FPIN_CONGESTION_CLEARED + 1];
- u64 pre[IBMVFC_AE_FPIN_CONGESTION_CLEARED + 1];
enum ibmvfc_ae_fpin_status fs;
- struct ibmvfc_async_crq crq;
+ struct ibmvfc_async_subq crq;
struct ibmvfc_target *tgt;
struct ibmvfc_host *vhost;
struct list_head *queue;
struct list_head *headp;
+ LIST_HEAD(evt_doneq);
+ u64 pre, post;
headp = ibmvfc_get_headp();
@@ -52,31 +52,23 @@ static void ibmvfc_handle_fpin_event_test(struct kunit *test)
crq.valid = 0x80;
crq.link_state = IBMVFC_AE_LS_LINK_UP;
crq.fpin_status = fs;
- crq.event = cpu_to_be64(IBMVFC_AE_FPIN);
- crq.scsi_id = cpu_to_be64(tgt->scsi_id);
+ crq.event = cpu_to_be16(IBMVFC_AE_FPIN);
crq.wwpn = cpu_to_be64(tgt->wwpn);
- crq.node_name = cpu_to_be64(tgt->ids.node_name);
- pre[fs] = *stats[fs];
- ibmvfc_handle_async(&crq, vhost);
- post[fs] = *stats[fs];
- KUNIT_EXPECT_EQ(test, post[fs], pre[fs]+1);
+ crq.id.node_name = cpu_to_be64(tgt->ids.node_name);
+ pre = *stats[fs];
+ ibmvfc_handle_asyncq((struct ibmvfc_crq *)&crq, vhost, &evt_doneq);
+ post = *stats[fs];
+ KUNIT_EXPECT_EQ(test, post, pre+1);
}
/* bad path */
- for (fs = IBMVFC_AE_FPIN_LINK_CONGESTED; fs <= IBMVFC_AE_FPIN_CONGESTION_CLEARED; fs++)
- pre[fs] = *stats[fs];
crq.valid = 0x80;
crq.link_state = IBMVFC_AE_LS_LINK_UP;
crq.fpin_status = 0; /* bad value */
- crq.event = cpu_to_be64(IBMVFC_AE_FPIN);
- crq.scsi_id = cpu_to_be64(tgt->scsi_id);
+ crq.event = cpu_to_be16(IBMVFC_AE_FPIN);
crq.wwpn = cpu_to_be64(tgt->wwpn);
- crq.node_name = cpu_to_be64(tgt->ids.node_name);
- ibmvfc_handle_async(&crq, vhost);
- for (fs = IBMVFC_AE_FPIN_LINK_CONGESTED; fs <= IBMVFC_AE_FPIN_CONGESTION_CLEARED; fs++) {
- post[fs] = *stats[fs];
- KUNIT_EXPECT_EQ(test, pre[fs], post[fs]);
- }
+ crq.id.node_name = cpu_to_be64(tgt->ids.node_name);
+ ibmvfc_handle_asyncq((struct ibmvfc_crq *)&crq, vhost, &evt_doneq);
}
/**
@@ -105,9 +97,29 @@ static void ibmvfc_noop_test(struct kunit *test)
ibmvfc_handle_crq(&crq, vhost, &evtq);
}
+/**
+ * ibmvfc_async_subq_test - unit test for allocating async subqueue
+ * @test: pointer to kunit structure
+ *
+ * Return: void
+ */
+static void ibmvfc_async_subq_test(struct kunit *test)
+{
+ struct ibmvfc_host *vhost;
+ struct list_head *queue;
+ struct list_head *headp;
+
+ headp = ibmvfc_get_headp();
+ queue = headp->next;
+ vhost = container_of(queue, struct ibmvfc_host, queue);
+
+ KUNIT_EXPECT_NOT_NULL(test, vhost->scsi_scrqs.async_scrq);
+}
+
static struct kunit_case ibmvfc_fpin_test_cases[] = {
KUNIT_CASE(ibmvfc_handle_fpin_event_test),
KUNIT_CASE(ibmvfc_noop_test),
+ KUNIT_CASE(ibmvfc_async_subq_test),
{},
};
--
2.53.0