summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/qla4xxx/ql4_os.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_os.c')
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c563
1 files changed, 536 insertions, 27 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index edf503437e9..3d9419460e0 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -32,14 +32,14 @@ static struct kmem_cache *srb_cachep;
/*
* Module parameter information and variables
*/
-int ql4xdisablesysfsboot = 1;
+static int ql4xdisablesysfsboot = 1;
module_param(ql4xdisablesysfsboot, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ql4xdisablesysfsboot,
" Set to disable exporting boot targets to sysfs.\n"
"\t\t 0 - Export boot targets\n"
"\t\t 1 - Do not export boot targets (Default)");
-int ql4xdontresethba = 0;
+int ql4xdontresethba;
module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ql4xdontresethba,
" Don't reset the HBA for driver recovery.\n"
@@ -71,7 +71,7 @@ MODULE_PARM_DESC(ql4xmaxqdepth,
static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO;
module_param(ql4xsess_recovery_tmo, int, S_IRUGO);
MODULE_PARM_DESC(ql4xsess_recovery_tmo,
- "Target Session Recovery Timeout.\n"
+ " Target Session Recovery Timeout.\n"
"\t\t Default: 120 sec.");
static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);
@@ -83,6 +83,8 @@ static void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha);
/*
* iSCSI template entry points
*/
+static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess,
+ enum iscsi_param param, char *buf);
static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,
enum iscsi_param param, char *buf);
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
@@ -118,6 +120,13 @@ 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);
+static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx,
+ uint32_t *num_entries, char *buf);
+static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx);
+
/*
* SCSI host template entry points
*/
@@ -179,7 +188,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
.destroy_conn = qla4xxx_conn_destroy,
.set_param = iscsi_set_param,
.get_conn_param = qla4xxx_conn_get_param,
- .get_session_param = iscsi_session_get_param,
+ .get_session_param = qla4xxx_session_get_param,
.get_ep_param = qla4xxx_get_ep_param,
.ep_connect = qla4xxx_ep_connect,
.ep_poll = qla4xxx_ep_poll,
@@ -194,10 +203,93 @@ 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,
+ .get_chap = qla4xxx_get_chap_list,
+ .delete_chap = qla4xxx_delete_chap,
};
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) {
@@ -206,6 +298,8 @@ static umode_t ql4_attr_is_visible(int param_type, int param)
case ISCSI_HOST_PARAM_HWADDRESS:
case ISCSI_HOST_PARAM_IPADDRESS:
case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ case ISCSI_HOST_PARAM_PORT_STATE:
+ case ISCSI_HOST_PARAM_PORT_SPEED:
return S_IRUGO;
default:
return 0;
@@ -225,6 +319,12 @@ static umode_t ql4_attr_is_visible(int param_type, int param)
case ISCSI_PARAM_MAX_RECV_DLENGTH:
case ISCSI_PARAM_MAX_XMIT_DLENGTH:
case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_CHAP_OUT_IDX:
+ case ISCSI_PARAM_CHAP_IN_IDX:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
return S_IRUGO;
default:
return 0;
@@ -255,6 +355,189 @@ static umode_t ql4_attr_is_visible(int param_type, int param)
return 0;
}
+static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx,
+ uint32_t *num_entries, char *buf)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct ql4_chap_table *chap_table;
+ struct iscsi_chap_rec *chap_rec;
+ int max_chap_entries = 0;
+ int valid_chap_entries = 0;
+ int ret = 0, i;
+
+ if (is_qla8022(ha))
+ max_chap_entries = (ha->hw.flt_chap_size / 2) /
+ sizeof(struct ql4_chap_table);
+ else
+ max_chap_entries = MAX_CHAP_ENTRIES_40XX;
+
+ ql4_printk(KERN_INFO, ha, "%s: num_entries = %d, CHAP idx = %d\n",
+ __func__, *num_entries, chap_tbl_idx);
+
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit_get_chap_list;
+ }
+
+ chap_rec = (struct iscsi_chap_rec *) buf;
+ mutex_lock(&ha->chap_sem);
+ for (i = chap_tbl_idx; i < max_chap_entries; i++) {
+ chap_table = (struct ql4_chap_table *)ha->chap_list + i;
+ if (chap_table->cookie !=
+ __constant_cpu_to_le16(CHAP_VALID_COOKIE))
+ continue;
+
+ chap_rec->chap_tbl_idx = i;
+ strncpy(chap_rec->username, chap_table->name,
+ ISCSI_CHAP_AUTH_NAME_MAX_LEN);
+ strncpy(chap_rec->password, chap_table->secret,
+ QL4_CHAP_MAX_SECRET_LEN);
+ chap_rec->password_length = chap_table->secret_len;
+
+ if (chap_table->flags & BIT_7) /* local */
+ chap_rec->chap_type = CHAP_TYPE_OUT;
+
+ if (chap_table->flags & BIT_6) /* peer */
+ chap_rec->chap_type = CHAP_TYPE_IN;
+
+ chap_rec++;
+
+ valid_chap_entries++;
+ if (valid_chap_entries == *num_entries)
+ break;
+ else
+ continue;
+ }
+ mutex_unlock(&ha->chap_sem);
+
+exit_get_chap_list:
+ ql4_printk(KERN_INFO, ha, "%s: Valid CHAP Entries = %d\n",
+ __func__, valid_chap_entries);
+ *num_entries = valid_chap_entries;
+ return ret;
+}
+
+static int __qla4xxx_is_chap_active(struct device *dev, void *data)
+{
+ int ret = 0;
+ uint16_t *chap_tbl_idx = (uint16_t *) data;
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *sess;
+ struct ddb_entry *ddb_entry;
+
+ if (!iscsi_is_session_dev(dev))
+ goto exit_is_chap_active;
+
+ cls_session = iscsi_dev_to_session(dev);
+ sess = cls_session->dd_data;
+ ddb_entry = sess->dd_data;
+
+ if (iscsi_session_chkready(cls_session))
+ goto exit_is_chap_active;
+
+ if (ddb_entry->chap_tbl_idx == *chap_tbl_idx)
+ ret = 1;
+
+exit_is_chap_active:
+ return ret;
+}
+
+static int qla4xxx_is_chap_active(struct Scsi_Host *shost,
+ uint16_t chap_tbl_idx)
+{
+ int ret = 0;
+
+ ret = device_for_each_child(&shost->shost_gendev, &chap_tbl_idx,
+ __qla4xxx_is_chap_active);
+
+ return ret;
+}
+
+static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct ql4_chap_table *chap_table;
+ dma_addr_t chap_dma;
+ int max_chap_entries = 0;
+ uint32_t offset = 0;
+ uint32_t chap_size;
+ int ret = 0;
+
+ chap_table = dma_pool_alloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
+ if (chap_table == NULL)
+ return -ENOMEM;
+
+ memset(chap_table, 0, sizeof(struct ql4_chap_table));
+
+ if (is_qla8022(ha))
+ max_chap_entries = (ha->hw.flt_chap_size / 2) /
+ sizeof(struct ql4_chap_table);
+ else
+ max_chap_entries = MAX_CHAP_ENTRIES_40XX;
+
+ if (chap_tbl_idx > max_chap_entries) {
+ ret = -EINVAL;
+ goto exit_delete_chap;
+ }
+
+ /* Check if chap index is in use.
+ * If chap is in use don't delet chap entry */
+ ret = qla4xxx_is_chap_active(shost, chap_tbl_idx);
+ if (ret) {
+ ql4_printk(KERN_INFO, ha, "CHAP entry %d is in use, cannot "
+ "delete from flash\n", chap_tbl_idx);
+ ret = -EBUSY;
+ goto exit_delete_chap;
+ }
+
+ chap_size = sizeof(struct ql4_chap_table);
+ if (is_qla40XX(ha))
+ offset = FLASH_CHAP_OFFSET | (chap_tbl_idx * chap_size);
+ else {
+ offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2);
+ /* flt_chap_size is CHAP table size for both ports
+ * so divide it by 2 to calculate the offset for second port
+ */
+ if (ha->port_num == 1)
+ offset += (ha->hw.flt_chap_size / 2);
+ offset += (chap_tbl_idx * chap_size);
+ }
+
+ ret = qla4xxx_get_flash(ha, chap_dma, offset, chap_size);
+ if (ret != QLA_SUCCESS) {
+ ret = -EINVAL;
+ goto exit_delete_chap;
+ }
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "Chap Cookie: x%x\n",
+ __le16_to_cpu(chap_table->cookie)));
+
+ if (__le16_to_cpu(chap_table->cookie) != CHAP_VALID_COOKIE) {
+ ql4_printk(KERN_ERR, ha, "No valid chap entry found\n");
+ goto exit_delete_chap;
+ }
+
+ chap_table->cookie = __constant_cpu_to_le16(0xFFFF);
+
+ offset = FLASH_CHAP_OFFSET |
+ (chap_tbl_idx * sizeof(struct ql4_chap_table));
+ ret = qla4xxx_set_flash(ha, chap_dma, offset, chap_size,
+ FLASH_OPT_RMW_COMMIT);
+ if (ret == QLA_SUCCESS && ha->chap_list) {
+ mutex_lock(&ha->chap_sem);
+ /* Update ha chap_list cache */
+ memcpy((struct ql4_chap_table *)ha->chap_list + chap_tbl_idx,
+ chap_table, sizeof(struct ql4_chap_table));
+ mutex_unlock(&ha->chap_sem);
+ }
+ if (ret != QLA_SUCCESS)
+ ret = -EINVAL;
+
+exit_delete_chap:
+ dma_pool_free(ha->chap_dma_pool, chap_table, chap_dma);
+ return ret;
+}
+
static int qla4xxx_get_iface_param(struct iscsi_iface *iface,
enum iscsi_param_type param_type,
int param, char *buf)
@@ -548,6 +831,43 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
return ret;
}
+static void qla4xxx_set_port_speed(struct Scsi_Host *shost)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct iscsi_cls_host *ihost = shost_priv(shost);
+ uint32_t speed = ISCSI_PORT_SPEED_UNKNOWN;
+
+ qla4xxx_get_firmware_state(ha);
+
+ switch (ha->addl_fw_state & 0x0F00) {
+ case FW_ADDSTATE_LINK_SPEED_10MBPS:
+ speed = ISCSI_PORT_SPEED_10MBPS;
+ break;
+ case FW_ADDSTATE_LINK_SPEED_100MBPS:
+ speed = ISCSI_PORT_SPEED_100MBPS;
+ break;
+ case FW_ADDSTATE_LINK_SPEED_1GBPS:
+ speed = ISCSI_PORT_SPEED_1GBPS;
+ break;
+ case FW_ADDSTATE_LINK_SPEED_10GBPS:
+ speed = ISCSI_PORT_SPEED_10GBPS;
+ break;
+ }
+ ihost->port_speed = speed;
+}
+
+static void qla4xxx_set_port_state(struct Scsi_Host *shost)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct iscsi_cls_host *ihost = shost_priv(shost);
+ uint32_t state = ISCSI_PORT_STATE_DOWN;
+
+ if (test_bit(AF_LINK_UP, &ha->flags))
+ state = ISCSI_PORT_STATE_UP;
+
+ ihost->port_state = state;
+}
+
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
@@ -564,6 +884,14 @@ static int qla4xxx_host_get_param(struct Scsi_Host *shost,
case ISCSI_HOST_PARAM_INITIATOR_NAME:
len = sprintf(buf, "%s\n", ha->name_string);
break;
+ case ISCSI_HOST_PARAM_PORT_STATE:
+ qla4xxx_set_port_state(shost);
+ len = sprintf(buf, "%s\n", iscsi_get_port_state_name(shost));
+ break;
+ case ISCSI_HOST_PARAM_PORT_SPEED:
+ qla4xxx_set_port_speed(shost);
+ len = sprintf(buf, "%s\n", iscsi_get_port_speed_name(shost));
+ break;
default:
return -ENOSYS;
}
@@ -968,6 +1296,41 @@ exit_init_fw_cb:
return rval;
}
+static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_session *sess = cls_sess->dd_data;
+ struct ddb_entry *ddb_entry = sess->dd_data;
+ struct scsi_qla_host *ha = ddb_entry->ha;
+ int rval, len;
+ uint16_t idx;
+
+ switch (param) {
+ case ISCSI_PARAM_CHAP_IN_IDX:
+ rval = qla4xxx_get_chap_index(ha, sess->username_in,
+ sess->password_in, BIDI_CHAP,
+ &idx);
+ if (rval)
+ return -EINVAL;
+
+ len = sprintf(buf, "%hu\n", idx);
+ break;
+ case ISCSI_PARAM_CHAP_OUT_IDX:
+ rval = qla4xxx_get_chap_index(ha, sess->username,
+ sess->password, LOCAL_CHAP,
+ &idx);
+ if (rval)
+ return -EINVAL;
+
+ len = sprintf(buf, "%hu\n", idx);
+ break;
+ default:
+ return iscsi_session_get_param(cls_sess, param, buf);
+ }
+
+ return len;
+}
+
static int qla4xxx_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
@@ -1506,13 +1869,17 @@ static void qla4xxx_copy_fwddb_param(struct scsi_qla_host *ha,
{
int buflen = 0;
struct iscsi_session *sess;
+ struct ddb_entry *ddb_entry;
struct iscsi_conn *conn;
char ip_addr[DDB_IPADDR_LEN];
uint16_t options = 0;
sess = cls_sess->dd_data;
+ ddb_entry = sess->dd_data;
conn = cls_conn->dd_data;
+ ddb_entry->chap_tbl_idx = le16_to_cpu(fw_ddb_entry->chap_tbl_idx);
+
conn->max_recv_dlength = BYTE_UNITS *
le16_to_cpu(fw_ddb_entry->iscsi_max_rcv_data_seg_len);
@@ -1552,6 +1919,8 @@ static void qla4xxx_copy_fwddb_param(struct scsi_qla_host *ha,
(char *)ha->name_string, buflen);
iscsi_set_param(cls_conn, ISCSI_PARAM_PERSISTENT_ADDRESS,
(char *)ip_addr, buflen);
+ iscsi_set_param(cls_conn, ISCSI_PARAM_TARGET_ALIAS,
+ (char *)fw_ddb_entry->iscsi_alias, buflen);
}
void qla4xxx_update_session_conn_fwddb_param(struct scsi_qla_host *ha,
@@ -1638,6 +2007,7 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,
le16_to_cpu(fw_ddb_entry->iscsi_def_time2wait);
/* Update params */
+ ddb_entry->chap_tbl_idx = le16_to_cpu(fw_ddb_entry->chap_tbl_idx);
conn->max_recv_dlength = BYTE_UNITS *
le16_to_cpu(fw_ddb_entry->iscsi_max_rcv_data_seg_len);
@@ -1666,6 +2036,9 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,
memcpy(sess->initiatorname, ha->name_string,
min(sizeof(ha->name_string), sizeof(sess->initiatorname)));
+ iscsi_set_param(cls_conn, ISCSI_PARAM_TARGET_ALIAS,
+ (char *)fw_ddb_entry->iscsi_alias, 0);
+
exit_session_conn_param:
if (fw_ddb_entry)
dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
@@ -2113,7 +2486,7 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
halt_status = qla4_8xxx_rd_32(ha,
QLA82XX_PEG_HALT_STATUS1);
- if (LSW(MSB(halt_status)) == 0x67)
+ if (QLA82XX_FWERROR_CODE(halt_status) == 0x67)
ql4_printk(KERN_ERR, ha, "%s:"
" Firmware aborted with"
" error code 0x00006700."
@@ -2230,6 +2603,10 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
}
}
+ /* Process any deferred work. */
+ if (!list_empty(&ha->work_list))
+ start_dpc++;
+
/* Wakeup the dpc routine for this adapter, if needed. */
if (start_dpc ||
test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
@@ -2795,6 +3172,109 @@ void qla4xxx_wake_dpc(struct scsi_qla_host *ha)
queue_work(ha->dpc_thread, &ha->dpc_work);
}
+static struct qla4_work_evt *
+qla4xxx_alloc_work(struct scsi_qla_host *ha, uint32_t data_size,
+ enum qla4_work_type type)
+{
+ struct qla4_work_evt *e;
+ uint32_t size = sizeof(struct qla4_work_evt) + data_size;
+
+ e = kzalloc(size, GFP_ATOMIC);
+ if (!e)
+ return NULL;
+
+ INIT_LIST_HEAD(&e->list);
+ e->type = type;
+ return e;
+}
+
+static void qla4xxx_post_work(struct scsi_qla_host *ha,
+ struct qla4_work_evt *e)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->work_lock, flags);
+ list_add_tail(&e->list, &ha->work_list);
+ spin_unlock_irqrestore(&ha->work_lock, flags);
+ qla4xxx_wake_dpc(ha);
+}
+
+int qla4xxx_post_aen_work(struct scsi_qla_host *ha,
+ enum iscsi_host_event_code aen_code,
+ uint32_t data_size, uint8_t *data)
+{
+ struct qla4_work_evt *e;
+
+ e = qla4xxx_alloc_work(ha, data_size, QLA4_EVENT_AEN);
+ if (!e)
+ return QLA_ERROR;
+
+ e->u.aen.code = aen_code;
+ e->u.aen.data_size = data_size;
+ memcpy(e->u.aen.data, data, data_size);
+
+ qla4xxx_post_work(ha, e);
+
+ 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;
+}
+
+static void qla4xxx_do_work(struct scsi_qla_host *ha)
+{
+ struct qla4_work_evt *e, *tmp;
+ unsigned long flags;
+ LIST_HEAD(work);
+
+ spin_lock_irqsave(&ha->work_lock, flags);
+ list_splice_init(&ha->work_list, &work);
+ spin_unlock_irqrestore(&ha->work_lock, flags);
+
+ list_for_each_entry_safe(e, tmp, &work, list) {
+ list_del_init(&e->list);
+
+ switch (e->type) {
+ case QLA4_EVENT_AEN:
+ iscsi_post_host_event(ha->host_no,
+ &qla4xxx_iscsi_transport,
+ e->u.aen.code,
+ 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);
+ }
+ kfree(e);
+ }
+}
+
/**
* qla4xxx_do_dpc - dpc routine
* @data: in our case pointer to adapter structure
@@ -2826,6 +3306,9 @@ static void qla4xxx_do_dpc(struct work_struct *work)
return;
}
+ /* post events to application */
+ qla4xxx_do_work(ha);
+
if (is_qla8022(ha)) {
if (test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags)) {
qla4_8xxx_idc_lock(ha);
@@ -3341,9 +3824,8 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
/* Check Boot Mode */
val = rd_nvram_byte(ha, addr);
if (!(val & 0x07)) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Failed Boot options : 0x%x\n",
- __func__, val));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Adapter boot "
+ "options : 0x%x\n", __func__, val));
ret = QLA_ERROR;
goto exit_boot_info;
}
@@ -3388,9 +3870,8 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
}
/* Check Boot Mode */
if (!(buf[1] & 0x07)) {
- DEBUG2(ql4_printk(KERN_INFO, ha,
- "Failed: Boot options : 0x%x\n",
- buf[1]));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "Firmware boot options"
+ " : 0x%x\n", buf[1]));
ret = QLA_ERROR;
goto exit_boot_info_free;
}
@@ -3411,12 +3892,11 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
" target ID %d\n", __func__, ddb_index[0],
ddb_index[1]));
- ha->pri_ddb_idx = ddb_index[0];
- ha->sec_ddb_idx = ddb_index[1];
-
exit_boot_info_free:
dma_free_coherent(&ha->pdev->dev, size, buf, buf_dma);
exit_boot_info:
+ ha->pri_ddb_idx = ddb_index[0];
+ ha->sec_ddb_idx = ddb_index[1];
return ret;
}
@@ -3497,8 +3977,8 @@ static int qla4xxx_get_boot_target(struct scsi_qla_host *ha,
if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
fw_ddb_entry_dma, ddb_index)) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Flash DDB read Failed\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: No Flash DDB found at "
+ "index [%d]\n", __func__, ddb_index));
ret = QLA_ERROR;
goto exit_boot_target;
}
@@ -3576,8 +4056,8 @@ static int qla4xxx_get_boot_info(struct scsi_qla_host *ha)
ddb_index[1] = 0xffff;
ret = get_fw_boot_info(ha, ddb_index);
if (ret != QLA_SUCCESS) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Failed to set boot info.\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: No boot target configured.\n", __func__));
return ret;
}
@@ -3590,8 +4070,8 @@ static int qla4xxx_get_boot_info(struct scsi_qla_host *ha)
rval = qla4xxx_get_boot_target(ha, &(ha->boot_tgt.boot_pri_sess),
ddb_index[0]);
if (rval != QLA_SUCCESS) {
- DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Failed to get "
- "primary target\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Primary boot target not "
+ "configured\n", __func__));
} else
ret = QLA_SUCCESS;
@@ -3602,8 +4082,8 @@ sec_target:
rval = qla4xxx_get_boot_target(ha, &(ha->boot_tgt.boot_sec_sess),
ddb_index[1]);
if (rval != QLA_SUCCESS) {
- DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Failed to get "
- "secondary target\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Secondary boot target not"
+ " configured\n", __func__));
} else
ret = QLA_SUCCESS;
@@ -3772,11 +4252,13 @@ static void qla4xxx_convert_param_ddb(struct dev_db_entry *fw_ddb_entry,
sprintf(tddb->ip_addr, "%pI4", fw_ddb_entry->ip_addr);
tddb->port = le16_to_cpu(fw_ddb_entry->port);
+ memcpy(&tddb->isid[0], &fw_ddb_entry->isid[0], sizeof(tddb->isid));
}
static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
struct ql4_tuple_ddb *old_tddb,
- struct ql4_tuple_ddb *new_tddb)
+ struct ql4_tuple_ddb *new_tddb,
+ uint8_t is_isid_compare)
{
if (strcmp(old_tddb->iscsi_name, new_tddb->iscsi_name))
return QLA_ERROR;
@@ -3787,6 +4269,26 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
if (old_tddb->port != new_tddb->port)
return QLA_ERROR;
+ /* For multi sessions, driver generates the ISID, so do not compare
+ * ISID in reset path since it would be a comparision between the
+ * driver generated ISID and firmware generated ISID. This could
+ * lead to adding duplicated DDBs in the list as driver generated
+ * ISID would not match firmware generated ISID.
+ */
+ if (is_isid_compare) {
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: old ISID [%02x%02x%02x"
+ "%02x%02x%02x] New ISID [%02x%02x%02x%02x%02x%02x]\n",
+ __func__, old_tddb->isid[5], old_tddb->isid[4],
+ old_tddb->isid[3], old_tddb->isid[2], old_tddb->isid[1],
+ old_tddb->isid[0], new_tddb->isid[5], new_tddb->isid[4],
+ new_tddb->isid[3], new_tddb->isid[2], new_tddb->isid[1],
+ new_tddb->isid[0]));
+
+ if (memcmp(&old_tddb->isid[0], &new_tddb->isid[0],
+ sizeof(old_tddb->isid)))
+ return QLA_ERROR;
+ }
+
DEBUG2(ql4_printk(KERN_INFO, ha,
"Match Found, fw[%d,%d,%s,%s], [%d,%d,%s,%s]",
old_tddb->port, old_tddb->tpgt, old_tddb->ip_addr,
@@ -3829,7 +4331,7 @@ static int qla4xxx_is_session_exists(struct scsi_qla_host *ha,
continue;
qla4xxx_get_param_ddb(ddb_entry, tmp_tddb);
- if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb)) {
+ if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb, false)) {
ret = QLA_SUCCESS; /* found */
goto exit_check;
}
@@ -3872,7 +4374,7 @@ static int qla4xxx_is_flash_ddb_exists(struct scsi_qla_host *ha,
list_for_each_entry_safe(nt_ddb_idx, nt_ddb_idx_tmp, list_nt, list) {
qla4xxx_convert_param_ddb(&nt_ddb_idx->fw_ddb, tmp_tddb);
- if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb)) {
+ if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb, true)) {
ret = QLA_SUCCESS; /* found */
goto exit_check;
}
@@ -4038,6 +4540,10 @@ static void qla4xxx_build_st_list(struct scsi_qla_host *ha,
if (ret == QLA_ERROR)
break;
+ /* Ignore DDB if invalid state (unassigned) */
+ if (state == DDB_DS_UNASSIGNED)
+ goto continue_next_st;
+
/* Check if ST, add to the list_st */
if (strlen((char *) fw_ddb_entry->iscsi_name) != 0)
goto continue_next_st;
@@ -4397,6 +4903,9 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
spin_lock_init(&ha->hardware_lock);
+ /* Initialize work list */
+ INIT_LIST_HEAD(&ha->work_list);
+
/* Allocate dma buffers */
if (qla4xxx_mem_alloc(ha)) {
ql4_printk(KERN_WARNING, ha,
@@ -4524,8 +5033,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
ha->patch_number, ha->build_number);
if (qla4xxx_setup_boot_info(ha))
- ql4_printk(KERN_ERR, ha, "%s:ISCSI boot info setup failed\n",
- __func__);
+ ql4_printk(KERN_ERR, ha,
+ "%s: No iSCSI boot target configured\n", __func__);
/* Perform the build ddb list and login to each */
qla4xxx_build_ddb_list(ha, INIT_ADAPTER);