[SCSI] qla4xxx: Added ping support
authorVikas Chaudhary <vikas.chaudhary@qlogic.com>
Mon, 13 Feb 2012 13:00:49 +0000 (18:30 +0530)
committerJames Bottomley <JBottomley@Parallels.com>
Sun, 19 Feb 2012 15:35:23 +0000 (09:35 -0600)
Added ping support for network connection diagnostics.

Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/qla4xxx/ql4_def.h
drivers/scsi/qla4xxx/ql4_fw.h
drivers/scsi/qla4xxx/ql4_glbl.h
drivers/scsi/qla4xxx/ql4_init.c
drivers/scsi/qla4xxx/ql4_iocb.c
drivers/scsi/qla4xxx/ql4_isr.c
drivers/scsi/qla4xxx/ql4_os.c

index f91808c..776714d 100644 (file)
@@ -221,6 +221,15 @@ struct srb {
        uint16_t reserved2;
 };
 
+/* Mailbox request block structure */
+struct mrb {
+       struct scsi_qla_host *ha;
+       struct mbox_cmd_iocb *mbox;
+       uint32_t mbox_cmd;
+       uint16_t iocb_cnt;              /* Number of used iocbs */
+       uint32_t pid;
+};
+
 /*
  * Asynchronous Event Queue structure
  */
@@ -303,6 +312,7 @@ struct ql4_tuple_ddb {
 
 enum qla4_work_type {
        QLA4_EVENT_AEN,
+       QLA4_EVENT_PING_STATUS,
 };
 
 struct qla4_work_evt {
@@ -314,6 +324,12 @@ struct qla4_work_evt {
                        uint32_t data_size;
                        uint8_t data[0];
                } aen;
+               struct {
+                       uint32_t status;
+                       uint32_t pid;
+                       uint32_t data_size;
+                       uint8_t data[0];
+               } ping;
        } u;
 };
 
