scsi: qla2xxx: edif: Add bsg interface to read doorbell events
authorQuinn Tran <qutran@marvell.com>
Tue, 7 Jun 2022 04:46:20 +0000 (21:46 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Wed, 8 Jun 2022 01:50:10 +0000 (21:50 -0400)
Add bsg interface for app to read doorbell events. This interface lets
driver know how much app can read based on return buffer size. When the
next event(s) occur, driver will return the bsg_job with the event(s) in
the return buffer.

If there is no event to read, driver will hold on to the bsg_job up to few
seconds as a way to control the polling interval.

Link: https://lore.kernel.org/r/20220607044627.19563-5-njavali@marvell.com
Fixes: dd30706e73b7 ("scsi: qla2xxx: edif: Add key update")
Signed-off-by: Quinn Tran <qutran@marvell.com>
Signed-off-by: Nilesh Javali <njavali@marvell.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/qla2xxx/qla_dbg.h
drivers/scsi/qla2xxx/qla_edif.c
drivers/scsi/qla2xxx/qla_edif.h
drivers/scsi/qla2xxx/qla_edif_bsg.h

index f1f6c740bdcd8aee725ed92a5999b841c4f38946..feeb1666227f1235de63b4c891195c9d95e76226 100644 (file)
@@ -383,5 +383,5 @@ ql_mask_match(uint level)
        if (ql2xextended_error_logging == 1)
                ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK;
 
-       return (level & ql2xextended_error_logging) == level;
+       return level && ((level & ql2xextended_error_logging) == level);
 }
index fffdf87d823aef38a3d0d2062a2841ba9d302bb0..0d84dad2612cd5f0968ccda57ee8fe6acf71d59e 100644 (file)
@@ -52,6 +52,31 @@ const char *sc_to_str(uint16_t cmd)
        return "unknown";
 }
 
+static struct edb_node *qla_edb_getnext(scsi_qla_host_t *vha)
+{
+       unsigned long   flags;
+       struct edb_node *edbnode = NULL;
+
+       spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+
+       /* db nodes are fifo - no qualifications done */
+       if (!list_empty(&vha->e_dbell.head)) {
+               edbnode = list_first_entry(&vha->e_dbell.head,
+                                          struct edb_node, list);
+               list_del_init(&edbnode->list);
+       }
+
+       spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+       return edbnode;
+}
+
+static void qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node)
+{
+       list_del_init(&node->list);
+       kfree(node);
+}
+
 static struct edif_list_entry *qla_edif_list_find_sa_index(fc_port_t *fcport,
                uint16_t handle)
 {
@@ -1071,6 +1096,130 @@ qla_edif_ack(scsi_qla_host_t *vha, struct bsg_job *bsg_job)
        return 0;
 }
 
