diff options
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 98 |
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 | **/ | ||
3192 | static 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 | **/ | ||
3212 | static 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 | **/ |
3186 | static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) | 3267 | static 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); |