diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-09-07 11:52:04 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-10-13 09:28:57 -0400 |
commit | 977b5a0af6d22a1a0170057c19cde37eeac68acd (patch) | |
tree | 247128ab00aa554eca5366ff8fb3505930729eaf | |
parent | b522d7d42d7ce843885d4c6740c5bd50876a2971 (diff) |
[SCSI] lpfc 8.2.8 v2 : Add sysfs control of target queue depth handling
Added new sysfs attribute lpfc_max_scsicmpl_time. Attribute, when enabled,
will control target queue depth based on I/O completion time.
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/lpfc/lpfc.h | 6 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_attr.c | 44 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_disc.h | 3 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_hbadisc.c | 2 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 29 | ||||
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.h | 1 |
6 files changed, 85 insertions, 0 deletions
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 18153846611..3a500d68306 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h | |||
@@ -34,6 +34,11 @@ struct lpfc_sli2_slim; | |||
34 | #define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */ | 34 | #define LPFC_IOCB_LIST_CNT 2250 /* list of IOCBs for fast-path usage. */ |
35 | #define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */ | 35 | #define LPFC_Q_RAMP_UP_INTERVAL 120 /* lun q_depth ramp up interval */ |
36 | #define LPFC_VNAME_LEN 100 /* vport symbolic name length */ | 36 | #define LPFC_VNAME_LEN 100 /* vport symbolic name length */ |
37 | #define LPFC_TGTQ_INTERVAL 40000 /* Min amount of time between tgt | ||
38 | queue depth change in millisecs */ | ||
39 | #define LPFC_TGTQ_RAMPUP_PCENT 5 /* Target queue rampup in percentage */ | ||
40 | #define LPFC_MIN_TGT_QDEPTH 100 | ||
41 | #define LPFC_MAX_TGT_QDEPTH 0xFFFF | ||
37 | 42 | ||
38 | /* | 43 | /* |
39 | * Following time intervals are used of adjusting SCSI device | 44 | * Following time intervals are used of adjusting SCSI device |
@@ -363,6 +368,7 @@ struct lpfc_vport { | |||
363 | uint32_t cfg_log_verbose; | 368 | uint32_t cfg_log_verbose; |
364 | uint32_t cfg_max_luns; | 369 | uint32_t cfg_max_luns; |
365 | uint32_t cfg_enable_da_id; | 370 | uint32_t cfg_enable_da_id; |
371 | uint32_t cfg_max_scsicmpl_time; | ||
366 | 372 | ||
367 | uint32_t dev_loss_tmo_changed; | 373 | uint32_t dev_loss_tmo_changed; |
368 | 374 | ||
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 21397f37010..343b0b36ed2 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c | |||
@@ -2297,6 +2297,48 @@ LPFC_VPORT_ATTR_RW(use_adisc, 0, 0, 1, | |||
2297 | "Use ADISC on rediscovery to authenticate FCP devices"); | 2297 | "Use ADISC on rediscovery to authenticate FCP devices"); |
2298 | 2298 | ||
2299 | /* | 2299 | /* |
2300 | # lpfc_max_scsicmpl_time: Use scsi command completion time to control I/O queue | ||
2301 | # depth. Default value is 0. When the value of this parameter is zero the | ||
2302 | # SCSI command completion time is not used for controlling I/O queue depth. When | ||
2303 | # the parameter is set to a non-zero value, the I/O queue depth is controlled | ||
2304 | # to limit the I/O completion time to the parameter value. | ||
2305 | # The value is set in milliseconds. | ||
2306 | */ | ||
2307 | static int lpfc_max_scsicmpl_time; | ||
2308 | module_param(lpfc_max_scsicmpl_time, int, 0); | ||
2309 | MODULE_PARM_DESC(lpfc_max_scsicmpl_time, | ||
2310 | "Use command completion time to control queue depth"); | ||
2311 | lpfc_vport_param_show(max_scsicmpl_time); | ||
2312 | lpfc_vport_param_init(max_scsicmpl_time, 0, 0, 60000); | ||
2313 | static int | ||
2314 | lpfc_max_scsicmpl_time_set(struct lpfc_vport *vport, int val) | ||
2315 | { | ||
2316 | struct Scsi_Host *shost = lpfc_shost_from_vport(vport); | ||
2317 | struct lpfc_nodelist *ndlp, *next_ndlp; | ||
2318 | |||
2319 | if (val == vport->cfg_max_scsicmpl_time) | ||
2320 | return 0; | ||
2321 | if ((val < 0) || (val > 60000)) | ||
2322 | return -EINVAL; | ||
2323 | vport->cfg_max_scsicmpl_time = val; | ||
2324 | |||
2325 | spin_lock_irq(shost->host_lock); | ||
2326 | list_for_each_entry_safe(ndlp, next_ndlp, &vport->fc_nodes, nlp_listp) { | ||
2327 | if (!NLP_CHK_NODE_ACT(ndlp)) | ||
2328 | continue; | ||
2329 | if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) | ||
2330 | continue; | ||
2331 | ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; | ||
2332 | } | ||
2333 | spin_unlock_irq(shost->host_lock); | ||
2334 | return 0; | ||
2335 | } | ||
2336 | lpfc_vport_param_store(max_scsicmpl_time); | ||
2337 | static DEVICE_ATTR(lpfc_max_scsicmpl_time, S_IRUGO | S_IWUSR, | ||
2338 | lpfc_max_scsicmpl_time_show, | ||
2339 | lpfc_max_scsicmpl_time_store); | ||
2340 | |||
2341 | /* | ||
2300 | # lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value | 2342 | # lpfc_ack0: Use ACK0, instead of ACK1 for class 2 acknowledgement. Value |
2301 | # range is [0,1]. Default value is 0. | 2343 | # range is [0,1]. Default value is 0. |
2302 | */ | 2344 | */ |
@@ -2459,6 +2501,7 @@ struct device_attribute *lpfc_hba_attrs[] = { | |||
2459 | &dev_attr_lpfc_enable_hba_reset, | 2501 | &dev_attr_lpfc_enable_hba_reset, |
2460 | &dev_attr_lpfc_enable_hba_heartbeat, | 2502 | &dev_attr_lpfc_enable_hba_heartbeat, |
2461 | &dev_attr_lpfc_sg_seg_cnt, | 2503 | &dev_attr_lpfc_sg_seg_cnt, |
2504 | &dev_attr_lpfc_max_scsicmpl_time, | ||
2462 | NULL, | 2505 | NULL, |
2463 | }; | 2506 | }; |
2464 | 2507 | ||
@@ -3580,6 +3623,7 @@ lpfc_get_vport_cfgparam(struct lpfc_vport *vport) | |||
3580 | lpfc_restrict_login_init(vport, lpfc_restrict_login); | 3623 | lpfc_restrict_login_init(vport, lpfc_restrict_login); |
3581 | lpfc_fcp_class_init(vport, lpfc_fcp_class); | 3624 | lpfc_fcp_class_init(vport, lpfc_fcp_class); |
3582 | lpfc_use_adisc_init(vport, lpfc_use_adisc); | 3625 | lpfc_use_adisc_init(vport, lpfc_use_adisc); |
3626 | lpfc_max_scsicmpl_time_init(vport, lpfc_max_scsicmpl_time); | ||
3583 | lpfc_fdmi_on_init(vport, lpfc_fdmi_on); | 3627 | lpfc_fdmi_on_init(vport, lpfc_fdmi_on); |
3584 | lpfc_discovery_threads_init(vport, lpfc_discovery_threads); | 3628 | lpfc_discovery_threads_init(vport, lpfc_discovery_threads); |
3585 | lpfc_max_luns_init(vport, lpfc_max_luns); | 3629 | lpfc_max_luns_init(vport, lpfc_max_luns); |
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index 2db0b74b6fa..ccf8f41f345 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h | |||
@@ -88,6 +88,9 @@ struct lpfc_nodelist { | |||
88 | unsigned long last_ramp_up_time; /* jiffy of last ramp up */ | 88 | unsigned long last_ramp_up_time; /* jiffy of last ramp up */ |
89 | unsigned long last_q_full_time; /* jiffy of last queue full */ | 89 | unsigned long last_q_full_time; /* jiffy of last queue full */ |
90 | struct kref kref; | 90 | struct kref kref; |
91 | atomic_t cmd_pending; | ||
92 | uint32_t cmd_qdepth; | ||
93 | unsigned long last_change_time; | ||
91 | }; | 94 | }; |
92 | 95 | ||
93 | /* Defines for nlp_flag (uint32) */ | 96 | /* Defines for nlp_flag (uint32) */ |
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 3b00d9b86c7..887a5283605 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c | |||
@@ -2988,6 +2988,8 @@ lpfc_nlp_init(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, | |||
2988 | INIT_LIST_HEAD(&ndlp->nlp_listp); | 2988 | INIT_LIST_HEAD(&ndlp->nlp_listp); |
2989 | kref_init(&ndlp->kref); | 2989 | kref_init(&ndlp->kref); |
2990 | NLP_INT_NODE_ACT(ndlp); | 2990 | NLP_INT_NODE_ACT(ndlp); |
2991 | atomic_set(&ndlp->cmd_pending, 0); | ||
2992 | ndlp->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; | ||
2991 | 2993 | ||
2992 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, | 2994 | lpfc_debugfs_disc_trc(vport, LPFC_DISC_TRC_NODE, |
2993 | "node init: did:x%x", | 2995 | "node init: did:x%x", |
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 72eef7e4a89..7b17f52660b 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c | |||
@@ -628,6 +628,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
628 | 628 | ||
629 | lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; | 629 | lpfc_cmd->result = pIocbOut->iocb.un.ulpWord[4]; |
630 | lpfc_cmd->status = pIocbOut->iocb.ulpStatus; | 630 | lpfc_cmd->status = pIocbOut->iocb.ulpStatus; |
631 | atomic_dec(&pnode->cmd_pending); | ||
631 | 632 | ||
632 | if (lpfc_cmd->status) { | 633 | if (lpfc_cmd->status) { |
633 | if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && | 634 | if (lpfc_cmd->status == IOSTAT_LOCAL_REJECT && |
@@ -688,6 +689,29 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, | |||
688 | 689 | ||
689 | result = cmd->result; | 690 | result = cmd->result; |
690 | sdev = cmd->device; | 691 | sdev = cmd->device; |
692 | if (vport->cfg_max_scsicmpl_time && | ||
693 | time_after(jiffies, lpfc_cmd->start_time + | ||
694 | msecs_to_jiffies(vport->cfg_max_scsicmpl_time))) { | ||
695 | spin_lock_irqsave(sdev->host->host_lock, flags); | ||
696 | if ((pnode->cmd_qdepth > atomic_read(&pnode->cmd_pending) && | ||
697 | (atomic_read(&pnode->cmd_pending) > LPFC_MIN_TGT_QDEPTH) && | ||
698 | ((cmd->cmnd[0] == READ_10) || (cmd->cmnd[0] == WRITE_10)))) | ||
699 | pnode->cmd_qdepth = atomic_read(&pnode->cmd_pending); | ||
700 | |||
701 | pnode->last_change_time = jiffies; | ||
702 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | ||
703 | } else if ((pnode->cmd_qdepth < LPFC_MAX_TGT_QDEPTH) && | ||
704 | time_after(jiffies, pnode->last_change_time + | ||
705 | msecs_to_jiffies(LPFC_TGTQ_INTERVAL))) { | ||
706 | spin_lock_irqsave(sdev->host->host_lock, flags); | ||
707 | pnode->cmd_qdepth += pnode->cmd_qdepth * | ||
708 | LPFC_TGTQ_RAMPUP_PCENT / 100; | ||
709 | if (pnode->cmd_qdepth > LPFC_MAX_TGT_QDEPTH) | ||
710 | pnode->cmd_qdepth = LPFC_MAX_TGT_QDEPTH; | ||
711 | pnode->last_change_time = jiffies; | ||
712 | spin_unlock_irqrestore(sdev->host->host_lock, flags); | ||
713 | } | ||
714 | |||
691 | lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); | 715 | lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); |
692 | cmd->scsi_done(cmd); | 716 | cmd->scsi_done(cmd); |
693 | 717 | ||
@@ -1075,6 +1099,8 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | |||
1075 | cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0); | 1099 | cmnd->result = ScsiResult(DID_TRANSPORT_DISRUPTED, 0); |
1076 | goto out_fail_command; | 1100 | goto out_fail_command; |
1077 | } | 1101 | } |
1102 | if (atomic_read(&ndlp->cmd_pending) >= ndlp->cmd_qdepth) | ||
1103 | goto out_host_busy; | ||
1078 | 1104 | ||
1079 | lpfc_cmd = lpfc_get_scsi_buf(phba); | 1105 | lpfc_cmd = lpfc_get_scsi_buf(phba); |
1080 | if (lpfc_cmd == NULL) { | 1106 | if (lpfc_cmd == NULL) { |
@@ -1093,6 +1119,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | |||
1093 | lpfc_cmd->pCmd = cmnd; | 1119 | lpfc_cmd->pCmd = cmnd; |
1094 | lpfc_cmd->rdata = rdata; | 1120 | lpfc_cmd->rdata = rdata; |
1095 | lpfc_cmd->timeout = 0; | 1121 | lpfc_cmd->timeout = 0; |
1122 | lpfc_cmd->start_time = jiffies; | ||
1096 | cmnd->host_scribble = (unsigned char *)lpfc_cmd; | 1123 | cmnd->host_scribble = (unsigned char *)lpfc_cmd; |
1097 | cmnd->scsi_done = done; | 1124 | cmnd->scsi_done = done; |
1098 | 1125 | ||
@@ -1102,6 +1129,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | |||
1102 | 1129 | ||
1103 | lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp); | 1130 | lpfc_scsi_prep_cmnd(vport, lpfc_cmd, ndlp); |
1104 | 1131 | ||
1132 | atomic_inc(&ndlp->cmd_pending); | ||
1105 | err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring], | 1133 | err = lpfc_sli_issue_iocb(phba, &phba->sli.ring[psli->fcp_ring], |
1106 | &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB); | 1134 | &lpfc_cmd->cur_iocbq, SLI_IOCB_RET_IOCB); |
1107 | if (err) | 1135 | if (err) |
@@ -1116,6 +1144,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) | |||
1116 | return 0; | 1144 | return 0; |
1117 | 1145 | ||
1118 | out_host_busy_free_buf: | 1146 | out_host_busy_free_buf: |
1147 | atomic_dec(&ndlp->cmd_pending); | ||
1119 | lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); | 1148 | lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); |
1120 | lpfc_release_scsi_buf(phba, lpfc_cmd); | 1149 | lpfc_release_scsi_buf(phba, lpfc_cmd); |
1121 | out_host_busy: | 1150 | out_host_busy: |
diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index daba9237498..6737cabe9a7 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h | |||
@@ -139,6 +139,7 @@ struct lpfc_scsi_buf { | |||
139 | */ | 139 | */ |
140 | struct lpfc_iocbq cur_iocbq; | 140 | struct lpfc_iocbq cur_iocbq; |
141 | wait_queue_head_t *waitq; | 141 | wait_queue_head_t *waitq; |
142 | unsigned long start_time; | ||
142 | }; | 143 | }; |
143 | 144 | ||
144 | #define LPFC_SCSI_DMA_EXT_SIZE 264 | 145 | #define LPFC_SCSI_DMA_EXT_SIZE 264 |