EXPERIMENTAL! patch to improve keeping S_ID/LOOP_ID/PortName mapping handling. Only slightly tested.

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2822 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2010-11-24 20:37:01 +00:00
parent 9f6db631d1
commit 1dce4e0ade
8 changed files with 607 additions and 225 deletions

View File

@@ -120,18 +120,6 @@ for more details why.
module, otherwise your initiators could not see the target, when it is
enabled after qla2x00tgt module load.
3. If you delete and then add back with the same WWN an NPIV initiator
on the initiator side, make sure it has the same port_id as well. In
Fibre Channel initiators identified by port_id (s_id in FC terms), so if
the recreated NPIV initiator has another port_id, which was already used
by another (NPIV) initiator, those initiators could be confused by the
target and assigned to incorrect security groups, hence they could see
incorrect LUNs.
If you can't ensure the same port_id's for recreated initiators, it is
safer to restart qla2x00tgt and qla2xxx modules on the target to make
sure the target doesn't have any initiator port_id cached.
Initiator and target modes
--------------------------

View File

@@ -265,12 +265,11 @@ static inline void q2t_sess_put(struct q2t_sess *sess)
/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
static inline struct q2t_sess *q2t_find_sess_by_loop_id(struct q2t_tgt *tgt,
uint16_t lid)
uint16_t loop_id)
{
struct q2t_sess *sess;
sBUG_ON(tgt == NULL);
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if (lid == (sess->loop_id))
if ((loop_id == sess->loop_id) && !sess->deleted)
return sess;
}
return NULL;
@@ -281,11 +280,11 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id(struct q2t_tgt *tgt,
const uint8_t *s_id)
{
struct q2t_sess *sess;
sBUG_ON(tgt == NULL);
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->s_id.b.al_pa == s_id[2]) &&
(sess->s_id.b.area == s_id[1]) &&
(sess->s_id.b.domain == s_id[0]))
(sess->s_id.b.domain == s_id[0]) &&
!sess->deleted)
return sess;
}
return NULL;
@@ -296,11 +295,30 @@ static inline struct q2t_sess *q2t_find_sess_by_s_id_le(struct q2t_tgt *tgt,
const uint8_t *s_id)
{
struct q2t_sess *sess;
sBUG_ON(tgt == NULL);
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->s_id.b.al_pa == s_id[0]) &&
(sess->s_id.b.area == s_id[1]) &&
(sess->s_id.b.domain == s_id[2]))
(sess->s_id.b.domain == s_id[2]) &&
!sess->deleted)
return sess;
}
return NULL;
}
/* pha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */
static inline struct q2t_sess *q2t_find_sess_by_port_name(struct q2t_tgt *tgt,
const uint8_t *port_name)
{
struct q2t_sess *sess;
list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) {
if ((sess->port_name[0] == port_name[0]) &&
(sess->port_name[1] == port_name[1]) &&
(sess->port_name[2] == port_name[2]) &&
(sess->port_name[3] == port_name[3]) &&
(sess->port_name[4] == port_name[4]) &&
(sess->port_name[5] == port_name[5]) &&
(sess->port_name[6] == port_name[6]) &&
(sess->port_name[7] == port_name[7]))
return sess;
}
return NULL;
@@ -653,6 +671,7 @@ static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd)
if (loop_id == 0xFFFF) {
/* Global event */
atomic_inc(&ha->tgt->tgt_global_resets_count);
q2t_clear_tgt_db(ha->tgt, 1);
if (!list_empty(&ha->tgt->sess_list)) {
sess = list_entry(ha->tgt->sess_list.next,
@@ -729,10 +748,9 @@ static void q2t_schedule_sess_for_deletion(struct q2t_sess *sess)
sess->deleted = 1;
sess->expires = jiffies + dev_loss_tmo * HZ;
PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:%02x:%02x:"
PRINT_INFO("qla2x00t(%ld): session for port %02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for "
"deletion in %d secs", tgt->ha->instance,
sess->local ? "local " : "",
sess->port_name[0], sess->port_name[1],
sess->port_name[2], sess->port_name[3],
sess->port_name[4], sess->port_name[5],
@@ -851,15 +869,120 @@ out:
return res;
}
static bool q2t_check_fcport_exist(scsi_qla_host_t *ha, const uint8_t *s_id)
static bool q2t_check_fcport_exist(scsi_qla_host_t *ha, struct q2t_sess *sess)
{
bool res;
uint16_t loop_id;
bool res, found = false;
int rc, i;
uint16_t loop_id = 0xFFFF; /* to eliminate compiler's warning */
uint16_t entries;
void *pmap;
int pmap_len;
fc_port_t *fcport;
int global_resets;
TRACE_ENTRY();
res = (q24_get_loop_id(ha, s_id, &loop_id) == 0);
retry:
global_resets = atomic_read(&ha->tgt->tgt_global_resets_count);
rc = qla2x00_get_node_name_list(ha, &pmap, &pmap_len);
if (rc != QLA_SUCCESS) {
res = false;
goto out;
}
if (IS_FWI2_CAPABLE(ha)) {
struct qla_port24_data *pmap24 = pmap;
entries = pmap_len/sizeof(*pmap24);
for (i = 0; i < entries; ++i) {
if ((sess->port_name[0] == pmap24[i].port_name[0]) &&
(sess->port_name[1] == pmap24[i].port_name[1]) &&
(sess->port_name[2] == pmap24[i].port_name[2]) &&
(sess->port_name[3] == pmap24[i].port_name[3]) &&
(sess->port_name[4] == pmap24[i].port_name[4]) &&
(sess->port_name[5] == pmap24[i].port_name[5]) &&
(sess->port_name[6] == pmap24[i].port_name[6]) &&
(sess->port_name[7] == pmap24[i].port_name[7])) {
loop_id = le16_to_cpu(pmap24[i].loop_id);
found = true;
break;
}
}
} else {
struct qla_port23_data *pmap2x = pmap;
entries = pmap_len/sizeof(*pmap2x);
for (i = 0; i < entries; ++i) {
if ((sess->port_name[0] == pmap2x[i].port_name[0]) &&
(sess->port_name[1] == pmap2x[i].port_name[1]) &&
(sess->port_name[2] == pmap2x[i].port_name[2]) &&
(sess->port_name[3] == pmap2x[i].port_name[3]) &&
(sess->port_name[4] == pmap2x[i].port_name[4]) &&
(sess->port_name[5] == pmap2x[i].port_name[5]) &&
(sess->port_name[6] == pmap2x[i].port_name[6]) &&
(sess->port_name[7] == pmap2x[i].port_name[7])) {
loop_id = le16_to_cpu(pmap2x[i].loop_id);
found = true;
break;
}
}
}
kfree(pmap);
if (!found) {
res = false;
goto out;
}
TRACE_MGMT_DBG("loop_id %d", loop_id);
fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
if (fcport == NULL) {
PRINT_ERROR("qla2x00t(%ld): Allocation of tmp FC port failed",
ha->instance);
res = false;
goto out;
}
fcport->loop_id = loop_id;
rc = qla2x00_get_port_database(ha, fcport, 0);
if (rc != QLA_SUCCESS) {
PRINT_ERROR("qla2x00t(%ld): Failed to retrieve fcport "
"information -- get_port_database() returned %x "
"(loop_id=0x%04x)", ha->instance, rc, loop_id);
res = false;
goto out_free_fcport;
}
if (global_resets != atomic_read(&ha->tgt->tgt_global_resets_count)) {
TRACE_MGMT_DBG("qla2x00t(%ld): global reset during session "
"discovery (counter was %d, new %d), retrying",
ha->instance, global_resets,
atomic_read(&ha->tgt->tgt_global_resets_count));
goto retry;
}
TRACE_MGMT_DBG("Updating sess %p s_id %x:%x:%x, "
"loop_id %d) to d_id %x:%x:%x, loop_id %d", sess,
sess->s_id.b.domain, sess->s_id.b.al_pa,
sess->s_id.b.area, sess->loop_id, fcport->d_id.b.domain,
fcport->d_id.b.al_pa, fcport->d_id.b.area, fcport->loop_id);
sess->s_id = fcport->d_id;
sess->loop_id = fcport->loop_id;
sess->conf_compl_supported = fcport->conf_compl_supported;
res = true;
out_free_fcport:
kfree(fcport);
out:
TRACE_EXIT_RES(res);
return res;
}
@@ -894,10 +1017,18 @@ static void q2t_del_sess_work_fn(struct delayed_work *work)
q2t_undelete_sess(sess);
spin_unlock_irqrestore(&pha->hardware_lock, flags);
cancel = q2t_check_fcport_exist(ha, (uint8_t *)&sess->s_id);
cancel = q2t_check_fcport_exist(ha, sess);
spin_lock_irqsave(&pha->hardware_lock, flags);
if (cancel) {
if (sess->deleted) {
/*
* sess was again deleted while we were
* discovering it
*/
continue;
}
PRINT_INFO("qla2x00t(%ld): cancel deletion of "
"session for port %02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x (loop ID %d), "
@@ -1028,8 +1159,9 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport,
goto out_free_sess_wwn;
}
spin_lock_irq(&pha->hardware_lock);
TRACE_MGMT_DBG("Adding sess %p to tgt %p", sess, tgt);
spin_lock_irq(&pha->hardware_lock);
list_add_tail(&sess->sess_list_entry, &tgt->sess_list);
tgt->sess_count++;
spin_unlock_irq(&pha->hardware_lock);
@@ -1037,7 +1169,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport,
PRINT_INFO("qla2x00t(%ld): %ssession for wwn %s (loop_id %d, "
"s_id %x:%x:%x, confirmed completion %ssupported) added",
ha->instance, local ? "local " : "", wwn_str, fcport->loop_id,
sess->s_id.b.domain, sess->s_id.b.al_pa, sess->s_id.b.area,
sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa,
sess->conf_compl_supported ? "" : "not ");
kfree(wwn_str);
@@ -1056,21 +1188,6 @@ out_free_sess:
goto out;
}
/* pha->hardware_lock supposed to be held on entry */
static void q2t_reappear_sess(struct q2t_sess *sess, const char *reason)
{
q2t_undelete_sess(sess);
PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
"reappeared%s", sess->tgt->ha->instance,
sess->local ? "local " : "", sess->port_name[0],
sess->port_name[1], sess->port_name[2], sess->port_name[3],
sess->port_name[4], sess->port_name[5], sess->port_name[6],
sess->port_name[7], sess->loop_id, reason);
TRACE_MGMT_DBG("Appeared sess %p", sess);
}
static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport)
{
struct q2t_tgt *tgt;
@@ -1091,7 +1208,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport)
spin_lock_irq(&pha->hardware_lock);
sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id);
sess = q2t_find_sess_by_port_name(tgt, fcport->port_name);
if (sess == NULL) {
spin_unlock_irq(&pha->hardware_lock);
sess = q2t_create_sess(ha, fcport, false);
@@ -1099,8 +1216,23 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport)
if (sess != NULL)
q2t_sess_put(sess); /* put the extra creation ref */
} else {
if (sess->deleted)
q2t_reappear_sess(sess, "");
if (sess->deleted) {
q2t_undelete_sess(sess);
PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
"reappeared", sess->tgt->ha->instance,
sess->local ? "local " : "", sess->port_name[0],
sess->port_name[1], sess->port_name[2],
sess->port_name[3], sess->port_name[4],
sess->port_name[5], sess->port_name[6],
sess->port_name[7], sess->loop_id);
TRACE_MGMT_DBG("Reappeared sess %p", sess);
}
sess->s_id = fcport->d_id;
sess->loop_id = fcport->loop_id;
sess->conf_compl_supported = fcport->conf_compl_supported;
}
if (sess->local) {
@@ -1144,7 +1276,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport)
spin_lock_irq(&pha->hardware_lock);
sess = q2t_find_sess_by_loop_id(tgt, fcport->loop_id);
sess = q2t_find_sess_by_port_name(tgt, fcport->port_name);
if (sess == NULL)
goto out_unlock_ha;
@@ -1270,6 +1402,49 @@ static int q2t_target_release(struct scst_tgt *scst_tgt)
return 0;
}
/* pha->hardware_lock supposed to be held on entry */
static int q2t_sched_sess_work(struct q2t_tgt *tgt, int type,
const void *param, unsigned int param_size)
{
int res;
struct q2t_sess_work_param *prm;
unsigned long flags;
TRACE_ENTRY();
prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
if (prm == NULL) {
PRINT_ERROR("qla2x00t(%ld): Unable to create session "
"work, command will be refused", tgt->ha->instance);
res = -ENOMEM;
goto out;
}
TRACE_MGMT_DBG("Scheduling work (type %d, prm %p) to find session for "
"param %p (size %d, tgt %p)", type, prm, param, param_size, tgt);
sBUG_ON(param_size > (sizeof(*prm) -
offsetof(struct q2t_sess_work_param, cmd)));
prm->type = type;
memcpy(&prm->cmd, param, param_size);
spin_lock_irqsave(&tgt->sess_work_lock, flags);
if (!tgt->sess_works_pending)
tgt->tm_to_unknown = 0;
list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list);
tgt->sess_works_pending = 1;
spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
schedule_work(&tgt->sess_work);
res = 0;
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* pha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
*/
@@ -1520,14 +1695,55 @@ out:
return;
}
/* pha->hardware_lock supposed to be held on entry */
static int __q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts,
struct q2t_sess *sess)
{
int res;
uint32_t tag = abts->exchange_addr_to_abort;
struct q2t_mgmt_cmd *mcmd;
TRACE_ENTRY();
TRACE_MGMT_DBG("qla2x00t(%ld): task abort (tag=%d)", ha->instance,
tag);
mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
if (mcmd == NULL) {
PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed",
ha->instance, __func__);
res = -ENOMEM;
goto out;
}
memset(mcmd, 0, sizeof(*mcmd));
mcmd->sess = sess;
memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
res = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag,
SCST_ATOMIC, mcmd);
if (res != 0) {
PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d",
ha->instance, res);
goto out_free;
}
out:
TRACE_EXIT_RES(res);
return res;
out_free:
mempool_free(mcmd, q2t_mgmt_cmd_mempool);
goto out;
}
/*
* pha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
*/
static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts)
{
uint32_t tag;
int rc;
struct q2t_mgmt_cmd *mcmd;
uint32_t tag = abts->exchange_addr_to_abort;
struct q2t_sess *sess;
TRACE_ENTRY();
@@ -1538,8 +1754,6 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts)
goto out_err;
}
tag = abts->exchange_addr_to_abort;
if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
TRACE_MGMT_DBG("qla2x00t(%ld): ABTS: Unknown Exchange "
"Address received", ha->instance);
@@ -1553,38 +1767,28 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts)
sess = q2t_find_sess_by_s_id_le(ha->tgt, abts->fcp_hdr_le.s_id);
if (sess == NULL) {
TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort for unexisting "
TRACE_MGMT_DBG("qla2x00t(%ld): task abort for unexisting "
"session", ha->instance);
ha->tgt->tm_to_unknown = 1;
goto out_err;
rc = q2t_sched_sess_work(ha->tgt, Q2T_SESS_WORK_ABORT, abts,
sizeof(*abts));
if (rc != 0) {
ha->tgt->tm_to_unknown = 1;
goto out_err;
}
goto out;
}
mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
if (mcmd == NULL) {
PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed",
ha->instance, __func__);
goto out_err;
}
memset(mcmd, 0, sizeof(*mcmd));
mcmd->sess = sess;
memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts));
rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag,
SCST_ATOMIC, mcmd);
rc = __q24_handle_abts(ha, abts, sess);
if (rc != 0) {
PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d",
ha->instance, rc);
goto out_err_free;
goto out_err;
}
out:
TRACE_EXIT();
return;
out_err_free:
mempool_free(mcmd, q2t_mgmt_cmd_mempool);
out_err:
q24_send_abts_resp(ha, abts, SCST_MGMT_STATUS_REJECTED, false);
goto out;
@@ -3560,9 +3764,6 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio)
}
}
if (unlikely(sess->deleted))
q2t_reappear_sess(sess, " by new commands");
res = q2t_do_send_cmd_to_scst(ha, cmd, sess);
if (unlikely(res != 0))
goto out_free_cmd;
@@ -3576,32 +3777,14 @@ out_free_cmd:
goto out;
out_sched:
{
struct q2t_sess_work_param *prm;
unsigned long flags;
prm = kzalloc(sizeof(*prm), GFP_ATOMIC);
if (prm == NULL) {
PRINT_ERROR("qla2x00t(%ld): Unable to create session "
"work, command will be refused", ha->instance);
res = -1;
goto out_free_cmd;
}
TRACE_MGMT_DBG("Scheduling work to find session for cmd %p",
cmd);
prm->cmd = cmd;
spin_lock_irqsave(&tgt->sess_work_lock, flags);
if (!tgt->sess_works_pending)
tgt->tm_to_unknown = 0;
list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list);
tgt->sess_works_pending = 1;
spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
schedule_work(&tgt->sess_work);
if (atio->entry_count > 1) {
TRACE_MGMT_DBG("Dropping multy entry cmd %p", cmd);
res = -EBUSY;
goto out_free_cmd;
}
res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_CMD, &cmd, sizeof(cmd));
if (res != 0)
goto out_free_cmd;
goto out;
}
@@ -3740,11 +3923,6 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb)
lun_size = sizeof(a->fcp_cmnd.lun);
fn = a->fcp_cmnd.task_mgmt_flags;
sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
if (sess != NULL) {
sess->s_id.b.al_pa = a->fcp_hdr.s_id[2];
sess->s_id.b.area = a->fcp_hdr.s_id[1];
sess->s_id.b.domain = a->fcp_hdr.s_id[0];
}
} else {
notify_entry_t *n = (notify_entry_t *)iocb;
/* make it be in network byte order */
@@ -3756,10 +3934,13 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb)
}
if (sess == NULL) {
TRACE(TRACE_MGMT, "qla2x00t(%ld): task mgmt fn 0x%x for "
TRACE_MGMT_DBG("qla2x00t(%ld): task mgmt fn 0x%x for "
"non-existant session", ha->instance, fn);
tgt->tm_to_unknown = 1;
res = -ESRCH;
res = q2t_sched_sess_work(tgt, Q2T_SESS_WORK_TM, iocb,
IS_FWI2_CAPABLE(ha) ? sizeof(atio7_entry_t) :
sizeof(notify_entry_t));
if (res != 0)
tgt->tm_to_unknown = 1;
goto out;
}
@@ -3771,28 +3952,14 @@ out:
}
/* pha->hardware_lock supposed to be held on entry */
static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb)
static int __q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb,
struct q2t_sess *sess)
{
int res = 0, rc;
int res, rc;
struct q2t_mgmt_cmd *mcmd;
struct q2t_sess *sess;
int loop_id;
uint32_t tag;
TRACE_ENTRY();
loop_id = GET_TARGET_ID(ha, iocb);
tag = le16_to_cpu(iocb->seq_id);
sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id);
if (sess == NULL) {
TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort for unexisting "
"session", ha->instance);
ha->tgt->tm_to_unknown = 1;
res = -EFAULT;
goto out;
}
mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
if (mcmd == NULL) {
PRINT_ERROR("qla2x00t(%ld): %s: Allocation of ABORT cmd failed",
@@ -3806,8 +3973,8 @@ static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb)
memcpy(&mcmd->orig_iocb.notify_entry, iocb,
sizeof(mcmd->orig_iocb.notify_entry));
rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag,
SCST_ATOMIC, mcmd);
rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK,
le16_to_cpu(iocb->seq_id), SCST_ATOMIC, mcmd);
if (rc != 0) {
PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d",
ha->instance, rc);
@@ -3815,6 +3982,8 @@ static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb)
goto out_free;
}
res = 0;
out:
TRACE_EXIT_RES(res);
return res;
@@ -3824,6 +3993,35 @@ out_free:
goto out;
}
/* pha->hardware_lock supposed to be held on entry */
static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb)
{
int res;
struct q2t_sess *sess;
int loop_id;
TRACE_ENTRY();
loop_id = GET_TARGET_ID(ha, iocb);
sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id);
if (sess == NULL) {
TRACE_MGMT_DBG("qla2x00t(%ld): task abort for unexisting "
"session", ha->instance);
res = q2t_sched_sess_work(sess->tgt, Q2T_SESS_WORK_ABORT, iocb,
sizeof(*iocb));
if (res != 0)
sess->tgt->tm_to_unknown = 1;
goto out;
}
res = __q2t_abort_task(ha, iocb, sess);
out:
TRACE_EXIT_RES(res);
return res;
}
/*
* pha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
*/
@@ -5093,39 +5291,38 @@ out:
}
/* Must be called under tgt_mutex */
static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio)
static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha,
const uint8_t *s_id, uint16_t loop_id)
{
struct q2t_sess *sess = NULL;
fc_port_t *fcport = NULL;
uint16_t loop_id = 0xFFFF; /* to remove warning */
int rc;
int rc, global_resets;
TRACE_ENTRY();
retry:
global_resets = atomic_read(&ha->tgt->tgt_global_resets_count);
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *atio7 = (atio7_entry_t *)atio;
rc = q24_get_loop_id(ha, atio7->fcp_hdr.s_id, &loop_id);
rc = q24_get_loop_id(ha, s_id, &loop_id);
if (rc != 0) {
if ((atio7->fcp_hdr.s_id[0] == 0xFF) &&
(atio7->fcp_hdr.s_id[1] == 0xFC)) {
if ((s_id[0] == 0xFF) &&
(s_id[1] == 0xFC)) {
/*
* This is Domain Controller, so it should be
* OK to drop SCSI commands from it.
*/
TRACE_MGMT_DBG("Unable to find initiator with "
"S_ID %x:%x:%x", atio7->fcp_hdr.s_id[0],
atio7->fcp_hdr.s_id[1],
atio7->fcp_hdr.s_id[2]);
"S_ID %x:%x:%x", s_id[0], s_id[1],
s_id[2]);
} else
PRINT_ERROR("qla2x00t(%ld): Unable to find "
"initiator with S_ID %x:%x:%x",
ha->instance, atio7->fcp_hdr.s_id[0],
atio7->fcp_hdr.s_id[1],
atio7->fcp_hdr.s_id[2]);
ha->instance, s_id[0], s_id[1],
s_id[2]);
goto out;
}
} else
loop_id = GET_TARGET_ID(ha, (atio_entry_t *)atio);
}
fcport = kzalloc(sizeof(*fcport), GFP_KERNEL);
if (fcport == NULL) {
@@ -5146,6 +5343,14 @@ static struct q2t_sess *q2t_make_local_sess(scsi_qla_host_t *ha, atio_t *atio)
goto out_free_fcport;
}
if (global_resets != atomic_read(&ha->tgt->tgt_global_resets_count)) {
TRACE_MGMT_DBG("qla2x00t(%ld): global reset during session "
"discovery (counter was %d, new %d), retrying",
ha->instance, global_resets,
atomic_read(&ha->tgt->tgt_global_resets_count));
goto retry;
}
sess = q2t_create_sess(ha, fcport, true);
out_free_fcport:
@@ -5156,19 +5361,19 @@ out:
return sess;
}
static int q2t_exec_sess_work(struct q2t_tgt *tgt,
static void q2t_exec_sess_work(struct q2t_tgt *tgt,
struct q2t_sess_work_param *prm)
{
scsi_qla_host_t *ha = tgt->ha;
scsi_qla_host_t *pha = to_qla_parent(ha);
int res = 0;
int rc;
struct q2t_sess *sess = NULL;
struct q2t_cmd *cmd = prm->cmd;
atio_t *atio = (atio_t *)&cmd->atio;
const uint8_t *s_id = NULL; /* to hide compiler warnings */
int loop_id = -1; /* to hide compiler warnings */
TRACE_ENTRY();
TRACE_MGMT_DBG("cmd %p", cmd);
TRACE_MGMT_DBG("prm %p", prm);
mutex_lock(&ha->tgt_mutex);
spin_lock_irq(&pha->hardware_lock);
@@ -5176,13 +5381,42 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt,
if (tgt->tgt_stop)
goto send;
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = (atio7_entry_t *)atio;
sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
} else
sess = q2t_find_sess_by_loop_id(tgt,
GET_TARGET_ID(ha, (atio_entry_t *)atio));
switch(prm->type) {
case Q2T_SESS_WORK_CMD:
{
struct q2t_cmd *cmd = prm->cmd;
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = (atio7_entry_t *)&cmd->atio;
s_id = a->fcp_hdr.s_id;
} else
loop_id = GET_TARGET_ID(ha, (atio_entry_t *)&cmd->atio);
break;
}
case Q2T_SESS_WORK_ABORT:
if (IS_FWI2_CAPABLE(ha)) {
sess = q2t_find_sess_by_s_id_le(tgt,
prm->abts.fcp_hdr_le.s_id);
goto after_find;
} else
loop_id = GET_TARGET_ID(ha, &prm->tm_iocb);
break;
case Q2T_SESS_WORK_TM:
if (IS_FWI2_CAPABLE(ha))
s_id = prm->tm_iocb2.fcp_hdr.s_id;
else
loop_id = GET_TARGET_ID(ha, &prm->tm_iocb);
break;
default:
sBUG_ON(1);
break;
}
if (IS_FWI2_CAPABLE(ha))
sess = q2t_find_sess_by_s_id(tgt, s_id);
else
sess = q2t_find_sess_by_loop_id(tgt, loop_id);
after_find:
if (sess != NULL) {
TRACE_MGMT_DBG("sess %p found", sess);
q2t_sess_get(sess);
@@ -5192,37 +5426,117 @@ static int q2t_exec_sess_work(struct q2t_tgt *tgt,
* behind us.
*/
spin_unlock_irq(&pha->hardware_lock);
sess = q2t_make_local_sess(ha, atio);
sess = q2t_make_local_sess(ha, s_id, loop_id);
spin_lock_irq(&pha->hardware_lock);
/* sess has got an extra creation ref */
}
send:
if (!tgt->tm_to_unknown && !tgt->tgt_stop && (sess != NULL)) {
if ((sess == NULL) || tgt->tgt_stop)
goto out_term;
switch(prm->type) {
case Q2T_SESS_WORK_CMD:
{
struct q2t_cmd *cmd = prm->cmd;
if (tgt->tm_to_unknown) {
/*
* Cmd might be already aborted behind us, so be safe
* and abort it. It should be OK, initiator will retry
* it.
*/
goto out_term;
}
TRACE_MGMT_DBG("Sending work cmd %p to SCST", cmd);
res = q2t_do_send_cmd_to_scst(ha, cmd, sess);
} else {
/*
* Cmd might be already aborted behind us, so be safe and
* abort it. It should be OK, initiator will retry it. It has
* not sent to SCST yet, so pass NULL as the second argument.
*/
TRACE_MGMT_DBG("Terminating work cmd %p", cmd);
rc = q2t_do_send_cmd_to_scst(ha, cmd, sess);
break;
}
case Q2T_SESS_WORK_ABORT:
if (IS_FWI2_CAPABLE(ha))
q24_send_term_exchange(ha, NULL , &cmd->atio.atio7, 1);
rc = __q24_handle_abts(ha, &prm->abts, sess);
else
q2x_send_term_exchange(ha, NULL, &cmd->atio.atio2x, 1);
q2t_free_cmd(cmd);
rc = __q2t_abort_task(ha, &prm->tm_iocb, sess);
break;
case Q2T_SESS_WORK_TM:
{
uint8_t *lun;
uint16_t lun_data;
int lun_size, fn;
void *iocb;
if (IS_FWI2_CAPABLE(ha)) {
atio7_entry_t *a = &prm->tm_iocb2;
iocb = a;
lun = (uint8_t *)&a->fcp_cmnd.lun;
lun_size = sizeof(a->fcp_cmnd.lun);
fn = a->fcp_cmnd.task_mgmt_flags;
} else {
notify_entry_t *n = &prm->tm_iocb;
iocb = n;
/* make it be in network byte order */
lun_data = swab16(le16_to_cpu(n->lun));
lun = (uint8_t *)&lun_data;
lun_size = sizeof(lun_data);
fn = n->task_flags >> IMM_NTFY_TASK_MGMT_SHIFT;
}
rc = q2t_issue_task_mgmt(sess, lun, lun_size, fn, iocb, 0);
break;
}
default:
sBUG_ON(1);
break;
}
if (rc != 0)
goto out_term;
out_put:
if (sess != NULL)
q2t_sess_put(sess);
spin_unlock_irq(&pha->hardware_lock);
mutex_unlock(&ha->tgt_mutex);
TRACE_EXIT_RES(res);
return res;
TRACE_EXIT();
return;
out_term:
switch(prm->type) {
case Q2T_SESS_WORK_CMD:
{
struct q2t_cmd *cmd = prm->cmd;
TRACE_MGMT_DBG("Terminating work cmd %p", cmd);
/*
* cmd has not sent to SCST yet, so pass NULL as the second
* argument
*/
if (IS_FWI2_CAPABLE(ha))
q24_send_term_exchange(ha, NULL, &cmd->atio.atio7, 1);
else
q2x_send_term_exchange(ha, NULL, &cmd->atio.atio2x, 1);
q2t_free_cmd(cmd);
break;
}
case Q2T_SESS_WORK_ABORT:
if (IS_FWI2_CAPABLE(ha))
q24_send_abts_resp(ha, &prm->abts,
SCST_MGMT_STATUS_REJECTED, false);
else
q2x_send_notify_ack(ha, &prm->tm_iocb, 0,
0, 0, 0, 0, 0);
break;
case Q2T_SESS_WORK_TM:
if (IS_FWI2_CAPABLE(ha))
q24_send_term_exchange(ha, NULL, &prm->tm_iocb2, 1);
else
q2x_send_notify_ack(ha, &prm->tm_iocb, 0,
0, 0, 0, 0, 0);
break;
default:
sBUG_ON(1);
break;
}
goto out_put;
}
static void q2t_sess_work_fn(struct work_struct *work)
@@ -5236,7 +5550,6 @@ static void q2t_sess_work_fn(struct work_struct *work)
spin_lock_irq(&tgt->sess_work_lock);
while (!list_empty(&tgt->sess_works_list)) {
int rc;
struct q2t_sess_work_param *prm = list_entry(
tgt->sess_works_list.next, typeof(*prm),
sess_works_list_entry);
@@ -5249,16 +5562,10 @@ static void q2t_sess_work_fn(struct work_struct *work)
spin_unlock_irq(&tgt->sess_work_lock);
rc = q2t_exec_sess_work(tgt, prm);
q2t_exec_sess_work(tgt, prm);
spin_lock_irq(&tgt->sess_work_lock);
if (rc != 0) {
TRACE_MGMT_DBG("Unable to complete sess work (tgt %p), "
"freeing cmd %p", tgt, prm->cmd);
q2t_free_cmd(prm->cmd);
}
kfree(prm);
}
spin_unlock_irq(&tgt->sess_work_lock);
@@ -5374,6 +5681,7 @@ static int q2t_add_target(scsi_qla_host_t *ha)
INIT_LIST_HEAD(&tgt->srr_ctio_list);
INIT_LIST_HEAD(&tgt->srr_imm_list);
INIT_WORK(&tgt->srr_work, q2t_handle_srr_work);
atomic_set(&tgt->tgt_global_resets_count, 0);
ha->q2t_tgt = tgt;

View File

@@ -164,6 +164,8 @@ struct q2t_tgt {
struct list_head srr_imm_list;
struct work_struct srr_work;
atomic_t tgt_global_resets_count;
struct list_head tgt_list_entry;
};
@@ -221,7 +223,18 @@ struct q2t_cmd {
struct q2t_sess_work_param {
struct list_head sess_works_list_entry;
struct q2t_cmd *cmd;
#define Q2T_SESS_WORK_CMD 0
#define Q2T_SESS_WORK_ABORT 1
#define Q2T_SESS_WORK_TM 2
int type;
union {
struct q2t_cmd *cmd;
abts24_recv_entry_t abts;
notify_entry_t tm_iocb;
atio7_entry_t tm_iocb2;
};
};
struct q2t_mgmt_cmd {

View File

@@ -44,13 +44,13 @@
* Must be changed on any change in any initiator visible interfaces or
* data in the target add-on
*/
#define QLA2X_TARGET_MAGIC 269
#define QLA2X_TARGET_MAGIC 270
/*
* Must be changed on any change in any target visible interfaces or
* data in the initiator
*/
#define QLA2X_INITIATOR_MAGIC 57222
#define QLA2X_INITIATOR_MAGIC 57223
#define QLA2X_INI_MODE_STR_EXCLUSIVE "exclusive"
#define QLA2X_INI_MODE_STR_DISABLED "disabled"

View File

@@ -319,17 +319,6 @@ static DEVICE_ATTR(resource_counts,
qla2x00_show_resource_counts,
NULL);
typedef struct {
uint8_t port_name[WWN_SIZE];
uint16_t loop_id;
} port_data_t;
typedef struct {
uint8_t port_name[WWN_SIZE];
uint16_t loop_id;
uint16_t reserved;
} port24_data_t;
static ssize_t
qla2x00_show_port_database(struct device *dev,
struct device_attribute *attr, char *buffer)
@@ -339,48 +328,25 @@ qla2x00_show_port_database(struct device *dev,
ulong size = 0;
int rval, i;
uint16_t entries;
mbx_cmd_t mc;
dma_addr_t pmap_dma;
void *pmap;
ulong dma_size = 0x100 * sizeof(port24_data_t);
pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size,
&pmap_dma, GFP_KERNEL);
if (pmap == NULL) {
size = scnprintf(buffer, max_size, "DMA Alloc failed of %ld",
dma_size);
goto out;
}
mc.mb[0] = MBC_PORT_NODE_NAME_LIST;
mc.mb[1] = BIT_1 | BIT_3;
mc.mb[2] = MSW(pmap_dma);
mc.mb[3] = LSW(pmap_dma);
mc.mb[6] = MSW(MSD(pmap_dma));
mc.mb[7] = LSW(MSD(pmap_dma));
mc.mb[8] = dma_size;
mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8;
mc.in_mb = MBX_0|MBX_1;
mc.tov = 30;
mc.flags = MBX_DMA_IN;
rval = qla2x00_mailbox_command(ha, &mc);
int pmap_len;
rval = qla2x00_get_node_name_list(ha, &pmap, &pmap_len);
if (rval != QLA_SUCCESS) {
size = scnprintf(buffer, max_size,
"Mailbox Command failed %d, mb0 %#x mb1 %#x\n",
rval, mc.mb[0], mc.mb[1]);
goto out_free;
"qla2x00_get_node_name_list() failed %d\n",
rval);
goto next;
}
size += scnprintf(buffer+size, max_size-size,
"Port Name List (%#04x) returned %d bytes\nL_ID WWPN\n",
MBC_PORT_NODE_NAME_LIST, le16_to_cpu(mc.mb[1]));
"Port Name List returned %d bytes\nL_ID WWPN\n",
pmap_len);
if (IS_FWI2_CAPABLE(ha)) {
port24_data_t *pmap24 = pmap;
struct qla_port24_data *pmap24 = pmap;
entries = le16_to_cpu(mc.mb[1])/sizeof(*pmap24);
entries = pmap_len/sizeof(*pmap24);
for (i = 0; (i < entries) && (size < max_size); ++i) {
size += scnprintf(buffer+size, max_size-size,
@@ -396,9 +362,9 @@ qla2x00_show_port_database(struct device *dev,
pmap24[i].port_name[0]);
}
} else {
port_data_t *pmap2x = pmap;
struct qla_port23_data *pmap2x = pmap;
entries = le16_to_cpu(mc.mb[1])/sizeof(*pmap2x);
entries = pmap_len/sizeof(*pmap2x);
for (i = 0; (i < entries) && (size < max_size); ++i) {
size += scnprintf(buffer+size, max_size-size,
@@ -415,9 +381,9 @@ qla2x00_show_port_database(struct device *dev,
}
}
out_free:
dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
kfree(pmap);
next:
if (size < max_size) {
dma_addr_t gid_list_dma;
struct gid_list_info *gid_list;

View File

@@ -26,6 +26,16 @@
#define PDO_FORCE_ADISC BIT_1
#define PDO_FORCE_PLOGI BIT_0
struct qla_port23_data {
uint8_t port_name[WWN_SIZE];
uint16_t loop_id;
};
struct qla_port24_data {
uint8_t port_name[WWN_SIZE];
uint16_t loop_id;
uint16_t reserved;
};
#define PORT_DATABASE_24XX_SIZE 64
struct port_database_24xx {

View File

@@ -183,6 +183,10 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
extern int
qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
extern int
qla2x00_get_node_name_list(scsi_qla_host_t *ha,
void **out_data, int *out_len);
extern int
qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);

View File

@@ -1082,6 +1082,99 @@ out:
return rval;
}
/*
* qla2x00_get_node_name_list
* Issue get node name list mailbox command, kmalloc()
* and return the resulting list. Caller must kfree() it!
*
* Input:
* ha = adapter state pointer.
* out_data = resulting list
* out_len = length of the resulting list
*
* Returns:
* qla2x00 local function return status code.
*
* Context:
* Kernel context.
*/
int qla2x00_get_node_name_list(scsi_qla_host_t *ha,
void **out_data, int *out_len)
{
int rval, left;
mbx_cmd_t mc;
dma_addr_t pmap_dma;
void *pmap;
struct qla_port24_data *list = NULL;
ulong dma_size;
BUILD_BUG_ON(sizeof(struct qla_port24_data) <
sizeof(struct qla_port23_data));
left = 1;
while (left > 0) {
dma_size = left * sizeof(*list);
pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size,
&pmap_dma, GFP_KERNEL);
if (pmap == NULL) {
printk(KERN_ERR "%s(%ld): DMA Alloc failed of "
"%ld\n", __func__, ha->host_no, dma_size);
rval = QLA_MEMORY_ALLOC_FAILED;
goto out;
}
mc.mb[0] = MBC_PORT_NODE_NAME_LIST;
mc.mb[1] = BIT_1 | BIT_3;
mc.mb[2] = MSW(pmap_dma);
mc.mb[3] = LSW(pmap_dma);
mc.mb[6] = MSW(MSD(pmap_dma));
mc.mb[7] = LSW(MSD(pmap_dma));
mc.mb[8] = dma_size;
mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8;
mc.in_mb = MBX_0|MBX_1;
mc.tov = 30;
mc.flags = MBX_DMA_IN;
rval = qla2x00_mailbox_command(ha, &mc);
if (rval != QLA_SUCCESS) {
if ((mc.mb[0] == MBS_COMMAND_ERROR) &&
(mc.mb[1] == 0xA)) {
if (IS_FWI2_CAPABLE(ha))
left += le16_to_cpu(mc.mb[2]) / sizeof(struct qla_port24_data);
else
left += le16_to_cpu(mc.mb[2]) / sizeof(struct qla_port23_data);
goto restart;
}
goto out_free;
}
left = 0;
list = kmalloc(dma_size, GFP_KERNEL);
if (list == NULL) {
printk(KERN_ERR "%s(%ld): failed to allocate node names"
" list structure.\n", __func__, ha->host_no);
rval = QLA_MEMORY_ALLOC_FAILED;
goto out_free;
}
memcpy(list, pmap, dma_size);
restart:
dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
}
*out_data = list;
*out_len = dma_size;
out:
return rval;
out_free:
dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma);
goto out;
}
EXPORT_SYMBOL(qla2x00_get_node_name_list);
/*
* qla2x00_get_port_database
* Issue normal/enhanced get port database mailbox command