aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ibmvscsi/ibmvfc.c
diff options
context:
space:
mode:
authorBrian King <brking@linux.vnet.ibm.com>2009-03-20 16:44:39 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-04-03 10:22:43 -0400
commit10501e1ce3d97cc84a8e29a3a139f74601b59b0f (patch)
tree47ba1a14b3c3843efc4b39eff17424da391c73b5 /drivers/scsi/ibmvscsi/ibmvfc.c
parent9ab3610f9c24e7f303f1b0e6bb88a5f804ab847e (diff)
[SCSI] ibmvfc: Improve ADISC timeout handling
The ibmvfc driver currently breaks the CRQ and essentially resets the entire virtual FC adapter, killing all outstanding ops to all attached targets, if an ADISC times out during target discover/rediscovery. This patch adds some code to cancel the ADISC if it times out, which prevents a single ADISC timeout from affecting the other devices attached to the fabric. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c98
1 files changed, 97 insertions, 1 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 0ac2dedb413c..ea4abee7a2a9 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -3123,6 +3123,7 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt)
3123 3123
3124 vhost->discovery_threads--; 3124 vhost->discovery_threads--;
3125 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 3125 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3126 del_timer(&tgt->timer);
3126 3127
3127 switch (status) { 3128 switch (status) {
3128 case IBMVFC_MAD_SUCCESS: 3129 case IBMVFC_MAD_SUCCESS:
@@ -3179,9 +3180,89 @@ static void ibmvfc_init_passthru(struct ibmvfc_event *evt)
3179} 3180}
3180 3181
3181/** 3182/**
3183 * ibmvfc_tgt_adisc_cancel_done - Completion handler when cancelling an ADISC
3184 * @evt: ibmvfc event struct
3185 *
3186 * Just cleanup this event struct. Everything else is handled by
3187 * the ADISC completion handler. If the ADISC never actually comes
3188 * back, we still have the timer running on the ADISC event struct
3189 * which will fire and cause the CRQ to get reset.
3190 *
3191 **/
3192static void ibmvfc_tgt_adisc_cancel_done(struct ibmvfc_event *evt)
3193{
3194 struct ibmvfc_host *vhost = evt->vhost;
3195 struct ibmvfc_target *tgt = evt->tgt;
3196
3197 tgt_dbg(tgt, "ADISC cancel complete\n");
3198 vhost->abort_threads--;
3199 ibmvfc_free_event(evt);
3200 kref_put(&tgt->kref, ibmvfc_release_tgt);
3201 wake_up(&vhost->work_wait_q);
3202}
3203
3204/**
3205 * ibmvfc_adisc_timeout - Handle an ADISC timeout
3206 * @tgt: ibmvfc target struct
3207 *
3208 * If an ADISC times out, send a cancel. If the cancel times
3209 * out, reset the CRQ. When the ADISC comes back as cancelled,
3210 * log back into the target.
3211 **/
3212static void ibmvfc_adisc_timeout(struct ibmvfc_target *tgt)
3213{
3214 struct ibmvfc_host *vhost = tgt->vhost;
3215 struct ibmvfc_event *evt;
3216 struct ibmvfc_tmf *tmf;
3217 unsigned long flags;
3218 int rc;
3219
3220 tgt_dbg(tgt, "ADISC timeout\n");
3221 spin_lock_irqsave(vhost->host->host_lock, flags);
3222 if (vhost->abort_threads >= disc_threads ||
3223 tgt->action != IBMVFC_TGT_ACTION_INIT_WAIT ||
3224 vhost->state != IBMVFC_INITIALIZING ||
3225 vhost->action != IBMVFC_HOST_ACTION_QUERY_TGTS) {
3226 spin_unlock_irqrestore(vhost->host->host_lock, flags);
3227 return;
3228 }
3229
3230 vhost->abort_threads++;
3231 kref_get(&tgt->kref);
3232 evt = ibmvfc_get_event(vhost);
3233 ibmvfc_init_event(evt, ibmvfc_tgt_adisc_cancel_done, IBMVFC_MAD_FORMAT);
3234
3235 evt->tgt = tgt;
3236 tmf = &evt->iu.tmf;
3237 memset(tmf, 0, sizeof(*tmf));
3238 tmf->common.version = 1;
3239 tmf->common.opcode = IBMVFC_TMF_MAD;
3240 tmf->common.length = sizeof(*tmf);
3241 tmf->scsi_id = tgt->scsi_id;
3242 tmf->cancel_key = tgt->cancel_key;
3243
3244 rc = ibmvfc_send_event(evt, vhost, default_timeout);
3245
3246 if (rc) {
3247 tgt_err(tgt, "Failed to send cancel event for ADISC. rc=%d\n", rc);
3248 vhost->abort_threads--;
3249 kref_put(&tgt->kref, ibmvfc_release_tgt);
3250 __ibmvfc_reset_host(vhost);
3251 } else
3252 tgt_dbg(tgt, "Attempting to cancel ADISC\n");
3253 spin_unlock_irqrestore(vhost->host->host_lock, flags);
3254}
3255
3256/**
3182 * ibmvfc_tgt_adisc - Initiate an ADISC for specified target 3257 * ibmvfc_tgt_adisc - Initiate an ADISC for specified target
3183 * @tgt: ibmvfc target struct 3258 * @tgt: ibmvfc target struct
3184 * 3259 *
3260 * When sending an ADISC we end up with two timers running. The
3261 * first timer is the timer in the ibmvfc target struct. If this
3262 * fires, we send a cancel to the target. The second timer is the
3263 * timer on the ibmvfc event for the ADISC, which is longer. If that
3264 * fires, it means the ADISC timed out and our attempt to cancel it
3265 * also failed, so we need to reset the CRQ.
3185 **/ 3266 **/
3186static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) 3267static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
3187{ 3268{
@@ -3202,6 +3283,7 @@ static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
3202 mad = &evt->iu.passthru; 3283 mad = &evt->iu.passthru;
3203 mad->iu.flags = IBMVFC_FC_ELS; 3284 mad->iu.flags = IBMVFC_FC_ELS;
3204 mad->iu.scsi_id = tgt->scsi_id; 3285 mad->iu.scsi_id = tgt->scsi_id;
3286 mad->iu.cancel_key = tgt->cancel_key;
3205 3287
3206 mad->fc_iu.payload[0] = IBMVFC_ADISC; 3288 mad->fc_iu.payload[0] = IBMVFC_ADISC;
3207 memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name, 3289 memcpy(&mad->fc_iu.payload[2], &vhost->login_buf->resp.port_name,
@@ -3210,9 +3292,19 @@ static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt)
3210 sizeof(vhost->login_buf->resp.node_name)); 3292 sizeof(vhost->login_buf->resp.node_name));
3211 mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff; 3293 mad->fc_iu.payload[6] = vhost->login_buf->resp.scsi_id & 0x00ffffff;
3212 3294
3295 if (timer_pending(&tgt->timer))
3296 mod_timer(&tgt->timer, jiffies + (IBMVFC_ADISC_TIMEOUT * HZ));
3297 else {
3298 tgt->timer.data = (unsigned long) tgt;
3299 tgt->timer.expires = jiffies + (IBMVFC_ADISC_TIMEOUT * HZ);
3300 tgt->timer.function = (void (*)(unsigned long))ibmvfc_adisc_timeout;
3301 add_timer(&tgt->timer);
3302 }
3303
3213 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); 3304 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT);
3214 if (ibmvfc_send_event(evt, vhost, default_timeout)) { 3305 if (ibmvfc_send_event(evt, vhost, IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT)) {
3215 vhost->discovery_threads--; 3306 vhost->discovery_threads--;
3307 del_timer(&tgt->timer);
3216 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); 3308 ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE);
3217 kref_put(&tgt->kref, ibmvfc_release_tgt); 3309 kref_put(&tgt->kref, ibmvfc_release_tgt);
3218 } else 3310 } else
@@ -3340,6 +3432,8 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id)
3340 tgt->new_scsi_id = scsi_id; 3432 tgt->new_scsi_id = scsi_id;
3341 tgt->vhost = vhost; 3433 tgt->vhost = vhost;
3342 tgt->need_login = 1; 3434 tgt->need_login = 1;
3435 tgt->cancel_key = vhost->task_set++;
3436 init_timer(&tgt->timer);
3343 kref_init(&tgt->kref); 3437 kref_init(&tgt->kref);
3344 ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); 3438 ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout);
3345 spin_lock_irqsave(vhost->host->host_lock, flags); 3439 spin_lock_irqsave(vhost->host->host_lock, flags);
@@ -3734,6 +3828,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost)
3734 spin_unlock_irqrestore(vhost->host->host_lock, flags); 3828 spin_unlock_irqrestore(vhost->host->host_lock, flags);
3735 if (rport) 3829 if (rport)
3736 fc_remote_port_delete(rport); 3830 fc_remote_port_delete(rport);
3831 del_timer_sync(&tgt->timer);
3737 kref_put(&tgt->kref, ibmvfc_release_tgt); 3832 kref_put(&tgt->kref, ibmvfc_release_tgt);
3738 return; 3833 return;
3739 } 3834 }
@@ -4061,6 +4156,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id)
4061 vhost->dev = dev; 4156 vhost->dev = dev;
4062 vhost->partition_number = -1; 4157 vhost->partition_number = -1;
4063 vhost->log_level = log_level; 4158 vhost->log_level = log_level;
4159 vhost->task_set = 1;
4064 strcpy(vhost->partition_name, "UNKNOWN"); 4160 strcpy(vhost->partition_name, "UNKNOWN");
4065 init_waitqueue_head(&vhost->work_wait_q); 4161 init_waitqueue_head(&vhost->work_wait_q);
4066 init_waitqueue_head(&vhost->init_wait_q); 4162 init_waitqueue_head(&vhost->init_wait_q);