@@ -690,6 +706,11 @@ struct scsi_qla_host {
        /* event work list */
        struct list_head work_list;
        spinlock_t work_lock;
+
+       /* mbox iocb */
+#define MAX_MRB                128
+       struct mrb *active_mrb_array[MAX_MRB];
+       uint32_t mrb_index;
 };
 
 struct ql4_task_data {
index 5f82b5d..210cd1d 100644 (file)
@@ -331,6 +331,10 @@ struct qla_flt_region {
 /*  Mailbox command definitions */
 #define MBOX_CMD_ABOUT_FW                      0x0009
 #define MBOX_CMD_PING                          0x000B
+#define PING_IPV6_PROTOCOL_ENABLE              0x1
+#define PING_IPV6_LINKLOCAL_ADDR               0x4
+#define PING_IPV6_ADDR0                                0x8
+#define PING_IPV6_ADDR1                                0xC
 #define MBOX_CMD_ENABLE_INTRS                  0x0010
 #define INTR_DISABLE                           0
 #define INTR_ENABLE                            1
@@ -922,6 +926,8 @@ struct qla4_header {
 #define ET_CMND_T3              0x19
 #define ET_PASSTHRU0            0x3A
 #define ET_PASSTHRU_STATUS      0x3C
+#define ET_MBOX_CMD            0x38
+#define ET_MBOX_STATUS         0x39
 
        uint8_t entryStatus;
        uint8_t systemDefined;
@@ -1122,6 +1128,20 @@ struct passthru_status {
        uint8_t res4[16];       /* 30-3F */
 };
 
+struct mbox_cmd_iocb {
+       struct qla4_header hdr; /* 00-03 */
+       uint32_t handle;        /* 04-07 */
+       uint32_t in_mbox[8];    /* 08-25 */
+       uint32_t res1[6];       /* 26-3F */
+};
+
+struct mbox_status_iocb {
+       struct qla4_header hdr; /* 00-03 */
+       uint32_t handle;        /* 04-07 */
+       uint32_t out_mbox[8];   /* 08-25 */
+       uint32_t res1[6];       /* 26-3F */
+};
+
 /*
  * ISP queue - response queue entry definition.
  */
index 34cf851..954ba91 100644 (file)
@@ -183,6 +183,11 @@ int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
 void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset);
 int qla4xxx_post_aen_work(struct scsi_qla_host *ha, uint32_t aen_code,
                          uint32_t data_size, uint8_t *data);
+int qla4xxx_ping_iocb(struct scsi_qla_host *ha, uint32_t options,
+                     uint32_t payload_size, uint32_t pid, uint8_t *ipaddr);
+int qla4xxx_post_ping_evt_work(struct scsi_qla_host *ha,
+                              uint32_t status, uint32_t pid,
+                              uint32_t data_size, uint8_t *data);
 
 /* BSG Functions */
 int qla4xxx_bsg_request(struct bsg_job *bsg_job);
index 90614f3..90ee5d8 100644 (file)
@@ -86,6 +86,7 @@ static void qla4xxx_init_response_q_entries(struct scsi_qla_host *ha)
 int qla4xxx_init_rings(struct scsi_qla_host *ha)
 {
        unsigned long flags = 0;
+       int i;
 
        /* Initialize request queue. */
        spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -125,6 +126,10 @@ int qla4xxx_init_rings(struct scsi_qla_host *ha)
 
        qla4xxx_init_response_q_entries(ha);
 
+       /* Initialize mabilbox active array */
+       for (i = 0; i < MAX_MRB; i++)
+               ha->active_mrb_array[i] = NULL;
+
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
        return QLA_SUCCESS;
index 4106693..c70651d 100644 (file)
@@ -445,3 +445,95 @@ queuing_error:
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
        return ret;
 }
+
+static struct mrb *qla4xxx_get_new_mrb(struct scsi_qla_host *ha)
+{
+       struct mrb *mrb;
+
+       mrb = kzalloc(sizeof(*mrb), GFP_KERNEL);
+       if (!mrb)
+               return mrb;
+
+       mrb->ha = ha;
+       return mrb;
+}
+
+int qla4xxx_send_mbox_iocb(struct scsi_qla_host *ha, struct mrb *mrb,
+                          uint32_t *in_mbox)
+{
+       int rval = QLA_SUCCESS;
+       uint32_t i;
+       unsigned long flags;
+       uint32_t index = 0;
+
+       /* Acquire hardware specific lock */
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       /* Get pointer to the queue entry for the marker */
+       rval = qla4xxx_get_req_pkt(ha, (struct queue_entry **) &(mrb->mbox));
+       if (rval != QLA_SUCCESS)
+               goto exit_mbox_iocb;
+
+       index = ha->mrb_index;
+       /* get valid mrb index*/
+       for (i = 0; i < MAX_MRB; i++) {
+               index++;
+               if (index == MAX_MRB)
+                       index = 1;
+               if (ha->active_mrb_array[index] == NULL) {
+                       ha->mrb_index = index;
+                       break;
+               }
+       }
+
+       mrb->iocb_cnt = 1;
+       ha->active_mrb_array[index] = mrb;
+       mrb->mbox->handle = index;
+       mrb->mbox->hdr.entryType = ET_MBOX_CMD;
+       mrb->mbox->hdr.entryCount = mrb->iocb_cnt;
+       memcpy(mrb->mbox->in_mbox, in_mbox, 32);
+       mrb->mbox_cmd = in_mbox[0];
+       wmb();
+
+       ha->isp_ops->queue_iocb(ha);
+exit_mbox_iocb:
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       return rval;
+}
+
+int qla4xxx_ping_iocb(struct scsi_qla_host *ha, uint32_t options,
+                     uint32_t payload_size, uint32_t pid, uint8_t *ipaddr)
+{
+       uint32_t in_mbox[8];
+       struct mrb *mrb = NULL;
+       int rval = QLA_SUCCESS;
+
+       memset(in_mbox, 0, sizeof(in_mbox));
+
+       mrb = qla4xxx_get_new_mrb(ha);
+       if (!mrb) {
+               DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: fail to get new mrb\n",
+                                 __func__));
+               rval = QLA_ERROR;
+               goto exit_ping;
+       }
+
+       in_mbox[0] = MBOX_CMD_PING;
+       in_mbox[1] = options;
+       memcpy(&in_mbox[2], &ipaddr[0], 4);
+       memcpy(&in_mbox[3], &ipaddr[4], 4);
+       memcpy(&in_mbox[4], &ipaddr[8], 4);
+       memcpy(&in_mbox[5], &ipaddr[12], 4);
+       in_mbox[6] = payload_size;
+
+       mrb->pid = pid;
+       rval = qla4xxx_send_mbox_iocb(ha, mrb, in_mbox);
+
+       if (rval != QLA_SUCCESS)
+               goto exit_ping;
+
+       return rval;
+exit_ping:
+       kfree(mrb);
+       return rval;
+}
index 954fe84..7c9f28b 100644 (file)
@@ -385,6 +385,71 @@ static void qla4xxx_passthru_status_entry(struct scsi_qla_host *ha,
        queue_work(ha->task_wq, &task_data->task_work);
 }
 
