diff options
Diffstat (limited to 'drivers/scsi/scsi_transport_fc.c')
| -rw-r--r-- | drivers/scsi/scsi_transport_fc.c | 116 |
1 files changed, 65 insertions, 51 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 0965f8a7134f..987befb02408 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -137,6 +137,7 @@ static const struct { { FCH_EVT_PORT_FABRIC, "port_fabric" }, { FCH_EVT_LINK_UNKNOWN, "link_unknown" }, { FCH_EVT_LINK_FPIN, "link_FPIN" }, + { FCH_EVT_LINK_FPIN_ACK, "link_FPIN_ACK" }, { FCH_EVT_VENDOR_UNIQUE, "vendor_unique" }, }; fc_enum_name_search(host_event_code, fc_host_event_code, @@ -440,23 +441,12 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev, fc_host->next_vport_number = 0; fc_host->npiv_vports_inuse = 0; - snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name), - "fc_wq_%d", shost->host_no); - fc_host->work_q = alloc_workqueue("%s", 0, 0, fc_host->work_q_name); + fc_host->work_q = alloc_workqueue("fc_wq_%d", WQ_PERCPU, 0, + shost->host_no); if (!fc_host->work_q) return -ENOMEM; fc_host->dev_loss_tmo = fc_dev_loss_tmo; - snprintf(fc_host->devloss_work_q_name, - sizeof(fc_host->devloss_work_q_name), - "fc_dl_%d", shost->host_no); - fc_host->devloss_work_q = alloc_workqueue("%s", 0, 0, - fc_host->devloss_work_q_name); - if (!fc_host->devloss_work_q) { - destroy_workqueue(fc_host->work_q); - fc_host->work_q = NULL; - return -ENOMEM; - } fc_bsg_hostadd(shost, fc_host); /* ignore any bsg add error - we just can't do sgio */ @@ -894,17 +884,20 @@ fc_fpin_congn_stats_update(struct Scsi_Host *shost, * @shost: host the FPIN was received on * @fpin_len: length of FPIN payload, in bytes * @fpin_buf: pointer to FPIN payload - * + * @event_acknowledge: 1, if LLDD handles this event. * Notes: * This routine assumes no locks are held on entry. */ void -fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf) +fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf, + u8 event_acknowledge) { struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf; struct fc_tlv_desc *tlv; - u32 desc_cnt = 0, bytes_remain; + u32 bytes_remain; u32 dtag; + enum fc_host_event_code event_code = + event_acknowledge ? FCH_EVT_LINK_FPIN_ACK : FCH_EVT_LINK_FPIN; /* Update Statistics */ tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0]; @@ -928,13 +921,12 @@ fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf) fc_fpin_congn_stats_update(shost, tlv); } - desc_cnt++; bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv); tlv = fc_tlv_next_desc(tlv); } fc_host_post_fc_event(shost, fc_get_event_number(), - FCH_EVT_LINK_FPIN, fpin_len, fpin_buf, 0); + event_code, fpin_len, fpin_buf, 0); } EXPORT_SYMBOL(fc_host_fpin_rcv); @@ -1252,7 +1244,7 @@ static ssize_t fc_rport_set_marginal_state(struct device *dev, */ if (rport->port_state == FC_PORTSTATE_ONLINE) rport->port_state = port_state; - else + else if (port_state != rport->port_state) return -EINVAL; } else if (port_state == FC_PORTSTATE_ONLINE) { /* @@ -1262,7 +1254,7 @@ static ssize_t fc_rport_set_marginal_state(struct device *dev, */ if (rport->port_state == FC_PORTSTATE_MARGINAL) rport->port_state = port_state; - else + else if (port_state != rport->port_state) return -EINVAL; } else return -EINVAL; @@ -2816,6 +2808,7 @@ fc_flush_work(struct Scsi_Host *shost) /** * fc_queue_devloss_work - Schedule work for the fc_host devloss workqueue. * @shost: Pointer to Scsi_Host bound to fc_host. + * @rport: rport associated with the devloss work * @work: Work to queue for execution. * @delay: jiffies to delay the work queuing * @@ -2823,10 +2816,10 @@ fc_flush_work(struct Scsi_Host *shost) * 1 on success / 0 already queued / < 0 for error */ static int -fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work, - unsigned long delay) +fc_queue_devloss_work(struct Scsi_Host *shost, struct fc_rport *rport, + struct delayed_work *work, unsigned long delay) { - if (unlikely(!fc_host_devloss_work_q(shost))) { + if (unlikely(!rport->devloss_work_q)) { printk(KERN_ERR "ERROR: FC host '%s' attempted to queue work, " "when no workqueue created.\n", shost->hostt->name); @@ -2835,17 +2828,18 @@ fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work, return -EINVAL; } - return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay); + return queue_delayed_work(rport->devloss_work_q, work, delay); } /** * fc_flush_devloss - Flush a fc_host's devloss workqueue. * @shost: Pointer to Scsi_Host bound to fc_host. + * @rport: rport associated with the devloss work */ static void -fc_flush_devloss(struct Scsi_Host *shost) +fc_flush_devloss(struct Scsi_Host *shost, struct fc_rport *rport) { - if (!fc_host_devloss_work_q(shost)) { + if (unlikely(!rport->devloss_work_q)) { printk(KERN_ERR "ERROR: FC host '%s' attempted to flush work, " "when no workqueue created.\n", shost->hostt->name); @@ -2853,7 +2847,7 @@ fc_flush_devloss(struct Scsi_Host *shost) return; } - flush_workqueue(fc_host_devloss_work_q(shost)); + flush_workqueue(rport->devloss_work_q); } @@ -2915,13 +2909,6 @@ fc_remove_host(struct Scsi_Host *shost) fc_host->work_q = NULL; destroy_workqueue(work_q); } - - /* flush all devloss work items, then kill it */ - if (fc_host->devloss_work_q) { - work_q = fc_host->devloss_work_q; - fc_host->devloss_work_q = NULL; - destroy_workqueue(work_q); - } } EXPORT_SYMBOL(fc_remove_host); @@ -2969,6 +2956,7 @@ fc_rport_final_delete(struct work_struct *work) struct device *dev = &rport->dev; struct Scsi_Host *shost = rport_to_shost(rport); struct fc_internal *i = to_fc_internal(shost->transportt); + struct workqueue_struct *work_q; unsigned long flags; int do_callback = 0; @@ -2990,9 +2978,9 @@ fc_rport_final_delete(struct work_struct *work) if (rport->flags & FC_RPORT_DEVLOSS_PENDING) { spin_unlock_irqrestore(shost->host_lock, flags); if (!cancel_delayed_work(&rport->fail_io_work)) - fc_flush_devloss(shost); + fc_flush_devloss(shost, rport); if (!cancel_delayed_work(&rport->dev_loss_work)) - fc_flush_devloss(shost); + fc_flush_devloss(shost, rport); cancel_work_sync(&rport->scan_work); spin_lock_irqsave(shost->host_lock, flags); rport->flags &= ~FC_RPORT_DEVLOSS_PENDING; @@ -3023,6 +3011,12 @@ fc_rport_final_delete(struct work_struct *work) fc_bsg_remove(rport->rqst_q); + if (rport->devloss_work_q) { + work_q = rport->devloss_work_q; + rport->devloss_work_q = NULL; + destroy_workqueue(work_q); + } + transport_remove_device(dev); device_del(dev); transport_destroy_device(dev); @@ -3095,6 +3089,22 @@ fc_remote_port_create(struct Scsi_Host *shost, int channel, spin_unlock_irqrestore(shost->host_lock, flags); + rport->devloss_work_q = alloc_workqueue("fc_dl_%d_%d", WQ_PERCPU, 0, + shost->host_no, rport->number); + if (!rport->devloss_work_q) { + printk(KERN_ERR "FC Remote Port alloc_workqueue failed\n"); +/* + * Note that we have not yet called device_initialize() / get_device() + * Cannot reclaim incremented rport->number because we released host_lock + */ + spin_lock_irqsave(shost->host_lock, flags); + list_del(&rport->peers); + scsi_host_put(shost); /* for fc_host->rport list */ + spin_unlock_irqrestore(shost->host_lock, flags); + kfree(rport); + return NULL; + } + dev = &rport->dev; device_initialize(dev); /* takes self reference */ dev->parent = get_device(&shost->shost_gendev); /* parent reference */ @@ -3257,9 +3267,9 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel, * be checked and will NOOP the function. */ if (!cancel_delayed_work(&rport->fail_io_work)) - fc_flush_devloss(shost); + fc_flush_devloss(shost, rport); if (!cancel_delayed_work(&rport->dev_loss_work)) - fc_flush_devloss(shost); + fc_flush_devloss(shost, rport); spin_lock_irqsave(shost->host_lock, flags); @@ -3448,16 +3458,17 @@ fc_remote_port_delete(struct fc_rport *rport) spin_unlock_irqrestore(shost->host_lock, flags); - scsi_target_block(&rport->dev); + scsi_block_targets(shost, &rport->dev); /* see if we need to kill io faster than waiting for device loss */ if ((rport->fast_io_fail_tmo != -1) && (rport->fast_io_fail_tmo < timeout)) - fc_queue_devloss_work(shost, &rport->fail_io_work, - rport->fast_io_fail_tmo * HZ); + fc_queue_devloss_work(shost, rport, &rport->fail_io_work, + rport->fast_io_fail_tmo * HZ); /* cap the length the devices can be blocked until they are deleted */ - fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ); + fc_queue_devloss_work(shost, rport, &rport->dev_loss_work, + timeout * HZ); } EXPORT_SYMBOL(fc_remote_port_delete); @@ -3511,14 +3522,14 @@ fc_remote_port_rolechg(struct fc_rport *rport, u32 roles) * state as the LLDD would not have had an rport * reference to pass us. * - * Take no action on the del_timer failure as the state + * Take no action on the timer_delete() failure as the state * machine state change will validate the * transaction. */ if (!cancel_delayed_work(&rport->fail_io_work)) - fc_flush_devloss(shost); + fc_flush_devloss(shost, rport); if (!cancel_delayed_work(&rport->dev_loss_work)) - fc_flush_devloss(shost); + fc_flush_devloss(shost, rport); spin_lock_irqsave(shost->host_lock, flags); rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT | @@ -4273,6 +4284,7 @@ fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) { struct device *dev = &shost->shost_gendev; struct fc_internal *i = to_fc_internal(shost->transportt); + struct queue_limits lim; struct request_queue *q; char bsg_name[20]; @@ -4283,16 +4295,16 @@ fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) snprintf(bsg_name, sizeof(bsg_name), "fc_host%d", shost->host_no); - - q = bsg_setup_queue(dev, bsg_name, fc_bsg_dispatch, fc_bsg_job_timeout, - i->f->dd_bsg_size); + scsi_init_limits(shost, &lim); + lim.max_segments = min_not_zero(lim.max_segments, i->f->max_bsg_segments); + q = bsg_setup_queue(dev, bsg_name, &lim, fc_bsg_dispatch, + fc_bsg_job_timeout, i->f->dd_bsg_size); if (IS_ERR(q)) { dev_err(dev, "fc_host%d: bsg interface failed to initialize - setup queue\n", shost->host_no); return PTR_ERR(q); } - __scsi_init_queue(shost, q); blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT); fc_host->rqst_q = q; return 0; @@ -4308,6 +4320,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) { struct device *dev = &rport->dev; struct fc_internal *i = to_fc_internal(shost->transportt); + struct queue_limits lim; struct request_queue *q; rport->rqst_q = NULL; @@ -4315,13 +4328,14 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) if (!i->f->bsg_request) return -ENOTSUPP; - q = bsg_setup_queue(dev, dev_name(dev), fc_bsg_dispatch_prep, + scsi_init_limits(shost, &lim); + lim.max_segments = min_not_zero(lim.max_segments, i->f->max_bsg_segments); + q = bsg_setup_queue(dev, dev_name(dev), &lim, fc_bsg_dispatch_prep, fc_bsg_job_timeout, i->f->dd_bsg_size); if (IS_ERR(q)) { dev_err(dev, "failed to setup bsg queue\n"); return PTR_ERR(q); } - __scsi_init_queue(shost, q); blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); rport->rqst_q = q; return 0; |
