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>
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
*/
enum qla4_work_type {
QLA4_EVENT_AEN,
+ QLA4_EVENT_PING_STATUS,
};
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;
};
/* 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 {
/* 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
#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;
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.
*/
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);
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);
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;
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;
+}
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.
"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
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
*/
.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) {
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;
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);