+static int qla_edif_consume_dbell(scsi_qla_host_t *vha, struct bsg_job *bsg_job)
+{
+       struct fc_bsg_reply     *bsg_reply = bsg_job->reply;
+       u32 sg_skip, reply_payload_len;
+       bool keep;
+       struct edb_node *dbnode = NULL;
+       struct edif_app_dbell ap;
+       int dat_size = 0;
+
+       sg_skip = 0;
+       reply_payload_len = bsg_job->reply_payload.payload_len;
+
+       while ((reply_payload_len - sg_skip) >= sizeof(struct edb_node)) {
+               dbnode = qla_edb_getnext(vha);
+               if (dbnode) {
+                       keep = true;
+                       dat_size = 0;
+                       ap.event_code = dbnode->ntype;
+                       switch (dbnode->ntype) {
+                       case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN:
+                       case VND_CMD_AUTH_STATE_NEEDED:
+                               ap.port_id = dbnode->u.plogi_did;
+                               dat_size += sizeof(ap.port_id);
+                               break;
+                       case VND_CMD_AUTH_STATE_ELS_RCVD:
+                               ap.port_id = dbnode->u.els_sid;
+                               dat_size += sizeof(ap.port_id);
+                               break;
+                       case VND_CMD_AUTH_STATE_SAUPDATE_COMPL:
+                               ap.port_id = dbnode->u.sa_aen.port_id;
+                               memcpy(&ap.event_data, &dbnode->u,
+                                   sizeof(struct edif_sa_update_aen));
+                               dat_size += sizeof(struct edif_sa_update_aen);
+                               break;
+                       default:
+                               keep = false;
+                               ql_log(ql_log_warn, vha, 0x09102,
+                                       "%s unknown DB type=%d %p\n",
+                                       __func__, dbnode->ntype, dbnode);
+                               break;
+                       }
+                       ap.event_data_size = dat_size;
+                       /* 8 = sizeof(ap.event_code + ap.event_data_size) */
+                       dat_size += 8;
+                       if (keep)
+                               sg_skip += sg_copy_buffer(bsg_job->reply_payload.sg_list,
+                                               bsg_job->reply_payload.sg_cnt,
+                                               &ap, dat_size, sg_skip, false);
+
+                       ql_dbg(ql_dbg_edif, vha, 0x09102,
+                               "%s Doorbell consumed : type=%d %p\n",
+                               __func__, dbnode->ntype, dbnode);
+
+                       kfree(dbnode);
+               } else {
+                       break;
+               }
+       }
+
+       SET_DID_STATUS(bsg_reply->result, DID_OK);
+       bsg_reply->reply_payload_rcv_len = sg_skip;
+       bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+
+       return 0;
+}
+
+static void __qla_edif_dbell_bsg_done(scsi_qla_host_t *vha, struct bsg_job *bsg_job,
+       u32 delay)
+{
+       struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+
+       /* small sleep for doorbell events to accumulate */
+       if (delay)
+               msleep(delay);
+
+       qla_edif_consume_dbell(vha, bsg_job);
+
+       bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len);
+}
+
+static void qla_edif_dbell_bsg_done(scsi_qla_host_t *vha)
+{
+       unsigned long flags;
+       struct bsg_job *prev_bsg_job = NULL;
+
+       spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+       if (vha->e_dbell.dbell_bsg_job) {
+               prev_bsg_job = vha->e_dbell.dbell_bsg_job;
+               vha->e_dbell.dbell_bsg_job = NULL;
+       }
+       spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+       if (prev_bsg_job)
+               __qla_edif_dbell_bsg_done(vha, prev_bsg_job, 0);
+}
+
+static int
+qla_edif_dbell_bsg(scsi_qla_host_t *vha, struct bsg_job *bsg_job)
+{
+       unsigned long flags;
+       bool return_bsg = false;
+
+       /* flush previous dbell bsg */
+       qla_edif_dbell_bsg_done(vha);
+
+       spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+       if (list_empty(&vha->e_dbell.head) && DBELL_ACTIVE(vha)) {
+               /*
+                * when the next db event happens, bsg_job will return.
+                * Otherwise, timer will return it.
+                */
+               vha->e_dbell.dbell_bsg_job = bsg_job;
+               vha->e_dbell.bsg_expire = jiffies + 10 * HZ;
+       } else {
+               return_bsg = true;
+       }
+       spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+       if (return_bsg)
+               __qla_edif_dbell_bsg_done(vha, bsg_job, 1);
+
+       return 0;
+}
+
 int32_t
 qla_edif_app_mgmt(struct bsg_job *bsg_job)
 {
@@ -1082,8 +1231,13 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
        bool done = true;
        int32_t         rval = 0;
        uint32_t        vnd_sc = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+       u32 level = ql_dbg_edif;
 
-       ql_dbg(ql_dbg_edif, vha, 0x911d, "%s vnd subcmd=%x\n",
+       /* doorbell is high traffic */
+       if (vnd_sc == QL_VND_SC_READ_DBELL)
+               level = 0;
+
+       ql_dbg(level, vha, 0x911d, "%s vnd subcmd=%x\n",
            __func__, vnd_sc);
 
        sg_copy_to_buffer(bsg_job->request_payload.sg_list,
@@ -1092,7 +1246,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
 
        if (!vha->hw->flags.edif_enabled ||
                test_bit(VPORT_DELETE, &vha->dpc_flags)) {
-               ql_dbg(ql_dbg_edif, vha, 0x911d,
+               ql_dbg(level, vha, 0x911d,
                    "%s edif not enabled or vp delete. bsg ptr done %p. dpc_flags %lx\n",
                    __func__, bsg_job, vha->dpc_flags);
 
@@ -1101,7 +1255,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
        }
 
        if (!qla_edif_app_check(vha, appcheck)) {
-               ql_dbg(ql_dbg_edif, vha, 0x911d,
+               ql_dbg(level, vha, 0x911d,
                    "%s app checked failed.\n",
                    __func__);
 
@@ -1136,6 +1290,10 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
        case QL_VND_SC_AEN_COMPLETE:
                rval = qla_edif_ack(vha, bsg_job);
                break;
+       case QL_VND_SC_READ_DBELL:
+               rval = qla_edif_dbell_bsg(vha, bsg_job);
+               done = false;
+               break;
        default:
                ql_dbg(ql_dbg_edif, vha, 0x911d, "%s unknown cmd=%x\n",
                    __func__,
@@ -1147,7 +1305,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job)
 
 done:
        if (done) {
-               ql_dbg(ql_dbg_user, vha, 0x7009,
+               ql_dbg(level, vha, 0x7009,
                    "%s: %d  bsg ptr done %p\n", __func__, __LINE__, bsg_job);
                bsg_job_done(bsg_job, bsg_reply->result,
                    bsg_reply->reply_payload_rcv_len);
@@ -1859,30 +2017,6 @@ qla_edb_init(scsi_qla_host_t *vha)
        /* initialize lock which protects doorbell & init list */
        spin_lock_init(&vha->e_dbell.db_lock);
        INIT_LIST_HEAD(&vha->e_dbell.head);
-
-       /* create and initialize doorbell */
-       init_completion(&vha->e_dbell.dbell);
-}
-
-static void
-qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node)
-{
-       /*
-        * releases the space held by this edb node entry
-        * this function does _not_ free the edb node itself
-        * NB: the edb node entry passed should not be on any list
-        *
-        * currently for doorbell there's no additional cleanup
-        * needed, but here as a placeholder for furture use.
-        */
-
-       if (!node) {
-               ql_dbg(ql_dbg_edif, vha, 0x09122,
-                   "%s error - no valid node passed\n", __func__);
-               return;
-       }
-
-       node->ntype = N_UNDEF;
 }
 
 static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid)
@@ -1929,11 +2063,8 @@ static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid)
        }
        spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
 
-       list_for_each_entry_safe(e, tmp, &edb_list, list) {
+       list_for_each_entry_safe(e, tmp, &edb_list, list)
                qla_edb_node_free(vha, e);
-               list_del_init(&e->list);
-               kfree(e);
-       }
 }
 
 /* function called when app is stopping */
@@ -1961,14 +2092,10 @@ qla_edb_stop(scsi_qla_host_t *vha)
                    "%s freeing edb_node type=%x\n",
                    __func__, node->ntype);
                qla_edb_node_free(vha, node);
-               list_del(&node->list);
-
-               kfree(node);
        }
        spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
 
