aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ibmvscsi/ibmvfc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/ibmvscsi/ibmvfc.c')
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c126
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. "
75module_param_named(max_targets, max_targets, uint, S_IRUGO); 75module_param_named(max_targets, max_targets, uint, S_IRUGO);
76MODULE_PARM_DESC(max_targets, "Maximum allowed targets. " 76MODULE_PARM_DESC(max_targets, "Maximum allowed targets. "
77 "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]"); 77 "[Default=" __stringify(IBMVFC_MAX_TARGETS) "]");
78module_param_named(disc_threads, disc_threads, uint, S_IRUGO | S_IWUSR); 78module_param_named(disc_threads, disc_threads, uint, S_IRUGO);
79MODULE_PARM_DESC(disc_threads, "Number of device discovery threads to use. " 79MODULE_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) "]");
81module_param_named(debug, ibmvfc_debug, uint, S_IRUGO | S_IWUSR); 81module_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)
2699static irqreturn_t ibmvfc_interrupt(int irq, void *dev_instance) 2700static 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 **/
2719static 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 **/
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/**
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 **/
3168static void ibmvfc_tgt_adisc(struct ibmvfc_target *tgt) 3267static 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
3876req_irq_failed: 3991req_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,