diff options
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r-- | drivers/scsi/ibmvscsi/ibmvfc.c | 126 |
1 files changed, 122 insertions, 4 deletions
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 93d1fbe4ee5d..ea4abee7a2a9 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c | |||
@@ -75,7 +75,7 @@ MODULE_PARM_DESC(max_lun, "Maximum allowed LUN. " | |||
75 | module_param_named(max_targets, max_targets, uint, S_IRUGO); | 75 | module_param_named(max_targets, max_targets, uint, S_IRUGO); |
76 | MODULE_PARM_DESC(max_targets, "Maximum allowed targets. " | 76 | MODULE_PARM_DESC(max_targets, "Maximum allowed targets. " |
77 | "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]"); | 77 | "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]"); |
78 | module_param_named(disc_threads, disc_threads, uint, S_IRUGO | S_IWUSR); | 78 | module_param_named(disc_threads, disc_threads, uint, S_IRUGO); |
79 | MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. " | 79 | MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. " |
80 | "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]"); | 80 | "[Default=" __stringify(IBMVFC_MAX_DISC_THREADS) "]"); |
81 | module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR); | 81 | module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR); |
@@ -640,6 +640,7 @@ static void ibmvfc_release_crq_queue(struct ibmvfc_host *vhost) | |||
640 | 640 | ||
641 | ibmvfc_dbg(vhost, "Releasing CRQ\n"); | 641 | ibmvfc_dbg(vhost, "Releasing CRQ\n"); |
642 | free_irq(vdev->irq, vhost); | 642 | free_irq(vdev->irq, vhost); |
643 | tasklet_kill(&vhost->tasklet); | ||
643 | do { | 644 | do { |
644 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 645 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
645 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 646 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
@@ -2699,6 +2700,25 @@ static struct ibmvfc_crq *ibmvfc_next_crq(struct ibmvfc_host *vhost) | |||
2699 | static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) | 2700 | static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) |
2700 | { | 2701 | { |
2701 | struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance; | 2702 | struct ibmvfc_host *vhost = (struct ibmvfc_host *)dev_instance; |
2703 | unsigned long flags; | ||
2704 | |||
2705 | spin_lock_irqsave(vhost->host->host_lock, flags); | ||
2706 | vio_disable_interrupts(to_vio_dev(vhost->dev)); | ||
2707 | tasklet_schedule(&vhost->tasklet); | ||
2708 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | ||
2709 | return IRQ_HANDLED; | ||
2710 | } | ||
2711 | |||
2712 | /** | ||
2713 | * ibmvfc_tasklet - Interrupt handler tasklet | ||
2714 | * @data: ibmvfc host struct | ||
2715 | * | ||
2716 | * Returns: | ||
2717 | * Nothing | ||
2718 | **/ | ||
2719 | static void ibmvfc_tasklet(void *data) | ||
2720 | { | ||
2721 | struct ibmvfc_host *vhost = data; | ||
2702 | struct vio_dev *vdev = to_vio_dev(vhost->dev); | 2722 | struct vio_dev *vdev = to_vio_dev(vhost->dev); |
2703 | struct ibmvfc_crq *crq; | 2723 | struct ibmvfc_crq *crq; |
2704 | struct ibmvfc_async_crq *async; | 2724 | struct ibmvfc_async_crq *async; |
@@ -2706,7 +2726,6 @@ static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) | |||
2706 | int done = 0; | 2726 | int done = 0; |
2707 | 2727 | ||
2708 | spin_lock_irqsave(vhost->host->host_lock, flags); | 2728 | spin_lock_irqsave(vhost->host->host_lock, flags); |
2709 | vio_disable_interrupts(to_vio_dev(vhost->dev)); | ||
2710 | while (!done) { | 2729 | while (!done) { |
2711 | /* Pull all the valid messages off the CRQ */ | 2730 | /* Pull all the valid messages off the CRQ */ |
2712 | while ((crq = ibmvfc_next_crq(vhost)) != NULL) { | 2731 | while ((crq = ibmvfc_next_crq(vhost)) != NULL) { |
@@ -2734,7 +2753,6 @@ static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) | |||
2734 | } | 2753 | } |
2735 | 2754 | ||
2736 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | 2755 | spin_unlock_irqrestore(vhost->host->host_lock, flags); |
2737 | return IRQ_HANDLED; | ||
2738 | } | 2756 | } |
2739 | 2757 | ||
2740 | /** | 2758 | /** |
@@ -3105,6 +3123,7 @@ static void ibmvfc_tgt_adisc_done(struct ibmvfc_event *evt) | |||
3105 | 3123 | ||
3106 | vhost->discovery_threads--; | 3124 | vhost->discovery_threads--; |
3107 | ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); | 3125 | ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); |
3126 | del_timer(&tgt->timer); | ||
3108 | 3127 | ||
3109 | switch (status) { | 3128 | switch (status) { |
3110 | case IBMVFC_MAD_SUCCESS: | 3129 | case IBMVFC_MAD_SUCCESS: |
@@ -3161,9 +3180,89 @@ static void ibmvfc_init_passthru(struct ibmvfc_event *evt) | |||
3161 | } | 3180 | } |
3162 | 3181 | ||
3163 | /** | 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 | /** | ||
3164 | * ibmvfc_tgt_adisc - Initiate an ADISC for specified target | 3257 | * ibmvfc_tgt_adisc - Initiate an ADISC for specified target |
3165 | * @tgt: ibmvfc target struct | 3258 | * @tgt: ibmvfc target struct |
3166 | * | 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. | ||
3167 | **/ | 3266 | **/ |
3168 | static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) | 3267 | static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) |
3169 | { | 3268 | { |
@@ -3184,6 +3283,7 @@ static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) | |||
3184 | mad = &evt->iu.passthru; | 3283 | mad = &evt->iu.passthru; |
3185 | mad->iu.flags = IBMVFC_FC_ELS; | 3284 | mad->iu.flags = IBMVFC_FC_ELS; |
3186 | mad->iu.scsi_id = tgt->scsi_id; | 3285 | mad->iu.scsi_id = tgt->scsi_id; |
3286 | mad->iu.cancel_key = tgt->cancel_key; | ||
3187 | 3287 | ||
3188 | mad->fc_iu.payload[0] = IBMVFC_ADISC; | 3288 | mad->fc_iu.payload[0] = IBMVFC_ADISC; |
3189 | 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, |
@@ -3192,9 +3292,19 @@ static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) | |||
3192 | sizeof(vhost->login_buf->resp.node_name)); | 3292 | sizeof(vhost->login_buf->resp.node_name)); |
3193 | 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; |
3194 | 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 | |||
3195 | ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); | 3304 | ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); |
3196 | if (ibmvfc_send_event(evt, vhost, default_timeout)) { | 3305 | if (ibmvfc_send_event(evt, vhost, IBMVFC_ADISC_PLUS_CANCEL_TIMEOUT)) { |
3197 | vhost->discovery_threads--; | 3306 | vhost->discovery_threads--; |
3307 | del_timer(&tgt->timer); | ||
3198 | ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); | 3308 | ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_NONE); |
3199 | kref_put(&tgt->kref, ibmvfc_release_tgt); | 3309 | kref_put(&tgt->kref, ibmvfc_release_tgt); |
3200 | } else | 3310 | } else |
@@ -3322,6 +3432,8 @@ static int ibmvfc_alloc_target(struct ibmvfc_host *vhost, u64 scsi_id) | |||
3322 | tgt->new_scsi_id = scsi_id; | 3432 | tgt->new_scsi_id = scsi_id; |
3323 | tgt->vhost = vhost; | 3433 | tgt->vhost = vhost; |
3324 | tgt->need_login = 1; | 3434 | tgt->need_login = 1; |
3435 | tgt->cancel_key = vhost->task_set++; | ||
3436 | init_timer(&tgt->timer); | ||
3325 | kref_init(&tgt->kref); | 3437 | kref_init(&tgt->kref); |
3326 | ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); | 3438 | ibmvfc_init_tgt(tgt, ibmvfc_tgt_implicit_logout); |
3327 | spin_lock_irqsave(vhost->host->host_lock, flags); | 3439 | spin_lock_irqsave(vhost->host->host_lock, flags); |
@@ -3716,6 +3828,7 @@ static void ibmvfc_do_work(struct ibmvfc_host *vhost) | |||
3716 | spin_unlock_irqrestore(vhost->host->host_lock, flags); | 3828 | spin_unlock_irqrestore(vhost->host->host_lock, flags); |
3717 | if (rport) | 3829 | if (rport) |
3718 | fc_remote_port_delete(rport); | 3830 | fc_remote_port_delete(rport); |
3831 | del_timer_sync(&tgt->timer); | ||
3719 | kref_put(&tgt->kref, ibmvfc_release_tgt); | 3832 | kref_put(&tgt->kref, ibmvfc_release_tgt); |
3720 | return; | 3833 | return; |
3721 | } | 3834 | } |
@@ -3859,6 +3972,8 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) | |||
3859 | 3972 | ||
3860 | retrc = 0; | 3973 | retrc = 0; |
3861 | 3974 | ||
3975 | tasklet_init(&vhost->tasklet, (void *)ibmvfc_tasklet, (unsigned long)vhost); | ||
3976 | |||
3862 | if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) { | 3977 | if ((rc = request_irq(vdev->irq, ibmvfc_interrupt, 0, IBMVFC_NAME, vhost))) { |
3863 | dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc); | 3978 | dev_err(dev, "Couldn't register irq 0x%x. rc=%d\n", vdev->irq, rc); |
3864 | goto req_irq_failed; | 3979 | goto req_irq_failed; |
@@ -3874,6 +3989,7 @@ static int ibmvfc_init_crq(struct ibmvfc_host *vhost) | |||
3874 | return retrc; | 3989 | return retrc; |
3875 | 3990 | ||
3876 | req_irq_failed: | 3991 | req_irq_failed: |
3992 | tasklet_kill(&vhost->tasklet); | ||
3877 | do { | 3993 | do { |
3878 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); | 3994 | rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); |
3879 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); | 3995 | } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); |
@@ -4040,6 +4156,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const struct vio_device_id *id) | |||
4040 | vhost->dev = dev; | 4156 | vhost->dev = dev; |
4041 | vhost->partition_number = -1; | 4157 | vhost->partition_number = -1; |
4042 | vhost->log_level = log_level; | 4158 | vhost->log_level = log_level; |
4159 | vhost->task_set = 1; | ||
4043 | strcpy(vhost->partition_name, "UNKNOWN"); | 4160 | strcpy(vhost->partition_name, "UNKNOWN"); |
4044 | init_waitqueue_head(&vhost->work_wait_q); | 4161 | init_waitqueue_head(&vhost->work_wait_q); |
4045 | init_waitqueue_head(&vhost->init_wait_q); | 4162 | init_waitqueue_head(&vhost->init_wait_q); |
@@ -4174,6 +4291,7 @@ static struct fc_function_template ibmvfc_transport_functions = { | |||
4174 | .show_host_supported_classes = 1, | 4291 | .show_host_supported_classes = 1, |
4175 | .show_host_port_type = 1, | 4292 | .show_host_port_type = 1, |
4176 | .show_host_port_id = 1, | 4293 | .show_host_port_id = 1, |
4294 | .show_host_maxframe_size = 1, | ||
4177 | 4295 | ||
4178 | .get_host_port_state = ibmvfc_get_host_port_state, | 4296 | .get_host_port_state = ibmvfc_get_host_port_state, |
4179 | .show_host_port_state = 1, | 4297 | .show_host_port_state = 1, |