diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-12-04 22:39:08 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-12-29 12:24:25 -0500 |
commit | 109f6ed05aadb7dd1cc9671a63603658d3ba518e (patch) | |
tree | 637d09437a45ab0f21e28a30ae4e876d59b6b733 /drivers/scsi/lpfc | |
parent | 9bad76719ee4fa8c305bb6cba6e19b4ddbe800b2 (diff) |
[SCSI] lpfc 8.3.0 : Fix system crash due to uninitialized node access
In the IOCB completion handler, always check if the node is valid
before accessing the node object. Added lpfc_initialize_node() to
initialize nodes.
Signed-off-by: James Smart <James.Smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/lpfc')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 52 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 44 |
2 files changed, 56 insertions, 40 deletions
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 58ed6859c921..ba120530cf80 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c | |||
@@ -1857,6 +1857,32 @@ lpfc_disable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) | |||
1857 | lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, | 1857 | lpfc_nlp_state_cleanup(vport, ndlp, ndlp->nlp_state, |
1858 | NLP_STE_UNUSED_NODE); | 1858 | NLP_STE_UNUSED_NODE); |
1859 | } | 1859 | } |
1860 | /** | ||
1861 | * lpfc_initialize_node: Initialize all fields of node object. | ||
1862 | * @vport: Pointer to Virtual Port object. | ||
1863 | * @ndlp: Pointer to FC node object. | ||
1864 | * @did: FC_ID of the node. | ||
1865 | * This function is always called when node object need to | ||
1866 | * be initialized. It initializes all the fields of the node | ||
1867 | * object. | ||
1868 | **/ | ||
1869 | static inline void | ||
1870 | lpfc_initialize_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | ||
1871 | uint32_t did) | ||
1872 | { | ||
1873 | INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); | ||
1874 | INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp); | ||
1875 | init_timer(&ndlp->nlp_delayfunc); | ||
1876 | ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; | ||
1877 | ndlp->nlp_delayfunc.data = (unsigned long)ndlp; | ||
1878 | ndlp->nlp_DID = did; | ||
1879 | ndlp->vport = vport; | ||
1880 | ndlp->nlp_sid = NLP_NO_SID; | ||
1881 | kref_init(&ndlp->kref); | ||
1882 | NLP_INT_NODE_ACT(ndlp); | ||
1883 | atomic_set(&ndlp->cmd_pending, 0); | ||
1884 | ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; | ||
1885 | } | ||
1860 | 1886 | ||
1861 | struct lpfc_nodelist * | 1887 | struct lpfc_nodelist * |
1862 | lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | 1888 | lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, |
@@ -1897,17 +1923,7 @@ lpfc_enable_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
1897 | /* re-initialize ndlp except of ndlp linked list pointer */ | 1923 | /* re-initialize ndlp except of ndlp linked list pointer */ |
1898 | memset((((char *)ndlp) + sizeof (struct list_head)), 0, | 1924 | memset((((char *)ndlp) + sizeof (struct list_head)), 0, |
1899 | sizeof (struct lpfc_nodelist) - sizeof (struct list_head)); | 1925 | sizeof (struct lpfc_nodelist) - sizeof (struct list_head)); |
1900 | INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); | 1926 | lpfc_initialize_node(vport, ndlp, did); |
1901 | INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp); | ||
1902 | init_timer(&ndlp->nlp_delayfunc); | ||
1903 | ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; | ||
1904 | ndlp->nlp_delayfunc.data = (unsigned long)ndlp; | ||
1905 | ndlp->nlp_DID = did; | ||
1906 | ndlp->vport = vport; | ||
1907 | ndlp->nlp_sid = NLP_NO_SID; | ||
1908 | /* ndlp management re-initialize */ | ||
1909 | kref_init(&ndlp->kref); | ||
1910 | NLP_INT_NODE_ACT(ndlp); | ||
1911 | 1927 | ||
1912 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); | 1928 | spin_unlock_irqrestore(&phba->ndlp_lock, flags); |
1913 | 1929 | ||
@@ -3121,19 +3137,9 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
3121 | uint32_t did) | 3137 | uint32_t did) |
3122 | { | 3138 | { |
3123 | memset(ndlp, 0, sizeof (struct lpfc_nodelist)); | 3139 | memset(ndlp, 0, sizeof (struct lpfc_nodelist)); |
3124 | INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp); | 3140 | |
3125 | INIT_LIST_HEAD(&ndlp->dev_loss_evt.evt_listp); | 3141 | lpfc_initialize_node(vport, ndlp, did); |
3126 | init_timer(&ndlp->nlp_delayfunc); | ||
3127 | ndlp->nlp_delayfunc.function = lpfc_els_retry_delay; | ||
3128 | ndlp->nlp_delayfunc.data = (unsigned long)ndlp; | ||
3129 | ndlp->nlp_DID = did; | ||
3130 | ndlp->vport = vport; | ||
3131 | ndlp->nlp_sid = NLP_NO_SID; | ||
3132 | INIT_LIST_HEAD(&ndlp->nlp_listp); | 3142 | INIT_LIST_HEAD(&ndlp->nlp_listp); |
3133 | kref_init(&ndlp->kref); | ||
3134 | NLP_INT_NODE_ACT(ndlp); | ||
3135 | atomic_set(&ndlp->cmd_pending, 0); | ||
3136 | ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; | ||
3137 | 3143 | ||
3138 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, | 3144 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, |
3139 | "node init: did:x%x", | 3145 | "node init: did:x%x", |
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 6293b6fc65a6..cf6b2d40a923 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c | |||
@@ -945,7 +945,8 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
945 | 945 | ||
946 | lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; | 946 | lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; |
947 | lpfc_cmd->status = pIocbOut->iocb.ulpStatus; | 947 | lpfc_cmd->status = pIocbOut->iocb.ulpStatus; |
948 | atomic_dec(&pnode->cmd_pending); | 948 | if (pnode && NLP_CHK_NODE_ACT(pnode)) |
949 | atomic_dec(&pnode->cmd_pending); | ||
949 | 950 | ||
950 | if (lpfc_cmd->status) { | 951 | if (lpfc_cmd->status) { |
951 | if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && | 952 | if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && |
@@ -1035,23 +1036,31 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
1035 | time_after(jiffies, lpfc_cmd->start_time + | 1036 | time_after(jiffies, lpfc_cmd->start_time + |
1036 | msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) { | 1037 | msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) { |
1037 | spin_lock_irqsave(sdev->host->host_lock, flags); | 1038 | spin_lock_irqsave(sdev->host->host_lock, flags); |
1038 | if ((pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) && | 1039 | if (pnode && NLP_CHK_NODE_ACT(pnode)) { |
1039 | (atomic_read(&pnode->cmd_pending) > LPFC_MIN_TGT_QDEPTH) && | 1040 | if (pnode->cmd_qdepth > |
1040 | ((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10)))) | 1041 | atomic_read(&pnode->cmd_pending) && |
1041 | pnode->cmd_qdepth = atomic_read(&pnode->cmd_pending); | 1042 | (atomic_read(&pnode->cmd_pending) > |
1042 | 1043 | LPFC_MIN_TGT_QDEPTH) && | |
1043 | pnode->last_change_time = jiffies; | 1044 | ((cmd->cmnd[0] == READ_10) || |
1045 | (cmd->cmnd[0] == WRITE_10))) | ||
1046 | pnode->cmd_qdepth = | ||
1047 | atomic_read(&pnode->cmd_pending); | ||
1048 | |||
1049 | pnode->last_change_time = jiffies; | ||
1050 | } | ||
1044 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | 1051 | spin_unlock_irqrestore(sdev->host->host_lock, flags); |
1045 | } else if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) && | 1052 | } else if (pnode && NLP_CHK_NODE_ACT(pnode)) { |
1053 | if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) && | ||
1046 | time_after(jiffies, pnode->last_change_time + | 1054 | time_after(jiffies, pnode->last_change_time + |
1047 | msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) { | 1055 | msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) { |
1048 | spin_lock_irqsave(sdev->host->host_lock, flags); | 1056 | spin_lock_irqsave(sdev->host->host_lock, flags); |
1049 | pnode->cmd_qdepth += pnode->cmd_qdepth * | 1057 | pnode->cmd_qdepth += pnode->cmd_qdepth * |
1050 | LPFC_TGTQ_RAMPUP_PCENT / 100; | 1058 | LPFC_TGTQ_RAMPUP_PCENT / 100; |
1051 | if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH) | 1059 | if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH) |
1052 | pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; | 1060 | pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; |
1053 | pnode->last_change_time = jiffies; | 1061 | pnode->last_change_time = jiffies; |
1054 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | 1062 | spin_unlock_irqrestore(sdev->host->host_lock, flags); |
1063 | } | ||
1055 | } | 1064 | } |
1056 | 1065 | ||
1057 | lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); | 1066 | lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); |
@@ -1536,7 +1545,8 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | |||
1536 | cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0); | 1545 | cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0); |
1537 | goto out_fail_command; | 1546 | goto out_fail_command; |
1538 | } | 1547 | } |
1539 | if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) | 1548 | if (vport->cfg_max_scsicmpl_time && |
1549 | (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth)) | ||
1540 | goto out_host_busy; | 1550 | goto out_host_busy; |
1541 | 1551 | ||
1542 | lpfc_cmd = lpfc_get_scsi_buf(phba); | 1552 | lpfc_cmd = lpfc_get_scsi_buf(phba); |