-       /* wake up doorbell waiters - they'll be dismissed with error code */
-       complete_all(&vha->e_dbell.dbell);
+       qla_edif_dbell_bsg_done(vha);
 }
 
 static struct edb_node *
@@ -2006,9 +2133,6 @@ qla_edb_node_add(scsi_qla_host_t *vha, struct edb_node *ptr)
        list_add_tail(&ptr->list, &vha->e_dbell.head);
        spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
 
-       /* ring doorbell for waiters */
-       complete(&vha->e_dbell.dbell);
-
        return true;
 }
 
@@ -2077,43 +2201,24 @@ qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype,
        default:
                ql_dbg(ql_dbg_edif, vha, 0x09102,
                        "%s unknown type: %x\n", __func__, dbtype);
-               qla_edb_node_free(vha, edbnode);
                kfree(edbnode);
                edbnode = NULL;
                break;
        }
 
-       if (edbnode && (!qla_edb_node_add(vha, edbnode))) {
+       if (edbnode) {
+               if (!qla_edb_node_add(vha, edbnode)) {
+                       ql_dbg(ql_dbg_edif, vha, 0x09102,
+                           "%s unable to add dbnode\n", __func__);
+                       kfree(edbnode);
+                       return;
+               }
                ql_dbg(ql_dbg_edif, vha, 0x09102,
-                   "%s unable to add dbnode\n", __func__);
-               qla_edb_node_free(vha, edbnode);
-               kfree(edbnode);
-               return;
-       }
-       if (edbnode && fcport)
-               fcport->edif.auth_state = dbtype;
-       ql_dbg(ql_dbg_edif, vha, 0x09102,
-           "%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode);
-}
-
-static struct edb_node *
-qla_edb_getnext(scsi_qla_host_t *vha)
-{
-       unsigned long   flags;
-       struct edb_node *edbnode = NULL;
-
-       spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
-
-       /* db nodes are fifo - no qualifications done */
-       if (!list_empty(&vha->e_dbell.head)) {
-               edbnode = list_first_entry(&vha->e_dbell.head,
-                   struct edb_node, list);
-               list_del(&edbnode->list);
+                   "%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode);
+               qla_edif_dbell_bsg_done(vha);
+               if (fcport)
+                       fcport->edif.auth_state = dbtype;
        }
-
-       spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
-
-       return edbnode;
 }
 
 void
@@ -2141,6 +2246,9 @@ qla_edif_timer(scsi_qla_host_t *vha)
                        ha->edif_post_stop_cnt_down = 60;
                }
        }
