diff options
author | Vikas Chaudhary <vikas.chaudhary@qlogic.com> | 2012-02-13 14:00:49 +0100 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-19 16:35:23 +0100 |
commit | c0b9d3f750520ad3005b99144260e486ef01b5d9 (patch) | |
tree | 2c16adb36151017344dea7681e312fb4f68fd3d5 | |
parent | [SCSI] iscsi_transport: Added Ping support (diff) | |
download | linux-c0b9d3f750520ad3005b99144260e486ef01b5d9.tar.xz linux-c0b9d3f750520ad3005b99144260e486ef01b5d9.zip |
[SCSI] qla4xxx: Added ping support
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>
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_def.h | 21 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_fw.h | 20 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_glbl.h | 5 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_init.c | 5 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_iocb.c | 92 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_isr.c | 72 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_os.c | 113 |
7 files changed, 328 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index f91808ce572e..776714d2d70d 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -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 { diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h index 5f82b5decd0f..210cd1d64475 100644 --- a/drivers/scsi/qla4xxx/ql4_fw.h +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -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. */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h index 34cf851978e9..954ba9188256 100644 --- a/drivers/scsi/qla4xxx/ql4_glbl.h +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -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); diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 90614f38b55d..90ee5d8fa731 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -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; diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c index 410669351906..c70651ddaf8b 100644 --- a/drivers/scsi/qla4xxx/ql4_iocb.c +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -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; +} diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c index 954fe84be575..7c9f28b7da72 100644 --- a/drivers/scsi/qla4xxx/ql4_isr.c +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -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 diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index d423f7afbbd7..877c0e220ac9 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -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); |