+static struct mrb *qla4xxx_del_mrb_from_active_array(struct scsi_qla_host *ha,
+                                                    uint32_t index)
+{
+       struct mrb *mrb = NULL;
+
+       /* validate handle and remove from active array */
+       if (index >= MAX_MRB)
+               return mrb;
+
+       mrb = ha->active_mrb_array[index];
+       ha->active_mrb_array[index] = NULL;
+       if (!mrb)
+               return mrb;
+
+       /* update counters */
+       ha->req_q_count += mrb->iocb_cnt;
+       ha->iocb_cnt -= mrb->iocb_cnt;
+
+       return mrb;
+}
+
+static void qla4xxx_mbox_status_entry(struct scsi_qla_host *ha,
+                                     struct mbox_status_iocb *mbox_sts_entry)
+{
+       struct mrb *mrb;
+       uint32_t status;
+       uint32_t data_size;
+
+       mrb = qla4xxx_del_mrb_from_active_array(ha,
+                                       le32_to_cpu(mbox_sts_entry->handle));
+
+       if (mrb == NULL) {
+               ql4_printk(KERN_WARNING, ha, "%s: mrb[%d] is null\n", __func__,
+                          mbox_sts_entry->handle);
+               return;
+       }
+
+       switch (mrb->mbox_cmd) {
+       case MBOX_CMD_PING:
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: mbox_cmd = 0x%x, "
+                                 "mbox_sts[0] = 0x%x, mbox_sts[6] = 0x%x\n",
+                                 __func__, mrb->mbox_cmd,
+                                 mbox_sts_entry->out_mbox[0],
+                                 mbox_sts_entry->out_mbox[6]));
+
+               if (mbox_sts_entry->out_mbox[0] == MBOX_STS_COMMAND_COMPLETE)
+                       status = QLA_SUCCESS;
+               else
+                       status = QLA_ERROR;
+
+               data_size = sizeof(mbox_sts_entry->out_mbox);
+
+               qla4xxx_post_ping_evt_work(ha, status, mrb->pid, data_size,
+                                       (uint8_t *) mbox_sts_entry->out_mbox);
+               break;
+
+       default:
+               DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: invalid mbox_cmd = "
+                                 "0x%x\n", __func__, mrb->mbox_cmd));
+       }
+
+       kfree(mrb);
+       return;
+}
+
 /**
  * qla4xxx_process_response_queue - process response queue completions
  * @ha: Pointer to host adapter structure.
@@ -461,6 +526,13 @@ void qla4xxx_process_response_queue(struct scsi_qla_host *ha)
                                      "ignoring\n", ha->host_no, __func__));
                        break;
 
+               case ET_MBOX_STATUS:
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "%s: mbox status IOCB\n", __func__));
+                       qla4xxx_mbox_status_entry(ha,
+                                       (struct mbox_status_iocb *)sts_entry);
+                       break;
+
                default:
                        /*
                         * Invalid entry in response queue, reset RISC
index d423f7a..877c0e2 100644 (file)
@@ -118,6 +118,10 @@ static void qla4xxx_task_cleanup(struct iscsi_task *);
 static void qla4xxx_fail_session(struct iscsi_cls_session *cls_session);
 static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn,
                                   struct iscsi_stats *stats);
+static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num,
+                            uint32_t iface_type, uint32_t payload_size,
+                            uint32_t pid, struct sockaddr *dst_addr);
+
 /*
  * SCSI host template entry points
  */
@@ -194,10 +198,91 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
        .set_iface_param        = qla4xxx_iface_set_param,
        .get_iface_param        = qla4xxx_get_iface_param,
        .bsg_request            = qla4xxx_bsg_request,
+       .send_ping              = qla4xxx_send_ping,
 };
 
 static struct scsi_transport_template *qla4xxx_scsi_transport;
 