+
+       if (vha->e_dbell.dbell_bsg_job && time_after_eq(jiffies, vha->e_dbell.bsg_expire))
+               qla_edif_dbell_bsg_done(vha);
 }
 
 /*
@@ -2208,7 +2316,6 @@ edif_doorbell_show(struct device *dev, struct device_attribute *attr,
                                "%s Doorbell consumed : type=%d %p\n",
                                __func__, dbnode->ntype, dbnode);
                        /* we're done with the db node, so free it up */
-                       qla_edb_node_free(vha, dbnode);
                        kfree(dbnode);
                } else {
                        break;
index a965ca8e47ce754e2ebd2289ce6160219beca268..3561e22b8f0fc81c03043b8e7c3b0172a2b222d0 100644 (file)
@@ -51,7 +51,8 @@ struct edif_dbell {
        enum db_flags_t         db_flags;
        spinlock_t              db_lock;
        struct  list_head       head;
-       struct  completion      dbell;
+       struct bsg_job *dbell_bsg_job;
+       unsigned long bsg_expire;
 };
 
 #define SA_UPDATE_IOCB_TYPE            0x71    /* Security Association Update IOCB entry */
index 301523e4f48345bb09c4188146e4a9da07428442..110843b13767d8a3ffa5c8815ec3af6897eb5bea 100644 (file)
@@ -183,6 +183,20 @@ struct qla_sa_update_frame {
 #define        QL_VND_SC_GET_FCINFO    7
 #define        QL_VND_SC_GET_STATS     8
 #define QL_VND_SC_AEN_COMPLETE  9
+#define QL_VND_SC_READ_DBELL   10
+
+/*
+ * bsg caller to provide empty buffer for doorbell events.
+ *
+ * sg_io_v4.din_xferp  = empty buffer for door bell events
+ * sg_io_v4.dout_xferp = struct edif_read_dbell *buf
+ */
+struct edif_read_dbell {
+       struct app_id app_info;
+       uint8_t version;
+       uint8_t pad[VND_CMD_PAD_SIZE];
+       uint8_t reserved[VND_CMD_APP_RESERVED_SIZE];
+};
 
 
 /* Application interface data structure for rtn data */