+static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num,
+                            uint32_t iface_type, uint32_t payload_size,
+                            uint32_t pid, struct sockaddr *dst_addr)
+{
+       struct scsi_qla_host *ha = to_qla_host(shost);
+       struct sockaddr_in *addr;
+       struct sockaddr_in6 *addr6;
+       uint32_t options = 0;
+       uint8_t ipaddr[IPv6_ADDR_LEN];
+       int rval;
+
+       memset(ipaddr, 0, IPv6_ADDR_LEN);
+       /* IPv4 to IPv4 */
+       if ((iface_type == ISCSI_IFACE_TYPE_IPV4) &&
+           (dst_addr->sa_family == AF_INET)) {
+               addr = (struct sockaddr_in *)dst_addr;
+               memcpy(ipaddr, &addr->sin_addr.s_addr, IP_ADDR_LEN);
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv4 Ping src: %pI4 "
+                                 "dest: %pI4\n", __func__,
+                                 &ha->ip_config.ip_address, ipaddr));
+               rval = qla4xxx_ping_iocb(ha, options, payload_size, pid,
+                                        ipaddr);
+               if (rval)
+                       rval = -EINVAL;
+       } else if ((iface_type == ISCSI_IFACE_TYPE_IPV6) &&
+                  (dst_addr->sa_family == AF_INET6)) {
+               /* IPv6 to IPv6 */
+               addr6 = (struct sockaddr_in6 *)dst_addr;
+               memcpy(ipaddr, &addr6->sin6_addr.in6_u.u6_addr8, IPv6_ADDR_LEN);
+
+               options |= PING_IPV6_PROTOCOL_ENABLE;
+
+               /* Ping using LinkLocal address */
+               if ((iface_num == 0) || (iface_num == 1)) {
+                       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: LinkLocal Ping "
+                                         "src: %pI6 dest: %pI6\n", __func__,
+                                         &ha->ip_config.ipv6_link_local_addr,
+                                         ipaddr));
+                       options |= PING_IPV6_LINKLOCAL_ADDR;
+                       rval = qla4xxx_ping_iocb(ha, options, payload_size,
+                                                pid, ipaddr);
+               } else {
+                       ql4_printk(KERN_WARNING, ha, "%s: iface num = %d "
+                                  "not supported\n", __func__, iface_num);
+                       rval = -ENOSYS;
+                       goto exit_send_ping;
+               }
+
+               /*
+                * If ping using LinkLocal address fails, try ping using
+                * IPv6 address
+                */
+               if (rval != QLA_SUCCESS) {
+                       options &= ~PING_IPV6_LINKLOCAL_ADDR;
+                       if (iface_num == 0) {
+                               options |= PING_IPV6_ADDR0;
+                               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv6 "
+                                                 "Ping src: %pI6 "
+                                                 "dest: %pI6\n", __func__,
+                                                 &ha->ip_config.ipv6_addr0,
+                                                 ipaddr));
+                       } else if (iface_num == 1) {
+                               options |= PING_IPV6_ADDR1;
+                               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv6 "
+                                                 "Ping src: %pI6 "
+                                                 "dest: %pI6\n", __func__,
+                                                 &ha->ip_config.ipv6_addr1,
+                                                 ipaddr));
+                       }
+                       rval = qla4xxx_ping_iocb(ha, options, payload_size,
+                                                pid, ipaddr);
+                       if (rval)
+                               rval = -EINVAL;
+               }
+       } else
+               rval = -ENOSYS;
+exit_send_ping:
+       return rval;
+}
+
 static umode_t ql4_attr_is_visible(int param_type, int param)
 {
        switch (param_type) {
@@ -2897,6 +2982,26 @@ int qla4xxx_post_aen_work(struct scsi_qla_host *ha,
        return QLA_SUCCESS;
 }
 
+int qla4xxx_post_ping_evt_work(struct scsi_qla_host *ha,
+                              uint32_t status, uint32_t pid,
+                              uint32_t data_size, uint8_t *data)
+{
+       struct qla4_work_evt *e;
+
+       e = qla4xxx_alloc_work(ha, data_size, QLA4_EVENT_PING_STATUS);
+       if (!e)
+               return QLA_ERROR;
+
+       e->u.ping.status = status;
+       e->u.ping.pid = pid;
+       e->u.ping.data_size = data_size;
+       memcpy(e->u.ping.data, data, data_size);
+
+       qla4xxx_post_work(ha, e);
+
+       return QLA_SUCCESS;
+}
+
 void qla4xxx_do_work(struct scsi_qla_host *ha)
 {
        struct qla4_work_evt *e, *tmp;
@@ -2918,6 +3023,14 @@ void qla4xxx_do_work(struct scsi_qla_host *ha)
                                              e->u.aen.data_size,
                                              e->u.aen.data);
                        break;
+               case QLA4_EVENT_PING_STATUS:
+                       iscsi_ping_comp_event(ha->host_no,
+                                             &qla4xxx_iscsi_transport,
+                                             e->u.ping.status,
+                                             e->u.ping.pid,
+                                             e->u.ping.data_size,
+                                             e->u.ping.data);
+                       break;
                default:
                        ql4_printk(KERN_WARNING, ha, "event type: 0x%x not "
                                   "supported", e->type);